dashing 1.0.4 → 1.0.5

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.
@@ -32,6 +32,7 @@ end
32
32
 
33
33
  get '/events', provides: 'text/event-stream' do
34
34
  protected!
35
+ response.headers['X-Accel-Buffering'] = 'no' # Disable buffering for nginx
35
36
  stream :keep_open do |out|
36
37
  settings.connections << out
37
38
  out << latest_events
@@ -91,8 +92,8 @@ def send_event(id, body)
91
92
  body[:id] = id
92
93
  body[:updatedAt] ||= Time.now.to_i
93
94
  event = format_event(body.to_json)
94
- settings.history[id] = event
95
- settings.connections.each { |out| out << event }
95
+ Sinatra::Application.settings.history[id] = event
96
+ Sinatra::Application.settings.connections.each { |out| out << event }
96
97
  end
97
98
 
98
99
  def format_event(body)
@@ -0,0 +1 @@
1
+ *DS_STORE
@@ -1,3 +1,3 @@
1
- source :rubygems
1
+ source 'https://rubygems.org'
2
2
 
3
3
  gem 'dashing'
@@ -1,6 +1,6 @@
1
- /*! gridster.js - v0.1.0 - 2012-08-14
1
+ /*! gridster.js - v0.1.0 - 2013-02-15
2
2
  * http://gridster.net/
3
- * Copyright (c) 2012 ducksboard; Licensed MIT */
3
+ * Copyright (c) 2013 ducksboard; Licensed MIT */
4
4
 
5
5
  ;(function($, window, document, undefined){
6
6
  /**
@@ -285,12 +285,8 @@
285
285
 
286
286
  fn.get_closest_colliders = function(player_data_coords){
287
287
  var colliders = this.find_collisions(player_data_coords);
288
- var min_area = 100;
289
- colliders.sort(function(a, b){
290
- if (a.area <= min_area) {
291
- return 1;
292
- }
293
288
 
289
+ colliders.sort(function(a, b) {
294
290
  /* if colliders are being overlapped by the "C" (center) region,
295
291
  * we have to set a lower index in the array to which they are placed
296
292
  * above in the grid. */
@@ -302,7 +298,7 @@
302
298
  }
303
299
  }
304
300
 
305
- if (a.area < b.area){
301
+ if (a.area < b.area) {
306
302
  return 1;
307
303
  }
308
304
 
@@ -369,8 +365,10 @@
369
365
  distance: 1,
370
366
  limit: true,
371
367
  offset_left: 0,
372
- autoscroll: true
373
- // ,drag: function(e){},
368
+ autoscroll: true,
369
+ ignore_dragging: ['INPUT', 'TEXTAREA', 'SELECT', 'BUTTON'],
370
+ handle: null
371
+ // drag: function(e){},
374
372
  // start : function(e, ui){},
375
373
  // stop : function(e){}
376
374
  };
@@ -424,12 +422,30 @@
424
422
  fn.init = function() {
425
423
  this.calculate_positions();
426
424
  this.$container.css('position', 'relative');
427
- this.enable();
425
+ this.disabled = false;
426
+ this.events();
428
427
 
429
- $(window).bind('resize',
430
- throttle($.proxy(this.calculate_positions, this), 200));
428
+ this.on_window_resize = throttle($.proxy(this.calculate_positions, this), 200);
429
+ $(window).bind('resize', this.on_window_resize);
431
430
  };
432
431
 
432
+ fn.events = function() {
433
+ this.proxied_on_select_start = $.proxy(this.on_select_start, this);
434
+ this.$container.on('selectstart', this.proxied_on_select_start);
435
+
436
+ this.proxied_drag_handler = $.proxy(this.drag_handler, this);
437
+ this.$container.on(pointer_events.start, this.options.items, this.proxied_drag_handler);
438
+
439
+ this.proxied_pointer_events_end = $.proxy(function(e) {
440
+ this.is_dragging = false;
441
+ if (this.disabled) { return; }
442
+ this.$body.off(pointer_events.move);
443
+ if (this.drag_start) {
444
+ this.on_dragstop(e);
445
+ }
446
+ }, this);
447
+ this.$body.on(pointer_events.end, this.proxied_pointer_events_end);
448
+ };
433
449
 
434
450
  fn.get_actual_pos = function($el) {
435
451
  var pos = $el.position();
@@ -441,7 +457,7 @@
441
457
  if (isTouch) {
442
458
  var oe = e.originalEvent;
443
459
  e = oe.touches.length ? oe.touches[0] : oe.changedTouches[0];
444
- };
460
+ }
445
461
 
446
462
  return {
447
463
  left: e.clientX,
@@ -500,7 +516,7 @@
500
516
  $window.scrollTop(nextScrollTop);
501
517
  this.scrollOffset = this.scrollOffset + 30;
502
518
  }
503
- };
519
+ }
504
520
 
505
521
  if (abs_mouse_top <= mouse_up_zone) {
506
522
  nextScrollTop = scrollTop - 30;
@@ -508,26 +524,24 @@
508
524
  $window.scrollTop(nextScrollTop);
509
525
  this.scrollOffset = this.scrollOffset - 30;
510
526
  }
511
- };
512
- }
527
+ }
528
+ };
513
529
 
514
530
 
515
531
  fn.calculate_positions = function(e) {
516
532
  this.window_height = $window.height();
517
- }
533
+ };
518
534
 
519
535
 
520
536
  fn.drag_handler = function(e) {
521
537
  var node = e.target.nodeName;
522
-
523
- if (e.which !== 1 && !isTouch) {
538
+ if (this.disabled || e.which !== 1 && !isTouch) {
524
539
  return;
525
540
  }
526
541
 
527
- if (node === 'INPUT' || node === 'TEXTAREA' || node === 'SELECT' ||
528
- node === 'BUTTON') {
542
+ if (this.ignore_drag(e)) {
529
543
  return;
530
- };
544
+ }
531
545
 
532
546
  var self = this;
533
547
  var first = true;
@@ -537,7 +551,7 @@
537
551
  this.mouse_init_pos = this.get_mouse_pos(e);
538
552
  this.offsetY = this.mouse_init_pos.top - this.el_init_pos.top;
539
553
 
540
- this.$body.on(pointer_events.move, function(mme){
554
+ this.on_pointer_events_move = function(mme){
541
555
  var mouse_actual_pos = self.get_mouse_pos(mme);
542
556
  var diff_x = Math.abs(
543
557
  mouse_actual_pos.left - self.mouse_init_pos.left);
@@ -545,7 +559,7 @@
545
559
  mouse_actual_pos.top - self.mouse_init_pos.top);
546
560
  if (!(diff_x > self.options.distance ||
547
561
  diff_y > self.options.distance)
548
- ) {
562
+ ) {
549
563
  return false;
550
564
  }
551
565
 
@@ -555,12 +569,14 @@
555
569
  return false;
556
570
  }
557
571
 
558
- if (self.is_dragging == true) {
572
+ if (self.is_dragging === true) {
559
573
  self.on_dragmove.call(self, mme);
560
574
  }
561
575
 
562
576
  return false;
563
- });
577
+ };
578
+
579
+ this.$body.on(pointer_events.move, this.on_pointer_events_move);
564
580
 
565
581
  return false;
566
582
  };
