scrivito_editors 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +48 -0
  3. data/LICENSE +4 -0
  4. data/README.md +55 -0
  5. data/Rakefile +9 -0
  6. data/app/assets/fonts/editing_icons-webfont.eot +0 -0
  7. data/app/assets/fonts/editing_icons-webfont.ttf +0 -0
  8. data/app/assets/fonts/editing_icons-webfont.woff +0 -0
  9. data/app/assets/javascripts/jquery_additions/jquery_center.js.coffee +6 -0
  10. data/app/assets/javascripts/mediabrowser/inspector.js.coffee +65 -0
  11. data/app/assets/javascripts/mediabrowser/mediabrowser.js.coffee +420 -0
  12. data/app/assets/javascripts/mediabrowser/uploader.js.coffee +132 -0
  13. data/app/assets/javascripts/scrivito_editors.js +18 -0
  14. data/app/assets/javascripts/scrivito_editors/date_editor.js.coffee +50 -0
  15. data/app/assets/javascripts/scrivito_editors/enum_editor.js.coffee +36 -0
  16. data/app/assets/javascripts/scrivito_editors/html_editor.js.coffee +140 -0
  17. data/app/assets/javascripts/scrivito_editors/linklist_editor.js.coffee +176 -0
  18. data/app/assets/javascripts/scrivito_editors/multienum_editor.js.coffee +37 -0
  19. data/app/assets/javascripts/scrivito_editors/placeholder.js.coffee +22 -0
  20. data/app/assets/javascripts/scrivito_editors/reference_editor.js.coffee +28 -0
  21. data/app/assets/javascripts/scrivito_editors/referencelist_editor.js.coffee +111 -0
  22. data/app/assets/javascripts/scrivito_editors/slider_editor.js.coffee +39 -0
  23. data/app/assets/javascripts/scrivito_editors/string_editor.js.coffee +83 -0
  24. data/app/assets/javascripts/scrivito_editors/text_editor.js.coffee +85 -0
  25. data/app/assets/stylesheets/scrivito_editors.css +16 -0
  26. data/app/assets/stylesheets/scrivito_editors/buttons.css +161 -0
  27. data/app/assets/stylesheets/scrivito_editors/editors/linklist_editor.css +105 -0
  28. data/app/assets/stylesheets/scrivito_editors/editors/referencelist_editor.css +67 -0
  29. data/app/assets/stylesheets/scrivito_editors/editors/text_editor.css +7 -0
  30. data/app/assets/stylesheets/scrivito_editors/icons.css.erb +229 -0
  31. data/app/assets/stylesheets/scrivito_editors/mediabrowser.css +1010 -0
  32. data/app/assets/stylesheets/scrivito_editors/placeholder.css +17 -0
  33. data/app/assets/stylesheets/scrivito_editors/widget_preview.css +38 -0
  34. data/app/controllers/scrivito_editors/mediabrowser_controller.rb +36 -0
  35. data/app/views/layouts/scrivito_editors/mediabrowser/inspector.html.erb +11 -0
  36. data/app/views/scrivito_editors/mediabrowser/_buttons.html.erb +16 -0
  37. data/app/views/scrivito_editors/mediabrowser/_header.html.erb +25 -0
  38. data/app/views/scrivito_editors/mediabrowser/modal.html.erb +12 -0
  39. data/app/views/scrivito_editors/obj/details.html +5 -0
  40. data/config/initializers/mediabrowser.rb +13 -0
  41. data/config/routes.rb +5 -0
  42. data/lib/scrivito_editors.rb +4 -0
  43. data/lib/scrivito_editors/engine.rb +7 -0
  44. data/lib/scrivito_editors/version.rb +3 -0
  45. data/spec/dummy/README.rdoc +28 -0
  46. data/spec/dummy/Rakefile +6 -0
  47. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  48. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  49. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  50. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  51. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  52. data/spec/dummy/bin/bundle +3 -0
  53. data/spec/dummy/bin/rails +4 -0
  54. data/spec/dummy/bin/rake +4 -0
  55. data/spec/dummy/config.ru +4 -0
  56. data/spec/dummy/config/application.rb +23 -0
  57. data/spec/dummy/config/boot.rb +5 -0
  58. data/spec/dummy/config/database.yml +25 -0
  59. data/spec/dummy/config/environment.rb +5 -0
  60. data/spec/dummy/config/environments/development.rb +29 -0
  61. data/spec/dummy/config/environments/production.rb +80 -0
  62. data/spec/dummy/config/environments/test.rb +36 -0
  63. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  64. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  65. data/spec/dummy/config/initializers/inflections.rb +16 -0
  66. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  67. data/spec/dummy/config/initializers/secret_token.rb +12 -0
  68. data/spec/dummy/config/initializers/session_store.rb +3 -0
  69. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  70. data/spec/dummy/config/locales/en.yml +23 -0
  71. data/spec/dummy/config/routes.rb +4 -0
  72. data/spec/dummy/public/404.html +58 -0
  73. data/spec/dummy/public/422.html +58 -0
  74. data/spec/dummy/public/500.html +57 -0
  75. data/spec/dummy/public/favicon.ico +0 -0
  76. data/spec/spec_helper.rb +13 -0
  77. data/vendor/assets/fonts/redactor-font.eot +0 -0
  78. data/vendor/assets/javascripts/jquery-ui-timepicker-addon.min.js +5 -0
  79. data/vendor/assets/javascripts/redactor.js +7869 -0
  80. data/vendor/assets/stylesheets/jquery-ui-timepicker-addon.min.css +5 -0
  81. data/vendor/assets/stylesheets/redactor.css.erb +968 -0
  82. metadata +240 -0
