/*global Gridster:false*/ /*global chai:false, describe:false, beforeEach:false, afterEach:false, it:false*/ 'use strict'; // add #fixture div when running tests with karma if (document.body) { var fixture = document.createElement('div'); fixture.setAttribute('id', 'fixture'); document.querySelector('#fixture') || document.body.appendChild(fixture); } var expect = chai.expect; var serialization = { 'default': function() { return [ {name: 'A', col: 1, row: 1, size_x: 1, size_y: 2}, {name: 'B', col: 2, row: 1, size_x: 3, size_y: 2}, {name: 'C', col: 5, row: 1, size_x: 3, size_y: 2}, {name: 'D', col: 8, row: 1, size_x: 2, size_y: 1}, {name: 'E', col: 1, row: 3, size_x: 4, size_y: 1}, {name: 'F', col: 10, row: 1, size_x: 1, size_y: 2}, {name: 'G', col: 8, row: 2, size_x: 2, size_y: 1}, {name: 'H', col: 5, row: 3, size_x: 3, size_y: 2}, {name: 'I', col: 8, row: 3, size_x: 1, size_y: 1}, {name: 'J', col: 9, row: 3, size_x: 2, size_y: 2}, {name: 'K', col: 1, row: 4, size_x: 1, size_y: 3} ]; } }; var SPEED = 100; var u = { pick: function(data, prop) { return data.map(function(elm) { return elm[prop]; }); }, getRandomColor: function() { var letters = '0123456789ABCDEF'.split(''); var color = '#'; for (var i = 0; i < 6; i++) { color += letters[Math.round(Math.random() * 10)]; } return color; }, createGridster: function(config, serialize, fromDom) { var defaults = { widget_margins: [5, 5], widget_base_dimensions: [100, 55], min_cols: 10, max_cols: 10 }; serialize || (serialize = serialization.default()); var widgets = []; $.each(serialize, function(i, w) { widgets.push(['
  • ' + w.name + '
  • ', w.size_x, w.size_y, w.col, w.row]); }); this.$fixture = $('#fixture'); this.$fixture.html('
    '); this.$el = $(".gridster > ul"); if (fromDom) { var html = []; $.each(serialize, function(i, w) { html.push('
  • ' + w.name + '
  • '); }); this.$el.html(html.join('\n')); } this.gridster = this.$el.gridster( $.extend({}, defaults, config)).data('gridster'); if (!fromDom) { $.each(widgets, function(i, widget) { this.gridster.add_widget.apply(this.gridster, widget); }.bind(this)); } this.drag_from_to = u._dragFromTo; return this.gridster; }, createEvent: function(type, offset) { var event = document.createEvent("MouseEvents"); event.initMouseEvent(type, true, true, window, 0, 0, 0, offset.left, offset.top, false, false, false, false, 0, null); return event; }, dispatchEvent: function(elem, type, event) { if (elem.dispatchEvent) { elem.dispatchEvent(event); } else if (elem.fireEvent) { elem.fireEvent('on' + type, event); } }, _dragFromTo: function(fromCoords, toCoords) { var d = $.Deferred(); var gridster = this.gridster; var $el; function getMousePos(coords) { var size = gridster.options.widget_base_dimensions; var margin = gridster.options.widget_margins; var left = ((coords[0] - 1) * size[0]) + (margin[0] * ((coords[0] * 2) - 1)); var top = ((coords[1] - 1) * size[1]) + (margin[1] * ((coords[1] * 2) - 1)); var parentOffset = gridster.$wrapper.offset(); return { left: parentOffset.left + left + 20, top: parentOffset.top + top + 20 }; } function addPoint(offset) { $('').css({ left: offset.left + 'px', top: offset.top + 'px', width: '2px', height: '2px', background: 'red', display: 'inline-block', position: 'absolute', zIndex: '9999' }).appendTo('body'); } var from_offset; if (fromCoords instanceof $) { $el = fromCoords; var offset = $el.offset(); from_offset = { left: offset.left + ($el.width() / 2), top: offset.top + ($el.height() / 2) }; } else { $el = this.gridster.gridmap[fromCoords[0]][fromCoords[1]]; from_offset = getMousePos(fromCoords); } if (! $el) { return; } var to_offset = getMousePos(toCoords); var el = $el.get(0); addPoint(from_offset); addPoint(to_offset); // Simulating drag start var type = 'mousedown'; var event = u.createEvent(type, from_offset); u.dispatchEvent(el, type, event); // Simulating drop type = 'mousemove'; u.dispatchEvent(el, type, u.createEvent(type, { top: from_offset.top + 2, left: from_offset.left + 2 })); this.gridster.$el.on('gridster:dragstop gridster:resizestop', function() { setTimeout(function() { d.resolveWith(this); }.bind(this), SPEED); }.bind(this)); var diff_x = to_offset.left - from_offset.left; var diff_y = to_offset.top - from_offset.top; var steps = 10; var step_x = diff_x / steps; var step_y = diff_y / steps; var tmp_offset = { left: from_offset.left, top: from_offset.top }; for (var i = 0; i < steps; i++) { tmp_offset.left += step_x; tmp_offset.top += step_y; addPoint(tmp_offset); u.dispatchEvent(el, type, u.createEvent(type, tmp_offset)); } u.dispatchEvent(el, type, u.createEvent(type, to_offset)); addPoint(to_offset); // Simulating drag end type = 'mouseup'; var dragEndEvent = u.createEvent(type, to_offset); u.dispatchEvent(el, type, dragEndEvent); return d.promise(); } }; describe('gridster.js', function() { it('should expose Gridster, Draggable, Coords and Collision classes to window', function() { expect(window.Gridster).to.be.a.function; expect(window.GridsterDraggable).to.be.a.function; expect(window.GridsterCoords).to.be.a.function; expect(window.GridsterCollision).to.be.a.function; }); it('should define the jquery bridge', function() { expect($.fn.gridster).to.be.a('function'); }); describe('Initialization', function() { beforeEach(function() { u.createGridster.call(this, { serialize_params: function($w, wgd) { return { name: $w.text(), col: wgd.col, row: wgd.row, size_x: wgd.size_x, size_y: wgd.size_y }; } }); }); afterEach(function() { this.gridster.destroy(true); }); it('should access the gridster instance', function() { expect(this.$el.data('gridster')).to.be.an('object'); expect(this.$el.data('gridster')).to.have.property('$widgets'); expect(this.$el.data('gridster').$widgets).to.have.length.above(1); }); it('should bind resize event to window', function() { var events = $._data(window, "events"); expect(events).to.have.property('resize'); // both gridster and dragabble resize event handlers expect(events.resize).to.have.length(2); }); it('should respect the serialized positions', function(done) { var serialize = this.gridster.serialize(); expect(serialize).to.deep.equal(serialization.default()); done(); }); }); describe('Instance methods', function() { describe('Register widgets from DOM', function() { beforeEach(function() { u.createGridster.call(this, { serialize_params: function($w, wgd) { return { name: $w.text(), col: wgd.col, row: wgd.row, size_x: wgd.size_x, size_y: wgd.size_y }; } }, null, true); }); afterEach(function() { this.gridster.destroy(true); }); it('should sync DOM widgets with gridster widgets', function() { expect(this.gridster.$widgets).to.have.length(serialization.default().length); }); it('should respect the serialized positions', function() { var serialize = this.gridster.serialize(); expect(serialize).to.deep.equal(serialization.default()); }); }); describe('Destroy', function() { beforeEach(function() { u.createGridster.call(this); }); afterEach(function() { this.gridster.destroy(true); }); it('should clean data attached but keep the grid on DOM', function() { this.gridster.destroy(); expect(this.$el.data('gridster')).to.be.undefined; expect(document.contains(this.$el[0])).to.be.true; }); it('should clean widgets data even if gridster is not removed from DOM', function() { var $el = this.gridster.$widgets.eq(1); this.gridster.destroy(); expect($el.data('coords')).to.be.undefined; }); it('should remove the grid from the DOM', function() { this.gridster.destroy(true); expect(document.contains(this.$el[0])).to.be.false; }); it('should clean widgets data', function() { var $el = this.gridster.$widgets.eq(1); this.gridster.destroy(true); expect($el.data('coords')).to.be.undefined; }); it('should unbind resize event from window', function() { this.gridster.destroy(true); expect($._data(window, "events")).to.be.undefined; }); it('should clean gridmap array', function() { this.gridster.destroy(); expect(this.gridster.gridmap).to.not.have.length; expect(this.gridster.faux_grid).to.not.have.length; }); }); describe('Add a widget dynamically', function() { beforeEach(function() { u.createGridster.call(this); }); afterEach(function() { this.gridster.destroy(true); }); it('should be added to the grid', function() { this.gridster.add_widget('
  • new
  • ', 2, 2); var $el = this.gridster.$el.find('.new'); expect($el).to.have.length(1); }); it('should be positioned in the top/leftmost available space', function(done) { this.gridster.remove_widget('[data-col=1][data-row=1]').done(function() { this.gridster.add_widget('
  • new
  • ', 1, 1); var $el = this.gridster.$el.find('.new'); expect($el.attr('data-col')).to.equal('1'); expect($el.attr('data-row')).to.equal('1'); expect(this.gridster.gridmap[1][1][0]).to.equal($el[0]); done(); }.bind(this)); }); it('should respect the specified dimensions and coords', function() { this.gridster.add_widget('
  • new
  • ', 2, 2, 2, 1); var $el = this.gridster.$el.find('.new'); expect($el.attr('data-col')).to.equal('2'); expect($el.attr('data-row')).to.equal('1'); expect($el.attr('data-sizex')).to.equal('2'); expect($el.attr('data-sizey')).to.equal('2'); expect(this.gridster.gridmap[2][1][0]).to.equal($el[0]); }); }); describe('Remove a widget', function() { beforeEach(function() { u.createGridster.call(this); }); afterEach(function() { this.gridster.destroy(true); }); it('should be removed from the grid', function(done) { var $el = this.gridster.$el.find('[data-col=1][data-row=1]'); this.gridster.remove_widget($el, false).done(function() { expect(this.gridmap[1][1]).to.be.false; expect(document.contains($el[0])).to.be.false; done(); }); }); it('should cause elements below moving up', function(done) { var $el1 = this.gridster.$el.find('[data-col=1][data-row=1]'); var $el2 = this.gridster.$el.find('[data-col=2][data-row=1]'); var $el3 = this.gridster.$el.find('[data-col=1][data-row=3]'); var silent = false; this.gridster.remove_widget($el1, silent).done(function() { this.remove_widget($el2, silent).done(function() { expect($el3.attr('data-col')).to.equal('1'); expect($el3.attr('data-row')).to.equal('1'); done(); }); }); }); it('shouldn\'t cause elements below moving up (silent=true)', function(done) { var $el1 = this.gridster.$el.find('[data-col=1][data-row=1]'); var $el2 = this.gridster.$el.find('[data-col=2][data-row=1]'); var $el3 = this.gridster.$el.find('[data-col=1][data-row=3]'); var silent = true; this.gridster.remove_widget($el1, silent).done(function() { this.remove_widget($el2, silent).done(function() { expect($el3.attr('data-col')).to.equal('1'); expect($el3.attr('data-row')).to.equal('3'); done(); }); }); }); }); describe('Resize a widget', function() { beforeEach(function() { u.createGridster.call(this); }); afterEach(function() { this.gridster.destroy(true); }); it('should be resized to the new dimensions', function(done) { var $el = this.gridster.$el.find('[data-col=1][data-row=1]'); this.gridster.resize_widget($el, 3, 3, function() { expect(this.gridmap[2][2][0]).to.equal($el[0]); done(); }); }); it('should cause elements below to be displaced', function(done) { var $el = this.gridster.$el.find('[data-col=1][data-row=1]'); var $el2 = this.gridster.$el.find('[data-col=2][data-row=1]'); this.gridster.resize_widget($el, 3, 3, function() { expect(this.gridmap[2][4][0]).to.equal($el2[0]); done(); }); }); it('should respect the resizing limits if specified', function(done) { var $el = this.gridster.$el.find('[data-col=1][data-row=1]'); $el.attr({ 'data-max-sizex': 2, 'data-max-sizey': 2 }); this.gridster.resize_widget($el, 3, 3, function() { expect(this.gridmap[3][3][0]).to.equal($el[0]); done(); }); }); }); }); describe('Class methods', function() { describe('Gridster.sort_by_row_asc', function() { it('should sort coords by row in ascending order', function() { var sorted = Gridster.sort_by_row_asc(serialization.default()); var result = u.pick(sorted, 'name').join(','); var expected = 'A,C,D,B,F,G,E,H,I,J,K'; expect(result).to.equal(expected); }); }); describe('Gridster.sort_by_row_and_col_asc', function() { it('should sort coords by row and col (top-left) in ascending order', function() { var sorted = Gridster.sort_by_row_and_col_asc(serialization.default()); var result = u.pick(sorted, 'name').join(','); var expected = 'A,B,C,D,F,G,E,H,I,J,K'; expect(result).to.equal(expected); }); }); describe('Gridster.sort_by_col_asc', function() { it('should sort coords by col in ascending order', function() { var sorted = Gridster.sort_by_col_asc(serialization.default()); var result = u.pick(sorted, 'name').join(','); var expected = 'A,E,K,B,C,H,D,G,I,J,F'; expect(result).to.equal(expected); }); }); describe('Gridster.sort_by_row_desc', function() { it('should sort coords by row in descending order', function() { var sorted = Gridster.sort_by_row_desc(serialization.default()); var result = u.pick(sorted, 'name').join(','); // note that size_y are taken into account var expected = 'K,J,H,E,I,C,B,G,A,F,D'; expect(result).to.equal(expected); }); }); }); describe('Interactions', function() { describe('Drag and drop', function() { beforeEach(function() { u.createGridster.call(this); }); afterEach(function() { this.gridster.destroy(true); }); describe('move up', function() { it('should displace the dragged element one cell above', function(done) { var $el = this.gridster.gridmap[1][3]; this.drag_from_to([1, 3], [1, 1]).done(function() { expect(this.gridster.gridmap[1][1][0]).to.equal($el[0]); done(); }); }); it('should displace elements above under the dragged element', function(done) { var $above1 = this.gridster.gridmap[1][1]; var $above2 = this.gridster.gridmap[2][1]; this.drag_from_to([1, 3], [1, 1]).done(function() { expect(this.gridster.gridmap[1][2][0]).to.equal($above1[0]); expect(this.gridster.gridmap[2][2][0]).to.equal($above2[0]); done(); }); }); }); describe('move down', function() { it('should displace the dragged element one cell below', function(done) { var $el = this.gridster.gridmap[5][1]; this.drag_from_to([5, 1], [5, 6]).done(function() { expect(this.gridster.gridmap[5][4]).to.be.ok; expect(this.gridster.gridmap[5][4][0]).to.equal($el[0]); done(); }); }); it('should displace elements below on the top of the dragged element', function(done) { var $below1 = this.gridster.gridmap[5][3]; this.drag_from_to([5, 1], [5, 6]).done(function() { expect(this.gridster.gridmap[5][1][0]).to.equal($below1[0]); done(); }); }); }); describe('move left', function() { it('should displace the dragged element one cell left', function(done) { var $el = this.gridster.gridmap[2][1]; this.drag_from_to([2, 1], [1, 1]).done(function() { expect(this.gridster.gridmap[1][1]).to.be.ok; expect(this.gridster.gridmap[1][1][0]).to.equal($el[0]); done(); }); }); it('should displace the item on the left under the dragged element', function(done) { var $left = this.gridster.gridmap[1][1]; var $left_bottom = this.gridster.gridmap[1][3]; this.drag_from_to([2, 1], [1, 1]).done(function() { expect(this.gridster.gridmap[1][3]).to.be.ok; expect(this.gridster.gridmap[1][3][0]).to.equal($left[0]); expect(this.gridster.gridmap[1][5]).to.be.ok; expect(this.gridster.gridmap[1][5][0]).to.equal($left_bottom[0]); done(); }); }); }); describe('move right', function() { it('should displace the dragged element one cell right', function(done) { var $el = this.gridster.gridmap[4][3]; this.drag_from_to([4, 3], [5, 3]).done(function() { expect(this.gridster.gridmap[5][3]).to.be.ok; expect(this.gridster.gridmap[5][3][0]).to.equal($el[0]); done(); }); }); it('should displace the item below one cell up', function(done) { var $below = this.gridster.gridmap[1][4]; this.drag_from_to([4, 3], [5, 3]).done(function() { expect(this.gridster.gridmap[1][3]).to.be.ok; expect(this.gridster.gridmap[1][3][0]).to.equal($below[0]); done(); }); }); it('should displace the item on the right one cell down', function(done) { var $right = this.gridster.gridmap[5][3]; this.drag_from_to([4, 3], [5, 3]).done(function() { expect(this.gridster.gridmap[5][4]).to.be.ok; expect(this.gridster.gridmap[5][4][0]).to.equal($right[0]); done(); }); }); }); }); describe('Resize', function() { describe('axis', function() { beforeEach(function() { u.createGridster.call(this, { resize: { enabled: true, axes: ['x', 'y', 'both'] } }); }); afterEach(function() { this.gridster.destroy(true); }); it('should resize in both x/y axis', function(done) { var $el = this.gridster.gridmap[1][1]; var $handle = $el.find('.gs-resize-handle-both'); this.drag_from_to($handle, [7, 4]).done(function() { expect($el.attr('data-sizex')).to.equal('7'); expect($el.attr('data-sizey')).to.equal('4'); this.drag_from_to($handle, [1, 1]).done(function() { expect($el.attr('data-sizex')).to.equal('1'); expect($el.attr('data-sizey')).to.equal('1'); done(); }); }); }); it('should resize horizontally', function(done) { var $el = this.gridster.gridmap[1][1]; var $handle = $el.find('.gs-resize-handle-x'); this.drag_from_to($handle, [6, 1]).done(function() { expect($el.attr('data-sizex')).to.equal('6'); expect($el.attr('data-sizey')).to.equal('2'); this.drag_from_to($handle, [1, 1]).done(function() { expect($el.attr('data-sizex')).to.equal('1'); expect($el.attr('data-sizey')).to.equal('2'); done(); }); }); }); it('should resize vertically', function(done) { var $el = this.gridster.gridmap[1][1]; var $handle = $el.find('.gs-resize-handle-y'); this.drag_from_to($handle, [1, 6]).done(function() { expect($el.attr('data-sizex')).to.equal('1'); expect($el.attr('data-sizey')).to.equal('6'); this.drag_from_to($handle, [1, 1]).done(function() { done(); }); }); }); }); describe('max/min size', function() { beforeEach(function() { u.createGridster.call(this, { resize: { enabled: true, max_size: [4, 4], min_size: [2, 2] } }); }); afterEach(function() { this.gridster.destroy(true); }); it('should respect specified resize.max_size option', function(done) { var $el = this.gridster.gridmap[1][1]; var $handle = $el.find('.gs-resize-handle-both'); this.drag_from_to($handle, [8, 8]).done(function() { expect($el.attr('data-sizex')).to.equal('4'); expect($el.attr('data-sizey')).to.equal('4'); done(); }); }); it('should respect specified resize.min_size option', function(done) { var $el = this.gridster.gridmap[1][1]; var $handle = $el.find('.gs-resize-handle-both'); this.drag_from_to($handle, [8, 8]).done(function() { this.drag_from_to($handle, [1, 1]).done(function() { expect($el.attr('data-sizex')).to.equal('2'); expect($el.attr('data-sizey')).to.equal('2'); done(); }); }); }); }); }); }); });