rails_admin_nestable 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,481 @@
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 window;
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 eStart = hasTouch ? 'touchstart' : 'mousedown',
30
+ eMove = hasTouch ? 'touchmove' : 'mousemove',
31
+ eEnd = hasTouch ? 'touchend' : 'mouseup';
32
+ eCancel = hasTouch ? 'touchcancel' : 'mouseup';
33
+
34
+ var defaults = {
35
+ listNodeName : 'ol',
36
+ itemNodeName : 'li',
37
+ rootClass : 'dd',
38
+ listClass : 'dd-list',
39
+ itemClass : 'dd-item',
40
+ dragClass : 'dd-dragel',
41
+ handleClass : 'dd-handle',
42
+ collapsedClass : 'dd-collapsed',
43
+ placeClass : 'dd-placeholder',
44
+ emptyClass : 'dd-empty',
45
+ expandBtnHTML : '<button data-action="expand" type="button">Expand</button>',
46
+ collapseBtnHTML : '<button data-action="collapse" type="button">Collapse</button>',
47
+ group : 0,
48
+ maxDepth : 5,
49
+ threshold : 20
50
+ };
51
+
52
+ function Plugin(element, options)
53
+ {
54
+ this.w = $(window);
55
+ this.el = $(element);
56
+ this.options = $.extend({}, defaults, options);
57
+ this.init();
58
+ }
59
+
60
+ Plugin.prototype = {
61
+
62
+ init: function()
63
+ {
64
+ var list = this;
65
+
66
+ list.reset();
67
+
68
+ list.el.data('nestable-group', this.options.group);
69
+
70
+ list.placeEl = $('<div class="' + list.options.placeClass + '"/>');
71
+
72
+ $.each(this.el.find(list.options.itemNodeName), function(k, el) {
73
+ list.setParent($(el));
74
+ });
75
+
76
+ list.el.on('click', 'button', function(e) {
77
+ if (list.dragEl || (!hasTouch && e.button !== 0)) {
78
+ return;
79
+ }
80
+ var target = $(e.currentTarget),
81
+ action = target.data('action'),
82
+ item = target.parent(list.options.itemNodeName);
83
+ if (action === 'collapse') {
84
+ list.collapseItem(item);
85
+ }
86
+ if (action === 'expand') {
87
+ list.expandItem(item);
88
+ }
89
+ });
90
+
91
+ var onStartEvent = function(e)
92
+ {
93
+ var handle = $(e.target);
94
+ if (!handle.hasClass(list.options.handleClass)) {
95
+ handle = handle.parents('.' + list.options.handleClass).first();
96
+ }
97
+ if (!handle.length || list.dragEl || (!hasTouch && e.button !== 0) || (hasTouch && e.touches.length !== 1)) {
98
+ return;
99
+ }
100
+ e.preventDefault();
101
+ list.dragStart(hasTouch ? e.touches[0] : e);
102
+ };
103
+
104
+ var onMoveEvent = function(e)
105
+ {
106
+ if (list.dragEl) {
107
+ e.preventDefault();
108
+ list.dragMove(hasTouch ? e.touches[0] : e);
109
+ }
110
+ };
111
+
112
+ var onEndEvent = function(e)
113
+ {
114
+ if (list.dragEl) {
115
+ e.preventDefault();
116
+ list.dragStop(hasTouch ? e.touches[0] : e);
117
+ }
118
+ };
119
+
120
+ if (hasTouch) {
121
+ list.el[0].addEventListener(eStart, onStartEvent, false);
122
+ window.addEventListener(eMove, onMoveEvent, false);
123
+ window.addEventListener(eEnd, onEndEvent, false);
124
+ window.addEventListener(eCancel, onEndEvent, false);
125
+ } else {
126
+ list.el.on(eStart, onStartEvent);
127
+ list.w.on(eMove, onMoveEvent);
128
+ list.w.on(eEnd, onEndEvent);
129
+ }
130
+
131
+ },
132
+
133
+ serialize: function()
134
+ {
135
+ var data,
136
+ depth = 0,
137
+ list = this;
138
+ step = function(level, depth)
139
+ {
140
+ var array = [ ],
141
+ items = level.children(list.options.itemNodeName);
142
+ items.each(function()
143
+ {
144
+ var li = $(this),
145
+ item = $.extend({}, li.data()),
146
+ sub = li.children(list.options.listNodeName);
147
+ if (sub.length) {
148
+ item.children = step(sub, depth + 1);
149
+ }
150
+ array.push(item);
151
+ });
152
+ return array;
153
+ };
154
+ data = step(list.el.find(list.options.listNodeName).first(), depth);
155
+ return data;
156
+ },
157
+
158
+ serialise: function()
159
+ {
160
+ return this.serialize();
161
+ },
162
+
163
+ reset: function()
164
+ {
165
+ this.mouse = {
166
+ offsetX : 0,
167
+ offsetY : 0,
168
+ startX : 0,
169
+ startY : 0,
170
+ lastX : 0,
171
+ lastY : 0,
172
+ nowX : 0,
173
+ nowY : 0,
174
+ distX : 0,
175
+ distY : 0,
176
+ dirAx : 0,
177
+ dirX : 0,
178
+ dirY : 0,
179
+ lastDirX : 0,
180
+ lastDirY : 0,
181
+ distAxX : 0,
182
+ distAxY : 0
183
+ };
184
+ this.moving = false;
185
+ this.dragEl = null;
186
+ this.dragRootEl = null;
187
+ this.dragDepth = 0;
188
+ this.hasNewRoot = false;
189
+ this.pointEl = null;
190
+ },
191
+
192
+ expandItem: function(li)
193
+ {
194
+ li.removeClass(this.options.collapsedClass);
195
+ li.children('[data-action="expand"]').hide();
196
+ li.children('[data-action="collapse"]').show();
197
+ li.children(this.options.listNodeName).show();
198
+ },
199
+
200
+ collapseItem: function(li)
201
+ {
202
+ var lists = li.children(this.options.listNodeName);
203
+ if (lists.length) {
204
+ li.addClass(this.options.collapsedClass);
205
+ li.children('[data-action="collapse"]').hide();
206
+ li.children('[data-action="expand"]').show();
207
+ li.children(this.options.listNodeName).hide();
208
+ }
209
+ },
210
+
211
+ expandAll: function()
212
+ {
213
+ var list = this;
214
+ list.el.find(list.options.itemNodeName).each(function() {
215
+ list.expandItem($(this));
216
+ });
217
+ },
218
+
219
+ collapseAll: function()
220
+ {
221
+ var list = this;
222
+ list.el.find(list.options.itemNodeName).each(function() {
223
+ list.collapseItem($(this));
224
+ });
225
+ },
226
+
227
+ setParent: function(li)
228
+ {
229
+ if (li.children(this.options.listNodeName).length) {
230
+ li.prepend($(this.options.expandBtnHTML));
231
+ li.prepend($(this.options.collapseBtnHTML));
232
+ }
233
+ li.children('[data-action="expand"]').hide();
234
+ },
235
+
236
+ unsetParent: function(li)
237
+ {
238
+ li.removeClass(this.options.collapsedClass);
239
+ li.children('[data-action]').remove();
240
+ li.children(this.options.listNodeName).remove();
241
+ },
242
+
243
+ dragStart: function(e)
244
+ {
245
+ var mouse = this.mouse,
246
+ target = $(e.target),
247
+ dragItem = target.parents(this.options.itemNodeName).first();
248
+
249
+ this.placeEl.css('height', dragItem.height());
250
+
251
+ mouse.offsetX = e.offsetX !== undefined ? e.offsetX : e.pageX - target.offset().left;
252
+ mouse.offsetY = e.offsetY !== undefined ? e.offsetY : e.pageY - target.offset().top;
253
+ mouse.startX = mouse.lastX = e.pageX;
254
+ mouse.startY = mouse.lastY = e.pageY;
255
+
256
+ this.dragRootEl = this.el;
257
+
258
+ this.dragEl = $(document.createElement(this.options.listNodeName)).addClass(this.options.listClass + ' ' + this.options.dragClass);
259
+ this.dragEl.css('width', dragItem.width());
260
+
261
+ // fix for zepto.js
262
+ //dragItem.after(this.placeEl).detach().appendTo(this.dragEl);
263
+ dragItem.after(this.placeEl);
264
+ dragItem[0].parentNode.removeChild(dragItem[0]);
265
+ dragItem.appendTo(this.dragEl);
266
+
267
+ $(document.body).append(this.dragEl);
268
+ this.dragEl.css({
269
+ 'left' : e.pageX - mouse.offsetX,
270
+ 'top' : e.pageY - mouse.offsetY
271
+ });
272
+ // total depth of dragging item
273
+ var i, depth,
274
+ items = this.dragEl.find(this.options.itemNodeName);
275
+ for (i = 0; i < items.length; i++) {
276
+ depth = $(items[i]).parents(this.options.listNodeName).length;
277
+ if (depth > this.dragDepth) {
278
+ this.dragDepth = depth;
279
+ }
280
+ }
281
+ },
282
+
283
+ dragStop: function(e)
284
+ {
285
+ // fix for zepto.js
286
+ //this.placeEl.replaceWith(this.dragEl.children(this.options.itemNodeName + ':first').detach());
287
+ var el = this.dragEl.children(this.options.itemNodeName).first();
288
+ el[0].parentNode.removeChild(el[0]);
289
+ this.placeEl.replaceWith(el);
290
+
291
+ this.dragEl.remove();
292
+ this.el.trigger('change');
293
+ if (this.hasNewRoot) {
294
+ this.dragRootEl.trigger('change');
295
+ }
296
+ this.reset();
297
+ },
298
+
299
+ dragMove: function(e)
300
+ {
301
+ var list, parent, prev, next, depth,
302
+ opt = this.options,
303
+ mouse = this.mouse;
304
+
305
+ this.dragEl.css({
306
+ 'left' : e.pageX - mouse.offsetX,
307
+ 'top' : e.pageY - mouse.offsetY
308
+ });
309
+
310
+ // mouse position last events
311
+ mouse.lastX = mouse.nowX;
312
+ mouse.lastY = mouse.nowY;
313
+ // mouse position this events
314
+ mouse.nowX = e.pageX;
315
+ mouse.nowY = e.pageY;
316
+ // distance mouse moved between events
317
+ mouse.distX = mouse.nowX - mouse.lastX;
318
+ mouse.distY = mouse.nowY - mouse.lastY;
319
+ // direction mouse was moving
320
+ mouse.lastDirX = mouse.dirX;
321
+ mouse.lastDirY = mouse.dirY;
322
+ // direction mouse is now moving (on both axis)
323
+ mouse.dirX = mouse.distX === 0 ? 0 : mouse.distX > 0 ? 1 : -1;
324
+ mouse.dirY = mouse.distY === 0 ? 0 : mouse.distY > 0 ? 1 : -1;
325
+ // axis mouse is now moving on
326
+ var newAx = Math.abs(mouse.distX) > Math.abs(mouse.distY) ? 1 : 0;
327
+
328
+ // do nothing on first move
329
+ if (!mouse.moving) {
330
+ mouse.dirAx = newAx;
331
+ mouse.moving = true;
332
+ return;
333
+ }
334
+
335
+ // calc distance moved on this axis (and direction)
336
+ if (mouse.dirAx !== newAx) {
337
+ mouse.distAxX = 0;
338
+ mouse.distAxY = 0;
339
+ } else {
340
+ mouse.distAxX += Math.abs(mouse.distX);
341
+ if (mouse.dirX !== 0 && mouse.dirX !== mouse.lastDirX) {
342
+ mouse.distAxX = 0;
343
+ }
344
+ mouse.distAxY += Math.abs(mouse.distY);
345
+ if (mouse.dirY !== 0 && mouse.dirY !== mouse.lastDirY) {
346
+ mouse.distAxY = 0;
347
+ }
348
+ }
349
+ mouse.dirAx = newAx;
350
+
351
+ /**
352
+ * move horizontal
353
+ */
354
+ if (mouse.dirAx && mouse.distAxX >= opt.threshold) {
355
+ // reset move distance on x-axis for new phase
356
+ mouse.distAxX = 0;
357
+ prev = this.placeEl.prev(opt.itemNodeName);
358
+ // increase horizontal level if previous sibling exists and is not collapsed
359
+ if (mouse.distX > 0 && prev.length && !prev.hasClass(opt.collapsedClass)) {
360
+ // cannot increase level when item above is collapsed
361
+ list = prev.find(opt.listNodeName).last();
362
+ // check if depth limit has reached
363
+ depth = this.placeEl.parents(opt.listNodeName).length;
364
+ if (depth + this.dragDepth <= opt.maxDepth) {
365
+ // create new sub-level if one doesn't exist
366
+ if (!list.length) {
367
+ list = $('<' + opt.listNodeName + '/>').addClass(opt.listClass);
368
+ list.append(this.placeEl);
369
+ prev.append(list);
370
+ this.setParent(prev);
371
+ } else {
372
+ // else append to next level up
373
+ list = prev.children(opt.listNodeName).last();
374
+ list.append(this.placeEl);
375
+ }
376
+ }
377
+ }
378
+ // decrease horizontal level
379
+ if (mouse.distX < 0) {
380
+ // we can't decrease a level if an item preceeds the current one
381
+ next = this.placeEl.next(opt.itemNodeName);
382
+ if (!next.length) {
383
+ parent = this.placeEl.parent();
384
+ this.placeEl.parents(opt.itemNodeName).first().after(this.placeEl);
385
+ if (!parent.children().length) {
386
+ this.unsetParent(parent.parent());
387
+ }
388
+ }
389
+ }
390
+ }
391
+
392
+ var isEmpty = false;
393
+
394
+ // find list item under cursor
395
+ if (!hasPointerEvents) {
396
+ this.dragEl[0].style.visibility = 'hidden';
397
+ }
398
+ this.pointEl = $(document.elementFromPoint(e.pageX - document.body.scrollLeft, e.pageY - document.body.scrollTop));
399
+ if (!hasPointerEvents) {
400
+ this.dragEl[0].style.visibility = 'visible';
401
+ }
402
+ if (this.pointEl.hasClass(opt.handleClass)) {
403
+ this.pointEl = this.pointEl.parent(opt.itemNodeName);
404
+ }
405
+ if (this.pointEl.hasClass(opt.emptyClass)) {
406
+ isEmpty = true;
407
+ }
408
+ else if (!this.pointEl.length || !this.pointEl.hasClass(opt.itemClass)) {
409
+ return;
410
+ }
411
+
412
+ // find parent list of item under cursor
413
+ var pointElRoot = this.pointEl.parents('.' + opt.rootClass).first(),
414
+ isNewRoot = this.dragRootEl.data('nestable-id') !== pointElRoot.data('nestable-id');
415
+
416
+ /**
417
+ * move vertical
418
+ */
419
+ if (!mouse.dirAx || isNewRoot || isEmpty) {
420
+ // check if groups match if dragging over new root
421
+ if (isNewRoot && opt.group !== pointElRoot.data('nestable-group')) {
422
+ return;
423
+ }
424
+ // check depth limit
425
+ depth = this.dragDepth - 1 + this.pointEl.parents(opt.listNodeName).length;
426
+ if (depth > opt.maxDepth) {
427
+ return;
428
+ }
429
+ var before = e.pageY < (this.pointEl.offset().top + this.pointEl.height() / 2);
430
+ parent = this.placeEl.parent();
431
+ // if empty create new list to replace empty placeholder
432
+ if (isEmpty) {
433
+ list = $(document.createElement(opt.listNodeName)).addClass(opt.listClass);
434
+ list.append(this.placeEl);
435
+ this.pointEl.replaceWith(list);
436
+ }
437
+ else if (before) {
438
+ this.pointEl.before(this.placeEl);
439
+ }
440
+ else {
441
+ this.pointEl.after(this.placeEl);
442
+ }
443
+ if (!parent.children().length) {
444
+ this.unsetParent(parent.parent());
445
+ }
446
+ if (!this.dragRootEl.find(opt.itemNodeName).length) {
447
+ this.dragRootEl.append('<div class="' + opt.emptyClass + '"/>');
448
+ }
449
+ // parent root list has changed
450
+ if (isNewRoot) {
451
+ this.hasNewRoot = true;
452
+ this.dragRootEl = pointElRoot;
453
+ }
454
+ }
455
+ }
456
+
457
+ };
458
+
459
+ $.fn.nestable = function(params)
460
+ {
461
+ var lists = this,
462
+ retval = this;
463
+
464
+ lists.each(function()
465
+ {
466
+ var plugin = $(this).data("nestable");
467
+
468
+ if (!plugin) {
469
+ $(this).data("nestable", new Plugin(this, params));
470
+ $(this).data("nestable-id", new Date().getTime());
471
+ } else {
472
+ if (typeof params === 'string' && typeof plugin[params] === 'function') {
473
+ retval = plugin[params]();
474
+ }
475
+ }
476
+ });
477
+
478
+ return retval || lists;
479
+ };
480
+
481
+ })(window.jQuery || window.Zepto, window, document);
@@ -1,3 +1,3 @@
1
1
  module RailsAdminNestable
2
- VERSION = "0.0.7"
2
+ VERSION = "0.0.8"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_admin_nestable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.0.8
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-28 00:00:00.000000000 Z
12
+ date: 2012-12-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -50,6 +50,7 @@ executables: []
50
50
  extensions: []
51
51
  extra_rdoc_files: []
52
52
  files:
53
+ - app/assets/javascripts/rails_admin/jquery.nestable.js
53
54
  - app/assets/javascripts/rails_admin/rails_admin_nestable.js.coffee
54
55
  - app/assets/stylesheets/rails_admin/rails_admin_nestable.css
55
56
  - app/helpers/rails_admin/nestable_helper.rb