scrivito_editors 0.0.8
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +48 -0
- data/LICENSE +4 -0
- data/README.md +55 -0
- data/Rakefile +9 -0
- data/app/assets/fonts/editing_icons-webfont.eot +0 -0
- data/app/assets/fonts/editing_icons-webfont.ttf +0 -0
- data/app/assets/fonts/editing_icons-webfont.woff +0 -0
- data/app/assets/javascripts/jquery_additions/jquery_center.js.coffee +6 -0
- data/app/assets/javascripts/mediabrowser/inspector.js.coffee +65 -0
- data/app/assets/javascripts/mediabrowser/mediabrowser.js.coffee +420 -0
- data/app/assets/javascripts/mediabrowser/uploader.js.coffee +132 -0
- data/app/assets/javascripts/scrivito_editors.js +18 -0
- data/app/assets/javascripts/scrivito_editors/date_editor.js.coffee +50 -0
- data/app/assets/javascripts/scrivito_editors/enum_editor.js.coffee +36 -0
- data/app/assets/javascripts/scrivito_editors/html_editor.js.coffee +140 -0
- data/app/assets/javascripts/scrivito_editors/linklist_editor.js.coffee +176 -0
- data/app/assets/javascripts/scrivito_editors/multienum_editor.js.coffee +37 -0
- data/app/assets/javascripts/scrivito_editors/placeholder.js.coffee +22 -0
- data/app/assets/javascripts/scrivito_editors/reference_editor.js.coffee +28 -0
- data/app/assets/javascripts/scrivito_editors/referencelist_editor.js.coffee +111 -0
- data/app/assets/javascripts/scrivito_editors/slider_editor.js.coffee +39 -0
- data/app/assets/javascripts/scrivito_editors/string_editor.js.coffee +83 -0
- data/app/assets/javascripts/scrivito_editors/text_editor.js.coffee +85 -0
- data/app/assets/stylesheets/scrivito_editors.css +16 -0
- data/app/assets/stylesheets/scrivito_editors/buttons.css +161 -0
- data/app/assets/stylesheets/scrivito_editors/editors/linklist_editor.css +105 -0
- data/app/assets/stylesheets/scrivito_editors/editors/referencelist_editor.css +67 -0
- data/app/assets/stylesheets/scrivito_editors/editors/text_editor.css +7 -0
- data/app/assets/stylesheets/scrivito_editors/icons.css.erb +229 -0
- data/app/assets/stylesheets/scrivito_editors/mediabrowser.css +1010 -0
- data/app/assets/stylesheets/scrivito_editors/placeholder.css +17 -0
- data/app/assets/stylesheets/scrivito_editors/widget_preview.css +38 -0
- data/app/controllers/scrivito_editors/mediabrowser_controller.rb +36 -0
- data/app/views/layouts/scrivito_editors/mediabrowser/inspector.html.erb +11 -0
- data/app/views/scrivito_editors/mediabrowser/_buttons.html.erb +16 -0
- data/app/views/scrivito_editors/mediabrowser/_header.html.erb +25 -0
- data/app/views/scrivito_editors/mediabrowser/modal.html.erb +12 -0
- data/app/views/scrivito_editors/obj/details.html +5 -0
- data/config/initializers/mediabrowser.rb +13 -0
- data/config/routes.rb +5 -0
- data/lib/scrivito_editors.rb +4 -0
- data/lib/scrivito_editors/engine.rb +7 -0
- data/lib/scrivito_editors/version.rb +3 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +23 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +29 -0
- data/spec/dummy/config/environments/production.rb +80 -0
- data/spec/dummy/config/environments/test.rb +36 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +12 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +4 -0
- data/spec/dummy/public/404.html +58 -0
- data/spec/dummy/public/422.html +58 -0
- data/spec/dummy/public/500.html +57 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/spec_helper.rb +13 -0
- data/vendor/assets/fonts/redactor-font.eot +0 -0
- data/vendor/assets/javascripts/jquery-ui-timepicker-addon.min.js +5 -0
- data/vendor/assets/javascripts/redactor.js +7869 -0
- data/vendor/assets/stylesheets/jquery-ui-timepicker-addon.min.css +5 -0
- data/vendor/assets/stylesheets/redactor.css.erb +968 -0
- 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)
|