dashing 1.0.4 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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));