summaryrefslogtreecommitdiff
path: root/templates
diff options
context:
space:
mode:
authorBen Thomas <github@bigredgiraffe.com>2013-04-14 13:22:06 -0600
committerpushmatrix <daniel.beauchamp@gmail.com>2013-05-09 22:39:13 +0200
commit627504c696541432ba6f53b54754f6c8a547d611 (patch)
treefd7519a8f8850e71536e671f9b4eeb50ae581503 /templates
parent8ad8097ed22cb557a2b5d946d11158575e82cb94 (diff)
updated version of gridster
Diffstat (limited to 'templates')
-rw-r--r--templates/project/assets/javascripts/gridster/jquery.gridster.js687
1 files changed, 530 insertions, 157 deletions
diff --git a/templates/project/assets/javascripts/gridster/jquery.gridster.js b/templates/project/assets/javascripts/gridster/jquery.gridster.js
index 9099177..9451c9a 100644
--- a/templates/project/assets/javascripts/gridster/jquery.gridster.js
+++ b/templates/project/assets/javascripts/gridster/jquery.gridster.js
@@ -1,6 +1,6 @@
-/*! gridster.js - v0.1.0 - 2012-08-14
+/*! gridster.js - v0.1.0 - 2013-02-15
* http://gridster.net/
-* Copyright (c) 2012 ducksboard; Licensed MIT */
+* Copyright (c) 2013 ducksboard; Licensed MIT */
;(function($, window, document, undefined){
/**
@@ -285,12 +285,8 @@
fn.get_closest_colliders = function(player_data_coords){
var colliders = this.find_collisions(player_data_coords);
- var min_area = 100;
- colliders.sort(function(a, b){
- if (a.area <= min_area) {
- return 1;
- }
+ colliders.sort(function(a, b) {
/* if colliders are being overlapped by the "C" (center) region,
* we have to set a lower index in the array to which they are placed
* above in the grid. */
@@ -302,7 +298,7 @@
}
}
- if (a.area < b.area){
+ if (a.area < b.area) {
return 1;
}
@@ -369,8 +365,10 @@
distance: 1,
limit: true,
offset_left: 0,
- autoscroll: true
- // ,drag: function(e){},
+ autoscroll: true,
+ ignore_dragging: ['INPUT', 'TEXTAREA', 'SELECT', 'BUTTON'],
+ handle: null
+ // drag: function(e){},
// start : function(e, ui){},
// stop : function(e){}
};
@@ -424,12 +422,30 @@
fn.init = function() {
this.calculate_positions();
this.$container.css('position', 'relative');
- this.enable();
+ this.disabled = false;
+ this.events();
- $(window).bind('resize',
- throttle($.proxy(this.calculate_positions, this), 200));
+ this.on_window_resize = throttle($.proxy(this.calculate_positions, this), 200);
+ $(window).bind('resize', this.on_window_resize);
};
+ fn.events = function() {
+ this.proxied_on_select_start = $.proxy(this.on_select_start, this);
+ this.$container.on('selectstart', this.proxied_on_select_start);
+
+ this.proxied_drag_handler = $.proxy(this.drag_handler, this);
+ this.$container.on(pointer_events.start, this.options.items, this.proxied_drag_handler);
+
+ this.proxied_pointer_events_end = $.proxy(function(e) {
+ this.is_dragging = false;
+ if (this.disabled) { return; }
+ this.$body.off(pointer_events.move);
+ if (this.drag_start) {
+ this.on_dragstop(e);
+ }
+ }, this);
+ this.$body.on(pointer_events.end, this.proxied_pointer_events_end);
+ };
fn.get_actual_pos = function($el) {
var pos = $el.position();
@@ -441,7 +457,7 @@
if (isTouch) {
var oe = e.originalEvent;
e = oe.touches.length ? oe.touches[0] : oe.changedTouches[0];
- };
+ }
return {
left: e.clientX,
@@ -500,7 +516,7 @@
$window.scrollTop(nextScrollTop);
this.scrollOffset = this.scrollOffset + 30;
}
- };
+ }
if (abs_mouse_top <= mouse_up_zone) {
nextScrollTop = scrollTop - 30;
@@ -508,26 +524,24 @@
$window.scrollTop(nextScrollTop);
this.scrollOffset = this.scrollOffset - 30;
}
- };
- }
+ }
+ };
fn.calculate_positions = function(e) {
this.window_height = $window.height();
- }
+ };
fn.drag_handler = function(e) {
var node = e.target.nodeName;
-
- if (e.which !== 1 && !isTouch) {
+ if (this.disabled || e.which !== 1 && !isTouch) {
return;
}
- if (node === 'INPUT' || node === 'TEXTAREA' || node === 'SELECT' ||
- node === 'BUTTON') {
+ if (this.ignore_drag(e)) {
return;
- };
+ }
var self = this;
var first = true;
@@ -537,7 +551,7 @@
this.mouse_init_pos = this.get_mouse_pos(e);
this.offsetY = this.mouse_init_pos.top - this.el_init_pos.top;
- this.$body.on(pointer_events.move, function(mme){
+ this.on_pointer_events_move = function(mme){
var mouse_actual_pos = self.get_mouse_pos(mme);
var diff_x = Math.abs(
mouse_actual_pos.left - self.mouse_init_pos.left);
@@ -545,7 +559,7 @@
mouse_actual_pos.top - self.mouse_init_pos.top);
if (!(diff_x > self.options.distance ||
diff_y > self.options.distance)
- ) {
+ ) {
return false;
}
@@ -555,12 +569,14 @@
return false;
}
- if (self.is_dragging == true) {
+ if (self.is_dragging === true) {
self.on_dragmove.call(self, mme);
}
return false;
- });
+ };
+
+ this.$body.on(pointer_events.move, this.on_pointer_events_move);
return false;
};
@@ -646,38 +662,43 @@
};
fn.on_select_start = function(e) {
- return false;
- }
+ if (this.disabled) { return; }
+ if (this.ignore_drag(e)) {
+ return;
+ }
- fn.enable = function(){
- this.$container.on('selectstart', this.on_select_start);
-
- this.$container.on(pointer_events.start, this.options.items, $.proxy(
- this.drag_handler, this));
-
- this.$body.on(pointer_events.end, $.proxy(function(e) {
- this.is_dragging = false;
- this.$body.off(pointer_events.move);
- if (this.drag_start) {
- this.on_dragstop(e);
- }
- }, this));
+ return false;
};
+ fn.enable = function() {
+ this.disabled = false;
+ };
- fn.disable = function(){
- this.$container.off(pointer_events.start);
- this.$body.off(pointer_events.end);
- this.$container.off('selectstart', this.on_select_start);
+ fn.disable = function() {
+ this.disabled = true;
};
fn.destroy = function(){
this.disable();
+
+ this.$container.off('selectstart', this.proxied_on_select_start);
+ this.$container.off(pointer_events.start, this.proxied_drag_handler);
+ this.$body.off(pointer_events.end, this.proxied_pointer_events_end);
+ this.$body.off(pointer_events.move, this.on_pointer_events_move);
+ $(window).unbind('resize', this.on_window_resize);
+
$.removeData(this.$container, 'drag');
};
+ fn.ignore_drag = function(event) {
+ if (this.options.handle) {
+ return !$(event.target).is(this.options.handle);
+ }
+
+ return $.inArray(event.target.nodeName, this.options.ignore_dragging) >= 0;
+ };
//jQuery adapter
$.fn.drag = function ( options ) {
@@ -694,7 +715,8 @@
;(function($, window, document, undefined) {
var defaults = {
- widget_selector: '> li',
+ namespace: '',
+ widget_selector: 'li',
widget_margins: [10, 10],
widget_base_dimensions: [400, 225],
extra_rows: 0,
@@ -702,13 +724,14 @@
min_cols: 1,
min_rows: 15,
max_size_x: 6,
- max_size_y: 6,
autogenerate_stylesheet: true,
avoid_overlapped_widgets: true,
serialize_params: function($w, wgd) {
return {
col: wgd.col,
- row: wgd.row
+ row: wgd.row,
+ size_x: wgd.size_x,
+ size_y: wgd.size_y
};
},
collision: {},
@@ -742,8 +765,6 @@
* @param {Number} [options.min_rows] The minimum required rows.
* @param {Number} [options.max_size_x] The maximum number of columns
* that a widget can span.
- * @param {Number} [options.max_size_y] The maximum number of rows
- * that a widget can span.
* @param {Boolean} [options.autogenerate_stylesheet] If true, all the
* CSS required to position all widgets in their respective columns
* and rows will be generated automatically and injected to the
@@ -770,7 +791,7 @@
this.options = $.extend(true, defaults, options);
this.$el = $(el);
this.$wrapper = this.$el.parent();
- this.$widgets = $(this.options.widget_selector, this.$el).addClass('gs_w');
+ this.$widgets = this.$el.children(this.options.widget_selector).addClass('gs_w');
this.widgets = [];
this.$changed = $([]);
this.wrapper_width = this.$wrapper.width();
@@ -786,14 +807,15 @@
var fn = Gridster.prototype;
fn.init = function() {
- this.generate_grid_and_stylesheet();
- this.get_widgets_from_DOM();
- this.set_dom_grid_height();
- this.$wrapper.addClass('ready');
- this.draggable();
+ this.generate_grid_and_stylesheet();
+ this.get_widgets_from_DOM();
+ this.set_dom_grid_height();
+ this.$wrapper.addClass('ready');
+ this.draggable();
+
+ this.on_window_resize = throttle($.proxy(this.recalculate_faux_grid, this), 200);
- $(window).bind(
- 'resize', throttle($.proxy(this.recalculate_faux_grid, this), 200));
+ $(window).bind('resize', this.on_window_resize);
};
@@ -826,32 +848,251 @@
* Add a new widget to the grid.
*
* @method add_widget
- * @param {String} html The string representing the HTML of the widget.
- * @param {Number} size_x The nº of rows the widget occupies horizontally.
- * @param {Number} size_y The nº of columns the widget occupies vertically.
+ * @param {String|HTMLElement} html The string representing the HTML of the widget
+ * or the HTMLElement.
+ * @param {Number} [size_x] The nº of rows the widget occupies horizontally.
+ * @param {Number} [size_y] The nº of columns the widget occupies vertically.
+ * @param {Number} [col] The column the widget should start in.
+ * @param {Number} [row] The row the widget should start in.
* @return {HTMLElement} Returns the jQuery wrapped HTMLElement representing.
* the widget that was just created.
*/
- fn.add_widget = function(html, size_x, size_y) {
- var next_pos = this.next_position(size_x, size_y);
+ fn.add_widget = function(html, size_x, size_y, col, row) {
+ var pos;
+ size_x || (size_x = 1);
+ size_y || (size_y = 1);
+
+ if (!col & !row) {
+ pos = this.next_position(size_x, size_y);
+ }else{
+ pos = {
+ col: col,
+ row: row
+ };
+
+ this.empty_cells(col, row, size_x, size_y);
+ }
var $w = $(html).attr({
- 'data-col': next_pos.col,
- 'data-row': next_pos.row,
- 'data-sizex' : next_pos.size_x,
- 'data-sizey' : next_pos.size_y
+ 'data-col': pos.col,
+ 'data-row': pos.row,
+ 'data-sizex' : size_x,
+ 'data-sizey' : size_y
}).addClass('gs_w').appendTo(this.$el).hide();
this.$widgets = this.$widgets.add($w);
this.register_widget($w);
+ this.add_faux_rows(pos.size_y);
+ //this.add_faux_cols(pos.size_x);
+
this.set_dom_grid_height();
return $w.fadeIn();
};
+
+ /**
+ * Change the size of a widget.
+ *
+ * @method resize_widget
+ * @param {HTMLElement} $widget The jQuery wrapped HTMLElement
+ * representing the widget.
+ * @param {Number} size_x The number of columns that will occupy the widget.
+ * @param {Number} size_y The number of rows that will occupy the widget.
+ * @return {HTMLElement} Returns $widget.
+ */
+ fn.resize_widget = function($widget, size_x, size_y) {
+ var wgd = $widget.coords().grid;
+ size_x || (size_x = wgd.size_x);
+ size_y || (size_y = wgd.size_y);
+
+ if (size_x > this.cols) {
+ size_x = this.cols;
+ }
+
+ var old_cells_occupied = this.get_cells_occupied(wgd);
+ var old_size_x = wgd.size_x;
+ var old_size_y = wgd.size_y;
+ var old_col = wgd.col;
+ var new_col = old_col;
+ var wider = size_x > old_size_x;
+ var taller = size_y > old_size_y;
+
+ if (old_col + size_x - 1 > this.cols) {
+ var diff = old_col + (size_x - 1) - this.cols;
+ var c = old_col - diff;
+ new_col = Math.max(1, c);
+ }
+
+ var new_grid_data = {
+ col: new_col,
+ row: wgd.row,
+ size_x: size_x,
+ size_y: size_y
+ };
+
+ var new_cells_occupied = this.get_cells_occupied(new_grid_data);
+
+ var empty_cols = [];
+ $.each(old_cells_occupied.cols, function(i, col) {
+ if ($.inArray(col, new_cells_occupied.cols) === -1) {
+ empty_cols.push(col);
+ }
+ });
+
+ var occupied_cols = [];
+ $.each(new_cells_occupied.cols, function(i, col) {
+ if ($.inArray(col, old_cells_occupied.cols) === -1) {
+ occupied_cols.push(col);
+ }
+ });
+
+ var empty_rows = [];
+ $.each(old_cells_occupied.rows, function(i, row) {
+ if ($.inArray(row, new_cells_occupied.rows) === -1) {
+ empty_rows.push(row);
+ }
+ });
+
+ var occupied_rows = [];
+ $.each(new_cells_occupied.rows, function(i, row) {
+ if ($.inArray(row, old_cells_occupied.rows) === -1) {
+ occupied_rows.push(row);
+ }
+ });
+
+ this.remove_from_gridmap(wgd);
+
+ if (occupied_cols.length) {
+ var cols_to_empty = [
+ new_col, wgd.row, size_x, Math.min(old_size_y, size_y), $widget
+ ];
+ this.empty_cells.apply(this, cols_to_empty);
+ }
+
+ if (occupied_rows.length) {
+ var rows_to_empty = [new_col, wgd.row, size_x, size_y, $widget];
+ this.empty_cells.apply(this, rows_to_empty);
+ }
+
+ wgd.col = new_col;
+ wgd.size_x = size_x;
+ wgd.size_y = size_y;
+ this.add_to_gridmap(new_grid_data, $widget);
+
+ //update coords instance attributes
+ $widget.data('coords').update({
+ width: (size_x * this.options.widget_base_dimensions[0] +
+ ((size_x - 1) * this.options.widget_margins[0]) * 2),
+ height: (size_y * this.options.widget_base_dimensions[1] +
+ ((size_y - 1) * this.options.widget_margins[1]) * 2)
+ });
+
+ if (size_y > old_size_y) {
+ this.add_faux_rows(size_y - old_size_y);
+ }
+
+ if (size_x > old_size_x) {
+ this.add_faux_cols(size_x - old_size_x);
+ }
+
+ $widget.attr({
+ 'data-col': new_col,
+ 'data-sizex': size_x,
+ 'data-sizey': size_y
+ });
+
+ if (empty_cols.length) {
+ var cols_to_remove_holes = [
+ empty_cols[0], wgd.row,
+ empty_cols.length,
+ Math.min(old_size_y, size_y),
+ $widget
+ ];
+
+ this.remove_empty_cells.apply(this, cols_to_remove_holes);
+ }
+
+ if (empty_rows.length) {
+ var rows_to_remove_holes = [
+ new_col, wgd.row, size_x, size_y, $widget
+ ];
+ this.remove_empty_cells.apply(this, rows_to_remove_holes);
+ }
+
+ return $widget;
+ };
+
+ /**
+ * Move down widgets in cells represented by the arguments col, row, size_x,
+ * size_y
+ *
+ * @method empty_cells
+ * @param {Number} col The column where the group of cells begin.
+ * @param {Number} row The row where the group of cells begin.
+ * @param {Number} size_x The number of columns that the group of cells
+ * occupy.
+ * @param {Number} size_y The number of rows that the group of cells
+ * occupy.
+ * @param {HTMLElement} $exclude Exclude widgets from being moved.
+ * @return {Class} Returns the instance of the Gridster Class.
+ */
+ fn.empty_cells = function(col, row, size_x, size_y, $exclude) {
+ var $nexts = this.widgets_below({
+ col: col,
+ row: row - size_y,
+ size_x: size_x,
+ size_y: size_y
+ });
+
+ $nexts.not($exclude).each($.proxy(function(i, w) {
+ var wgd = $(w).coords().grid;
+ if (!(wgd.row <= (row + size_y - 1))) { return; }
+ var diff = (row + size_y) - wgd.row;
+ this.move_widget_down($(w), diff);
+ }, this));
+
+ this.set_dom_grid_height();
+
+ return this;
+ };
+
+
+ /**
+ * Move up widgets below cells represented by the arguments col, row, size_x,
+ * size_y.
+ *
+ * @method remove_empty_cells
+ * @param {Number} col The column where the group of cells begin.
+ * @param {Number} row The row where the group of cells begin.
+ * @param {Number} size_x The number of columns that the group of cells
+ * occupy.
+ * @param {Number} size_y The number of rows that the group of cells
+ * occupy.
+ * @param {HTMLElement} exclude Exclude widgets from being moved.
+ * @return {Class} Returns the instance of the Gridster Class.
+ */
+ fn.remove_empty_cells = function(col, row, size_x, size_y, exclude) {
+ var $nexts = this.widgets_below({
+ col: col,
+ row: row,
+ size_x: size_x,
+ size_y: size_y
+ });
+
+ $nexts.not(exclude).each($.proxy(function(i, widget) {
+ this.move_widget_up( $(widget), size_y );
+ }, this));
+
+ this.set_dom_grid_height();
+
+ return this;
+ };
+
+
/**
* Get the most left column below to add a new widget.
*
@@ -867,9 +1108,10 @@
var ga = this.gridmap;
var cols_l = ga.length;
var valid_pos = [];
+ var rows_l;
for (var c = 1; c < cols_l; c++) {
- var rows_l = ga[c].length;
+ rows_l = ga[c].length;
for (var r = 1; r <= rows_l; r++) {
var can_move_to = this.can_move_to({
size_x: size_x,
@@ -899,12 +1141,21 @@
*
* @method remove_widget
* @param {HTMLElement} el The jQuery wrapped HTMLElement you want to remove.
+ * @param {Boolean|Function} silent If true, widgets below the removed one
+ * will not move up. If a Function is passed it will be used as callback.
+ * @param {Function} callback Function executed when the widget is removed.
* @return {Class} Returns the instance of the Gridster Class.
*/
- fn.remove_widget = function(el, callback) {
+ fn.remove_widget = function(el, silent, callback) {
var $el = el instanceof jQuery ? el : $(el);
var wgd = $el.coords().grid;
+ // if silent is a function assume it's a callback
+ if ($.isFunction(silent)) {
+ callback = silent;
+ silent = false;
+ }
+
this.cells_occupied_by_placeholder = {};
this.$widgets = this.$widgets.not($el);
@@ -915,9 +1166,11 @@
$el.fadeOut($.proxy(function() {
$el.remove();
- $nexts.each($.proxy(function(i, widget) {
- this.move_widget_up( $(widget), wgd.size_y );
- }, this));
+ if (!silent) {
+ $nexts.each($.proxy(function(i, widget) {
+ this.move_widget_up( $(widget), wgd.size_y );
+ }, this));
+ }
this.set_dom_grid_height();
@@ -929,6 +1182,22 @@
/**
+ * Remove all widgets from the grid.
+ *
+ * @method remove_all_widgets
+ * @param {Function} callback Function executed for each widget removed.
+ * @return {Class} Returns the instance of the Gridster Class.
+ */
+ fn.remove_all_widgets = function(callback) {
+ this.$widgets.each($.proxy(function(i, el){
+ this.remove_widget(el, true, callback);
+ }, this));
+
+ return this;
+ };
+
+
+ /**
* Returns a serialized array of the widgets in the grid.
*
* @method serialize
@@ -1001,7 +1270,7 @@
$el.data('coords').grid = wgd;
this.add_to_gridmap(wgd, $el);
- this.widgets.push($el);
+
return this;
};
@@ -1073,7 +1342,6 @@
var self = this;
var draggable_options = $.extend(true, {}, this.options.draggable, {
offset_left: this.options.widget_margins[0],
- items: '.gs_w',
start: function(event, ui) {
self.$widgets.filter('.player-revert')
.removeClass('player-revert');
@@ -1105,8 +1373,8 @@
* This function is executed when the player begins to be dragged.
*
* @method on_start_drag
- * @param {Event} The original browser event
- * @param {Object} A prepared ui object.
+ * @param {Event} event The original browser event
+ * @param {Object} ui A prepared ui object.
*/
fn.on_start_drag = function(event, ui) {
@@ -1156,14 +1424,14 @@
* This function is executed when the player is being dragged.
*
* @method on_drag
- * @param {Event} The original browser event
- * @param {Object} A prepared ui object.
+ * @param {Event} event The original browser event
+ * @param {Object} ui A prepared ui object.
*/
fn.on_drag = function(event, ui) {
//break if dragstop has been fired
if (this.$player === null) {
return false;
- };
+ }
var abs_offset = {
left: ui.position.left + this.baseX,
@@ -1199,8 +1467,8 @@
* This function is executed when the player stops being dragged.
*
* @method on_stop_drag
- * @param {Event} The original browser event
- * @param {Object} A prepared ui object.
+ * @param {Event} event The original browser event
+ * @param {Object} ui A prepared ui object.
*/
fn.on_stop_drag = function(event, ui) {
this.$helper.add(this.$player).add(this.$wrapper)
@@ -1244,7 +1512,13 @@
}
this.$preview_holder.remove();
+
this.$player = null;
+ this.$helper = null;
+ this.placeholder_grid_data = {};
+ this.player_grid_data = {};
+ this.cells_occupied_by_placeholder = {};
+ this.cells_occupied_by_player = {};
this.set_dom_grid_height();
};
@@ -1263,7 +1537,7 @@
*/
fn.on_overlapped_column_change = function(start_callback, stop_callback) {
if (!this.colliders_data.length) {
- return;
+ return this;
}
var cols = this.get_targeted_columns(
this.colliders_data[0].el.data.col);
@@ -1296,14 +1570,14 @@
*
* @param {Function} start_callback Function executed when a new row begins
* to be overlapped. The row is passed as first argument.
- * @param {Function} stop_callback Function executed when a row stops being
+ * @param {Function} end_callback Function executed when a row stops being
* overlapped. The row is passed as first argument.
* @method on_overlapped_row_change
* @return {Class} Returns the instance of the Gridster Class.
*/
fn.on_overlapped_row_change = function(start_callback, end_callback) {
if (!this.colliders_data.length) {
- return;
+ return this;
}
var rows = this.get_targeted_rows(this.colliders_data[0].el.data.row);
var last_n_rows = this.last_rows.length;
@@ -1329,18 +1603,18 @@
/**
* Sets the current position of the player
*
- * @param {Function} start_callback Function executed when a new row begins
- * to be overlapped. The row is passed as first argument.
- * @param {Function} stop_callback Function executed when a row stops being
- * overlapped. The row is passed as first argument.
+ * @param {Number} col
+ * @param {Number} row
+ * @param {Boolean} no_player
* @method set_player
- * @return {Class} Returns the instance of the Gridster Class.
+ * @return {object}
*/
- fn.set_player = function(col, row) {
- this.empty_cells_player_occupies();
-
+ fn.set_player = function(col, row, no_player) {
var self = this;
- var cell = self.colliders_data[0].el.data;
+ if (!no_player) {
+ this.empty_cells_player_occupies();
+ }
+ var cell = !no_player ? self.colliders_data[0].el.data : {col: col};
var to_col = cell.col;
var to_row = row || cell.row;
@@ -1384,9 +1658,9 @@
* a upper row and which not.
*
* @method widgets_contraints
- * @param {HTMLElements} $widgets A jQuery wrapped collection of
+ * @param {jQuery} $widgets A jQuery wrapped collection of
* HTMLElements.
- * @return {Array} Returns a literal Object with two keys: `can_go_up` &
+ * @return {object} Returns a literal Object with two keys: `can_go_up` &
* `can_not_go_up`. Each contains a set of HTMLElements.
*/
fn.widgets_constraints = function($widgets) {
@@ -1425,6 +1699,11 @@
*/
fn.sort_by_row_asc = function(widgets) {
widgets = widgets.sort(function(a, b) {
+ if (!a.row) {
+ a = $(a).coords().grid;
+ b = $(b).coords().grid;
+ }
+
if (a.row > b.row) {
return 1;
}
@@ -1445,7 +1724,7 @@
*/
fn.sort_by_row_and_col_asc = function(widgets) {
widgets = widgets.sort(function(a, b) {
- if (a.row > b.row || a.row == b.row && a.col > b.col) {
+ if (a.row > b.row || a.row === b.row && a.col > b.col) {
return 1;
}
return -1;
@@ -1499,7 +1778,7 @@
* each widget) in descending way.
*
* @method manage_movements
- * @param {HTMLElements} $widgets A jQuery collection of HTMLElements
+ * @param {jQuery} $widgets A jQuery collection of HTMLElements
* representing the widgets you want to move.
* @param {Number} to_col The column to which we want to move the widgets.
* @param {Number} to_row The row to which we want to move the widgets.
@@ -1680,13 +1959,14 @@
/**
- * Get widgets overlapping with the player.
+ * Get widgets overlapping with the player or with the object passed
+ * representing the grid cells.
*
* @method get_widgets_under_player
* @return {HTMLElement} Returns a jQuery collection of HTMLElements
*/
- fn.get_widgets_under_player = function() {
- var cells = this.cells_occupied_by_player;
+ fn.get_widgets_under_player = function(cells) {
+ cells || (cells = this.cells_occupied_by_player || {cols: [], rows: []});
var $widgets = $([]);
$.each(cells.cols, $.proxy(function(i, col) {
@@ -1720,7 +2000,7 @@
size_x: phgd.size_x
});
- //Prevents widgets go out of the grid
+ // Prevents widgets go out of the grid
var right_col = (col + phgd.size_x - 1);
if (right_col > this.cols) {
col = col - (right_col - col);
@@ -1747,6 +2027,16 @@
}, this));
}
+
+ var $widgets_under_ph = this.get_widgets_under_player(this.cells_occupied_by_placeholder);
+ if ($widgets_under_ph.length) {
+ $widgets_under_ph.each($.proxy(function(i, widget) {
+ var $w = $(widget);
+ this.move_widget_down(
+ $w, row + phgd.size_y - $w.data('coords').grid.row);
+ }, this));
+ }
+
};
@@ -1814,33 +2104,26 @@
var upper_rows = [];
var min_row = 10000;
- if (widget_grid_data.col < this.player_grid_data.col &&
- (widget_grid_data.col + widget_grid_data.size_y - 1) >
- (this.player_grid_data.col + this.player_grid_data.size_y - 1)
- ) {
- return false;
- };
-
- /* generate an array with columns as index and array with upper rows
+ /* generate an array with columns as index and array with topmost rows
* empty as value */
this.for_each_column_occupied(widget_grid_data, function(tcol) {
var grid_col = this.gridmap[tcol];
upper_rows[tcol] = [];
var r = p_bottom_row + 1;
-
+ // iterate over each row
while (--r > 0) {
if (this.is_widget(tcol, r) && !this.is_player_in(tcol, r)) {
if (!grid_col[r].is(widget_grid_data.el)) {
break;
- };
+ }
}
if (!this.is_player(tcol, r) &&
!this.is_placeholder_in(tcol, r) &&
!this.is_player_in(tcol, r)) {
upper_rows[tcol].push(r);
- };
+ }
if (r < min_row) {
min_row = r;
@@ -1942,7 +2225,7 @@
* Get widgets overlapping with the player.
*
* @method get_widgets_overlapped
- * @return {HTMLElements} Returns a jQuery collection of HTMLElements.
+ * @return {jQuery} Returns a jQuery collection of HTMLElements.
*/
fn.get_widgets_overlapped = function() {
var $w;
@@ -1956,7 +2239,6 @@
// if there is a widget in the player position
if (!this.gridmap[col]) { return true; } //next iteration
var $w = this.gridmap[col][row];
-
if (this.is_occupied(col, row) && !this.is_player($w) &&
$.inArray($w, used) === -1
) {
@@ -1976,7 +2258,7 @@
*
* @method on_start_overlapping_column
* @param {Number} col The collided column.
- * @return {HTMLElements} Returns a jQuery collection of HTMLElements.
+ * @return {jQuery} Returns a jQuery collection of HTMLElements.
*/
fn.on_start_overlapping_column = function(col) {
this.set_player(col, false);
@@ -1987,8 +2269,8 @@
* A callback executed when the player begins to collide with a row.
*
* @method on_start_overlapping_row
- * @param {Number} col The collided row.
- * @return {HTMLElements} Returns a jQuery collection of HTMLElements.
+ * @param {Number} row The collided row.
+ * @return {jQuery} Returns a jQuery collection of HTMLElements.
*/
fn.on_start_overlapping_row = function(row) {
this.set_player(false, row);
@@ -2000,7 +2282,7 @@
*
* @method on_stop_overlapping_column
* @param {Number} col The collided row.
- * @return {HTMLElements} Returns a jQuery collection of HTMLElements.
+ * @return {jQuery} Returns a jQuery collection of HTMLElements.
*/
fn.on_stop_overlapping_column = function(col) {
this.set_player(col, false);
@@ -2018,7 +2300,7 @@
*
* @method on_stop_overlapping_row
* @param {Number} row The collided row.
- * @return {HTMLElements} Returns a jQuery collection of HTMLElements.
+ * @return {jQuery} Returns a jQuery collection of HTMLElements.
*/
fn.on_stop_overlapping_row = function(row) {
this.set_player(false, row);
@@ -2128,9 +2410,9 @@
* Move down the specified widget and all below it.
*
* @method move_widget_down
- * @param {HTMLElement} $widget The jQuery object representing the widget
+ * @param {jQuery} $widget The jQuery object representing the widget
* you want to move.
- * @param {Number} The number of cells that the widget has to move.
+ * @param {Number} y_units The number of cells that the widget has to move.
* @return {Class} Returns the instance of the Gridster Class.
*/
fn.move_widget_down = function($widget, y_units) {
@@ -2266,7 +2548,7 @@
*
* @method widgets_below
* @param {HTMLElement} $el The jQuery wrapped HTMLElement.
- * @return {HTMLElements} A jQuery collection of HTMLElements.
+ * @return {jQuery} A jQuery collection of HTMLElements.
*/
fn.widgets_below = function($el) {
var el_grid_data = $.isPlainObject($el) ? $el : $el.coords().grid;
@@ -2276,14 +2558,12 @@
var $nexts = $([]);
this.for_each_column_occupied(el_grid_data, function(col) {
- self.for_each_widget_below(col, next_row,
- function(tcol, trow) {
- if (!self.is_player(this) &&
- $.inArray(this, $nexts) === -1) {
- $nexts = $nexts.add(this);
- return true; // break
- }
- });
+ self.for_each_widget_below(col, next_row, function(tcol, trow) {
+ if (!self.is_player(this) && $.inArray(this, $nexts) === -1) {
+ $nexts = $nexts.add(this);
+ return true; // break
+ }
+ });
});
return this.sort_by_row_asc($nexts);
@@ -2331,6 +2611,7 @@
this.for_each_column_occupied(el_grid_data, function(col) {
var $w = this.is_widget(col, prev_row);
+
if (this.is_occupied(col, prev_row) ||
this.is_player(col, prev_row) ||
this.is_placeholder_in(col, prev_row) ||
@@ -2356,9 +2637,10 @@
* the widget.
* @param {Object} col The col to check.
* @param {Object} row The row to check.
+ * @param {Number} [max_row] The max row allowed.
* @return {Boolean} Returns true if all cells are empty, else return false.
*/
- fn.can_move_to = function(widget_grid_data, col, row) {
+ fn.can_move_to = function(widget_grid_data, col, row, max_row) {
var ga = this.gridmap;
var $w = widget_grid_data.el;
var future_wd = {
@@ -2373,7 +2655,11 @@
var right_col = col + widget_grid_data.size_x - 1;
if (right_col > this.cols) {
return false;
- };
+ }
+
+ if (max_row && max_row < row + widget_grid_data.size_y - 1) {
+ return false;
+ }
this.for_each_cell_occupied(future_wd, function(tcol, trow) {
var $tw = this.is_widget(tcol, trow);
@@ -2646,7 +2932,7 @@
}
return $widgets;
- }
+ };
/**
@@ -2672,16 +2958,16 @@
*/
fn.generate_stylesheet = function(opts) {
var styles = '';
- var extra_cells = 10;
- var max_size_y = this.options.max_size_y;
var max_size_x = this.options.max_size_x;
+ var max_rows = 0;
+ var max_cols = 0;
var i;
var rules;
opts || (opts = {});
opts.cols || (opts.cols = this.cols);
opts.rows || (opts.rows = this.rows);
- opts.namespace || (opts.namespace = '');
+ opts.namespace || (opts.namespace = this.options.namespace);
opts.widget_base_dimensions ||
(opts.widget_base_dimensions = this.options.widget_base_dimensions);
opts.widget_margins ||
@@ -2691,8 +2977,8 @@
opts.min_widget_height = (opts.widget_margins[1] * 2) +
opts.widget_base_dimensions[1];
- var serialized_opts = $.param(opts);
// don't duplicate stylesheets for the same configuration
+ var serialized_opts = $.param(opts);
if ($.inArray(serialized_opts, Gridster.generated_stylesheets) >= 0) {
return false;
}
@@ -2700,7 +2986,7 @@
Gridster.generated_stylesheets.push(serialized_opts);
/* generate CSS styles for cols */
- for (i = opts.cols + extra_cells; i >= 0; i--) {
+ for (i = opts.cols; i >= 0; i--) {
styles += (opts.namespace + ' [data-col="'+ (i + 1) + '"] { left:' +
((i * opts.widget_base_dimensions[0]) +
(i * opts.widget_margins[0]) +
@@ -2708,14 +2994,14 @@
}
/* generate CSS styles for rows */
- for (i = opts.rows + extra_cells; i >= 0; i--) {
+ for (i = opts.rows; i >= 0; i--) {
styles += (opts.namespace + ' [data-row="' + (i + 1) + '"] { top:' +
((i * opts.widget_base_dimensions[1]) +
(i * opts.widget_margins[1]) +
((i + 1) * opts.widget_margins[1]) ) + 'px;} ');
}
- for (var y = 1; y <= max_size_y; y++) {
+ for (var y = 1; y <= opts.rows; y++) {
styles += (opts.namespace + ' [data-sizey="' + y + '"] { height:' +
(y * opts.widget_base_dimensions[1] +
(y - 1) * (opts.widget_margins[1] * 2)) + 'px;}');
@@ -2771,7 +3057,23 @@
for (col = cols; col > 0; col--) {
this.gridmap[col] = [];
for (row = rows; row > 0; row--) {
- var coords = $({
+ this.add_faux_cell(row, col);
+ }
+ }
+ return this;
+ };
+
+
+ /**
+ * Add cell to the faux grid.
+ *
+ * @method add_faux_cell
+ * @param {Number} row The row for the new faux cell.
+ * @param {Number} col The col for the new faux cell.
+ * @return {Object} Returns the instance of the Gridster class.
+ */
+ fn.add_faux_cell = function(row, col) {
+ var coords = $({
left: this.baseX + ((col - 1) * this.min_widget_width),
top: this.baseY + (row -1) * this.min_widget_height,
width: this.min_widget_width,
@@ -2782,10 +3084,66 @@
original_row: row
}).coords();
- this.gridmap[col][row] = false;
- this.faux_grid.push(coords);
+ if (!$.isArray(this.gridmap[col])) {
+ this.gridmap[col] = [];
+ }
+
+ this.gridmap[col][row] = false;
+ this.faux_grid.push(coords);
+
+ return this;
+ };
+
+
+ /**
+ * Add rows to the faux grid.
+ *
+ * @method add_faux_rows
+ * @param {Number} rows The number of rows you want to add to the faux grid.
+ * @return {Object} Returns the instance of the Gridster class.
+ */
+ fn.add_faux_rows = function(rows) {
+ var actual_rows = this.rows;
+ var max_rows = actual_rows + (rows || 1);
+
+ for (var r = max_rows; r > actual_rows; r--) {
+ for (var c = this.cols; c >= 1; c--) {
+ this.add_faux_cell(r, c);
}
}
+
+ this.rows = max_rows;
+
+ if (this.options.autogenerate_stylesheet) {
+ this.generate_stylesheet();
+ }
+
+ return this;
+ };
+
+ /**
+ * Add cols to the faux grid.
+ *
+ * @method add_faux_cols
+ * @param {Number} cols The number of cols you want to add to the faux grid.
+ * @return {Object} Returns the instance of the Gridster class.
+ */
+ fn.add_faux_cols = function(cols) {
+ var actual_cols = this.cols;
+ var max_cols = actual_cols + (cols || 1);
+
+ for (var c = actual_cols; c < max_cols; c++) {
+ for (var r = this.rows; r >= 1; r--) {
+ this.add_faux_cell(r, c);
+ }
+ }
+
+ this.cols = max_cols;
+
+ if (this.options.autogenerate_stylesheet) {
+ this.generate_stylesheet();
+ }
+
return this;
};
@@ -2841,8 +3199,6 @@
var cols = Math.floor(aw / this.min_widget_width) +
this.options.extra_cols;
- var rows = Math.floor(ah / this.min_widget_height) +
- this.options.extra_rows;
var actual_cols = this.$widgets.map(function() {
return $(this).attr('data-col');
@@ -2851,18 +3207,16 @@
//needed to pass tests with phantomjs
actual_cols.length || (actual_cols = [0]);
- var actual_rows = this.$widgets.map(function() {
- return $(this).attr('data-row');
- });
- actual_rows = Array.prototype.slice.call(actual_rows, 0);
- //needed to pass tests with phantomjs
- actual_rows.length || (actual_rows = [0]);
-
var min_cols = Math.max.apply(Math, actual_cols);
- var min_rows = Math.max.apply(Math, actual_rows);
+
+ // get all rows that could be occupied by the current widgets
+ var max_rows = this.options.extra_rows;
+ this.$widgets.each(function(i, w) {
+ max_rows += (+$(w).attr('data-sizey'));
+ });
this.cols = Math.max(min_cols, cols, this.options.min_cols);
- this.rows = Math.max(min_rows, rows, this.options.min_rows);
+ this.rows = Math.max(max_rows, this.options.min_rows);
this.baseX = ($(window).width() - aw) / 2;
this.baseY = this.$wrapper.offset().top;
@@ -2871,11 +3225,29 @@
this.generate_stylesheet();
}
- /* more faux rows that needed are created so that there are cells
- * where drag beyond the limits */
return this.generate_faux_grid(this.rows, this.cols);
};
+ /**
+ * Destroy this gridster by removing any sign of its presence, making it easy to avoid memory leaks
+ *
+ * @method destroy
+ * @return {undefined}
+ */
+ fn.destroy = function(){
+ // remove bound callback on window resize
+ $(window).unbind('resize', this.on_window_resize);
+
+ if(this.drag_api){
+ this.drag_api.destroy();
+ }
+
+ // lastly, remove gridster element
+ // this will additionally cause any data associated to this element to be removed, including this
+ // very gridster instance
+ this.$el.remove();
+ };
+
//jQuery adapter
$.fn.gridster = function(options) {
@@ -2886,5 +3258,6 @@
});
};
+ $.Gridster = fn;
}(jQuery, window, document)); \ No newline at end of file