@@ -0,0 +1,132 @@
1
+ @MediabrowserUploader = do ->
2
+ dropZoneSelector: '.editing-mediabrowser-items'
3
+ dropOverCssClass: 'uploader-drag-over'
4
+ mimeTypeMapping:
5
+ 'image/*': 'Image'
6
+ 'video/*': 'Video'
7
+
8
+ _initializeBindings: ->
9
+ dropZone = @modal.find(@dropZoneSelector)
10
+
11
+ dropZone.on 'dragover', (event) =>
12
+ $(event.currentTarget).addClass(@dropOverCssClass)
13
+ event.preventDefault()
14
+
15
+ dropZone.on 'dragleave', (event) =>
16
+ $(event.currentTarget).removeClass(@dropOverCssClass)
17
+ event.preventDefault()
18
+
19
+ dropZone.on 'drop', (event) =>
20
+ $(event.currentTarget).removeClass(@dropOverCssClass)
21
+ @_onDrop(event)
22
+ event.preventDefault()
23
+
24
+ _objClassForMimeType: (mimeType) ->
25
+ for mime, objClass of @mimeTypeMapping
26
+ return objClass if mimeType.match(mime)
27
+
28
+ undefined
29
+
30
+ _processQueue: (queue, createdObjs, promise) ->
31
+ promise.then (data) =>
32
+ @onUploadSuccess(data)
33
+
34
+ file = queue.pop()
35
+
36
+ if file?
37
+ @_createResource(file).then (obj) =>
38
+ @_updateProgress(file, '100%')
39
+ createdObjs.push(obj)
40
+ .always =>
41
+ @_processQueue(queue, createdObjs, promise)
42
+
43
+ return promise
44
+ else
45
+ return promise.resolve(createdObjs)
46
+
47
+ _addProgressWrapper: () ->
48
+ itemsElement = $('.editing-mediabrowser-items').empty()
49
+
50
+ $('<div></div>')
51
+ .addClass('editing-mediabrowser-loading')
52
+ .appendTo itemsElement
53
+
54
+ $('<div></div>')
55
+ .addClass('editing-mediabrowser-progress-wrapper')
56
+ .appendTo itemsElement
57
+
58
+ _addProgress: (file) ->
59
+ progressBar = $('<div></div>')
60
+ .addClass('editing-mediabrowser-progress-bar')
61
+ .css('width', '10%')
62
+
63
+ progress = $('<div></div>')
64
+ .addClass('editing-mediabrowser-progress')
65
+ .html(progressBar)
66
+
67
+ fileName = $('<p></p>')
68
+ .html(file.name)
69
+
70
+ $('<div></div>')
71
+ .addClass('editing-mediabrowser-progress-file')
72
+ .append(fileName)
73
+ .append(progress)
74
+ .prependTo $('.editing-mediabrowser-progress-wrapper')
75
+
76
+ file['progressBar'] = progressBar
77
+
78
+ _updateProgress: (file, percent) ->
79
+ file.progressBar.css('width', percent)
80
+
81
+ _onDrop: (event) ->
82
+ dataTransfer = event.originalEvent.dataTransfer
83
+
84
+ unless dataTransfer?
85
+ return
86
+
87
+ files = dataTransfer.files
88
+
89
+ if files.length == 0
90
+ return
91
+
92
+ @onUploadStart(queue)
93
+ @_addProgressWrapper()
94
+
95
+ promise = $.Deferred()
96
+
97
+ queue = for file in files
98
+ @_addProgress(file)
99
+ file
100
+
101
+ @_processQueue(queue, [], promise)
102
+
103
+ promise
104
+
105
+ _randomResourceId: ->
106
+ hex = Math.floor(Math.random() * Math.pow(16, 8)).toString(16)
107
+
108
+ while (hex.length < 8)
109
+ hex = '0' + hex
110
+
111
+ hex
112
+
113
+ _createResource: (file) ->
114
+ objName = file.name.replace(/[^a-z0-9_.$\-]/ig, '-')
115
+ path = "_resources/#{@_randomResourceId()}/#{objName}"
116
+
117
+ scrivito.create_obj
118
+ blob: file
119
+ _path: path
120
+ _obj_class: @_objClassForMimeType(file.type)
121
+
122
+ init: (@modal) ->
123
+ @_initializeBindings()
124
+
125
+ # Hook for 3rd parties when the upload starts.
126
+ onUploadStart: (files) ->
127
+
128
+ # Hook for 3rd parties when the upload fails.
129
+ onUploadFailure: (error) ->
130
+
131
+ # Hook for 3rd parties when the upload was successful.
132
+ onUploadSuccess: (objs) ->
@@ -0,0 +1,18 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file.
9
+ //
10
+ // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require jquery.ui.sortable
14
+ //= require jquery.ui.datepicker
15
+ //= require jquery.ui.slider
16
+ //= require jquery-ui-timepicker-addon.min
17
+ //= require redactor
18
+ //= require_tree .
@@ -0,0 +1,50 @@
1
+ $ ->
2
+ # Define editor behavior for date attributes.
3
+
4
+ scrivito.on 'editing', ->
5
+ template = ->
6
+ $('<input />')
7
+ .attr('type', 'text')
8
+
9
+ onKeyup = (event) ->
10
+ key = event.keyCode || event.which
11
+
12
+ switch key
13
+ when 27 # Esc
14
+ # Prevent the property view to close when the ESC key is pressed.
15
+ event.stopPropagation()
16
+
17
+ save = (dateTimeText) ->
18
+ element = $(this)
19
+ cmsField = element.data('cmsField')
20
+
21
+ if dateTimeText? && dateTimeText.length > 0
22
+ dateTimeText = new Date(dateTimeText)
23
+
24
+ cmsField.scrivito('save', dateTimeText)
25
+ .done ->
26
+ cmsField.trigger('scrivito_reload')
27
+
28
+ $('body').on 'click', '[data-scrivito-field-type="date"]:not(.hasDatepicker):not([data-editor]), [data-editor="date"]', (event) ->
29
+ event.preventDefault()
30
+
31
+ cmsField = $(this)
32
+ content = cmsField.html().trim()
33
+ dateFormat = cmsField.attr('data-date-format') || 'yy-mm-dd'
34
+ timeFormat = cmsField.attr('data-time-format') || 'HH:mm:ss'
35
+
36
+ $('body').keyup(onKeyup)
37
+
38
+ template()
39
+ .data('cmsField', cmsField)
40
+ .insertAfter(cmsField)
41
+ .val(content)
42
+ .keyup(onKeyup)
43
+ .datetimepicker(
44
+ dateFormat: dateFormat
45
+ timeFormat: timeFormat
46
+ onClose: save
47
+ )
48
+ .focus()
49
+
50
+ cmsField.hide()
@@ -0,0 +1,36 @@
1
+ $ ->
2
+ # Define editor behavior for enum attributes.
3
+
4
+ scrivito.on 'editing', ->
5
+ template = (values) ->
6
+ element = $('<select></select>')
7
+ .addClass('form-control')
8
+
9
+ $.each values, (index, value) ->
10
+ $('<option></option>')
11
+ .attr('value', value)
12
+ .text(value)
13
+ .appendTo(element)
14
+
15
+ element
16
+
17
+ save = (event) ->
18
+ element = $(event.currentTarget)
19
+ cmsField = element.data('cmsField')
20
+ content = element.val()
21
+ cmsField.scrivito('save', content).done ->
22
+ cmsField.trigger('scrivito_reload')
23
+
24
+ $(document).on 'click', '[data-scrivito-field-type="enum"]:not([data-editor]), [data-editor="enum"]', (event) ->
25
+ cmsField = $(event.currentTarget)
26
+ selected = cmsField.scrivito('content')
27
+ values = cmsField.data('values')
28
+
29
+ template(values)
30
+ .data('cmsField', cmsField)
31
+ .val(selected)
32
+ .insertAfter(cmsField)
33
+ .focusout(save)
34
+ .focus()
35
+
36
+ cmsField.hide()
@@ -0,0 +1,140 @@
1
+ $ ->
2
+ # Configuration and behavior of Redactor html editor. The editor is used for all html CMS
3
+ # attributes and provides autosave, undo and redo functionality on top of the default Redactor
4
+ # settings.
5
+
6
+ # Stores the id of the last triggered timeout for later reference.
7
+ timeoutID = undefined
8
+
9
+ # Milliseconds after which to save the content automatically.
10
+ autosaveInterval = 3000
11
+
12
+ # Stores the last saved content written to the CMS to allow comparison with editor content.
13
+ savedContent = undefined
14
+
15
+ # Stores the content before the editor is opened and changes occur.
16
+ originalContent = undefined
17
+
18
+ # Stores redactor options, custom settings and configures callbacks.
19
+ redactorOptions = ->
20
+ return {} =
21
+ # This option allows you to add your own buttons with callback to the toolbar.
22
+ # http://imperavi.com/redactor/docs/settings/#set-buttonsCustom
23
+ buttonsCustom:
24
+ undoButton:
25
+ title: 'Undo'
26
+ callback: undoAction
27
+ redoButton:
28
+ title: 'Redo'
29
+ callback: redoAction
30
+
31
+ # This setting defines the array of toolbar buttons.
32
+ # http://imperavi.com/redactor/docs/settings/#set-buttons
33
+ buttons: ['undoButton', 'redoButton',
34
+ '|', 'formatting',
35
+ '|', 'bold', 'italic', 'deleted', 'underline',
36
+ '|', 'unorderedlist', 'orderedlist',
37
+ '|', 'table', 'link',
38
+ '|', 'html'
39
+ ]
40
+
41
+ # This option allows you to set whether Redactor gets cursor focus on load or not.
42
+ # http://imperavi.com/redactor/docs/settings/#set-focus
43
+ focus: true
44
+
45
+ # With this option turned on, Redactor will automatically replace divs to paragraphs.
46
+ # http://imperavi.com/redactor/docs/settings/#set-convertDivs
47
+ convertDivs: false
48
+
49
+ # This callback is triggered after Redactor is launched.
50
+ # http://imperavi.com/redactor/docs/callbacks/#callback-initCallback
51
+ initCallback: ->
52
+ originalContent = @get()
53
+
54
+ # This callback fires every time when content changes in Redactor.
55
+ # http://imperavi.com/redactor/docs/callbacks/#callback-changeCallback
56
+ changeCallback: ->
57
+ autosaveAction(@)
58
+
59
+ # This callback is triggered when Redactor loses focus.
60
+ # http://imperavi.com/redactor/docs/callbacks/#callback-blurCallback
61
+ blurCallback: ->
62
+ saveContents(@).done =>
63
+ @.destroy()
64
+ reload(@)
65
+
66
+ # This callback is triggered when a key is released.
67
+ # http://imperavi.com/redactor/docs/callbacks/#callback-keyupCallback
68
+ keyupCallback: (event) ->
69
+ event.stopPropagation()
70
+ key = event.keyCode || event.which
71
+
72
+ if key == 27
73
+ cancelEditing(@)
74
+ else
75
+ autosaveAction(@)
76
+
77
+ # This callback allows to get pasted code after clean on paste.
78
+ # http://imperavi.com/redactor/docs/callbacks/#callback-pasteAfterCallback
79
+ pasteAfterCallback: (html) ->
80
+ autosaveAction(@)
81
+ html
82
+
83
+ # Registers a timeout to save the editor content after a certain interval. The timeout gets reset
84
+ # on every change.
85
+ autosaveAction = (editor) ->
86
+ if timeoutID
87
+ clearTimeout(timeoutID)
88
+
89
+ timeoutID = setTimeout ( ->
90
+ saveContents(editor)
91
+ ), autosaveInterval
92
+
93
+ undoAction = ->
94
+ @execCommand('undo')
95
+
96
+ redoAction = ->
97
+ @execCommand('redo')
98
+
99
+ # Saves the current editor content to the CMS if it has changed.
100
+ saveContents = (editor) ->
101
+ content = editor.get()
102
+
103
+ if savedContent != content
104
+ cmsField = editor.$element
105
+ cmsField.scrivito('save', content).done ->
106
+ savedContent = content
107
+
108
+ else
109
+ $.Deferred().resolve()
110
+
111
+ reload = (editor) ->
112
+ cmsField = editor.$element
113
+ cmsField.trigger('scrivito_reload')
114
+
115
+ # Restores the original content before the editor was opened, also saves it back to the CMS
116
+ # because autosave could have overwritten the content and closes the editor.
117
+ cancelEditing = (editor) ->
118
+ editor.set(originalContent)
119
+ saveContents(editor).done ->
120
+ reload(editor)
121
+ editor.destroy()
122
+
123
+ # Registers Redactor for all CMS html attributes found in the given scope of the DOM element.
124
+ addOnclickRedactorHandlers = (domElement) ->
125
+ domElement.on 'click', '[data-scrivito-field-type="html"]:not([data-editor]), [data-editor="html"]', (event) ->
126
+ event.preventDefault()
127
+ cmsField = $(@)
128
+
129
+ unless cmsField.hasClass('redactor_editor')
130
+ cmsField.html(cmsField.scrivito('content') || '')
131
+ cmsField.redactor(redactorOptions())
132
+ cmsField.redactor('focus')
133
+
134
+ # Registers all handlers when inplace editing is activated.
135
+ scrivito.on 'editing', ->
136
+ addOnclickRedactorHandlers($('body'))
137
+
138
+ # Registers all handlers when content has changed.
139
+ scrivito.on 'new_content', (domElement) ->
140
+ addOnclickRedactorHandlers($(domElement))
@@ -0,0 +1,176 @@
1
+ $ ->
2
+ # An editor for CMS linklist attributes.
3
+
4
+ # Creates the DOM for one link element of the linklist and substitutes the
5
+ # title and url attribute.
6
+ template = (attributes) ->
7
+ attributes ||= {}
8
+
9
+ title = attributes['title'] || ''
10
+ url = attributes['url'] || ''
11
+
12
+ $("<input type=\"text\" name=\"title\" value=\"#{title}\" placeholder=\"Title\" />
13
+ <input type=\"text\" name=\"url\" value=\"#{url}\" placeholder=\"Url\" class=\"editing-url\" />
14
+ <div class=\"actions\">
15
+ <a href=\"#\" class=\"editing-button mediabrowser-open editing-green\">
16
+ <i class=\"editing-icon editing-icon-search\" />
17
+ </a>
18
+ <a href=\"#\" class=\"editing-button editing-red delete\">
19
+ <i class=\"editing-icon editing-icon-trash\" />
20
+ </a>
21
+ </div>")
22
+
23
+ mediabrowserButtonTemplate = ->
24
+ icon = $('<i></i>')
25
+ .addClass('editing-icon')
26
+ .addClass('editing-icon-plus')
27
+
28
+ button = $('<button></button>')
29
+ .addClass('editing-button')
30
+ .addClass('editing-green')
31
+ .addClass('add-link')
32
+ .html(icon)
33
+
34
+ button
35
+
36
+ # Returns the closest linklist DOM element.
37
+ getCmsField = (element) ->
38
+ element.closest('[data-scrivito-field-type=linklist]')
39
+
40
+ # Saves the entire linklist to the CMS and stores the last successfully saved value.
41
+ save = (cmsField) ->
42
+ value = getAttributes(cmsField)
43
+ lastSaved = getLastSaved(cmsField)
44
+
45
+ unless JSON.stringify(value) == JSON.stringify(lastSaved)
46
+ cmsField.scrivito('save', value).done ->
47
+ storeLastSaved(cmsField, value)
48
+
49
+ # Run when clicking the '...' button inside a li.
50
+ onOpenMediabrowser = (event) ->
51
+ event.preventDefault()
52
+
53
+ linkItem = $(event.currentTarget).closest('li')
54
+ cmsField = getCmsField(linkItem)
55
+ filters = cmsField.data('filters') || cmsField.data('filter')
56
+
57
+ Mediabrowser.open
58
+ selection: []
59
+ filters: filters
60
+ onSave: (selection) =>
61
+ onMediabrowserSaveLinkItem(selection, linkItem)
62
+
63
+ # Media browser callback for saving a single link.
64
+ onMediabrowserSaveLinkItem = (selection, linkItem) ->
65
+ url = buildUrl(selection[0])
66
+ linkItem.find('[name=url]').val(url)
67
+
68
+ # trigger save after inserting the value
69
+ cmsField = getCmsField(linkItem)
70
+ save(cmsField)
71
+
72
+ true
73
+
74
+ # Transforms an obj id into an url that can be parsed by Scrivito
75
+ # to establish an internal link.
76
+ buildUrl = (id) ->
77
+ "/#{id}"
78
+
79
+ # Collects all link attributes for a given linklist.
80
+ getAttributes = (cmsField) ->
81
+ items = $(cmsField).find('li')
82
+
83
+ attributes =
84
+ for item in items
85
+ item = $(item)
86
+ title = item.find('[name=title]').val()
87
+ url = item.find('[name=url]').val()
88
+
89
+ # Make sure the url is not empty.
90
+ if !isEmpty(url)
91
+ 'title': title
92
+ 'url': url
93
+
94
+ # Remove empty array elements.
95
+ removeEmptyElements(attributes)
96
+
97
+ isEmpty = (value) ->
98
+ !value
99
+
100
+ removeEmptyElements = (array) ->
101
+ $.grep(array, (n) -> n)
102
+
103
+ # Adds a new link to the linklist.
104
+ addLink = (event) ->
105
+ event.preventDefault()
106
+
107
+ cmsField = getCmsField($(event.currentTarget))
108
+ content = $('<li>').html(template())
109
+
110
+ cmsField.find('ul').append(content)
111
+
112
+ # Removes a link from the linklist.
113
+ removeLink = (event) ->
114
+ event.preventDefault()
115
+
116
+ target = $(event.currentTarget)
117
+ cmsField = getCmsField(target)
118
+
119
+ target.closest('li').remove()
120
+ save(cmsField)
121
+
122
+ # Turns the server side generated linklist data into the linklist editor using a template.
123
+ transformLinks = (cmsFields) ->
124
+ cmsFields.append(mediabrowserButtonTemplate)
125
+
126
+ items = cmsFields.find('li')
127
+
128
+ for item in items
129
+ item = $(item)
130
+
131
+ content = template
132
+ title: item.data('title')
133
+ url: item.data('url')
134
+
135
+ item.html(content)
136
+
137
+ # Returns the last saved value.
138
+ getLastSaved = (cmsField) ->
139
+ cmsField.data('last-saved')
140
+
141
+ # Stores a given value as last saved.
142
+ storeLastSaved = (cmsField, value) ->
143
+ $(cmsField).data('last-saved', value)
144
+
145
+ # Automatically save when focus is lost.
146
+ onBlur = (event) ->
147
+ cmsField = getCmsField($(event.currentTarget))
148
+
149
+ save(cmsField)
150
+
151
+ initialize = (root) ->
152
+ linklistElements = $(root).find('[data-scrivito-field-type="linklist"]:not([data-editor]), [data-editor="linklist"]')
153
+
154
+ if linklistElements.length
155
+ transformLinks(linklistElements)
156
+
157
+ for linklistElement in linklistElements
158
+ storeLastSaved(linklistElement, getAttributes(linklistElement))
159
+
160
+ linklistElements.on 'blur', 'li input', onBlur
161
+ linklistElements.on 'click', 'li a.delete', removeLink
162
+ linklistElements.on 'click', 'button.add-link', addLink
163
+ linklistElements.on 'click', 'a.mediabrowser-open', onOpenMediabrowser
164
+
165
+ linklistElements.find('ul').sortable
166
+ update: (event) ->
167
+ cmsField = getCmsField($(event.target))
168
+
169
+ save(cmsField)
170
+
171
+ # Initialize linklist editor and setup event callbacks.
172
+ scrivito.on 'new_content', (root) ->
173
+ initialize(root)
174
+
175
+ scrivito.on 'editing', ->
176
+ initialize(document)