@@ -646,38 +662,43 @@
646
662
  };
647
663
 
648
664
  fn.on_select_start = function(e) {
649
- return false;
650
- }
665
+ if (this.disabled) { return; }
651
666
 
667
+ if (this.ignore_drag(e)) {
668
+ return;
669
+ }
652
670
 
653
- fn.enable = function(){
654
- this.$container.on('selectstart', this.on_select_start);
655
-
656
- this.$container.on(pointer_events.start, this.options.items, $.proxy(
657
- this.drag_handler, this));
658
-
659
- this.$body.on(pointer_events.end, $.proxy(function(e) {
660
- this.is_dragging = false;
661
- this.$body.off(pointer_events.move);
662
- if (this.drag_start) {
663
- this.on_dragstop(e);
664
- }
665
- }, this));
671
+ return false;
666
672
  };
667
673
 
674
+ fn.enable = function() {
675
+ this.disabled = false;
676
+ };
668
677
 
669
- fn.disable = function(){
670
- this.$container.off(pointer_events.start);
671
- this.$body.off(pointer_events.end);
672
- this.$container.off('selectstart', this.on_select_start);
678
+ fn.disable = function() {
679
+ this.disabled = true;
673
680
  };
674
681
 
675
682
 
676
683
  fn.destroy = function(){
677
684
  this.disable();
685
+
686
+ this.$container.off('selectstart', this.proxied_on_select_start);
687
+ this.$container.off(pointer_events.start, this.proxied_drag_handler);
688
+ this.$body.off(pointer_events.end, this.proxied_pointer_events_end);
689
+ this.$body.off(pointer_events.move, this.on_pointer_events_move);
690
+ $(window).unbind('resize', this.on_window_resize);
691
+
678
692
  $.removeData(this.$container, 'drag');
679
693
  };
680
694
 
695
+ fn.ignore_drag = function(event) {
696
+ if (this.options.handle) {
697
+ return !$(event.target).is(this.options.handle);
698
+ }
699
+
700
+ return $.inArray(event.target.nodeName, this.options.ignore_dragging) >= 0;
701
+ };
681
702
 
682
703
  //jQuery adapter
683
704
  $.fn.drag = function ( options ) {
@@ -694,7 +715,8 @@
694
715
  ;(function($, window, document, undefined) {
695
716
 
696
717
  var defaults = {
697
- widget_selector: '> li',
718
+ namespace: '',
719
+ widget_selector: 'li',
698
720
  widget_margins: [10, 10],
699
721
  widget_base_dimensions: [400, 225],
700
722
  extra_rows: 0,
@@ -702,13 +724,14 @@
702
724
  min_cols: 1,
703
725
  min_rows: 15,
704
726
  max_size_x: 6,
705
- max_size_y: 6,
706
727
  autogenerate_stylesheet: true,
707
728
  avoid_overlapped_widgets: true,
708
729
  serialize_params: function($w, wgd) {
709
730
  return {
710
731
  col: wgd.col,
711
- row: wgd.row
732
+ row: wgd.row,
733
+ size_x: wgd.size_x,
734
+ size_y: wgd.size_y
712
735
  };
713
736
  },
714
737
  collision: {},
@@ -742,8 +765,6 @@
742
765
  * @param {Number} [options.min_rows] The minimum required rows.
743
766
  * @param {Number} [options.max_size_x] The maximum number of columns
744
767
  * that a widget can span.
745
- * @param {Number} [options.max_size_y] The maximum number of rows
746
- * that a widget can span.
747
768
  * @param {Boolean} [options.autogenerate_stylesheet] If true, all the
748
769
  * CSS required to position all widgets in their respective columns
749
770
  * and rows will be generated automatically and injected to the
@@ -770,7 +791,7 @@
770
791
  this.options = $.extend(true, defaults, options);
771
792
  this.$el = $(el);
772
793
  this.$wrapper = this.$el.parent();
773
- this.$widgets = $(this.options.widget_selector, this.$el).addClass('gs_w');
794
+ this.$widgets = this.$el.children(this.options.widget_selector).addClass('gs_w');
774
795
  this.widgets = [];
775
796
  this.$changed = $([]);
776
797
  this.wrapper_width = this.$wrapper.width();
@@ -786,14 +807,15 @@
786
807
  var fn = Gridster.prototype;
787
808
 
788
809
  fn.init = function() {
789
- this.generate_grid_and_stylesheet();
790
- this.get_widgets_from_DOM();
791
- this.set_dom_grid_height();
792
- this.$wrapper.addClass('ready');
793
- this.draggable();
810
+ this.generate_grid_and_stylesheet();
811
+ this.get_widgets_from_DOM();
812
+ this.set_dom_grid_height();
813
+ this.$wrapper.addClass('ready');
814
+ this.draggable();
815
+
816
+ this.on_window_resize = throttle($.proxy(this.recalculate_faux_grid, this), 200);
794
817
 
795
- $(window).bind(
796
- 'resize', throttle($.proxy(this.recalculate_faux_grid, this), 200));
818
+ $(window).bind('resize', this.on_window_resize);
797
819
  };
798
820
 
799
821
 
@@ -826,32 +848,251 @@
826
848
  * Add a new widget to the grid.
827
849
  *
828
850
  * @method add_widget
829
- * @param {String} html The string representing the HTML of the widget.
830
- * @param {Number} size_x The nº of rows the widget occupies horizontally.
831
- * @param {Number} size_y The nº of columns the widget occupies vertically.
851
+ * @param {String|HTMLElement} html The string representing the HTML of the widget
852
+ * or the HTMLElement.
853
+ * @param {Number} [size_x] The nº of rows the widget occupies horizontally.
854
+ * @param {Number} [size_y] The nº of columns the widget occupies vertically.
855
+ * @param {Number} [col] The column the widget should start in.
856
+ * @param {Number} [row] The row the widget should start in.
832
857
  * @return {HTMLElement} Returns the jQuery wrapped HTMLElement representing.
833
858
  * the widget that was just created.
834
859
  */
835
- fn.add_widget = function(html, size_x, size_y) {
836
- var next_pos = this.next_position(size_x, size_y);
860
+ fn.add_widget = function(html, size_x, size_y, col, row) {
861
+ var pos;
862
+ size_x || (size_x = 1);
863
+ size_y || (size_y = 1);
864
+
865
+ if (!col & !row) {
866
+ pos = this.next_position(size_x, size_y);
867
+ }else{
868
+ pos = {
869
+ col: col,
870
+ row: row
871
+ };
872
+
873
+ this.empty_cells(col, row, size_x, size_y);
874
+ }
837
875
 
838
876
  var $w = $(html).attr({
839
- 'data-col': next_pos.col,
840
- 'data-row': next_pos.row,
841
- 'data-sizex' : next_pos.size_x,
842
- 'data-sizey' : next_pos.size_y
877
+ 'data-col': pos.col,
878
+ 'data-row': pos.row,
879
+ 'data-sizex' : size_x,
880
+ 'data-sizey' : size_y
843
881
  }).addClass('gs_w').appendTo(this.$el).hide();
844
882
 
845
883
  this.$widgets = this.$widgets.add($w);
846
884
 
847
885
  this.register_widget($w);
848
886
 
887
+ this.add_faux_rows(pos.size_y);
888
+ //this.add_faux_cols(pos.size_x);
889
+
849
890
  this.set_dom_grid_height();
850
891
 
851
892
  return $w.fadeIn();
852
893
  };
853
894
 
854
895
 
896
+
897
+ /**
898
+ * Change the size of a widget.
899
+ *
900
+ * @method resize_widget
901
+ * @param {HTMLElement} $widget The jQuery wrapped HTMLElement
902
+ * representing the widget.
903
+ * @param {Number} size_x The number of columns that will occupy the widget.
904
+ * @param {Number} size_y The number of rows that will occupy the widget.
905
+ * @return {HTMLElement} Returns $widget.
906
+ */
907
+ fn.resize_widget = function($widget, size_x, size_y) {
908
+ var wgd = $widget.coords().grid;
909
+ size_x || (size_x = wgd.size_x);
910
+ size_y || (size_y = wgd.size_y);
911
+
912
+ if (size_x > this.cols) {
913
+ size_x = this.cols;
914
+ }
915
+
916
+ var old_cells_occupied = this.get_cells_occupied(wgd);
917
+ var old_size_x = wgd.size_x;
918
+ var old_size_y = wgd.size_y;
919
+ var old_col = wgd.col;
920
+ var new_col = old_col;
921
+ var wider = size_x > old_size_x;
922
+ var taller = size_y > old_size_y;
923
+
924
+ if (old_col + size_x - 1 > this.cols) {
925
+ var diff = old_col + (size_x - 1) - this.cols;
926
+ var c = old_col - diff;
927
+ new_col = Math.max(1, c);
928
+ }
929
+
930
+ var new_grid_data = {
931
+ col: new_col,
932
+ row: wgd.row,
933
+ size_x: size_x,
934
+ size_y: size_y
935
+ };
936
+
937
+ var new_cells_occupied = this.get_cells_occupied(new_grid_data);
938
+
939
+ var empty_cols = [];
940
+ $.each(old_cells_occupied.cols, function(i, col) {
941
+ if ($.inArray(col, new_cells_occupied.cols) === -1) {
942
+ empty_cols.push(col);
943
+ }
944
+ });
945
+
946
+ var occupied_cols = [];
947
+ $.each(new_cells_occupied.cols, function(i, col) {
948
+ if ($.inArray(col, old_cells_occupied.cols) === -1) {
949
+ occupied_cols.push(col);
950
+ }
951
+ });
952
+
953
+ var empty_rows = [];
954
+ $.each(old_cells_occupied.rows, function(i, row) {
955
+ if ($.inArray(row, new_cells_occupied.rows) === -1) {
956
+ empty_rows.push(row);
957
+ }
958
+ });
959
+
960
+ var occupied_rows = [];
961
+ $.each(new_cells_occupied.rows, function(i, row) {
962
+ if ($.inArray(row, old_cells_occupied.rows) === -1) {
963
+ occupied_rows.push(row);
964
+ }
965
+ });
966
+
967
+ this.remove_from_gridmap(wgd);
968
+
969
+ if (occupied_cols.length) {
970
+ var cols_to_empty = [
971
+ new_col, wgd.row, size_x, Math.min(old_size_y, size_y), $widget
972
+ ];
973
+ this.empty_cells.apply(this, cols_to_empty);
974
+ }
975
+
976
+ if (occupied_rows.length) {
977
+ var rows_to_empty = [new_col, wgd.row, size_x, size_y, $widget];
978
+ this.empty_cells.apply(this, rows_to_empty);
979
+ }
980
+
981
+ wgd.col = new_col;
982
+ wgd.size_x = size_x;
983
+ wgd.size_y = size_y;
984
+ this.add_to_gridmap(new_grid_data, $widget);
985
+
986
+ //update coords instance attributes
987
+ $widget.data('coords').update({
988
+ width: (size_x * this.options.widget_base_dimensions[0] +
989
+ ((size_x - 1) * this.options.widget_margins[0]) * 2),
990
+ height: (size_y * this.options.widget_base_dimensions[1] +
991
+ ((size_y - 1) * this.options.widget_margins[1]) * 2)
992
+ });
993
+
994
+ if (size_y > old_size_y) {
995
+ this.add_faux_rows(size_y - old_size_y);
996
+ }
997
+
998
+ if (size_x > old_size_x) {
999
+ this.add_faux_cols(size_x - old_size_x);
1000
+ }
1001
+
1002
+ $widget.attr({
1003
+ 'data-col': new_col,
1004
+ 'data-sizex': size_x,
1005
+ 'data-sizey': size_y
1006
+ });
1007
+
1008
+ if (empty_cols.length) {
1009
+ var cols_to_remove_holes = [
1010
+ empty_cols[0], wgd.row,
1011
+ empty_cols.length,
1012
+ Math.min(old_size_y, size_y),
1013
+ $widget
1014
+ ];
1015
+
1016
+ this.remove_empty_cells.apply(this, cols_to_remove_holes);
1017
+ }
1018
+
1019
+ if (empty_rows.length) {
1020
+ var rows_to_remove_holes = [
1021
+ new_col, wgd.row, size_x, size_y, $widget
1022
+ ];
1023
+ this.remove_empty_cells.apply(this, rows_to_remove_holes);
1024
+ }
1025
+
1026
+ return $widget;
1027
+ };
1028
+
1029
+ /**
1030
+ * Move down widgets in cells represented by the arguments col, row, size_x,
1031
+ * size_y
1032
+ *
1033
+ * @method empty_cells
1034
+ * @param {Number} col The column where the group of cells begin.
1035
+ * @param {Number} row The row where the group of cells begin.
1036
+ * @param {Number} size_x The number of columns that the group of cells
1037
+ * occupy.
1038
+ * @param {Number} size_y The number of rows that the group of cells
1039
+ * occupy.
1040
+ * @param {HTMLElement} $exclude Exclude widgets from being moved.
1041
+ * @return {Class} Returns the instance of the Gridster Class.
1042
+ */
1043
+ fn.empty_cells = function(col, row, size_x, size_y, $exclude) {
1044
+ var $nexts = this.widgets_below({
1045
+ col: col,
1046
+ row: row - size_y,
1047
+ size_x: size_x,
1048
+ size_y: size_y
1049
+ });
1050
+
1051
+ $nexts.not($exclude).each($.proxy(function(i, w) {
1052
+ var wgd = $(w).coords().grid;
1053
+ if (!(wgd.row <= (row + size_y - 1))) { return; }
1054
+ var diff = (row + size_y) - wgd.row;
1055
+ this.move_widget_down($(w), diff);
1056
+ }, this));
1057
+
1058
+ this.set_dom_grid_height();
1059
+
1060
+ return this;
1061
+ };
1062
+
1063
+
1064
+ /**
1065
+ * Move up widgets below cells represented by the arguments col, row, size_x,
1066
+ * size_y.
1067
+ *
1068
+ * @method remove_empty_cells
1069
+ * @param {Number} col The column where the group of cells begin.
1070
+ * @param {Number} row The row where the group of cells begin.
1071
+ * @param {Number} size_x The number of columns that the group of cells
1072
+ * occupy.
1073
+ * @param {Number} size_y The number of rows that the group of cells
1074
+ * occupy.
1075
+ * @param {HTMLElement} exclude Exclude widgets from being moved.
1076
+ * @return {Class} Returns the instance of the Gridster Class.
1077
+ */
1078
+ fn.remove_empty_cells = function(col, row, size_x, size_y, exclude) {
1079
+ var $nexts = this.widgets_below({
1080
+ col: col,
1081
+ row: row,
1082
+ size_x: size_x,
1083
+ size_y: size_y
1084
+ });
1085
+
1086
+ $nexts.not(exclude).each($.proxy(function(i, widget) {
1087
+ this.move_widget_up( $(widget), size_y );
1088
+ }, this));
1089
+
1090
+ this.set_dom_grid_height();
1091
+
1092
+ return this;
1093
+ };
1094
+
1095
+
855
1096
  /**
856
1097
  * Get the most left column below to add a new widget.
857
1098
  *
@@ -867,9 +1108,10 @@
867
1108
  var ga = this.gridmap;
868
1109
  var cols_l = ga.length;
869
1110
  var valid_pos = [];
1111
+ var rows_l;
870
1112
 
871
1113
  for (var c = 1; c < cols_l; c++) {
872
- var rows_l = ga[c].length;
1114
+ rows_l = ga[c].length;
873
1115
  for (var r = 1; r <= rows_l; r++) {
874
1116
  var can_move_to = this.can_move_to({
875
1117
  size_x: size_x,
@@ -899,12 +1141,21 @@
899
1141
  *
900
1142
  * @method remove_widget
901
1143
  * @param {HTMLElement} el The jQuery wrapped HTMLElement you want to remove.
1144
+ * @param {Boolean|Function} silent If true, widgets below the removed one
1145
+ * will not move up. If a Function is passed it will be used as callback.
1146
+ * @param {Function} callback Function executed when the widget is removed.
902
1147
  * @return {Class} Returns the instance of the Gridster Class.
903
1148
  */
904
- fn.remove_widget = function(el, callback) {
1149
+ fn.remove_widget = function(el, silent, callback) {
905
1150
  var $el = el instanceof jQuery ? el : $(el);
906
1151
  var wgd = $el.coords().grid;
907
1152
 
1153
+ // if silent is a function assume it's a callback
1154
+ if ($.isFunction(silent)) {
1155
+ callback = silent;
1156
+ silent = false;
1157
+ }
1158
+
908
1159
  this.cells_occupied_by_placeholder = {};
909
1160
  this.$widgets = this.$widgets.not($el);
910
1161
 
@@ -915,9 +1166,11 @@
915
1166
  $el.fadeOut($.proxy(function() {
916
1167
  $el.remove();
917
1168
 
918
- $nexts.each($.proxy(function(i, widget) {
919
- this.move_widget_up( $(widget), wgd.size_y );
920
- }, this));
1169
+ if (!silent) {
1170
+ $nexts.each($.proxy(function(i, widget) {
1171
+ this.move_widget_up( $(widget), wgd.size_y );
1172
+ }, this));
1173
+ }
921
1174
 
922
1175
  this.set_dom_grid_height();
923
1176
 
@@ -928,6 +1181,22 @@
928
1181
  };
929
1182
 
930
1183
 
1184
+ /**
1185
+ * Remove all widgets from the grid.
1186
+ *
1187
+ * @method remove_all_widgets
1188
+ * @param {Function} callback Function executed for each widget removed.
1189
+ * @return {Class} Returns the instance of the Gridster Class.
1190
+ */
1191
+ fn.remove_all_widgets = function(callback) {
1192
+ this.$widgets.each($.proxy(function(i, el){
1193
+ this.remove_widget(el, true, callback);
1194
+ }, this));
1195
+
1196
+ return this;
1197
+ };
1198
+
1199
+
931
1200
  /**
932
1201
  * Returns a serialized array of the widgets in the grid.
933
1202
  *
@@ -1001,7 +1270,7 @@
1001
1270
  $el.data('coords').grid = wgd;
1002
1271
 
1003
1272
  this.add_to_gridmap(wgd, $el);
1004
- this.widgets.push($el);
1273
+
1005
1274
  return this;
1006
1275
  };
1007
1276
 
@@ -1073,7 +1342,6 @@
1073
1342
  var self = this;
1074
1343
  var draggable_options = $.extend(true, {}, this.options.draggable, {
1075
1344
  offset_left: this.options.widget_margins[0],
1076
- items: '.gs_w',
1077
1345
  start: function(event, ui) {
1078
1346
  self.$widgets.filter('.player-revert')
1079
1347
  .removeClass('player-revert');
@@ -1105,8 +1373,8 @@
1105
1373
  * This function is executed when the player begins to be dragged.
1106
1374
  *
1107
1375
  * @method on_start_drag
1108
- * @param {Event} The original browser event
1109
- * @param {Object} A prepared ui object.
1376
+ * @param {Event} event The original browser event
1377
+ * @param {Object} ui A prepared ui object.
1110
1378
  */
1111
1379
  fn.on_start_drag = function(event, ui) {
1112
1380
 
@@ -1156,14 +1424,14 @@
1156
1424
  * This function is executed when the player is being dragged.
1157
1425
  *
1158
1426
  * @method on_drag
1159
- * @param {Event} The original browser event
1160
- * @param {Object} A prepared ui object.
1427
+ * @param {Event} event The original browser event
1428
+ * @param {Object} ui A prepared ui object.
1161
1429
  */
1162
1430
  fn.on_drag = function(event, ui) {
1163
1431
  //break if dragstop has been fired
1164
1432
  if (this.$player === null) {
1165
1433
  return false;
1166
- };
1434
+ }
1167
1435
 
1168
1436
  var abs_offset = {
1169
1437
  left: ui.position.left + this.baseX,
@@ -1199,8 +1467,8 @@
1199
1467
  * This function is executed when the player stops being dragged.
1200
1468
  *
1201
1469
  * @method on_stop_drag
1202
- * @param {Event} The original browser event
1203
- * @param {Object} A prepared ui object.
1470
+ * @param {Event} event The original browser event
1471
+ * @param {Object} ui A prepared ui object.
1204
1472
  */
1205
1473
  fn.on_stop_drag = function(event, ui) {
1206
1474
  this.$helper.add(this.$player).add(this.$wrapper)
@@ -1244,7 +1512,13 @@
1244
1512
  }
1245
1513
 
1246
1514
  this.$preview_holder.remove();
1515
+
1247
1516
  this.$player = null;
1517
+ this.$helper = null;
1518
+ this.placeholder_grid_data = {};
1519
+ this.player_grid_data = {};
1520
+ this.cells_occupied_by_placeholder = {};
1521
+ this.cells_occupied_by_player = {};
1248
1522
 
1249
1523
  this.set_dom_grid_height();
1250
1524
  };
@@ -1263,7 +1537,7 @@
1263
1537
  */
1264
1538
  fn.on_overlapped_column_change = function(start_callback, stop_callback) {
1265
1539
  if (!this.colliders_data.length) {
1266
- return;
1540
+ return this;
1267
1541
  }
1268
1542
  var cols = this.get_targeted_columns(
1269
1543
  this.colliders_data[0].el.data.col);
@@ -1296,14 +1570,14 @@
1296
1570
  *
1297
1571
  * @param {Function} start_callback Function executed when a new row begins
1298
1572
  * to be overlapped. The row is passed as first argument.
1299
- * @param {Function} stop_callback Function executed when a row stops being
1573
+ * @param {Function} end_callback Function executed when a row stops being
1300
1574
  * overlapped. The row is passed as first argument.
1301
1575
  * @method on_overlapped_row_change
1302
1576
  * @return {Class} Returns the instance of the Gridster Class.
1303
1577
  */
1304
1578
  fn.on_overlapped_row_change = function(start_callback, end_callback) {
1305
1579
  if (!this.colliders_data.length) {
1306
- return;
1580
+ return this;
1307
1581
  }
1308
1582
  var rows = this.get_targeted_rows(this.colliders_data[0].el.data.row);
1309
1583
  var last_n_rows = this.last_rows.length;
@@ -1329,18 +1603,18 @@
1329
1603
  /**
1330
1604
  * Sets the current position of the player
1331
1605
  *
1332
- * @param {Function} start_callback Function executed when a new row begins
1333
- * to be overlapped. The row is passed as first argument.
1334
- * @param {Function} stop_callback Function executed when a row stops being
1335
- * overlapped. The row is passed as first argument.
1606
+ * @param {Number} col
1607
+ * @param {Number} row
1608
+ * @param {Boolean} no_player
1336
1609
  * @method set_player
1337
- * @return {Class} Returns the instance of the Gridster Class.
1610
+ * @return {object}
1338
1611
  */
1339
- fn.set_player = function(col, row) {
1340
- this.empty_cells_player_occupies();
1341
-
1612
+ fn.set_player = function(col, row, no_player) {
1342
1613
  var self = this;
1343
- var cell = self.colliders_data[0].el.data;
1614
+ if (!no_player) {
1615
+ this.empty_cells_player_occupies();
1616
+ }
1617
+ var cell = !no_player ? self.colliders_data[0].el.data : {col: col};
1344
1618
  var to_col = cell.col;
1345
1619
  var to_row = row || cell.row;
1346
1620
 
@@ -1384,9 +1658,9 @@
1384
1658
  * a upper row and which not.
1385
1659
  *
1386
1660
  * @method widgets_contraints
1387
- * @param {HTMLElements} $widgets A jQuery wrapped collection of
1661
+ * @param {jQuery} $widgets A jQuery wrapped collection of
1388
1662
  * HTMLElements.
1389
- * @return {Array} Returns a literal Object with two keys: `can_go_up` &
1663
+ * @return {object} Returns a literal Object with two keys: `can_go_up` &
1390
1664
  * `can_not_go_up`. Each contains a set of HTMLElements.
1391
1665
  */
1392
1666
  fn.widgets_constraints = function($widgets) {
@@ -1425,6 +1699,11 @@
1425
1699
  */
1426
1700
  fn.sort_by_row_asc = function(widgets) {
1427
1701
  widgets = widgets.sort(function(a, b) {
1702
+ if (!a.row) {
1703
+ a = $(a).coords().grid;
1704
+ b = $(b).coords().grid;
1705
+ }
1706
+
1428
1707
  if (a.row > b.row) {
1429
1708
  return 1;
1430
1709
  }
@@ -1445,7 +1724,7 @@
1445
1724
  */
1446
1725
  fn.sort_by_row_and_col_asc = function(widgets) {
1447
1726
  widgets = widgets.sort(function(a, b) {
1448
- if (a.row > b.row || a.row == b.row && a.col > b.col) {
1727
+ if (a.row > b.row || a.row === b.row && a.col > b.col) {
1449
1728
  return 1;
1450
1729
  }
1451
1730
  return -1;
@@ -1499,7 +1778,7 @@
1499
1778
  * each widget) in descending way.
1500
1779
  *
1501
1780
  * @method manage_movements
1502
- * @param {HTMLElements} $widgets A jQuery collection of HTMLElements
1781
+ * @param {jQuery} $widgets A jQuery collection of HTMLElements
1503
1782
  * representing the widgets you want to move.
1504
1783
  * @param {Number} to_col The column to which we want to move the widgets.
1505
1784
  * @param {Number} to_row The row to which we want to move the widgets.
@@ -1680,13 +1959,14 @@
1680
1959
 
1681
1960
 
1682
1961
  /**
1683
- * Get widgets overlapping with the player.
1962
+ * Get widgets overlapping with the player or with the object passed
1963
+ * representing the grid cells.
1684
1964
  *
1685
1965
  * @method get_widgets_under_player
1686
1966
  * @return {HTMLElement} Returns a jQuery collection of HTMLElements
1687
1967
  */
1688
- fn.get_widgets_under_player = function() {
1689
- var cells = this.cells_occupied_by_player;
1968
+ fn.get_widgets_under_player = function(cells) {
1969
+ cells || (cells = this.cells_occupied_by_player || {cols: [], rows: []});
1690
1970
  var $widgets = $([]);
1691
1971
 
1692
1972
  $.each(cells.cols, $.proxy(function(i, col) {
@@ -1720,7 +2000,7 @@
1720
2000
  size_x: phgd.size_x
1721
2001
  });
1722
2002
 
1723
- //Prevents widgets go out of the grid
2003
+ // Prevents widgets go out of the grid
1724
2004
  var right_col = (col + phgd.size_x - 1);
1725
2005
  if (right_col > this.cols) {
1726
2006
  col = col - (right_col - col);
@@ -1747,6 +2027,16 @@
1747
2027
  }, this));
1748
2028
  }
1749
2029
 
2030
+
2031
+ var $widgets_under_ph = this.get_widgets_under_player(this.cells_occupied_by_placeholder);
2032
+ if ($widgets_under_ph.length) {
2033
+ $widgets_under_ph.each($.proxy(function(i, widget) {
2034
+ var $w = $(widget);
2035
+ this.move_widget_down(
2036
+ $w, row + phgd.size_y - $w.data('coords').grid.row);
2037
+ }, this));
2038
+ }
2039
+
1750
2040
  };
1751
2041
 
1752
2042
 
@@ -1814,33 +2104,26 @@
1814
2104
  var upper_rows = [];
1815
2105
  var min_row = 10000;
1816
2106
 
1817
- if (widget_grid_data.col < this.player_grid_data.col &&
1818
- (widget_grid_data.col + widget_grid_data.size_y - 1) >
1819
- (this.player_grid_data.col + this.player_grid_data.size_y - 1)
1820
- ) {
1821
- return false;
1822
- };
1823
-
1824
- /* generate an array with columns as index and array with upper rows
2107
+ /* generate an array with columns as index and array with topmost rows
1825
2108
  * empty as value */
1826
2109
  this.for_each_column_occupied(widget_grid_data, function(tcol) {
1827
2110
  var grid_col = this.gridmap[tcol];
1828
2111
  upper_rows[tcol] = [];
1829
2112
 
1830
2113
  var r = p_bottom_row + 1;
1831
-
2114
+ // iterate over each row
1832
2115
  while (--r > 0) {
1833
2116
  if (this.is_widget(tcol, r) && !this.is_player_in(tcol, r)) {
1834
2117
  if (!grid_col[r].is(widget_grid_data.el)) {
1835
2118
  break;
1836
- };
2119
+ }
1837
2120
  }
1838
2121
 
1839
2122
  if (!this.is_player(tcol, r) &&
1840
2123
  !this.is_placeholder_in(tcol, r) &&
1841
2124
  !this.is_player_in(tcol, r)) {
1842
2125
  upper_rows[tcol].push(r);
1843
- };
2126
+ }
1844
2127
 
1845
2128
  if (r < min_row) {
1846
2129
  min_row = r;
@@ -1942,7 +2225,7 @@
1942
2225
  * Get widgets overlapping with the player.
1943
2226
  *
1944
2227
  * @method get_widgets_overlapped
1945
- * @return {HTMLElements} Returns a jQuery collection of HTMLElements.
2228
+ * @return {jQuery} Returns a jQuery collection of HTMLElements.
1946
2229
  */
1947
2230
  fn.get_widgets_overlapped = function() {
1948
2231
  var $w;
@@ -1956,7 +2239,6 @@
1956
2239
  // if there is a widget in the player position
1957
2240
  if (!this.gridmap[col]) { return true; } //next iteration
1958
2241
  var $w = this.gridmap[col][row];
1959
-
1960
2242
  if (this.is_occupied(col, row) && !this.is_player($w) &&
1961
2243
  $.inArray($w, used) === -1
1962
2244
  ) {
@@ -1976,7 +2258,7 @@
1976
2258
  *
1977
2259
  * @method on_start_overlapping_column
1978
2260
  * @param {Number} col The collided column.
1979
- * @return {HTMLElements} Returns a jQuery collection of HTMLElements.
2261
+ * @return {jQuery} Returns a jQuery collection of HTMLElements.
1980
2262
  */
1981
2263
  fn.on_start_overlapping_column = function(col) {
1982
2264
  this.set_player(col, false);
@@ -1987,8 +2269,8 @@
1987
2269
  * A callback executed when the player begins to collide with a row.
1988
2270
  *
1989
2271
  * @method on_start_overlapping_row
1990
- * @param {Number} col The collided row.
1991
- * @return {HTMLElements} Returns a jQuery collection of HTMLElements.
2272
+ * @param {Number} row The collided row.
2273
+ * @return {jQuery} Returns a jQuery collection of HTMLElements.
1992
2274
  */
1993
2275
  fn.on_start_overlapping_row = function(row) {
1994
2276
  this.set_player(false, row);
@@ -2000,7 +2282,7 @@
2000
2282
  *
2001
2283
  * @method on_stop_overlapping_column
2002
2284
  * @param {Number} col The collided row.
2003
- * @return {HTMLElements} Returns a jQuery collection of HTMLElements.
2285
+ * @return {jQuery} Returns a jQuery collection of HTMLElements.
2004
2286
  */
2005
2287
  fn.on_stop_overlapping_column = function(col) {
2006
2288
  this.set_player(col, false);
@@ -2018,7 +2300,7 @@
2018
2300
  *
2019
2301
  * @method on_stop_overlapping_row
2020
2302
  * @param {Number} row The collided row.
2021
- * @return {HTMLElements} Returns a jQuery collection of HTMLElements.
2303
+ * @return {jQuery} Returns a jQuery collection of HTMLElements.
2022
2304
  */
2023
2305
  fn.on_stop_overlapping_row = function(row) {
2024
2306
  this.set_player(false, row);
@@ -2128,9 +2410,9 @@
2128
2410
  * Move down the specified widget and all below it.
2129
2411
  *
2130
2412
  * @method move_widget_down
2131
- * @param {HTMLElement} $widget The jQuery object representing the widget
2413
+ * @param {jQuery} $widget The jQuery object representing the widget
2132
2414
  * you want to move.
2133
- * @param {Number} The number of cells that the widget has to move.
2415
+ * @param {Number} y_units The number of cells that the widget has to move.
2134
2416
  * @return {Class} Returns the instance of the Gridster Class.
2135
2417
  */
2136
2418
  fn.move_widget_down = function($widget, y_units) {
@@ -2266,7 +2548,7 @@
2266
2548
  *
2267
2549
  * @method widgets_below
2268
2550
  * @param {HTMLElement} $el The jQuery wrapped HTMLElement.
2269
- * @return {HTMLElements} A jQuery collection of HTMLElements.
2551
+ * @return {jQuery} A jQuery collection of HTMLElements.
2270
2552
  */
2271
2553
  fn.widgets_below = function($el) {
2272
2554
  var el_grid_data = $.isPlainObject($el) ? $el : $el.coords().grid;
@@ -2276,14 +2558,12 @@
2276
2558
  var $nexts = $([]);
2277
2559
 
2278
2560
  this.for_each_column_occupied(el_grid_data, function(col) {
2279
- self.for_each_widget_below(col, next_row,
2280
- function(tcol, trow) {
2281
- if (!self.is_player(this) &&
2282
- $.inArray(this, $nexts) === -1) {
2283
- $nexts = $nexts.add(this);
2284
- return true; // break
2285
- }
2286
- });
2561
+ self.for_each_widget_below(col, next_row, function(tcol, trow) {
2562
+ if (!self.is_player(this) && $.inArray(this, $nexts) === -1) {
2563
+ $nexts = $nexts.add(this);
2564
+ return true; // break
2565
+ }
2566
+ });
2287
2567
  });
2288
2568
 
2289
2569
  return this.sort_by_row_asc($nexts);
@@ -2331,6 +2611,7 @@
2331
2611
 
2332
2612
  this.for_each_column_occupied(el_grid_data, function(col) {
2333
2613
  var $w = this.is_widget(col, prev_row);
2614
+
2334
2615
  if (this.is_occupied(col, prev_row) ||
2335
2616
  this.is_player(col, prev_row) ||
2336
2617
  this.is_placeholder_in(col, prev_row) ||
@@ -2356,9 +2637,10 @@
2356
2637
  * the widget.
2357
2638
  * @param {Object} col The col to check.
2358
2639
  * @param {Object} row The row to check.
2640
+ * @param {Number} [max_row] The max row allowed.
2359
2641
  * @return {Boolean} Returns true if all cells are empty, else return false.
2360
2642
  */
2361
- fn.can_move_to = function(widget_grid_data, col, row) {
2643
+ fn.can_move_to = function(widget_grid_data, col, row, max_row) {
2362
2644
  var ga = this.gridmap;
2363
2645
  var $w = widget_grid_data.el;
2364
2646
  var future_wd = {
@@ -2373,7 +2655,11 @@
2373
2655
  var right_col = col + widget_grid_data.size_x - 1;
2374
2656
  if (right_col > this.cols) {
2375
2657
  return false;
2376
- };
2658
+ }
2659
+
2660
+ if (max_row && max_row < row + widget_grid_data.size_y - 1) {
2661
+ return false;
2662
+ }
2377
2663
 
2378
2664
  this.for_each_cell_occupied(future_wd, function(tcol, trow) {
2379
2665
  var $tw = this.is_widget(tcol, trow);
@@ -2646,7 +2932,7 @@
2646
2932
  }
2647
2933
 
2648
2934
  return $widgets;
2649
- }
2935
+ };
2650
2936
 
2651
2937
 
2652
2938
  /**
@@ -2672,16 +2958,16 @@
2672
2958
  */
2673
2959
  fn.generate_stylesheet = function(opts) {
2674
2960
  var styles = '';
2675
- var extra_cells = 10;
2676
- var max_size_y = this.options.max_size_y;
2677
2961
  var max_size_x = this.options.max_size_x;
2962
+ var max_rows = 0;
2963
+ var max_cols = 0;
2678
2964
  var i;
2679
2965
  var rules;
2680
2966
 
2681
2967
  opts || (opts = {});
2682
2968
  opts.cols || (opts.cols = this.cols);
2683
2969
  opts.rows || (opts.rows = this.rows);
2684
- opts.namespace || (opts.namespace = '');
2970
+ opts.namespace || (opts.namespace = this.options.namespace);
2685
2971
  opts.widget_base_dimensions ||
2686
2972
  (opts.widget_base_dimensions = this.options.widget_base_dimensions);
2687
2973
  opts.widget_margins ||
@@ -2691,8 +2977,8 @@
2691
2977
  opts.min_widget_height = (opts.widget_margins[1] * 2) +
2692
2978
  opts.widget_base_dimensions[1];
2693
2979
 
2694
- var serialized_opts = $.param(opts);
2695
2980
  // don't duplicate stylesheets for the same configuration
2981
+ var serialized_opts = $.param(opts);
2696
2982
  if ($.inArray(serialized_opts, Gridster.generated_stylesheets) >= 0) {
2697
2983
  return false;
2698
2984
  }
@@ -2700,7 +2986,7 @@
2700
2986
  Gridster.generated_stylesheets.push(serialized_opts);
2701
2987
 
2702
2988
  /* generate CSS styles for cols */
2703
- for (i = opts.cols + extra_cells; i >= 0; i--) {
2989
+ for (i = opts.cols; i >= 0; i--) {
2704
2990
  styles += (opts.namespace + ' [data-col="'+ (i + 1) + '"] { left:' +
2705
2991
  ((i * opts.widget_base_dimensions[0]) +
2706
2992
  (i * opts.widget_margins[0]) +
@@ -2708,14 +2994,14 @@
2708
2994
  }
2709
2995
 
2710
2996
  /* generate CSS styles for rows */
2711
- for (i = opts.rows + extra_cells; i >= 0; i--) {
2997
+ for (i = opts.rows; i >= 0; i--) {
2712
2998
  styles += (opts.namespace + ' [data-row="' + (i + 1) + '"] { top:' +
2713
2999
  ((i * opts.widget_base_dimensions[1]) +
2714
3000
  (i * opts.widget_margins[1]) +
2715
3001
  ((i + 1) * opts.widget_margins[1]) ) + 'px;} ');
2716
3002
  }
2717
3003
 
2718
- for (var y = 1; y <= max_size_y; y++) {
3004
+ for (var y = 1; y <= opts.rows; y++) {
2719
3005
  styles += (opts.namespace + ' [data-sizey="' + y + '"] { height:' +
2720
3006
  (y * opts.widget_base_dimensions[1] +
2721
3007
  (y - 1) * (opts.widget_margins[1] * 2)) + 'px;}');
@@ -2771,7 +3057,23 @@
2771
3057
  for (col = cols; col > 0; col--) {
2772
3058
  this.gridmap[col] = [];
2773
3059
  for (row = rows; row > 0; row--) {
2774
- var coords = $({
3060
+ this.add_faux_cell(row, col);
3061
+ }
3062
+ }
3063
+ return this;
3064
+ };
3065
+
3066
+
3067
+ /**
3068
+ * Add cell to the faux grid.
3069
+ *
3070
+ * @method add_faux_cell
3071
+ * @param {Number} row The row for the new faux cell.
3072
+ * @param {Number} col The col for the new faux cell.
3073
+ * @return {Object} Returns the instance of the Gridster class.
3074
+ */
3075
+ fn.add_faux_cell = function(row, col) {
3076
+ var coords = $({
2775
3077
  left: this.baseX + ((col - 1) * this.min_widget_width),
2776
3078
  top: this.baseY + (row -1) * this.min_widget_height,
2777
3079
  width: this.min_widget_width,
@@ -2782,10 +3084,66 @@
2782
3084
  original_row: row
2783
3085
  }).coords();
2784
3086
 
2785
- this.gridmap[col][row] = false;
2786
- this.faux_grid.push(coords);
3087
+ if (!$.isArray(this.gridmap[col])) {
3088
+ this.gridmap[col] = [];
3089
+ }
3090
+
3091
+ this.gridmap[col][row] = false;
3092
+ this.faux_grid.push(coords);
3093
+
3094
+ return this;
3095
+ };
3096
+
3097
+
3098
+ /**
3099
+ * Add rows to the faux grid.
3100
+ *
3101
+ * @method add_faux_rows
3102
+ * @param {Number} rows The number of rows you want to add to the faux grid.
3103
+ * @return {Object} Returns the instance of the Gridster class.
3104
+ */
3105
+ fn.add_faux_rows = function(rows) {
3106
+ var actual_rows = this.rows;
3107
+ var max_rows = actual_rows + (rows || 1);
3108
+
3109
+ for (var r = max_rows; r > actual_rows; r--) {
3110
+ for (var c = this.cols; c >= 1; c--) {
3111
+ this.add_faux_cell(r, c);
2787
3112
  }
2788
3113
  }
3114
+
3115
+ this.rows = max_rows;
3116
+
3117
+ if (this.options.autogenerate_stylesheet) {
3118
+ this.generate_stylesheet();
3119
+ }
3120
+
3121
+ return this;
3122
+ };
3123
+
3124
+ /**
3125
+ * Add cols to the faux grid.
3126
+ *
3127
+ * @method add_faux_cols
3128
+ * @param {Number} cols The number of cols you want to add to the faux grid.
3129
+ * @return {Object} Returns the instance of the Gridster class.
3130
+ */
3131
+ fn.add_faux_cols = function(cols) {
3132
+ var actual_cols = this.cols;
3133
+ var max_cols = actual_cols + (cols || 1);
3134
+
3135
+ for (var c = actual_cols; c < max_cols; c++) {
3136
+ for (var r = this.rows; r >= 1; r--) {
3137
+ this.add_faux_cell(r, c);
3138
+ }
3139
+ }
3140
+
3141
+ this.cols = max_cols;
3142
+
3143
+ if (this.options.autogenerate_stylesheet) {
3144
+ this.generate_stylesheet();
3145
+ }
3146
+
2789
3147
  return this;
2790
3148
  };
2791
3149
 
@@ -2841,8 +3199,6 @@
2841
3199
 
2842
3200
  var cols = Math.floor(aw / this.min_widget_width) +
2843
3201
  this.options.extra_cols;
2844
- var rows = Math.floor(ah / this.min_widget_height) +
2845
- this.options.extra_rows;
2846
3202
 
2847
3203
  var actual_cols = this.$widgets.map(function() {
2848
3204
  return $(this).attr('data-col');
@@ -2851,18 +3207,16 @@
2851
3207
  //needed to pass tests with phantomjs
2852
3208
  actual_cols.length || (actual_cols = [0]);
2853
3209
 
2854
- var actual_rows = this.$widgets.map(function() {
2855
- return $(this).attr('data-row');
2856
- });
2857
- actual_rows = Array.prototype.slice.call(actual_rows, 0);
2858
- //needed to pass tests with phantomjs
2859
- actual_rows.length || (actual_rows = [0]);
2860
-
2861
3210
  var min_cols = Math.max.apply(Math, actual_cols);
2862
- var min_rows = Math.max.apply(Math, actual_rows);
3211
+
3212
+ // get all rows that could be occupied by the current widgets
3213
+ var max_rows = this.options.extra_rows;
3214
+ this.$widgets.each(function(i, w) {
3215
+ max_rows += (+$(w).attr('data-sizey'));
3216
+ });
2863
3217
 
2864
3218
  this.cols = Math.max(min_cols, cols, this.options.min_cols);
2865
- this.rows = Math.max(min_rows, rows, this.options.min_rows);
3219
+ this.rows = Math.max(max_rows, this.options.min_rows);
2866
3220
 
2867
3221
  this.baseX = ($(window).width() - aw) / 2;
2868
3222
  this.baseY = this.$wrapper.offset().top;
@@ -2871,11 +3225,29 @@
2871
3225
  this.generate_stylesheet();
2872
3226
  }
2873
3227
 
2874
- /* more faux rows that needed are created so that there are cells
2875
- * where drag beyond the limits */
2876
3228
  return this.generate_faux_grid(this.rows, this.cols);
2877
3229
  };
2878
3230
 
3231
+ /**
3232
+ * Destroy this gridster by removing any sign of its presence, making it easy to avoid memory leaks
3233
+ *
3234
+ * @method destroy
3235
+ * @return {undefined}
3236
+ */
3237
+ fn.destroy = function(){
3238
+ // remove bound callback on window resize
3239
+ $(window).unbind('resize', this.on_window_resize);
3240
+
3241
+ if(this.drag_api){
3242
+ this.drag_api.destroy();
3243
+ }
3244
+
3245
+ // lastly, remove gridster element
3246
+ // this will additionally cause any data associated to this element to be removed, including this
3247
+ // very gridster instance
3248
+ this.$el.remove();
3249
+ };
3250
+
2879
3251
 
2880
3252
  //jQuery adapter
2881
3253
  $.fn.gridster = function(options) {
@@ -2886,5 +3258,6 @@
2886
3258
  });
2887
3259
  };
2888
3260
 
3261
+ $.Gridster = fn;
2889
3262
 
2890
3263
  }(jQuery, window, document));