rails_admin_nestable_sb 0.0.2

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.
@@ -0,0 +1,508 @@
1
+ /*!
2
+ * Nestable jQuery Plugin - Copyright (c) 2012 David Bushell - http://dbushell.com/
3
+ * Dual-licensed under the BSD or MIT licenses
4
+ */
5
+ ;(function($, window, document, undefined)
6
+ {
7
+ var hasTouch = 'ontouchstart' in document;
8
+
9
+ /**
10
+ * Detect CSS pointer-events property
11
+ * events are normally disabled on the dragging element to avoid conflicts
12
+ * https://github.com/ausi/Feature-detection-technique-for-pointer-events/blob/master/modernizr-pointerevents.js
13
+ */
14
+ var hasPointerEvents = (function()
15
+ {
16
+ var el = document.createElement('div'),
17
+ docEl = document.documentElement;
18
+ if (!('pointerEvents' in el.style)) {
19
+ return false;
20
+ }
21
+ el.style.pointerEvents = 'auto';
22
+ el.style.pointerEvents = 'x';
23
+ docEl.appendChild(el);
24
+ var supports = window.getComputedStyle && window.getComputedStyle(el, '').pointerEvents === 'auto';
25
+ docEl.removeChild(el);
26
+ return !!supports;
27
+ })();
28
+
29
+ var defaults = {
30
+ listNodeName : 'ol',
31
+ itemNodeName : 'li',
32
+ rootClass : 'dd',
33
+ listClass : 'dd-list',
34
+ itemClass : 'dd-item',
35
+ dragClass : 'dd-dragel',
36
+ handleClass : 'dd-handle',
37
+ collapsedClass : 'dd-collapsed',
38
+ placeClass : 'dd-placeholder',
39
+ noDragClass : 'dd-nodrag',
40
+ emptyClass : 'dd-empty',
41
+ expandBtnHTML : '<button data-action="expand" type="button">Expand</button>',
42
+ collapseBtnHTML : '<button data-action="collapse" type="button">Collapse</button>',
43
+ group : 0,
44
+ maxDepth : 5,
45
+ threshold : 20
46
+ };
47
+
48
+ function Plugin(element, options)
49
+ {
50
+ this.w = $(document);
51
+ this.el = $(element);
52
+ this.options = $.extend({}, defaults, options);
53
+ this.init();
54
+ }
55
+
56
+ Plugin.prototype = {
57
+
58
+ init: function()
59
+ {
60
+ var list = this;
61
+
62
+ list.reset();
63
+
64
+ list.el.data('nestable-group', this.options.group);
65
+
66
+ list.placeEl = $('<div class="' + list.options.placeClass + '"/>');
67
+
68
+ $.each(this.el.find(list.options.itemNodeName), function(k, el) {
69
+ list.setParent($(el));
70
+ });
71
+
72
+ list.el.on('click', 'button', function(e) {
73
+ if (list.dragEl) {
74
+ return;
75
+ }
76
+ var target = $(e.currentTarget),
77
+ action = target.data('action'),
78
+ item = target.parent(list.options.itemNodeName);
79
+ if (action === 'collapse') {
80
+ list.collapseItem(item);
81
+ }
82
+ if (action === 'expand') {
83
+ list.expandItem(item);
84
+ }
85
+ });
86
+
87
+ var onStartEvent = function(e)
88
+ {
89
+ var handle = $(e.target);
90
+ if (!handle.hasClass(list.options.handleClass)) {
91
+ if (handle.closest('.' + list.options.noDragClass).length) {
92
+ return;
93
+ }
94
+ handle = handle.closest('.' + list.options.handleClass);
95
+ }
96
+
97
+ if (!handle.length || list.dragEl) {
98
+ return;
99
+ }
100
+
101
+ list.isTouch = /^touch/.test(e.type);
102
+ if (list.isTouch && e.touches.length !== 1) {
103
+ return;
104
+ }
105
+
106
+ e.preventDefault();
107
+ list.dragStart(e.touches ? e.touches[0] : e);
108
+ };
109
+
110
+ var onMoveEvent = function(e)
111
+ {
112
+ if (list.dragEl) {
113
+ e.preventDefault();
114
+ list.dragMove(e.touches ? e.touches[0] : e);
115
+ }
116
+ };
117
+
118
+ var onEndEvent = function(e)
119
+ {
120
+ if (list.dragEl) {
121
+ e.preventDefault();
122
+ list.dragStop(e.touches ? e.touches[0] : e);
123
+ }
124
+ };
125
+
126
+ if (hasTouch) {
127
+ list.el[0].addEventListener('touchstart', onStartEvent, false);
128
+ window.addEventListener('touchmove', onMoveEvent, false);
129
+ window.addEventListener('touchend', onEndEvent, false);
130
+ window.addEventListener('touchcancel', onEndEvent, false);
131
+ }
132
+
133
+ list.el.on('mousedown', onStartEvent);
134
+ list.w.on('mousemove', onMoveEvent);
135
+ list.w.on('mouseup', onEndEvent);
136
+
137
+ },
138
+
139
+ serialize: function()
140
+ {
141
+ var data,
142
+ depth = 0,
143
+ list = this;
144
+ step = function(level, depth)
145
+ {
146
+ var array = [ ],
147
+ items = level.children(list.options.itemNodeName);
148
+ items.each(function()
149
+ {
150
+ var li = $(this),
151
+ item = $.extend({}, li.data()),
152
+ sub = li.children(list.options.listNodeName);
153
+ if (sub.length) {
154
+ item.children = step(sub, depth + 1);
155
+ }
156
+ array.push(item);
157
+ });
158
+ return array;
159
+ };
160
+ data = step(list.el.find(list.options.listNodeName).first(), depth);
161
+ return data;
162
+ },
163
+
164
+ serialise: function()
165
+ {
166
+ return this.serialize();
167
+ },
168
+
169
+ reset: function()
170
+ {
171
+ this.mouse = {
172
+ offsetX : 0,
173
+ offsetY : 0,
174
+ startX : 0,
175
+ startY : 0,
176
+ lastX : 0,
177
+ lastY : 0,
178
+ nowX : 0,
179
+ nowY : 0,
180
+ distX : 0,
181
+ distY : 0,
182
+ dirAx : 0,
183
+ dirX : 0,
184
+ dirY : 0,
185
+ lastDirX : 0,
186
+ lastDirY : 0,
187
+ distAxX : 0,
188
+ distAxY : 0
189
+ };
190
+ this.isTouch = false;
191
+ this.moving = false;
192
+ this.dragEl = null;
193
+ this.dragRootEl = null;
194
+ this.dragDepth = 0;
195
+ this.hasNewRoot = false;
196
+ this.pointEl = null;
197
+ },
198
+
199
+ expandItem: function(li)
200
+ {
201
+ li.removeClass(this.options.collapsedClass);
202
+ li.children('[data-action="expand"]').hide();
203
+ li.children('[data-action="collapse"]').show();
204
+ li.children(this.options.listNodeName).show();
205
+ },
206
+
207
+ collapseItem: function(li)
208
+ {
209
+ var lists = li.children(this.options.listNodeName);
210
+ if (lists.length) {
211
+ li.addClass(this.options.collapsedClass);
212
+ li.children('[data-action="collapse"]').hide();
213
+ li.children('[data-action="expand"]').show();
214
+ li.children(this.options.listNodeName).hide();
215
+ }
216
+ },
217
+
218
+ expandAll: function()
219
+ {
220
+ var list = this;
221
+ list.el.find(list.options.itemNodeName).each(function() {
222
+ list.expandItem($(this));
223
+ });
224
+ },
225
+
226
+ collapseAll: function()
227
+ {
228
+ var list = this;
229
+ list.el.find(list.options.itemNodeName).each(function() {
230
+ list.collapseItem($(this));
231
+ });
232
+ },
233
+
234
+ setParent: function(li)
235
+ {
236
+ if (li.children(this.options.listNodeName).length) {
237
+ li.prepend($(this.options.expandBtnHTML));
238
+ li.prepend($(this.options.collapseBtnHTML));
239
+ }
240
+ li.children('[data-action="expand"]').hide();
241
+ },
242
+
243
+ unsetParent: function(li)
244
+ {
245
+ li.removeClass(this.options.collapsedClass);
246
+ li.children('[data-action]').remove();
247
+ li.children(this.options.listNodeName).remove();
248
+ },
249
+
250
+ dragStart: function(e)
251
+ {
252
+ var mouse = this.mouse,
253
+ target = $(e.target),
254
+ dragItem = target.closest(this.options.itemNodeName);
255
+
256
+ if(dragItem.data("dragable") == false){
257
+ return;
258
+ }
259
+
260
+ this.dragParent = dragItem[0].parentNode;
261
+ this.dragElement = dragItem[0];
262
+
263
+ this.placeEl.css('height', dragItem.height());
264
+
265
+ mouse.offsetX = e.offsetX !== undefined ? e.offsetX : e.pageX - target.offset().left;
266
+ mouse.offsetY = e.offsetY !== undefined ? e.offsetY : e.pageY - target.offset().top;
267
+ mouse.startX = mouse.lastX = e.pageX;
268
+ mouse.startY = mouse.lastY = e.pageY;
269
+
270
+ this.dragRootEl = this.el;
271
+
272
+ this.dragEl = $(document.createElement(this.options.listNodeName)).addClass(this.options.listClass + ' ' + this.options.dragClass);
273
+ this.dragEl.css('width', dragItem.width());
274
+
275
+ dragItem.after(this.placeEl);
276
+ dragItem[0].parentNode.removeChild(dragItem[0]);
277
+ dragItem.appendTo(this.dragEl);
278
+
279
+ $(document.body).append(this.dragEl);
280
+ this.dragEl.css({
281
+ 'left' : e.pageX - mouse.offsetX,
282
+ 'top' : e.pageY - mouse.offsetY
283
+ });
284
+ // total depth of dragging item
285
+ var i, depth,
286
+ items = this.dragEl.find(this.options.itemNodeName);
287
+ for (i = 0; i < items.length; i++) {
288
+ depth = $(items[i]).parents(this.options.listNodeName).length;
289
+ if (depth > this.dragDepth) {
290
+ this.dragDepth = depth;
291
+ }
292
+ }
293
+ },
294
+
295
+ dragStop: function(e)
296
+ {
297
+ var el = this.dragEl.children(this.options.itemNodeName).first();
298
+ el[0].parentNode.removeChild(el[0]);
299
+ this.placeEl.replaceWith(el);
300
+
301
+ this.dragEl.remove();
302
+ this.el.trigger('change');
303
+ if (this.hasNewRoot) {
304
+ this.dragRootEl.trigger('change');
305
+ }
306
+ this.reset();
307
+ },
308
+
309
+ dragMove: function(e)
310
+ {
311
+ var list, parent, prev, next, depth,
312
+ opt = this.options,
313
+ mouse = this.mouse;
314
+
315
+ this.dragEl.css({
316
+ 'left' : e.pageX - mouse.offsetX,
317
+ 'top' : e.pageY - mouse.offsetY
318
+ });
319
+
320
+ // mouse position last events
321
+ mouse.lastX = mouse.nowX;
322
+ mouse.lastY = mouse.nowY;
323
+ // mouse position this events
324
+ mouse.nowX = e.pageX;
325
+ mouse.nowY = e.pageY;
326
+ // distance mouse moved between events
327
+ mouse.distX = mouse.nowX - mouse.lastX;
328
+ mouse.distY = mouse.nowY - mouse.lastY;
329
+ // direction mouse was moving
330
+ mouse.lastDirX = mouse.dirX;
331
+ mouse.lastDirY = mouse.dirY;
332
+ // direction mouse is now moving (on both axis)
333
+ mouse.dirX = mouse.distX === 0 ? 0 : mouse.distX > 0 ? 1 : -1;
334
+ mouse.dirY = mouse.distY === 0 ? 0 : mouse.distY > 0 ? 1 : -1;
335
+ // axis mouse is now moving on
336
+ var newAx = Math.abs(mouse.distX) > Math.abs(mouse.distY) ? 1 : 0;
337
+
338
+ // do nothing on first move
339
+ if (!mouse.moving) {
340
+ mouse.dirAx = newAx;
341
+ mouse.moving = true;
342
+ return;
343
+ }
344
+
345
+ // calc distance moved on this axis (and direction)
346
+ if (mouse.dirAx !== newAx) {
347
+ mouse.distAxX = 0;
348
+ mouse.distAxY = 0;
349
+ } else {
350
+ mouse.distAxX += Math.abs(mouse.distX);
351
+ if (mouse.dirX !== 0 && mouse.dirX !== mouse.lastDirX) {
352
+ mouse.distAxX = 0;
353
+ }
354
+ mouse.distAxY += Math.abs(mouse.distY);
355
+ if (mouse.dirY !== 0 && mouse.dirY !== mouse.lastDirY) {
356
+ mouse.distAxY = 0;
357
+ }
358
+ }
359
+ mouse.dirAx = newAx;
360
+
361
+ /**
362
+ * move horizontal (Not needed)
363
+
364
+ if (mouse.dirAx && mouse.distAxX >= opt.threshold) {
365
+
366
+ // reset move distance on x-axis for new phase
367
+ mouse.distAxX = 0;
368
+
369
+ prev = this.placeEl.prev(opt.itemNodeName);
370
+ // increase horizontal level if previous sibling exists and is not collapsed
371
+ if (mouse.distX > 0 && prev.length && !prev.hasClass(opt.collapsedClass)) {
372
+ // cannot increase level when item above is collapsed
373
+ list = prev.find(opt.listNodeName).last();
374
+ // check if depth limit has reached
375
+ depth = this.placeEl.parents(opt.listNodeName).length;
376
+
377
+ if (depth + this.dragDepth <= opt.maxDepth) {
378
+ // create new sub-level if one doesn't exist
379
+ if (!list.length) {
380
+ list = $('<' + opt.listNodeName + '/>').addClass(opt.listClass);
381
+ list.append(this.placeEl);
382
+ prev.append(list);
383
+ this.setParent(prev);
384
+ } else {
385
+ // else append to next level up
386
+ list = prev.children(opt.listNodeName).last();
387
+ list.append(this.placeEl);
388
+ }
389
+ }
390
+ }
391
+ // decrease horizontal level
392
+ if (mouse.distX < 0) {
393
+ // we can't decrease a level if an item preceeds the current one
394
+ next = this.placeEl.next(opt.itemNodeName);
395
+ if (!next.length) {
396
+ parent = this.placeEl.parent();
397
+ this.placeEl.closest(opt.itemNodeName).after(this.placeEl);
398
+ if (!parent.children().length) {
399
+ this.unsetParent(parent.parent());
400
+ }
401
+ }
402
+ }
403
+ }*/
404
+
405
+ var parentRect = this.dragParent.getBoundingClientRect();
406
+ var mouseYpos = e.pageY - (window.pageYOffset || document.documentElement.scrollTop);
407
+ if(mouseYpos < parentRect.top || mouseYpos > parentRect.top + parentRect.height){
408
+ return;
409
+ }
410
+
411
+ var isEmpty = false;
412
+
413
+ // find list item under cursor
414
+ if (!hasPointerEvents) {
415
+ this.dragEl[0].style.visibility = 'hidden';
416
+ }
417
+
418
+ this.pointEl = $(document.elementFromPoint(e.pageX - document.body.scrollLeft, e.pageY - (window.pageYOffset || document.documentElement.scrollTop)));
419
+ if (!hasPointerEvents) {
420
+ this.dragEl[0].style.visibility = 'visible';
421
+ }
422
+
423
+ if (this.pointEl.hasClass(opt.handleClass)) {
424
+ this.pointEl = this.pointEl.parent(opt.itemNodeName);
425
+ }
426
+ if (this.pointEl.hasClass(opt.emptyClass)) {
427
+ isEmpty = true;
428
+ }
429
+ else if (!this.pointEl.length || !this.pointEl.hasClass(opt.itemClass)) {
430
+ return;
431
+ }
432
+
433
+ // find parent list of item under cursor
434
+ var pointElRoot = this.pointEl.closest('.' + opt.rootClass),
435
+ isNewRoot = this.dragRootEl.data('nestable-id') !== pointElRoot.data('nestable-id');
436
+
437
+ /**
438
+ * move vertical
439
+ */
440
+
441
+ if (!mouse.dirAx || isNewRoot || isEmpty) {
442
+ // check if groups match if dragging over new root
443
+
444
+ if (isNewRoot && opt.group !== pointElRoot.data('nestable-group')) {
445
+ return;
446
+ }
447
+ // check depth limit
448
+ depth = this.dragDepth - 1 + this.pointEl.parents(opt.listNodeName).length;
449
+ if (depth > opt.maxDepth || depth < opt.minDepth) {
450
+ return;
451
+ }
452
+ var before = e.pageY < (this.pointEl.offset().top + this.pointEl.height() / 2);
453
+ parent = this.placeEl.parent();
454
+
455
+ // if empty create new list to replace empty placeholder
456
+
457
+ if (isEmpty) {
458
+
459
+ list = $(document.createElement(opt.listNodeName)).addClass(opt.listClass);
460
+ list.append(this.placeEl);
461
+ this.pointEl.replaceWith(list);
462
+ }
463
+ else if (before) {
464
+ this.pointEl.before(this.placeEl);
465
+ }
466
+ else {
467
+ this.pointEl.after(this.placeEl);
468
+ }
469
+
470
+ if (!parent.children().length) {
471
+ this.unsetParent(parent.parent());
472
+ }
473
+ if (!this.dragRootEl.find(opt.itemNodeName).length) {
474
+ this.dragRootEl.append('<div class="' + opt.emptyClass + '"/>');
475
+ }
476
+ // parent root list has changed
477
+ if (isNewRoot) {
478
+ this.dragRootEl = pointElRoot;
479
+ this.hasNewRoot = this.el[0] !== this.dragRootEl[0];
480
+ }
481
+ }
482
+ }
483
+
484
+ };
485
+
486
+ $.fn.nestable = function(params)
487
+ {
488
+ var lists = this,
489
+ retval = this;
490
+
491
+ lists.each(function()
492
+ {
493
+ var plugin = $(this).data("nestable");
494
+
495
+ if (!plugin) {
496
+ $(this).data("nestable", new Plugin(this, params));
497
+ $(this).data("nestable-id", new Date().getTime());
498
+ } else {
499
+ if (typeof params === 'string' && typeof plugin[params] === 'function') {
500
+ retval = plugin[params]();
501
+ }
502
+ }
503
+ });
504
+
505
+ return retval || lists;
506
+ };
507
+
508
+ })(window.jQuery || window.Zepto, window, document);
@@ -0,0 +1,39 @@
1
+ (function($){
2
+ //init
3
+ $("#tree_nodes").nestable({
4
+ maxDepth: 2,
5
+ minDepth: 2,
6
+ minDragDepth: 2
7
+ });
8
+ $("#save").click(function(){
9
+ var tree_nodes = $("#tree_nodes");
10
+ var tree_url = tree_nodes.data('update-path');
11
+ var tree_data = $("#tree_nodes").nestable('serialize');
12
+ var self = $(this);
13
+ self.attr("disabled", true);
14
+ $("#result").html("");
15
+ $.ajax ({
16
+ url: tree_url,
17
+ data: {
18
+ tree_nodes: tree_data
19
+ },
20
+ method: 'POST',
21
+ complete: function(){
22
+ self.attr("disabled", false);
23
+ },
24
+ success: function(data){
25
+ $("#result").html(data);
26
+ },
27
+ error: function(data, status, e){
28
+ console.log(data);
29
+ var error = "";
30
+ if(data.status == 0){
31
+ error = "Is the server up?";
32
+ } else {
33
+ error = e;
34
+ }
35
+ $("#result").html("<span style = 'color: red'>Error: " + error + "</span>");
36
+ }
37
+ });
38
+ });
39
+ })(jQuery);
@@ -0,0 +1,46 @@
1
+ jQuery ->
2
+ updateNodes = (tree_nodes) ->
3
+ serialized_tree = tree_nodes.nestable('serialize')
4
+
5
+ $.ajax
6
+ url: tree_nodes.data('update-path'),
7
+ type: 'POST',
8
+ data:
9
+ tree_nodes: serialized_tree
10
+ success: (data) ->
11
+ $flash = $('<div>')
12
+ .addClass('nestable-flash alert alert-success')
13
+ .append( $('<button>').addClass('close').data('dismiss', 'alert').html('&times;') )
14
+ .append( $('<span>').addClass('body').html( data ) )
15
+
16
+ $('#rails_admin_nestable')
17
+ .append( $flash )
18
+
19
+ $flash.fadeIn(200)
20
+ .delay(2000).fadeOut 200, ->
21
+ $(this).remove()
22
+
23
+ $tree_nodes = $('#tree_nodes')
24
+ $tree_nodes_options = {}
25
+ $tree_nodes_max_depth = $tree_nodes.data('max-depth')
26
+ $live_update = $('#rails_admin_nestable input[type=checkbox]')
27
+ $update_button = $('#rails_admin_nestable button')
28
+ live_update_mode = if (!$live_update.length && !$update_button.length) then true else $live_update.prop('checked')
29
+ $('#rails_admin_nestable button').prop('disabled', $live_update.prop('checked'))
30
+
31
+ $live_update.change ->
32
+ live_update_mode = $(this).prop('checked')
33
+ $update_button.prop('disabled', live_update_mode)
34
+
35
+ $update_button.click ->
36
+ updateNodes($tree_nodes)
37
+
38
+ if $tree_nodes_max_depth && $tree_nodes_max_depth != 'false'
39
+ $tree_nodes_options['maxDepth'] = $tree_nodes_max_depth
40
+
41
+ $tree_nodes
42
+ .nestable( $tree_nodes_options )
43
+ .on
44
+ change: (event) ->
45
+ if live_update_mode
46
+ updateNodes($tree_nodes)