effective_ckeditor 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/assets/javascripts/ckeditor/plugins/effective_menus/plugin.js.coffee +543 -0
- data/app/assets/javascripts/ckeditor/plugins/effective_regions/plugin.js.coffee +11 -5
- data/app/assets/javascripts/effective_ckeditor/config.js.coffee +1 -1
- data/app/assets/stylesheets/effective_ckeditor/menu_editor.css.scss +64 -0
- data/app/assets/stylesheets/effective_ckeditor.css.scss +1 -0
- data/lib/effective_ckeditor/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6abe1be063968942b886583f1efd66fa0922f8ad
|
4
|
+
data.tar.gz: f212652bc21b13501653bec315ba1f86fdf832f6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7cff60407f65c4b8423ef6ac68fd22df8dfd9c34e9156d90e52a027c3d547c2b01f471e2df3b4baef32dc6abbcd793549eabb4eec0a780062e9eb27f3ac91081
|
7
|
+
data.tar.gz: e4dfd0bb28ece84c558e37f782943569676447e5c9eede0bd74f271bbd78e2a29c8d6f0fc4b2f693cf30384be9861e304769628c197f456aae7b20a2145ad5b2
|
@@ -0,0 +1,543 @@
|
|
1
|
+
# OKAY THE TOP OF THIS IS A JQUERY PLUGIN
|
2
|
+
# When the CKEditor plugin below is initialized via an /edit/ route
|
3
|
+
# it initialized this jquery on any $(.effective-menu) objects present on the page
|
4
|
+
|
5
|
+
(($, window) ->
|
6
|
+
class EffectiveMenuEditor
|
7
|
+
defaults:
|
8
|
+
menuClass: 'effective-menu'
|
9
|
+
expandThreshold: 250 # Seconds before a leaf li item will be auto-expanded into a dropdown
|
10
|
+
maxDepth: 9999
|
11
|
+
|
12
|
+
menu: null
|
13
|
+
draggable: null
|
14
|
+
droppable: null
|
15
|
+
|
16
|
+
constructor: (el, options) ->
|
17
|
+
@menu = $(el)
|
18
|
+
|
19
|
+
@options = $.extend({}, @defaults, options)
|
20
|
+
@options.maxDepth = @menu.data('effective-menu-maxdepth') if @menu.data('effective-menu-maxdepth')
|
21
|
+
|
22
|
+
@initCkEditorEvents()
|
23
|
+
@initAddButtonEvents()
|
24
|
+
@initDestroyButtonEvents()
|
25
|
+
@initDragDropEvents()
|
26
|
+
@initAdditionalEvents()
|
27
|
+
true
|
28
|
+
|
29
|
+
# All 3 of these events are basically the same:
|
30
|
+
# Remove the open class and
|
31
|
+
|
32
|
+
initCkEditorEvents: ->
|
33
|
+
# Oh yea, you like that selector. The comma means or, so there are actually 3 selectors being created here
|
34
|
+
# They need to be in this order for the stopPropagation to work properly.
|
35
|
+
@menu.on 'click', 'li.dropdown.open,.dropdown-menu > li.dropdown,li:not(.dropdown)', (event) =>
|
36
|
+
event.stopPropagation()
|
37
|
+
@menu.find('.open').removeClass('open')
|
38
|
+
# Open the CKEDITOR dialog and pass the source effective_menu_item $('li') to the dialog
|
39
|
+
CKEDITOR.instances[Object.keys(CKEDITOR.instances)[0]].openDialog 'effectiveMenusDialog', (dialog) ->
|
40
|
+
dialog.effective_menu_item = $(event.currentTarget)
|
41
|
+
|
42
|
+
initAddButtonEvents: ->
|
43
|
+
@menu.on 'mouseenter', (event) => @menu.children('.actions').children('.add-item').show()
|
44
|
+
@menu.on 'mouseleave', (event) => @menu.children('.actions').children('.add-item').hide()
|
45
|
+
@menu.on 'mouseenter', '.add-item', (event) -> $(event.currentTarget).addClass('large') ; event.stopPropagation()
|
46
|
+
@menu.on 'mouseleave', '.add-item', (event) -> $(event.currentTarget).removeClass('large'); event.stopPropagation()
|
47
|
+
|
48
|
+
@menu.on 'click', '.add-item', (event) =>
|
49
|
+
event.preventDefault()
|
50
|
+
unique_id = new Date().getTime()
|
51
|
+
item = $(@menu.data('effective-menu-new-html').replace(':new', "#{unique_id}", 'g'))
|
52
|
+
|
53
|
+
@menu.children('.actions').before(item)
|
54
|
+
|
55
|
+
CKEDITOR.instances[Object.keys(CKEDITOR.instances)[0]].openDialog 'effectiveMenusDialog', (dialog) ->
|
56
|
+
dialog.effective_menu_item = item
|
57
|
+
|
58
|
+
initDestroyButtonEvents: ->
|
59
|
+
@menu.on 'dragenter', '.remove-item', (event) => $(event.currentTarget).addClass('large')
|
60
|
+
@menu.on 'dragleave', '.remove-item', (event) => $(event.currentTarget).removeClass('large')
|
61
|
+
|
62
|
+
@menu.on 'dragover', '.remove-item', (event) =>
|
63
|
+
return false unless @draggable
|
64
|
+
glyphicon = $(event.currentTarget).addClass('large') # Garbage can
|
65
|
+
|
66
|
+
event.preventDefault() # Enable drag and drop
|
67
|
+
event.stopPropagation()
|
68
|
+
|
69
|
+
@menu.on 'drop', '.remove-item', (event) =>
|
70
|
+
return false unless @draggable
|
71
|
+
glyphicon = $(event.currentTarget).removeClass('large')
|
72
|
+
|
73
|
+
@markDestroyed(@draggable)
|
74
|
+
|
75
|
+
@cleanupMenuAfterDrop()
|
76
|
+
event.stopPropagation()
|
77
|
+
event.preventDefault()
|
78
|
+
|
79
|
+
initAdditionalEvents: ->
|
80
|
+
@menu.on 'click', 'a', (event) -> event.preventDefault()
|
81
|
+
|
82
|
+
initDragDropEvents: ->
|
83
|
+
@menu.on 'dragenter', 'li', (event) =>
|
84
|
+
@droppable = null
|
85
|
+
@droppable = {item: $(event.currentTarget), time: new Date().getTime()}
|
86
|
+
event.preventDefault() if @draggable # enable drag and drop
|
87
|
+
|
88
|
+
@menu.on 'dragleave', 'li', (event) =>
|
89
|
+
event.preventDefault() if @draggable # enable drag and drop
|
90
|
+
|
91
|
+
@menu.on 'dragstart', 'li', (event) =>
|
92
|
+
@draggable = item = $(event.currentTarget)
|
93
|
+
@menu.children('.actions').children('.add-item').hide()
|
94
|
+
item.removeClass('open').find('.open').removeClass('open')
|
95
|
+
|
96
|
+
event.originalEvent.dataTransfer.setData('text/html', item[0].outerHTML)
|
97
|
+
|
98
|
+
item.css('opacity', '0.4') # Show it slightly removed from the DOM
|
99
|
+
@menu.addClass('dragging')
|
100
|
+
event.stopPropagation()
|
101
|
+
|
102
|
+
@menu.on 'dragover', 'li', (event) =>
|
103
|
+
return false unless @draggable
|
104
|
+
|
105
|
+
item = $(event.currentTarget)
|
106
|
+
|
107
|
+
if item.hasClass('dropdown') && !item.hasClass('open') # This is a menu, expand it
|
108
|
+
@menu.find('.open').removeClass('open')
|
109
|
+
item.parentsUntil(@menu, 'li').andSelf().addClass('open')
|
110
|
+
else
|
111
|
+
event.preventDefault() # Enable drag and drop
|
112
|
+
@expandToDropdown(item) if (new Date().getTime()) > @options.expandThreshold + (@droppable.time || 0)
|
113
|
+
|
114
|
+
# If I don't have the placeholder class already
|
115
|
+
if item.hasClass('placeholder') == false
|
116
|
+
@menu.find('.placeholder').removeClass('placeholder')
|
117
|
+
item.addClass('placeholder')
|
118
|
+
|
119
|
+
event.stopPropagation()
|
120
|
+
|
121
|
+
@menu.on 'dragend', 'li', (event) =>
|
122
|
+
return false unless @draggable
|
123
|
+
item = $(event.currentTarget)
|
124
|
+
|
125
|
+
@cleanupMenuAfterDrop()
|
126
|
+
item.css('opacity', '1.0')
|
127
|
+
|
128
|
+
@menu.on 'drop', 'li', (event) =>
|
129
|
+
item = $(event.currentTarget)
|
130
|
+
|
131
|
+
# Don't allow to drop into myself or my own children
|
132
|
+
return false if !@draggable? || @draggable.is(item) || @draggable.find(item).length > 0
|
133
|
+
|
134
|
+
new_item = $(event.originalEvent.dataTransfer.getData('text/html'))
|
135
|
+
|
136
|
+
item.before(new_item)
|
137
|
+
@draggable.remove()
|
138
|
+
|
139
|
+
@cleanupMenuAfterDrop()
|
140
|
+
@cleanupItemAfterDrop(new_item)
|
141
|
+
|
142
|
+
event.stopPropagation()
|
143
|
+
event.preventDefault()
|
144
|
+
|
145
|
+
# If it hasn't already been expanded...
|
146
|
+
# Append some html to make it into a faux dropdown
|
147
|
+
# which is just a ul.dropdown-menu > li
|
148
|
+
expandToDropdown: (item) ->
|
149
|
+
return false if item.hasClass('dropdown') || item.hasClass('effective-menu-expand')
|
150
|
+
return false if @depthOf(item) >= @options.maxDepth
|
151
|
+
|
152
|
+
item.append(@menu.data('effective-menu-expand-html'))
|
153
|
+
item.addClass('dropdown')
|
154
|
+
item.children('a').attr('data-toggle', 'dropdown')
|
155
|
+
|
156
|
+
# If I'm a top level dropdown
|
157
|
+
if item.parent().hasClass('effective-menu')
|
158
|
+
item.children('a').append("<span class='caret'></span>")
|
159
|
+
|
160
|
+
@menu.find('.open').removeClass('open')
|
161
|
+
item.parentsUntil(@menu, 'li.dropdown').addClass('open')
|
162
|
+
|
163
|
+
cleanupMenuAfterDrop: ->
|
164
|
+
# chrome puts in a weird meta tag that firefox doesnt
|
165
|
+
# so we delete it here
|
166
|
+
@menu.find('meta,li.effective-menu-expand').remove()
|
167
|
+
|
168
|
+
# Collapse any empty dropdowns we may have expanded back to leafs
|
169
|
+
@menu.find('.dropdown-menu:empty').each (index, item) ->
|
170
|
+
item = $(item).closest('.dropdown')
|
171
|
+
item.removeClass('dropdown').removeClass('open')
|
172
|
+
item.children('a').removeAttr('data-toggle').find('span.caret').remove()
|
173
|
+
item.children('.dropdown-menu').remove()
|
174
|
+
|
175
|
+
@menu.removeClass('dragging')
|
176
|
+
@menu.children('.actions').children('.add-item').show()
|
177
|
+
@menu.find('.placeholder,.open').removeClass('placeholder').removeClass('open')
|
178
|
+
|
179
|
+
@draggable = null
|
180
|
+
@droppable = null
|
181
|
+
|
182
|
+
cleanupItemAfterDrop: (item) ->
|
183
|
+
item.children('a').find('span.caret').remove() # Just always remove the caret if present
|
184
|
+
|
185
|
+
# And add it back if we're a top level node
|
186
|
+
if item.parent().hasClass('effective-menu') && item.children('.dropdown-menu').length
|
187
|
+
item.children('a').append("<span class='caret'></span>")
|
188
|
+
|
189
|
+
item.parentsUntil(@menu, 'li.dropdown').addClass('open')
|
190
|
+
|
191
|
+
# Very top level items are assigned Depth of 0
|
192
|
+
# The first dropdowns all have Depth of 1
|
193
|
+
depthOf: (item) -> item.parentsUntil(@menu, 'li').length
|
194
|
+
|
195
|
+
# Just pass _destroyed = 1 back to rails to delete this item
|
196
|
+
# Rails seems to disregard new items set to the new Date.now() values anyhow
|
197
|
+
# Put all deleted items after the .actions div just to be tidy
|
198
|
+
markDestroyed: (item) ->
|
199
|
+
item.hide().addClass('destroyed').find("input[name$='[_destroy]']").val(1)
|
200
|
+
@menu.children('.actions').after(item.remove())
|
201
|
+
|
202
|
+
# This method is called with a Hash value
|
203
|
+
# that is called by reference from the parent function
|
204
|
+
#
|
205
|
+
# This serialize method is called from the effective_ckeditor gem plugins/effective_regions plugin
|
206
|
+
# by the SaveAll method to actually persist the menus when it also saves the regions
|
207
|
+
# This way the saves happen all at once
|
208
|
+
|
209
|
+
serialize: (retval) ->
|
210
|
+
# console.log "============ BEFORE =============="
|
211
|
+
# @menu.find('li').each (index, item) =>
|
212
|
+
# item = $(item)
|
213
|
+
# label = item.children('a').first().text()
|
214
|
+
# left = item.children('.menu-item').children("input[name$='[lft]']").val()
|
215
|
+
# right = item.children('.menu-item').children("input[name$='[rgt]']").val()
|
216
|
+
# console.log "[#{label}] #{left}, #{right}"
|
217
|
+
# console.log "=================================="
|
218
|
+
|
219
|
+
@assignLftRgt(@menu, 1)
|
220
|
+
|
221
|
+
# console.log "============ AFTER =============="
|
222
|
+
# @menu.find('li').each (index, item) =>
|
223
|
+
# item = $(item)
|
224
|
+
# label = item.children('a').first().text()
|
225
|
+
# left = item.children('.menu-item').children("input[name$='[lft]']").val()
|
226
|
+
# right = item.children('.menu-item').children("input[name$='[rgt]']").val()
|
227
|
+
# console.log "[#{label}] #{left}, #{right}"
|
228
|
+
# console.log "=================================="
|
229
|
+
|
230
|
+
items = {}
|
231
|
+
|
232
|
+
# This next bit just massages some of the form serialization
|
233
|
+
# and translates the jquery serializeArray into the formnat needed by Rails accepts_nested_attributes
|
234
|
+
|
235
|
+
$.each @menu.find('input').serializeArray(), ->
|
236
|
+
@name = @name.replace("effective_menu[menu_items_attributes]", "menu_items_attributes")
|
237
|
+
|
238
|
+
if items[@name]?
|
239
|
+
items[@name] = [items[@name]] unless items[@name].push
|
240
|
+
items[@name].push (@value || '')
|
241
|
+
else
|
242
|
+
items[@name] = (@value || '')
|
243
|
+
|
244
|
+
# This retVal has to account for multiple effective-menus on one page
|
245
|
+
retval[@menu.data('effective-menu-id')] = items
|
246
|
+
|
247
|
+
assignLftRgt: (parent, lft) ->
|
248
|
+
rgt = lft + 1
|
249
|
+
|
250
|
+
parent.children('.dropdown-menu').children('li:not(.destroyed)').each (_, child) =>
|
251
|
+
rgt = @assignLftRgt($(child), rgt)
|
252
|
+
|
253
|
+
parent.children('li:not(.destroyed)').each (_, child) =>
|
254
|
+
rgt = @assignLftRgt($(child), rgt)
|
255
|
+
|
256
|
+
parent.children('.menu-item').children("input[name$='[lft]']").val(lft)
|
257
|
+
parent.children('.menu-item').children("input[name$='[rgt]']").val(rgt)
|
258
|
+
|
259
|
+
rgt + 1
|
260
|
+
|
261
|
+
|
262
|
+
$.fn.extend effectiveMenuEditor: (option, args...) ->
|
263
|
+
@each ->
|
264
|
+
$this = $(this)
|
265
|
+
data = $this.data('effectiveMenuEditor')
|
266
|
+
|
267
|
+
$this.data('effectiveMenuEditor', (data = new EffectiveMenuEditor(this, option))) if !data
|
268
|
+
data[option].apply(data, args) if typeof option == 'string'
|
269
|
+
$this
|
270
|
+
|
271
|
+
) window.jQuery, window
|
272
|
+
|
273
|
+
|
274
|
+
# AND THE REST IS A CKEDITOR PLUGIN
|
275
|
+
|
276
|
+
# This plugin is registered with CkEditor and a dialog to edit menu item is created
|
277
|
+
# See the initCkEditorEvents() function in the jquery plugin that calls this dialog
|
278
|
+
# When the dialog is called, it sets dialog.effective_menu_item to
|
279
|
+
# to the event.currentTarget, i.e. the jquery element $(li)
|
280
|
+
|
281
|
+
CKEDITOR.plugins.add 'effective_menus',
|
282
|
+
init: (editor) ->
|
283
|
+
$('.effective-menu').effectiveMenuEditor() # Initialize the EffectiveMenus
|
284
|
+
|
285
|
+
CKEDITOR.dialog.add 'effectiveMenusDialog', (editor) ->
|
286
|
+
{
|
287
|
+
title: 'Effective Menu Item'
|
288
|
+
minWidth: 350,
|
289
|
+
minHeight: 200,
|
290
|
+
contents: [
|
291
|
+
{
|
292
|
+
id: 'item',
|
293
|
+
label: 'Menu Item'
|
294
|
+
elements: [
|
295
|
+
{
|
296
|
+
id: 'add_or_edit',
|
297
|
+
type: 'html',
|
298
|
+
html: '',
|
299
|
+
setup: (element) ->
|
300
|
+
# This just doesnt work and Im not sure why
|
301
|
+
if this.getDialog().effective_menu_item.hasClass('new-item')
|
302
|
+
this.setValue('<p>Create a new menu item</p>')
|
303
|
+
else
|
304
|
+
this.setValue('<p>Editing an existing menu item</p>')
|
305
|
+
},
|
306
|
+
{
|
307
|
+
id: 'title',
|
308
|
+
type: 'text',
|
309
|
+
label: 'Title',
|
310
|
+
validate: CKEDITOR.dialog.validate.notEmpty('please enter a title')
|
311
|
+
setup: (element) ->
|
312
|
+
this.setValue(element.children('.menu-item').children("input[name$='[title]']").val())
|
313
|
+
commit: (element) ->
|
314
|
+
element.children('.menu-item').children("input[name$='[title]']").val(this.getValue())
|
315
|
+
|
316
|
+
if element.children('a').find('span.caret').length > 0
|
317
|
+
element.children('a').text(this.getValue())
|
318
|
+
element.children('a').append("<span class='caret'></span>")
|
319
|
+
else
|
320
|
+
element.children('a').text(this.getValue())
|
321
|
+
validate: ->
|
322
|
+
if this.getDialog().getValueOf('item', 'source') != 'Divider' && (this.getValue() || '').length == 0
|
323
|
+
CKEDITOR.dialog.validate.notEmpty('please enter a title').apply(this)
|
324
|
+
},
|
325
|
+
{type: 'html', html: '<br>'},
|
326
|
+
{
|
327
|
+
id: 'source',
|
328
|
+
type: 'radio',
|
329
|
+
label: 'Link Type',
|
330
|
+
items: [['Page', 'Page'], ['URL', 'URL'], ['Route', 'Route'], ['Divider', 'Divider']]
|
331
|
+
setup: (element) ->
|
332
|
+
menuable_id = element.children('.menu-item').children("input[name$='[menuable_id]']").val() || ''
|
333
|
+
special = element.children('.menu-item').children("input[name$='[special]']").val() || ''
|
334
|
+
url = element.children('.menu-item').children("input[name$='[url]']").val() || ''
|
335
|
+
|
336
|
+
if menuable_id.length > 0
|
337
|
+
this.setValue('Page')
|
338
|
+
else if special == 'divider'
|
339
|
+
this.setValue('Divider')
|
340
|
+
else if special.length > 0
|
341
|
+
this.setValue('Route')
|
342
|
+
else if url.length > 0 && url != '#'
|
343
|
+
this.setValue('URL')
|
344
|
+
else
|
345
|
+
this.setValue('Page')
|
346
|
+
|
347
|
+
onChange: (event) ->
|
348
|
+
if this.getValue() == 'Page'
|
349
|
+
this.getDialog().getContentElement('item', 'title').getElement().show()
|
350
|
+
this.getDialog().getContentElement('item', 'menuable_id').getElement().show()
|
351
|
+
|
352
|
+
this.getDialog().getContentElement('item', 'url').getElement().hide()
|
353
|
+
this.getDialog().getContentElement('item', 'special').getElement().hide()
|
354
|
+
|
355
|
+
if this.getValue() == 'URL'
|
356
|
+
this.getDialog().getContentElement('item', 'title').getElement().show()
|
357
|
+
this.getDialog().getContentElement('item', 'url').getElement().show()
|
358
|
+
|
359
|
+
this.getDialog().getContentElement('item', 'menuable_id').getElement().hide()
|
360
|
+
this.getDialog().getContentElement('item', 'special').getElement().hide()
|
361
|
+
|
362
|
+
if this.getValue() == 'Divider'
|
363
|
+
this.getDialog().getContentElement('item', 'url').getElement().hide()
|
364
|
+
this.getDialog().getContentElement('item', 'title').getElement().hide()
|
365
|
+
this.getDialog().getContentElement('item', 'menuable_id').getElement().hide()
|
366
|
+
this.getDialog().getContentElement('item', 'special').getElement().hide()
|
367
|
+
|
368
|
+
if this.getValue() == 'Route'
|
369
|
+
this.getDialog().getContentElement('item', 'title').getElement().show()
|
370
|
+
this.getDialog().getContentElement('item', 'special').getElement().show()
|
371
|
+
|
372
|
+
this.getDialog().getContentElement('item', 'menuable_id').getElement().hide()
|
373
|
+
this.getDialog().getContentElement('item', 'url').getElement().hide()
|
374
|
+
},
|
375
|
+
{
|
376
|
+
id: 'menuable_id',
|
377
|
+
type: 'select',
|
378
|
+
label: 'Page',
|
379
|
+
items: (
|
380
|
+
pages = []
|
381
|
+
$.ajax
|
382
|
+
url: '/admin/pages'
|
383
|
+
dataType: 'json'
|
384
|
+
async: false
|
385
|
+
complete: (data) -> pages = data.responseJSON
|
386
|
+
pages
|
387
|
+
),
|
388
|
+
setup: (element) ->
|
389
|
+
this.setValue(element.children('.menu-item').children("input[name$='[menuable_id]']").val())
|
390
|
+
commit: (element) ->
|
391
|
+
if this.getElement().isVisible()
|
392
|
+
element.children('.menu-item').children("input[name$='[menuable_id]']").val(this.getValue())
|
393
|
+
element.children('.menu-item').children("input[name$='[menuable_type]']").val('Effective::Page')
|
394
|
+
else
|
395
|
+
element.children('.menu-item').children("input[name$='[menuable_id]']").val('')
|
396
|
+
element.children('.menu-item').children("input[name$='[menuable_type]']").val('')
|
397
|
+
validate: ->
|
398
|
+
if this.getElement().isVisible() && (this.getValue() || '').length == 0
|
399
|
+
CKEDITOR.dialog.validate.notEmpty('please select a page').apply(this)
|
400
|
+
},
|
401
|
+
{
|
402
|
+
id: 'url',
|
403
|
+
type: 'text',
|
404
|
+
label: 'URL',
|
405
|
+
setup: (element) ->
|
406
|
+
this.setValue(element.children('.menu-item').children("input[name$='[url]']").val())
|
407
|
+
commit: (element) ->
|
408
|
+
if this.getElement().isVisible() then value = this.getValue() else value = ''
|
409
|
+
|
410
|
+
element.children('.menu-item').children("input[name$='[url]']").val(value)
|
411
|
+
element.children('a').attr('href', value || '#')
|
412
|
+
validate: ->
|
413
|
+
if this.getElement().isVisible() && (this.getValue() || '').length == 0
|
414
|
+
CKEDITOR.dialog.validate.notEmpty('please enter a URL').apply(this)
|
415
|
+
},
|
416
|
+
{
|
417
|
+
id: 'special',
|
418
|
+
type: 'text',
|
419
|
+
label: 'Route',
|
420
|
+
setup: (element) ->
|
421
|
+
this.setValue(element.children('.menu-item').children("input[name$='[special]']").val())
|
422
|
+
commit: (element) ->
|
423
|
+
if this.getDialog().getValueOf('item', 'source') == 'Divider'
|
424
|
+
element.children('.menu-item').children("input[name$='[special]']").val('divider')
|
425
|
+
else if this.getElement().isVisible()
|
426
|
+
element.children('.menu-item').children("input[name$='[special]']").val(this.getValue())
|
427
|
+
else
|
428
|
+
element.children('.menu-item').children("input[name$='[special]']").val('')
|
429
|
+
# There is more stuff in the classes commit
|
430
|
+
validate: ->
|
431
|
+
if this.getDialog().getValueOf('item', 'source') == 'Divider'
|
432
|
+
if this.getDialog().effective_menu_item.hasClass('dropdown')
|
433
|
+
CKEDITOR.dialog.validate.notEmpty('cannot convert existing dropdown menu to a Divider').apply(this)
|
434
|
+
else if this.getElement().isVisible()
|
435
|
+
if (this.getValue() || '').length == 0
|
436
|
+
CKEDITOR.dialog.validate.notEmpty('please enter a route').apply(this)
|
437
|
+
}
|
438
|
+
] # /tab1 elements
|
439
|
+
},
|
440
|
+
{
|
441
|
+
id: 'permissions',
|
442
|
+
label: 'Permissions',
|
443
|
+
elements: [
|
444
|
+
{
|
445
|
+
id: 'signed_out',
|
446
|
+
type: 'checkbox',
|
447
|
+
label: 'Only visible when signed out',
|
448
|
+
setup: (element) ->
|
449
|
+
value = element.children('.menu-item').children("input[name$='[roles_mask]']").val()
|
450
|
+
this.setValue(value == '-1')
|
451
|
+
onChange: (event) ->
|
452
|
+
if this.getValue() == true
|
453
|
+
this.getDialog().setValueOf('permissions', 'signed_in', false)
|
454
|
+
this.getDialog().setValueOf('permissions', 'roles_mask', '')
|
455
|
+
},
|
456
|
+
{
|
457
|
+
id: 'signed_in',
|
458
|
+
type: 'checkbox',
|
459
|
+
label: 'Only visible when signed in',
|
460
|
+
setup: (element) ->
|
461
|
+
value = element.children('.menu-item').children("input[name$='[roles_mask]']").val()
|
462
|
+
this.setValue(value.length > 0 && parseInt(value, 10) >= 0)
|
463
|
+
onChange: (event) ->
|
464
|
+
if this.getValue() == true
|
465
|
+
this.getDialog().setValueOf('permissions', 'signed_out', false)
|
466
|
+
},
|
467
|
+
{
|
468
|
+
id: 'roles_mask',
|
469
|
+
type: 'text',
|
470
|
+
label: 'Roles Mask',
|
471
|
+
setup: (element) ->
|
472
|
+
value = parseInt(element.children('.menu-item').children("input[name$='[roles_mask]']").val(), 10)
|
473
|
+
if value > 0 then this.setValue(value) else this.setValue('')
|
474
|
+
commit: (element) ->
|
475
|
+
if ('' + this.getValue()).length > 0
|
476
|
+
element.children('.menu-item').children("input[name$='[roles_mask]']").val(this.getValue())
|
477
|
+
else if this.getDialog().getValueOf('permissions', 'signed_in') == true
|
478
|
+
element.children('.menu-item').children("input[name$='[roles_mask]']").val(0)
|
479
|
+
else if this.getDialog().getValueOf('permissions', 'signed_out') == true
|
480
|
+
element.children('.menu-item').children("input[name$='[roles_mask]']").val(-1)
|
481
|
+
else
|
482
|
+
element.children('.menu-item').children("input[name$='[roles_mask]']").val('')
|
483
|
+
onKeyup: (event) ->
|
484
|
+
if ('' + this.getValue()).length > 0
|
485
|
+
this.getDialog().setValueOf('permissions', 'signed_in', true)
|
486
|
+
this.getDialog().setValueOf('permissions', 'signed_out', false)
|
487
|
+
validate: ->
|
488
|
+
if ('' + this.getValue()).length > 0
|
489
|
+
CKEDITOR.dialog.validate.integer('roles_mask must be an integer').apply(this)
|
490
|
+
}
|
491
|
+
]
|
492
|
+
},
|
493
|
+
{
|
494
|
+
id: 'advanced',
|
495
|
+
label: 'Advanced'
|
496
|
+
elements: [
|
497
|
+
{
|
498
|
+
id: 'new_window',
|
499
|
+
type: 'checkbox',
|
500
|
+
label: 'Open in new window',
|
501
|
+
setup: (element) ->
|
502
|
+
value = element.children('.menu-item').children("input[name$='[new_window]']").val()
|
503
|
+
if value == 'true' then this.setValue(true) else this.setValue(false)
|
504
|
+
commit: (element) ->
|
505
|
+
element.children('.menu-item').children("input[name$='[new_window]']").val(this.getValue())
|
506
|
+
},
|
507
|
+
{
|
508
|
+
id: 'classes',
|
509
|
+
type: 'text',
|
510
|
+
label: 'HTML Classes',
|
511
|
+
setup: (element) ->
|
512
|
+
this.setValue(element.children('.menu-item').children("input[name$='[classes]']").val())
|
513
|
+
commit: (element) ->
|
514
|
+
value = this.getValue()
|
515
|
+
element.children('.menu-item').children("input[name$='[classes]']").val(value)
|
516
|
+
|
517
|
+
dropdown = element.hasClass('dropdown')
|
518
|
+
|
519
|
+
element.prop('class', value)
|
520
|
+
|
521
|
+
# Put back classes we need
|
522
|
+
element.addClass('dropdown') if dropdown
|
523
|
+
|
524
|
+
if this.getDialog().getValueOf('item', 'source') == 'Divider'
|
525
|
+
element.addClass('divider')
|
526
|
+
else
|
527
|
+
element.removeClass('divider')
|
528
|
+
}
|
529
|
+
]
|
530
|
+
}
|
531
|
+
], # /contents
|
532
|
+
|
533
|
+
onShow: -> this.setupContent(this.effective_menu_item) if this.effective_menu_item
|
534
|
+
|
535
|
+
onOk: ->
|
536
|
+
this.commitContent(this.effective_menu_item)
|
537
|
+
this.effective_menu_item.removeClass('new-item') if this.effective_menu_item.hasClass('new-item')
|
538
|
+
this.effective_menu_item = undefined
|
539
|
+
|
540
|
+
onCancel: ->
|
541
|
+
this.effective_menu_item.remove() if this.effective_menu_item.hasClass('new-item')
|
542
|
+
this.effective_menu_item = undefined
|
543
|
+
}
|
@@ -36,18 +36,24 @@ SaveAll = {
|
|
36
36
|
button.css('background-image', 'url(/assets/ckeditor/plugins/effective_regions/icons/saving.png)')
|
37
37
|
setTimeout(
|
38
38
|
-> button.css('background-image', 'url(/assets/ckeditor/plugins/effective_regions/icons/save.png)')
|
39
|
-
|
40
|
-
|
41
|
-
data = {}
|
42
|
-
data[name] = @instanceData(instance) for name, instance of CKEDITOR.instances
|
39
|
+
2000
|
40
|
+
)
|
43
41
|
|
44
42
|
url = window.location.protocol + '//' + window.location.host + '/edit' + window.location.pathname
|
45
43
|
|
44
|
+
# Collect all Effective::Region Data
|
45
|
+
regionData = {}
|
46
|
+
regionData[name] = @instanceData(instance) for name, instance of CKEDITOR.instances
|
47
|
+
|
48
|
+
# Collect all the Effective::Menu Data
|
49
|
+
menuData = {}
|
50
|
+
$('.effective-menu').effectiveMenuEditor('serialize', menuData)
|
51
|
+
|
46
52
|
$.ajax
|
47
53
|
url: url
|
48
54
|
type: 'PUT'
|
49
55
|
dataType: 'json'
|
50
|
-
data: { effective_regions:
|
56
|
+
data: { effective_regions: regionData, effective_menus: menuData }
|
51
57
|
async: false
|
52
58
|
complete: (data) ->
|
53
59
|
if data.responseText == 'refresh'
|
@@ -1,7 +1,7 @@
|
|
1
1
|
CKEDITOR.editorConfig = (config) ->
|
2
2
|
config.startupShowBorders = true
|
3
3
|
|
4
|
-
config.extraPlugins = 'effective_regions,effective_assets'
|
4
|
+
config.extraPlugins = 'effective_regions,effective_assets,effective_menus'
|
5
5
|
config.format_tags = 'p;h1;h2;h3;h4;h5;h6;pre;div'
|
6
6
|
|
7
7
|
config.templates = 'effective_regions'
|
@@ -0,0 +1,64 @@
|
|
1
|
+
.effective-menu.dragging, {
|
2
|
+
cursor: move !important; // I'm having trouble getting the cursor working
|
3
|
+
li, a, * { cursor: move !important; }
|
4
|
+
|
5
|
+
> .actions {
|
6
|
+
.remove-item { display: inline-block; }
|
7
|
+
}
|
8
|
+
}
|
9
|
+
|
10
|
+
.effective-menu {
|
11
|
+
position: relative;
|
12
|
+
border: 1px dotted #4195fc; // Match the effective_regions editable border color
|
13
|
+
|
14
|
+
min-width: 30px; // What to do with an empty menu...
|
15
|
+
min-height: 30px;
|
16
|
+
|
17
|
+
li.divider { height: 3px; }
|
18
|
+
.menu-item { display: none; } // under each li, Contains the form controls. Nothing to view here
|
19
|
+
|
20
|
+
// Sub-level placeholder
|
21
|
+
li.placeholder {
|
22
|
+
position: relative;
|
23
|
+
|
24
|
+
&:before {
|
25
|
+
position: absolute;
|
26
|
+
content: "";
|
27
|
+
height: 0px;
|
28
|
+
width: 0px;
|
29
|
+
left: 2px; // Was -5px;
|
30
|
+
top: -4px;
|
31
|
+
margin-top: -5px;
|
32
|
+
border-left: 5px solid red;
|
33
|
+
border-top: 5px solid transparent;
|
34
|
+
border-bottom: 5px solid transparent;
|
35
|
+
border-right: none;
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
// Top level placeholder
|
40
|
+
> li.placeholder {
|
41
|
+
&:before {
|
42
|
+
top: 7px;
|
43
|
+
|
44
|
+
margin-top: -6px;
|
45
|
+
border-left: 5px solid transparent;
|
46
|
+
border-top: 5px solid red;
|
47
|
+
border-bottom: none;
|
48
|
+
border-right: 5px solid transparent;
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
> .actions {
|
53
|
+
cursor: pointer;
|
54
|
+
position: absolute;
|
55
|
+
right: 0;
|
56
|
+
top: 0;
|
57
|
+
font-size: 14px;
|
58
|
+
|
59
|
+
.add-item { display: none; } // Bootstrap3 glyphicon
|
60
|
+
.remove-item { display: none; } // Bootstrap3 glyphicon
|
61
|
+
|
62
|
+
.large { font-size: 20px; }
|
63
|
+
}
|
64
|
+
}
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: effective_ckeditor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Code and Effect
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-01-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -51,6 +51,7 @@ files:
|
|
51
51
|
- app/assets/javascripts/ckeditor/plugins/effective_assets/icons/effectiveassets.png
|
52
52
|
- app/assets/javascripts/ckeditor/plugins/effective_assets/icons/hidpi/effectiveassets.png
|
53
53
|
- app/assets/javascripts/ckeditor/plugins/effective_assets/plugin.js.coffee
|
54
|
+
- app/assets/javascripts/ckeditor/plugins/effective_menus/plugin.js.coffee
|
54
55
|
- app/assets/javascripts/ckeditor/plugins/effective_regions/icons/exit.png
|
55
56
|
- app/assets/javascripts/ckeditor/plugins/effective_regions/icons/hidpi/exit.png
|
56
57
|
- app/assets/javascripts/ckeditor/plugins/effective_regions/icons/hidpi/save.png
|
@@ -176,6 +177,7 @@ files:
|
|
176
177
|
- app/assets/javascripts/vendor/jquery.cookie.js
|
177
178
|
- app/assets/stylesheets/effective_ckeditor.css.scss
|
178
179
|
- app/assets/stylesheets/effective_ckeditor/layout.css.scss
|
180
|
+
- app/assets/stylesheets/effective_ckeditor/menu_editor.css.scss
|
179
181
|
- app/assets/stylesheets/effective_ckeditor/overrides.css.scss
|
180
182
|
- lib/effective_ckeditor.rb
|
181
183
|
- lib/effective_ckeditor/engine.rb
|