effective_ckeditor 1.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|