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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 46022b1ae3481ec3be596a93d32458777be3ad34
4
- data.tar.gz: 6c956c4d07044bc4f3d6626cce09d2a586c8143d
3
+ metadata.gz: 6abe1be063968942b886583f1efd66fa0922f8ad
4
+ data.tar.gz: f212652bc21b13501653bec315ba1f86fdf832f6
5
5
  SHA512:
6
- metadata.gz: ae12510c6e4da3293a0a7744ac1e9a2599dd4aa327819fc3dd4a64eda6fe221620622193637952a0acaa57ee7b2432dd9b7d020ad99ca5b014da1570785b5e47
7
- data.tar.gz: a97ab6455c7fa248c58a75471f502a6144db5dfe4c15f80d3a974d7b9043409cf73ab0080f9eb7601f6076ada000b7ad0eba161e22f79d0f97ff8e1993dfa242
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
- 2000)
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: data }
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
+ }
@@ -1,2 +1,3 @@
1
1
  @import 'effective_ckeditor/layout';
2
2
  @import 'effective_ckeditor/overrides';
3
+ @import 'effective_ckeditor/menu_editor';
@@ -1,3 +1,3 @@
1
1
  module EffectiveCkeditor
2
- VERSION = '1.0.1'.freeze
2
+ VERSION = '1.1.0'.freeze
3
3
  end
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.1
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: 2014-12-31 00:00:00.000000000 Z
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