pyk 0.1.6 → 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- YTkzZjBhOWU4OTY1YTI4NmYwYTkwZjEyYzkxNzZhMTMzMjFmYTgzZA==
4
+ NWMyMjA1MGEwYmQzNDJmMTRkM2ZiZWZkYTZmNzI2OGNiMzhiZjNjYg==
5
5
  data.tar.gz: !binary |-
6
- YWEyNzQ4MmUxNGM5ZTc1NjI1MTgzMzE4MDdlZjc2YTg1ZWMzYTdiNg==
6
+ YTQ2MmEyNTlhOTMwYjkzZWM2MzJkOWFmNWVjNjFmOTJiMmZlZjNiMw==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- YmQ5NTk2ZGMzYTU1NDNiYmQyNThhMjhjZWZjNTBmZGNiNTFlMzNjYWQ0ODE4
10
- OWFhZmRhMjU2ZGU5NTdjMGMxMmIwYzc4NjM2OGFlNWEwM2RjZDljNWFjMjMx
11
- YWJlMWZjYWIyYjcyYTg4MTQ0Y2QzMTgyMzE3MDBhYzk2YzIwZWU=
9
+ M2VkOTI0NWU3MDE2NmFlMzcyODFjZmVkYmJmNDgxMmRiMzZlYmY0MDI5NjJj
10
+ ZWE2ODdjNTNlZWE0M2Q4YmEwMzhhNjA4MzI5MDI5NjVkZTg4YmMzMzU5NGM4
11
+ NzY0ZjdlYzc5MTViZDk3MzU2MTNjNGIxODZkZWI2MjllNDU4MzA=
12
12
  data.tar.gz: !binary |-
13
- NWY1NjgwY2I1ZTY3MjE0MDMwYTQ0NDJkMThiZjcwODc3MjYyMjg4NWVjZjMw
14
- NjczMjg5YmZhMTMyZGQ3ZDNmMGNhMDY3NjRmYmUzODkwNWEwNzdiODUxMmRh
15
- ZTZkNTQxOWU4MjlkOGFiN2Y3OWY0OGU1YzdhNGFjYWU2Mzc2NzU=
13
+ OWQwODc4ZGZhMDI3MjBhYjEyOTRmMWEwYzhkOGEyODFmNmRmYmNjMjcyN2Q1
14
+ NGYxOWEzOWVmODZjYWRhY2Y3YTU3ZjBiM2E4ZTBmMzAzM2VkNTMyOTM4NmI3
15
+ YzNlOWEzNDI5YzQyZjJiODlhYmM2ZjRiMDlkZGUwOTQ2NzA5OGY=
@@ -0,0 +1,152 @@
1
+ $(document).ready(function() {
2
+ $(".gridster ul").gridster({
3
+ widget_selector: "> li",
4
+ widget_margins: [5, 5],
5
+ max_cols: 5,
6
+ max_size_x: 5,
7
+ autogenerate_stylesheet: false,
8
+ widget_base_dimensions: [200, 200]
9
+ });
10
+ });
11
+
12
+
13
+ function pykih_gridster(base_url, account_slug, auth_token) {
14
+ var url = base_url + "/api/v1/accounts/" + account_slug + "/grid/get.json?access_token=" + auth_token;
15
+ $.getJSON(url,
16
+ function (data) {
17
+ if (data != null) {
18
+ $.each(data, function (i, value) {
19
+
20
+ // var localData = JSON.parse(localStorage.getItem('position'));
21
+ var id_name, col, row, size_x, size_y;
22
+
23
+ id_name = "#";
24
+ id_name = id_name + value.id;
25
+ //console.log(id_name);
26
+
27
+ if (id_name == '#li1') {
28
+ $('#li1').attr('data-row', value.row);
29
+ $('#li1').attr('data-col', value.col);
30
+ $('#li1').attr('data-sizex', value.size_x);
31
+ $('#li1').attr('data-sizey', value.size_y);
32
+
33
+ } else if (id_name == '#li2') {
34
+ $('#li2').attr('data-row', value.row);
35
+ $('#li2').attr('data-col', value.col);
36
+ $('#li2').attr('data-sizex', value.size_x);
37
+ $('#li2').attr('data-sizey', value.size_y);
38
+
39
+ } else if (id_name == '#li3') {
40
+ $('#li3').attr('data-row', value.row);
41
+ $('#li3').attr('data-col', value.col);
42
+ $('#li3').attr('data-sizex', value.size_x);
43
+ $('#li3').attr('data-sizey', value.size_y);
44
+
45
+ } else if (id_name == '#li4') {
46
+ $('#li4').attr('data-row', value.row);
47
+ $('#li4').attr('data-col', value.col);
48
+ $('#li4').attr('data-sizex', value.size_x);
49
+ $('#li4').attr('data-sizey', value.size_y);
50
+
51
+ } else if (id_name == '#li5') {
52
+ $('#li5').attr('data-row', value.row);
53
+ $('#li5').attr('data-col', value.col);
54
+ $('#li5').attr('data-sizex', value.size_x);
55
+ $('#li5').attr('data-sizey', value.size_y);
56
+
57
+ } else if (id_name == '#li6') {
58
+ $('#li6').attr('data-row', value.row);
59
+ $('#li6').attr('data-col', value.col);
60
+ $('#li6').attr('data-sizex', value.size_x);
61
+ $('#li6').attr('data-sizey', value.size_y);
62
+
63
+ } else if (id_name == '#li7') {
64
+ $('#li7').attr('data-row', value.row);
65
+ $('#li7').attr('data-col', value.col);
66
+ $('#li7').attr('data-sizex', value.size_x);
67
+ $('#li7').attr('data-sizey', value.size_y);
68
+
69
+ } else if (id_name == '#li8') {
70
+ $('#li8').attr('data-row', value.row);
71
+ $('#li8').attr('data-col', value.col);
72
+ $('#li8').attr('data-sizex', value.size_x);
73
+ $('#li8').attr('data-sizey', value.size_y);
74
+
75
+ } else if (id_name == '#li9') {
76
+ $('#li9').attr('data-row', value.row);
77
+ $('#li9').attr('data-col', value.col);
78
+ $('#li9').attr('data-sizex', value.size_x);
79
+ $('#li9').attr('data-sizey', value.size_y);
80
+
81
+ } else if (id_name == '#li10') {
82
+ $('#li10').attr('data-row', value.row);
83
+ $('#li10').attr('data-col', value.col);
84
+ $('#li10').attr('data-sizex', value.size_x);
85
+ $('#li10').attr('data-sizey', value.size_y);
86
+
87
+ } else if (id_name == '#li11') {
88
+ $('#li11').attr('data-row', value.row);
89
+ $('#li11').attr('data-col', value.col);
90
+ $('#li11').attr('data-sizex', value.size_x);
91
+ $('#li11').attr('data-sizey', value.size_y);
92
+
93
+ } else if (id_name == '#li12') {
94
+ $('#li12').attr('data-row', value.row);
95
+ $('#li12').attr('data-col', value.col);
96
+ $('#li12').attr('data-sizex', value.size_x);
97
+ $('#li12').attr('data-sizey', value.size_y);
98
+
99
+ }
100
+
101
+
102
+ });
103
+ } else {
104
+ console.log('Daily Health Grid Error: No data returned by the server');
105
+ }
106
+ });
107
+
108
+
109
+
110
+ var grid_canvas = $(".gridster > ul").gridster({
111
+ widget_margins: [10, 10],
112
+ widget_base_dimensions: [140, 140],
113
+
114
+
115
+ serialize_params: function ($w, wgd) {
116
+ return {
117
+ id: $($w).attr('id'),
118
+ col: wgd.col,
119
+ row: wgd.row,
120
+ size_x: wgd.size_x,
121
+ size_y: wgd.size_y,
122
+
123
+ };
124
+ },
125
+ draggable: {
126
+
127
+ stop: function (event, ui) {
128
+
129
+ var positions = JSON.stringify(this.serialize());
130
+ //localStorage.setItem('position', positions);
131
+
132
+ var url_save = base_url + "/api/v1/accounts/" + account_slug + "/grid/save.json?access_token=" + auth_token;
133
+ $.post(url_save, {
134
+ "positions": positions
135
+ },
136
+ function (data) {
137
+ //var resultado = jQuery.parseJSON(data);
138
+ //console.log(resultado);
139
+
140
+
141
+ // var localData = JSON.parse(localStorage.getItem('position'));
142
+ // $.each(localData, function(key, value){
143
+ // console.log(key + ' = ' + value.id);
144
+ // });
145
+ });
146
+
147
+ }
148
+ }
149
+ }).data('gridster');
150
+
151
+
152
+ }
@@ -0,0 +1,3621 @@
1
+ /*
2
+ * jquery.coords
3
+ * https://github.com/ducksboard/gridster.js
4
+ *
5
+ * Copyright (c) 2012 ducksboard
6
+ * Licensed under the MIT licenses.
7
+ */
8
+
9
+ ;(function($, window, document, undefined){
10
+ /**
11
+ * Creates objects with coordinates (x1, y1, x2, y2, cx, cy, width, height)
12
+ * to simulate DOM elements on the screen.
13
+ * Coords is used by Gridster to create a faux grid with any DOM element can
14
+ * collide.
15
+ *
16
+ * @class Coords
17
+ * @param {HTMLElement|Object} obj The jQuery HTMLElement or a object with: left,
18
+ * top, width and height properties.
19
+ * @return {Object} Coords instance.
20
+ * @constructor
21
+ */
22
+ function Coords(obj) {
23
+ if (obj[0] && $.isPlainObject(obj[0])) {
24
+ this.data = obj[0];
25
+ }else {
26
+ this.el = obj;
27
+ }
28
+
29
+ this.isCoords = true;
30
+ this.coords = {};
31
+ this.init();
32
+ return this;
33
+ }
34
+
35
+
36
+ var fn = Coords.prototype;
37
+
38
+
39
+ fn.init = function(){
40
+ this.set();
41
+ this.original_coords = this.get();
42
+ };
43
+
44
+
45
+ fn.set = function(update, not_update_offsets) {
46
+ var el = this.el;
47
+
48
+ if (el && !update) {
49
+ this.data = el.offset();
50
+ this.data.width = el.width();
51
+ this.data.height = el.height();
52
+ }
53
+
54
+ if (el && update && !not_update_offsets) {
55
+ var offset = el.offset();
56
+ this.data.top = offset.top;
57
+ this.data.left = offset.left;
58
+ }
59
+
60
+ var d = this.data;
61
+
62
+ this.coords.x1 = d.left;
63
+ this.coords.y1 = d.top;
64
+ this.coords.x2 = d.left + d.width;
65
+ this.coords.y2 = d.top + d.height;
66
+ this.coords.cx = d.left + (d.width / 2);
67
+ this.coords.cy = d.top + (d.height / 2);
68
+ this.coords.width = d.width;
69
+ this.coords.height = d.height;
70
+ this.coords.el = el || false ;
71
+
72
+ return this;
73
+ };
74
+
75
+
76
+ fn.update = function(data){
77
+ if (!data && !this.el) {
78
+ return this;
79
+ }
80
+
81
+ if (data) {
82
+ var new_data = $.extend({}, this.data, data);
83
+ this.data = new_data;
84
+ return this.set(true, true);
85
+ }
86
+
87
+ this.set(true);
88
+ return this;
89
+ };
90
+
91
+
92
+ fn.get = function(){
93
+ return this.coords;
94
+ };
95
+
96
+
97
+ //jQuery adapter
98
+ $.fn.coords = function() {
99
+ if (this.data('coords') ) {
100
+ return this.data('coords');
101
+ }
102
+
103
+ var ins = new Coords(this, arguments[0]);
104
+ this.data('coords', ins);
105
+ return ins;
106
+ };
107
+
108
+ }(jQuery, window, document));
109
+
110
+ /*
111
+ * jquery.collision
112
+ * https://github.com/ducksboard/gridster.js
113
+ *
114
+ * Copyright (c) 2012 ducksboard
115
+ * Licensed under the MIT licenses.
116
+ */
117
+
118
+ ;(function($, window, document, undefined){
119
+
120
+ var defaults = {
121
+ colliders_context: document.body
122
+ // ,on_overlap: function(collider_data){},
123
+ // on_overlap_start : function(collider_data){},
124
+ // on_overlap_stop : function(collider_data){}
125
+ };
126
+
127
+
128
+ /**
129
+ * Detects collisions between a DOM element against other DOM elements or
130
+ * Coords objects.
131
+ *
132
+ * @class Collision
133
+ * @uses Coords
134
+ * @param {HTMLElement} el The jQuery wrapped HTMLElement.
135
+ * @param {HTMLElement|Array} colliders Can be a jQuery collection
136
+ * of HTMLElements or an Array of Coords instances.
137
+ * @param {Object} [options] An Object with all options you want to
138
+ * overwrite:
139
+ * @param {Function} [options.on_overlap_start] Executes a function the first
140
+ * time each `collider ` is overlapped.
141
+ * @param {Function} [options.on_overlap_stop] Executes a function when a
142
+ * `collider` is no longer collided.
143
+ * @param {Function} [options.on_overlap] Executes a function when the
144
+ * mouse is moved during the collision.
145
+ * @return {Object} Collision instance.
146
+ * @constructor
147
+ */
148
+ function Collision(el, colliders, options) {
149
+ this.options = $.extend(defaults, options);
150
+ this.$element = el;
151
+ this.last_colliders = [];
152
+ this.last_colliders_coords = [];
153
+ if (typeof colliders === 'string' || colliders instanceof jQuery) {
154
+ this.$colliders = $(colliders,
155
+ this.options.colliders_context).not(this.$element);
156
+ }else{
157
+ this.colliders = $(colliders);
158
+ }
159
+
160
+ this.init();
161
+ }
162
+
163
+
164
+ var fn = Collision.prototype;
165
+
166
+
167
+ fn.init = function() {
168
+ this.find_collisions();
169
+ };
170
+
171
+
172
+ fn.overlaps = function(a, b) {
173
+ var x = false;
174
+ var y = false;
175
+
176
+ if ((b.x1 >= a.x1 && b.x1 <= a.x2) ||
177
+ (b.x2 >= a.x1 && b.x2 <= a.x2) ||
178
+ (a.x1 >= b.x1 && a.x2 <= b.x2)
179
+ ) { x = true; }
180
+
181
+ if ((b.y1 >= a.y1 && b.y1 <= a.y2) ||
182
+ (b.y2 >= a.y1 && b.y2 <= a.y2) ||
183
+ (a.y1 >= b.y1 && a.y2 <= b.y2)
184
+ ) { y = true; }
185
+
186
+ return (x && y);
187
+ };
188
+
189
+
190
+ fn.detect_overlapping_region = function(a, b){
191
+ var regionX = '';
192
+ var regionY = '';
193
+
194
+ if (a.y1 > b.cy && a.y1 < b.y2) { regionX = 'N'; }
195
+ if (a.y2 > b.y1 && a.y2 < b.cy) { regionX = 'S'; }
196
+ if (a.x1 > b.cx && a.x1 < b.x2) { regionY = 'W'; }
197
+ if (a.x2 > b.x1 && a.x2 < b.cx) { regionY = 'E'; }
198
+
199
+ return (regionX + regionY) || 'C';
200
+ };
201
+
202
+
203
+ fn.calculate_overlapped_area_coords = function(a, b){
204
+ var x1 = Math.max(a.x1, b.x1);
205
+ var y1 = Math.max(a.y1, b.y1);
206
+ var x2 = Math.min(a.x2, b.x2);
207
+ var y2 = Math.min(a.y2, b.y2);
208
+
209
+ return $({
210
+ left: x1,
211
+ top: y1,
212
+ width : (x2 - x1),
213
+ height: (y2 - y1)
214
+ }).coords().get();
215
+ };
216
+
217
+
218
+ fn.calculate_overlapped_area = function(coords){
219
+ return (coords.width * coords.height);
220
+ };
221
+
222
+
223
+ fn.manage_colliders_start_stop = function(new_colliders_coords, start_callback, stop_callback){
224
+ var last = this.last_colliders_coords;
225
+
226
+ for (var i = 0, il = last.length; i < il; i++) {
227
+ if ($.inArray(last[i], new_colliders_coords) === -1) {
228
+ start_callback.call(this, last[i]);
229
+ }
230
+ }
231
+
232
+ for (var j = 0, jl = new_colliders_coords.length; j < jl; j++) {
233
+ if ($.inArray(new_colliders_coords[j], last) === -1) {
234
+ stop_callback.call(this, new_colliders_coords[j]);
235
+ }
236
+
237
+ }
238
+ };
239
+
240
+
241
+ fn.find_collisions = function(player_data_coords){
242
+ var self = this;
243
+ var colliders_coords = [];
244
+ var colliders_data = [];
245
+ var $colliders = (this.colliders || this.$colliders);
246
+ var count = $colliders.length;
247
+ var player_coords = self.$element.coords()
248
+ .update(player_data_coords || false).get();
249
+
250
+ while(count--){
251
+ var $collider = self.$colliders ?
252
+ $($colliders[count]) : $colliders[count];
253
+ var $collider_coords_ins = ($collider.isCoords) ?
254
+ $collider : $collider.coords();
255
+ var collider_coords = $collider_coords_ins.get();
256
+ var overlaps = self.overlaps(player_coords, collider_coords);
257
+
258
+ if (!overlaps) {
259
+ continue;
260
+ }
261
+
262
+ var region = self.detect_overlapping_region(
263
+ player_coords, collider_coords);
264
+
265
+ //todo: make this an option
266
+ if (region === 'C'){
267
+ var area_coords = self.calculate_overlapped_area_coords(
268
+ player_coords, collider_coords);
269
+ var area = self.calculate_overlapped_area(area_coords);
270
+ var collider_data = {
271
+ area: area,
272
+ area_coords : area_coords,
273
+ region: region,
274
+ coords: collider_coords,
275
+ player_coords: player_coords,
276
+ el: $collider
277
+ };
278
+
279
+ if (self.options.on_overlap) {
280
+ self.options.on_overlap.call(this, collider_data);
281
+ }
282
+ colliders_coords.push($collider_coords_ins);
283
+ colliders_data.push(collider_data);
284
+ }
285
+ }
286
+
287
+ if (self.options.on_overlap_stop || self.options.on_overlap_start) {
288
+ this.manage_colliders_start_stop(colliders_coords,
289
+ self.options.on_overlap_start, self.options.on_overlap_stop);
290
+ }
291
+
292
+ this.last_colliders_coords = colliders_coords;
293
+
294
+ return colliders_data;
295
+ };
296
+
297
+
298
+ fn.get_closest_colliders = function(player_data_coords){
299
+ var colliders = this.find_collisions(player_data_coords);
300
+
301
+ colliders.sort(function(a, b) {
302
+ /* if colliders are being overlapped by the "C" (center) region,
303
+ * we have to set a lower index in the array to which they are placed
304
+ * above in the grid. */
305
+ if (a.region === 'C' && b.region === 'C') {
306
+ if (a.coords.y1 < b.coords.y1 || a.coords.x1 < b.coords.x1) {
307
+ return - 1;
308
+ }else{
309
+ return 1;
310
+ }
311
+ }
312
+
313
+ if (a.area < b.area) {
314
+ return 1;
315
+ }
316
+
317
+ return 1;
318
+ });
319
+ return colliders;
320
+ };
321
+
322
+
323
+ //jQuery adapter
324
+ $.fn.collision = function(collider, options) {
325
+ return new Collision( this, collider, options );
326
+ };
327
+
328
+
329
+ }(jQuery, window, document));
330
+
331
+ ;(function(window, undefined) {
332
+ /* Debounce and throttle functions taken from underscore.js */
333
+ window.debounce = function(func, wait, immediate) {
334
+ var timeout;
335
+ return function() {
336
+ var context = this, args = arguments;
337
+ var later = function() {
338
+ timeout = null;
339
+ if (!immediate) func.apply(context, args);
340
+ };
341
+ if (immediate && !timeout) func.apply(context, args);
342
+ clearTimeout(timeout);
343
+ timeout = setTimeout(later, wait);
344
+ };
345
+ };
346
+
347
+
348
+ window.throttle = function(func, wait) {
349
+ var context, args, timeout, throttling, more, result;
350
+ var whenDone = debounce(
351
+ function(){ more = throttling = false; }, wait);
352
+ return function() {
353
+ context = this; args = arguments;
354
+ var later = function() {
355
+ timeout = null;
356
+ if (more) func.apply(context, args);
357
+ whenDone();
358
+ };
359
+ if (!timeout) timeout = setTimeout(later, wait);
360
+ if (throttling) {
361
+ more = true;
362
+ } else {
363
+ result = func.apply(context, args);
364
+ }
365
+ whenDone();
366
+ throttling = true;
367
+ return result;
368
+ };
369
+ };
370
+
371
+ })(window);
372
+
373
+ /*
374
+ * jquery.draggable
375
+ * https://github.com/ducksboard/gridster.js
376
+ *
377
+ * Copyright (c) 2012 ducksboard
378
+ * Licensed under the MIT licenses.
379
+ */
380
+
381
+ ;(function($, window, document, undefined){
382
+
383
+ var defaults = {
384
+ items: '.gs_w',
385
+ distance: 1,
386
+ limit: true,
387
+ offset_left: 0,
388
+ autoscroll: true,
389
+ ignore_dragging: ['INPUT', 'TEXTAREA', 'SELECT', 'BUTTON'],
390
+ handle: null
391
+ // drag: function(e){},
392
+ // start : function(e, ui){},
393
+ // stop : function(e){}
394
+ };
395
+
396
+ var $window = $(window);
397
+ var isTouch = !!('ontouchstart' in window);
398
+ var pointer_events = {
399
+ start: isTouch ? 'touchstart' : 'mousedown.draggable',
400
+ move: isTouch ? 'touchmove' : 'mousemove.draggable',
401
+ end: isTouch ? 'touchend' : 'mouseup.draggable'
402
+ };
403
+
404
+ /**
405
+ * Basic drag implementation for DOM elements inside a container.
406
+ * Provide start/stop/drag callbacks.
407
+ *
408
+ * @class Draggable
409
+ * @param {HTMLElement} el The HTMLelement that contains all the widgets
410
+ * to be dragged.
411
+ * @param {Object} [options] An Object with all options you want to
412
+ * overwrite:
413
+ * @param {HTMLElement|String} [options.items] Define who will
414
+ * be the draggable items. Can be a CSS Selector String or a
415
+ * collection of HTMLElements.
416
+ * @param {Number} [options.distance] Distance in pixels after mousedown
417
+ * the mouse must move before dragging should start.
418
+ * @param {Boolean} [options.limit] Constrains dragging to the width of
419
+ * the container
420
+ * @param {offset_left} [options.offset_left] Offset added to the item
421
+ * that is being dragged.
422
+ * @param {Number} [options.drag] Executes a callback when the mouse is
423
+ * moved during the dragging.
424
+ * @param {Number} [options.start] Executes a callback when the drag
425
+ * starts.
426
+ * @param {Number} [options.stop] Executes a callback when the drag stops.
427
+ * @return {Object} Returns `el`.
428
+ * @constructor
429
+ */
430
+ function Draggable(el, options) {
431
+ this.options = $.extend({}, defaults, options);
432
+ this.$body = $(document.body);
433
+ this.$container = $(el);
434
+ this.$dragitems = $(this.options.items, this.$container);
435
+ this.is_dragging = false;
436
+ this.player_min_left = 0 + this.options.offset_left;
437
+ this.init();
438
+ }
439
+
440
+ var fn = Draggable.prototype;
441
+
442
+ fn.init = function() {
443
+ this.calculate_positions();
444
+ this.$container.css('position', 'relative');
445
+ this.disabled = false;
446
+ this.events();
447
+
448
+ this.on_window_resize = throttle($.proxy(this.calculate_positions, this), 200);
449
+ $(window).bind('resize', this.on_window_resize);
450
+ };
451
+
452
+ fn.events = function() {
453
+ this.proxied_on_select_start = $.proxy(this.on_select_start, this);
454
+ this.$container.on('selectstart', this.proxied_on_select_start);
455
+
456
+ this.proxied_drag_handler = $.proxy(this.drag_handler, this);
457
+ this.$container.on(pointer_events.start, this.options.items, this.proxied_drag_handler);
458
+
459
+ this.proxied_pointer_events_end = $.proxy(function(e) {
460
+ this.is_dragging = false;
461
+ if (this.disabled) { return; }
462
+ this.$body.off(pointer_events.move);
463
+ if (this.drag_start) {
464
+ this.on_dragstop(e);
465
+ }
466
+ }, this);
467
+ this.$body.on(pointer_events.end, this.proxied_pointer_events_end);
468
+ };
469
+
470
+ fn.get_actual_pos = function($el) {
471
+ var pos = $el.position();
472
+ return pos;
473
+ };
474
+
475
+
476
+ fn.get_mouse_pos = function(e) {
477
+ if (isTouch) {
478
+ var oe = e.originalEvent;
479
+ e = oe.touches.length ? oe.touches[0] : oe.changedTouches[0];
480
+ }
481
+
482
+ return {
483
+ left: e.clientX,
484
+ top: e.clientY
485
+ };
486
+ };
487
+
488
+
489
+ fn.get_offset = function(e) {
490
+ e.preventDefault();
491
+ var mouse_actual_pos = this.get_mouse_pos(e);
492
+ var diff_x = Math.round(
493
+ mouse_actual_pos.left - this.mouse_init_pos.left);
494
+ var diff_y = Math.round(mouse_actual_pos.top - this.mouse_init_pos.top);
495
+
496
+ var left = Math.round(this.el_init_offset.left + diff_x - this.baseX);
497
+ var top = Math.round(
498
+ this.el_init_offset.top + diff_y - this.baseY + this.scrollOffset);
499
+
500
+ if (this.options.limit) {
501
+ if (left > this.player_max_left) {
502
+ left = this.player_max_left;
503
+ }else if(left < this.player_min_left) {
504
+ left = this.player_min_left;
505
+ }
506
+ }
507
+
508
+ return {
509
+ left: left,
510
+ top: top,
511
+ mouse_left: mouse_actual_pos.left,
512
+ mouse_top: mouse_actual_pos.top
513
+ };
514
+ };
515
+
516
+
517
+ fn.manage_scroll = function(offset) {
518
+ /* scroll document */
519
+ var nextScrollTop;
520
+ var scrollTop = $window.scrollTop();
521
+ var min_window_y = scrollTop;
522
+ var max_window_y = min_window_y + this.window_height;
523
+
524
+ var mouse_down_zone = max_window_y - 50;
525
+ var mouse_up_zone = min_window_y + 50;
526
+
527
+ var abs_mouse_left = offset.mouse_left;
528
+ var abs_mouse_top = min_window_y + offset.mouse_top;
529
+
530
+ var max_player_y = (this.doc_height - this.window_height +
531
+ this.player_height);
532
+
533
+ if (abs_mouse_top >= mouse_down_zone) {
534
+ nextScrollTop = scrollTop + 30;
535
+ if (nextScrollTop < max_player_y) {
536
+ $window.scrollTop(nextScrollTop);
537
+ this.scrollOffset = this.scrollOffset + 30;
538
+ }
539
+ }
540
+
541
+ if (abs_mouse_top <= mouse_up_zone) {
542
+ nextScrollTop = scrollTop - 30;
543
+ if (nextScrollTop > 0) {
544
+ $window.scrollTop(nextScrollTop);
545
+ this.scrollOffset = this.scrollOffset - 30;
546
+ }
547
+ }
548
+ };
549
+
550
+
551
+ fn.calculate_positions = function(e) {
552
+ this.window_height = $window.height();
553
+ };
554
+
555
+
556
+ fn.drag_handler = function(e) {
557
+ var node = e.target.nodeName;
558
+ if (this.disabled || e.which !== 1 && !isTouch) {
559
+ return;
560
+ }
561
+
562
+ if (this.ignore_drag(e)) {
563
+ return;
564
+ }
565
+
566
+ var self = this;
567
+ var first = true;
568
+ this.$player = $(e.currentTarget);
569
+
570
+ this.el_init_pos = this.get_actual_pos(this.$player);
571
+ this.mouse_init_pos = this.get_mouse_pos(e);
572
+ this.offsetY = this.mouse_init_pos.top - this.el_init_pos.top;
573
+
574
+ this.on_pointer_events_move = function(mme){
575
+ var mouse_actual_pos = self.get_mouse_pos(mme);
576
+ var diff_x = Math.abs(
577
+ mouse_actual_pos.left - self.mouse_init_pos.left);
578
+ var diff_y = Math.abs(
579
+ mouse_actual_pos.top - self.mouse_init_pos.top);
580
+ if (!(diff_x > self.options.distance ||
581
+ diff_y > self.options.distance)
582
+ ) {
583
+ return false;
584
+ }
585
+
586
+ if (first) {
587
+ first = false;
588
+ self.on_dragstart.call(self, mme);
589
+ return false;
590
+ }
591
+
592
+ if (self.is_dragging === true) {
593
+ self.on_dragmove.call(self, mme);
594
+ }
595
+
596
+ return false;
597
+ };
598
+
599
+ this.$body.on(pointer_events.move, this.on_pointer_events_move);
600
+
601
+ return false;
602
+ };
603
+
604
+
605
+ fn.on_dragstart = function(e) {
606
+ e.preventDefault();
607
+ this.drag_start = true;
608
+ this.is_dragging = true;
609
+ var offset = this.$container.offset();
610
+ this.baseX = Math.round(offset.left);
611
+ this.baseY = Math.round(offset.top);
612
+ this.doc_height = $(document).height();
613
+
614
+ if (this.options.helper === 'clone') {
615
+ this.$helper = this.$player.clone()
616
+ .appendTo(this.$container).addClass('helper');
617
+ this.helper = true;
618
+ }else{
619
+ this.helper = false;
620
+ }
621
+ this.scrollOffset = 0;
622
+ this.el_init_offset = this.$player.offset();
623
+ this.player_width = this.$player.width();
624
+ this.player_height = this.$player.height();
625
+ this.player_max_left = (this.$container.width() - this.player_width +
626
+ this.options.offset_left);
627
+
628
+ if (this.options.start) {
629
+ this.options.start.call(this.$player, e, {
630
+ helper: this.helper ? this.$helper : this.$player
631
+ });
632
+ }
633
+ return false;
634
+ };
635
+
636
+
637
+ fn.on_dragmove = function(e) {
638
+ var offset = this.get_offset(e);
639
+
640
+ this.options.autoscroll && this.manage_scroll(offset);
641
+
642
+ (this.helper ? this.$helper : this.$player).css({
643
+ 'position': 'absolute',
644
+ 'left' : offset.left,
645
+ 'top' : offset.top
646
+ });
647
+
648
+ var ui = {
649
+ 'position': {
650
+ 'left': offset.left,
651
+ 'top': offset.top
652
+ }
653
+ };
654
+
655
+ if (this.options.drag) {
656
+ this.options.drag.call(this.$player, e, ui);
657
+ }
658
+ return false;
659
+ };
660
+
661
+
662
+ fn.on_dragstop = function(e) {
663
+ var offset = this.get_offset(e);
664
+ this.drag_start = false;
665
+
666
+ var ui = {
667
+ 'position': {
668
+ 'left': offset.left,
669
+ 'top': offset.top
670
+ }
671
+ };
672
+
673
+ if (this.options.stop) {
674
+ this.options.stop.call(this.$player, e, ui);
675
+ }
676
+
677
+ if (this.helper) {
678
+ this.$helper.remove();
679
+ }
680
+
681
+ return false;
682
+ };
683
+
684
+ fn.on_select_start = function(e) {
685
+ if (this.disabled) { return; }
686
+
687
+ if (this.ignore_drag(e)) {
688
+ return;
689
+ }
690
+
691
+ return false;
692
+ };
693
+
694
+ fn.enable = function() {
695
+ this.disabled = false;
696
+ };
697
+
698
+ fn.disable = function() {
699
+ this.disabled = true;
700
+ };
701
+
702
+
703
+ fn.destroy = function(){
704
+ this.disable();
705
+
706
+ this.$container.off('selectstart', this.proxied_on_select_start);
707
+ this.$container.off(pointer_events.start, this.proxied_drag_handler);
708
+ this.$body.off(pointer_events.end, this.proxied_pointer_events_end);
709
+ this.$body.off(pointer_events.move, this.on_pointer_events_move);
710
+ $(window).unbind('resize', this.on_window_resize);
711
+
712
+ $.removeData(this.$container, 'drag');
713
+ };
714
+
715
+ fn.ignore_drag = function(event) {
716
+ if (this.options.handle) {
717
+ return !$(event.target).is(this.options.handle);
718
+ }
719
+
720
+ return $.inArray(event.target.nodeName, this.options.ignore_dragging) >= 0;
721
+ };
722
+
723
+ //jQuery adapter
724
+ $.fn.dragg = function ( options ) {
725
+ return this.each(function () {
726
+ if (!$.data(this, 'drag')) {
727
+ $.data(this, 'drag', new Draggable( this, options ));
728
+ }
729
+ });
730
+ };
731
+
732
+
733
+ }(jQuery, window, document));
734
+
735
+ /*
736
+ * jquery.gridster
737
+ * https://github.com/ducksboard/gridster.js
738
+ *
739
+ * Copyright (c) 2012 ducksboard
740
+ * Licensed under the MIT licenses.
741
+ */
742
+ ;(function($, window, document, undefined) {
743
+
744
+ //ToDo Max_cols and Max_size_x conflict.. need to unify
745
+ var defaults = {
746
+ namespace: '',
747
+ widget_selector: 'li',
748
+ static_class: 'static',
749
+ widget_margins: [10, 10],
750
+ widget_base_dimensions: [400, 225],
751
+ extra_rows: 0,
752
+ extra_cols: 0,
753
+ min_cols: 1,
754
+ max_cols: 60,
755
+ min_rows: 15,
756
+ max_rows: 15,
757
+ max_size_x: 6,
758
+ autogenerate_stylesheet: true,
759
+ avoid_overlapped_widgets: true,
760
+ shift_larger_widgets_down: true,
761
+ serialize_params: function($w, wgd) {
762
+ return {
763
+ col: wgd.col,
764
+ row: wgd.row,
765
+ size_x: wgd.size_x,
766
+ size_y: wgd.size_y
767
+ };
768
+ },
769
+ collision: {},
770
+ draggable: {
771
+ distance: 4,
772
+ items: ".gs_w:not(.static)"
773
+ }
774
+ };
775
+
776
+
777
+ /**
778
+ * @class Gridster
779
+ * @uses Draggable
780
+ * @uses Collision
781
+ * @param {HTMLElement} el The HTMLelement that contains all the widgets.
782
+ * @param {Object} [options] An Object with all options you want to
783
+ * overwrite:
784
+ * @param {HTMLElement|String} [options.widget_selector] Define who will
785
+ * be the draggable widgets. Can be a CSS Selector String or a
786
+ * collection of HTMLElements
787
+ * @param {Array} [options.widget_margins] Margin between widgets.
788
+ * The first index for the horizontal margin (left, right) and
789
+ * the second for the vertical margin (top, bottom).
790
+ * @param {Array} [options.widget_base_dimensions] Base widget dimensions
791
+ * in pixels. The first index for the width and the second for the
792
+ * height.
793
+ * @param {Number} [options.extra_cols] Add more columns in addition to
794
+ * those that have been calculated.
795
+ * @param {Number} [options.extra_rows] Add more rows in addition to
796
+ * those that have been calculated.
797
+ * @param {Number} [options.min_cols] The minimum required columns.
798
+ * @param {Number} [options.min_rows] The minimum required rows.
799
+ * @param {Number} [options.max_size_x] The maximum number of columns
800
+ * that a widget can span.
801
+ * @param {Boolean} [options.autogenerate_stylesheet] If true, all the
802
+ * CSS required to position all widgets in their respective columns
803
+ * and rows will be generated automatically and injected to the
804
+ * `<head>` of the document. You can set this to false, and write
805
+ * your own CSS targeting rows and cols via data-attributes like so:
806
+ * `[data-col="1"] { left: 10px; }`
807
+ * @param {Boolean} [options.avoid_overlapped_widgets] Avoid that widgets loaded
808
+ * from the DOM can be overlapped. It is helpful if the positions were
809
+ * bad stored in the database or if there was any conflict.
810
+ * @param {Function} [options.serialize_params] Return the data you want
811
+ * for each widget in the serialization. Two arguments are passed:
812
+ * `$w`: the jQuery wrapped HTMLElement, and `wgd`: the grid
813
+ * coords object (`col`, `row`, `size_x`, `size_y`).
814
+ * @param {Object} [options.collision] An Object with all options for
815
+ * Collision class you want to overwrite. See Collision docs for
816
+ * more info.
817
+ * @param {Object} [options.draggable] An Object with all options for
818
+ * Draggable class you want to overwrite. See Draggable docs for more
819
+ * info.
820
+ *
821
+ * @constructor
822
+ */
823
+ function Gridster(el, options) {
824
+ this.options = $.extend(true, defaults, options);
825
+ this.$el = $(el);
826
+ this.$wrapper = this.$el.parent();
827
+ this.$widgets = this.$el.children(this.options.widget_selector).addClass('gs_w');
828
+ this.widgets = [];
829
+ this.$changed = $([]);
830
+ this.w_queue = {};
831
+ this.wrapper_width = this.$wrapper.width();
832
+ this.min_widget_width = (this.options.widget_margins[0] * 2) +
833
+ this.options.widget_base_dimensions[0];
834
+ this.min_widget_height = (this.options.widget_margins[1] * 2) +
835
+ this.options.widget_base_dimensions[1];
836
+ this.init();
837
+ }
838
+
839
+ Gridster.generated_stylesheets = [];
840
+
841
+ var fn = Gridster.prototype;
842
+
843
+ fn.init = function() {
844
+ this.generate_grid_and_stylesheet();
845
+ this.get_widgets_from_DOM();
846
+ this.set_dom_grid_height();
847
+ this.$wrapper.addClass('ready');
848
+ this.draggable();
849
+
850
+ $(window).bind(
851
+ 'resize', throttle($.proxy(this.recalculate_faux_grid, this), 200));
852
+ };
853
+
854
+
855
+ /**
856
+ * Disables dragging.
857
+ *
858
+ * @method disable
859
+ * @return {Class} Returns the instance of the Gridster Class.
860
+ */
861
+ fn.disable = function() {
862
+ this.$wrapper.find('.player-revert').removeClass('player-revert');
863
+ this.drag_api.disable();
864
+ return this;
865
+ };
866
+
867
+
868
+ /**
869
+ * Enables dragging.
870
+ *
871
+ * @method enable
872
+ * @return {Class} Returns the instance of the Gridster Class.
873
+ */
874
+ fn.enable = function() {
875
+ this.drag_api.enable();
876
+ return this;
877
+ };
878
+
879
+
880
+ /**
881
+ * Add a new widget to the grid.
882
+ *
883
+ * @method add_widget
884
+ * @param {String|HTMLElement} html The string representing the HTML of the widget
885
+ * or the HTMLElement.
886
+ * @param {Number} [size_x] The nº of rows the widget occupies horizontally.
887
+ * @param {Number} [size_y] The nº of columns the widget occupies vertically.
888
+ * @param {Number} [col] The column the widget should start in.
889
+ * @param {Number} [row] The row the widget should start in.
890
+ * @return {HTMLElement} Returns the jQuery wrapped HTMLElement representing.
891
+ * the widget that was just created.
892
+ */
893
+ fn.add_widget = function(html, size_x, size_y, col, row) {
894
+ var pos;
895
+ size_x || (size_x = 1);
896
+ size_y || (size_y = 1);
897
+
898
+ if (!col & !row) {
899
+ pos = this.next_position(size_x, size_y);
900
+ }else{
901
+ pos = {
902
+ col: col,
903
+ row: row
904
+ };
905
+
906
+ this.empty_cells(col, row, size_x, size_y);
907
+ }
908
+
909
+ var $w = $(html).attr({
910
+ 'data-col': pos.col,
911
+ 'data-row': pos.row,
912
+ 'data-sizex' : size_x,
913
+ 'data-sizey' : size_y
914
+ }).addClass('gs_w').appendTo(this.$el).hide();
915
+
916
+ this.$widgets = this.$widgets.add($w);
917
+ this.$changed = this.$changed.add($w);
918
+
919
+ this.register_widget($w);
920
+
921
+ this.add_faux_rows(pos.size_y);
922
+ //this.add_faux_cols(pos.size_x);
923
+
924
+ this.set_dom_grid_height();
925
+
926
+ return $w.fadeIn();
927
+ };
928
+
929
+
930
+
931
+ /**
932
+ * Change the size of a widget.
933
+ *
934
+ * @method resize_widget
935
+ * @param {HTMLElement} $widget The jQuery wrapped HTMLElement
936
+ * representing the widget.
937
+ * @param {Number} size_x The number of columns that will occupy the widget.
938
+ * @param {Number} size_y The number of rows that will occupy the widget.
939
+ * @return {HTMLElement} Returns $widget.
940
+ */
941
+ fn.resize_widget = function($widget, size_x, size_y) {
942
+ var wgd = $widget.coords().grid;
943
+ size_x || (size_x = wgd.size_x);
944
+ size_y || (size_y = wgd.size_y);
945
+
946
+ if (size_x > this.cols) {
947
+ size_x = this.cols;
948
+ }
949
+
950
+ var old_cells_occupied = this.get_cells_occupied(wgd);
951
+ var old_size_x = wgd.size_x;
952
+ var old_size_y = wgd.size_y;
953
+ var old_col = wgd.col;
954
+ var new_col = old_col;
955
+ var wider = size_x > old_size_x;
956
+ var taller = size_y > old_size_y;
957
+
958
+ if (old_col + size_x - 1 > this.cols) {
959
+ var diff = old_col + (size_x - 1) - this.cols;
960
+ var c = old_col - diff;
961
+ new_col = Math.max(1, c);
962
+ }
963
+
964
+ var new_grid_data = {
965
+ col: new_col,
966
+ row: wgd.row,
967
+ size_x: size_x,
968
+ size_y: size_y
969
+ };
970
+
971
+ var new_cells_occupied = this.get_cells_occupied(new_grid_data);
972
+
973
+ var empty_cols = [];
974
+ $.each(old_cells_occupied.cols, function(i, col) {
975
+ if ($.inArray(col, new_cells_occupied.cols) === -1) {
976
+ empty_cols.push(col);
977
+ }
978
+ });
979
+
980
+ var occupied_cols = [];
981
+ $.each(new_cells_occupied.cols, function(i, col) {
982
+ if ($.inArray(col, old_cells_occupied.cols) === -1) {
983
+ occupied_cols.push(col);
984
+ }
985
+ });
986
+
987
+ var empty_rows = [];
988
+ $.each(old_cells_occupied.rows, function(i, row) {
989
+ if ($.inArray(row, new_cells_occupied.rows) === -1) {
990
+ empty_rows.push(row);
991
+ }
992
+ });
993
+
994
+ var occupied_rows = [];
995
+ $.each(new_cells_occupied.rows, function(i, row) {
996
+ if ($.inArray(row, old_cells_occupied.rows) === -1) {
997
+ occupied_rows.push(row);
998
+ }
999
+ });
1000
+
1001
+ this.remove_from_gridmap(wgd);
1002
+
1003
+ if (occupied_cols.length) {
1004
+ var cols_to_empty = [
1005
+ new_col, wgd.row, size_x, Math.min(old_size_y, size_y), $widget
1006
+ ];
1007
+ this.empty_cells.apply(this, cols_to_empty);
1008
+ }
1009
+
1010
+ if (occupied_rows.length) {
1011
+ var rows_to_empty = [new_col, wgd.row, size_x, size_y, $widget];
1012
+ this.empty_cells.apply(this, rows_to_empty);
1013
+ }
1014
+
1015
+ wgd.col = new_col;
1016
+ wgd.size_x = size_x;
1017
+ wgd.size_y = size_y;
1018
+ this.add_to_gridmap(new_grid_data, $widget);
1019
+
1020
+ //update coords instance attributes
1021
+ $widget.data('coords').update({
1022
+ width: (size_x * this.options.widget_base_dimensions[0] +
1023
+ ((size_x - 1) * this.options.widget_margins[0]) * 2),
1024
+ height: (size_y * this.options.widget_base_dimensions[1] +
1025
+ ((size_y - 1) * this.options.widget_margins[1]) * 2)
1026
+ });
1027
+
1028
+ if (size_y > old_size_y) {
1029
+ this.add_faux_rows(size_y - old_size_y);
1030
+ }
1031
+
1032
+ if (size_x > old_size_x) {
1033
+ this.add_faux_cols(size_x - old_size_x);
1034
+ }
1035
+
1036
+ $widget.attr({
1037
+ 'data-col': new_col,
1038
+ 'data-sizex': size_x,
1039
+ 'data-sizey': size_y
1040
+ });
1041
+
1042
+ if (empty_cols.length) {
1043
+ var cols_to_remove_holes = [
1044
+ empty_cols[0], wgd.row,
1045
+ empty_cols.length,
1046
+ Math.min(old_size_y, size_y),
1047
+ $widget
1048
+ ];
1049
+
1050
+ this.remove_empty_cells.apply(this, cols_to_remove_holes);
1051
+ }
1052
+
1053
+ if (empty_rows.length) {
1054
+ var rows_to_remove_holes = [
1055
+ new_col, wgd.row, size_x, size_y, $widget
1056
+ ];
1057
+ this.remove_empty_cells.apply(this, rows_to_remove_holes);
1058
+ }
1059
+
1060
+ return $widget;
1061
+ };
1062
+
1063
+ /**
1064
+ * Move down widgets in cells represented by the arguments col, row, size_x,
1065
+ * size_y
1066
+ *
1067
+ * @method empty_cells
1068
+ * @param {Number} col The column where the group of cells begin.
1069
+ * @param {Number} row The row where the group of cells begin.
1070
+ * @param {Number} size_x The number of columns that the group of cells
1071
+ * occupy.
1072
+ * @param {Number} size_y The number of rows that the group of cells
1073
+ * occupy.
1074
+ * @param {HTMLElement} $exclude Exclude widgets from being moved.
1075
+ * @return {Class} Returns the instance of the Gridster Class.
1076
+ */
1077
+ fn.empty_cells = function(col, row, size_x, size_y, $exclude) {
1078
+ var $nexts = this.widgets_below({
1079
+ col: col,
1080
+ row: row - size_y,
1081
+ size_x: size_x,
1082
+ size_y: size_y
1083
+ });
1084
+
1085
+ $nexts.not($exclude).each($.proxy(function(i, w) {
1086
+ var wgd = $(w).coords().grid;
1087
+ if (!(wgd.row <= (row + size_y - 1))) { return; }
1088
+ var diff = (row + size_y) - wgd.row;
1089
+ this.move_widget_down($(w), diff);
1090
+ }, this));
1091
+
1092
+ this.set_dom_grid_height();
1093
+
1094
+ return this;
1095
+ };
1096
+
1097
+
1098
+ /**
1099
+ * Move up widgets below cells represented by the arguments col, row, size_x,
1100
+ * size_y.
1101
+ *
1102
+ * @method remove_empty_cells
1103
+ * @param {Number} col The column where the group of cells begin.
1104
+ * @param {Number} row The row where the group of cells begin.
1105
+ * @param {Number} size_x The number of columns that the group of cells
1106
+ * occupy.
1107
+ * @param {Number} size_y The number of rows that the group of cells
1108
+ * occupy.
1109
+ * @param {HTMLElement} $exclude Exclude widgets from being moved.
1110
+ * @return {Class} Returns the instance of the Gridster Class.
1111
+ */
1112
+ fn.remove_empty_cells = function(col, row, size_x, size_y, exclude) {
1113
+ var $nexts = this.widgets_below({
1114
+ col: col,
1115
+ row: row,
1116
+ size_x: size_x,
1117
+ size_y: size_y
1118
+ });
1119
+
1120
+ /*
1121
+ $nexts.not(exclude).each($.proxy(function(i, widget) {
1122
+ console.log("from_remove")
1123
+ this.move_widget_up( $(widget), size_y );
1124
+ }, this));
1125
+ */
1126
+
1127
+ this.set_dom_grid_height();
1128
+
1129
+ return this;
1130
+ };
1131
+
1132
+
1133
+ /**
1134
+ * Get the most left column below to add a new widget.
1135
+ *
1136
+ * @method next_position
1137
+ * @param {Number} size_x The nº of rows the widget occupies horizontally.
1138
+ * @param {Number} size_y The nº of columns the widget occupies vertically.
1139
+ * @return {Object} Returns a grid coords object representing the future
1140
+ * widget coords.
1141
+ */
1142
+ fn.next_position = function(size_x, size_y) {
1143
+ size_x || (size_x = 1);
1144
+ size_y || (size_y = 1);
1145
+ var ga = this.gridmap;
1146
+ var cols_l = ga.length;
1147
+ var valid_pos = [];
1148
+ var rows_l;
1149
+
1150
+ for (var c = 1; c < cols_l; c++) {
1151
+ rows_l = ga[c].length;
1152
+ for (var r = 1; r <= rows_l; r++) {
1153
+ var can_move_to = this.can_move_to({
1154
+ size_x: size_x,
1155
+ size_y: size_y
1156
+ }, c, r);
1157
+
1158
+ if (can_move_to) {
1159
+ valid_pos.push({
1160
+ col: c,
1161
+ row: r,
1162
+ size_y: size_y,
1163
+ size_x: size_x
1164
+ });
1165
+ }
1166
+ }
1167
+ }
1168
+
1169
+ if (valid_pos.length) {
1170
+ return this.sort_by_row_and_col_asc(valid_pos)[0];
1171
+ }
1172
+ return false;
1173
+ };
1174
+
1175
+ fn.remove_by_grid = function(col, row){
1176
+ var $w = this.is_widget(col, row);
1177
+ if($w){
1178
+ this.remove_widget($w);
1179
+ }
1180
+ }
1181
+
1182
+
1183
+ /**
1184
+ * Remove a widget from the grid.
1185
+ *
1186
+ * @method remove_widget
1187
+ * @param {HTMLElement} el The jQuery wrapped HTMLElement you want to remove.
1188
+ * @param {Boolean|Function} silent If true, widgets below the removed one
1189
+ * will not move up. If a Function is passed it will be used as callback.
1190
+ * @param {Function} callback Function executed when the widget is removed.
1191
+ * @return {Class} Returns the instance of the Gridster Class.
1192
+ */
1193
+ fn.remove_widget = function(el, silent, callback) {
1194
+ var $el = el instanceof jQuery ? el : $(el);
1195
+ var wgd = $el.coords().grid;
1196
+
1197
+ // if silent is a function assume it's a callback
1198
+ if ($.isFunction(silent)) {
1199
+ callback = silent;
1200
+ silent = false;
1201
+ }
1202
+
1203
+ this.cells_occupied_by_placeholder = {};
1204
+ this.$widgets = this.$widgets.not($el);
1205
+
1206
+ var $nexts = this.widgets_below($el);
1207
+
1208
+ this.remove_from_gridmap(wgd);
1209
+
1210
+ $el.fadeOut($.proxy(function() {
1211
+ $el.remove();
1212
+
1213
+ if (!silent) {
1214
+ $nexts.each($.proxy(function(i, widget) {
1215
+ this.move_widget_up( $(widget), wgd.size_y );
1216
+ }, this));
1217
+ }
1218
+
1219
+ this.set_dom_grid_height();
1220
+
1221
+ if (callback) {
1222
+ callback.call(this, el);
1223
+ }
1224
+ }, this));
1225
+ };
1226
+
1227
+
1228
+ /**
1229
+ * Remove all widgets from the grid.
1230
+ *
1231
+ * @method remove_all_widgets
1232
+ * @param {Function} callback Function executed for each widget removed.
1233
+ * @return {Class} Returns the instance of the Gridster Class.
1234
+ */
1235
+ fn.remove_all_widgets = function(callback) {
1236
+ this.$widgets.each($.proxy(function(i, el){
1237
+ this.remove_widget(el, true, callback);
1238
+ }, this));
1239
+
1240
+ return this;
1241
+ };
1242
+
1243
+
1244
+ /**
1245
+ * Returns a serialized array of the widgets in the grid.
1246
+ *
1247
+ * @method serialize
1248
+ * @param {HTMLElement} [$widgets] The collection of jQuery wrapped
1249
+ * HTMLElements you want to serialize. If no argument is passed all widgets
1250
+ * will be serialized.
1251
+ * @return {Array} Returns an Array of Objects with the data specified in
1252
+ * the serialize_params option.
1253
+ */
1254
+ fn.serialize = function($widgets) {
1255
+ $widgets || ($widgets = this.$widgets);
1256
+ var result = [];
1257
+ $widgets.each($.proxy(function(i, widget) {
1258
+ if(typeof($(widget).coords().grid) != "undefined"){
1259
+ result.push(this.options.serialize_params(
1260
+ $(widget), $(widget).coords().grid ) );
1261
+ }
1262
+ }, this));
1263
+
1264
+ return result;
1265
+ };
1266
+
1267
+ /**
1268
+ * Returns a serialized array of the widgets that have changed their
1269
+ * position.
1270
+ *
1271
+ * @method serialize_changed
1272
+ * @return {Array} Returns an Array of Objects with the data specified in
1273
+ * the serialize_params option.
1274
+ */
1275
+ fn.serialize_changed = function() {
1276
+ return this.serialize(this.$changed);
1277
+ };
1278
+
1279
+
1280
+ /**
1281
+ * Creates the grid coords object representing the widget a add it to the
1282
+ * mapped array of positions.
1283
+ *
1284
+ * @method register_widget
1285
+ * @return {Array} Returns the instance of the Gridster class.
1286
+ */
1287
+ fn.register_widget = function($el) {
1288
+
1289
+ var wgd = {
1290
+ 'col': parseInt($el.attr('data-col'), 10),
1291
+ 'row': parseInt($el.attr('data-row'), 10),
1292
+ 'size_x': parseInt($el.attr('data-sizex'), 10),
1293
+ 'size_y': parseInt($el.attr('data-sizey'), 10),
1294
+ 'el': $el
1295
+ };
1296
+
1297
+ if (this.options.avoid_overlapped_widgets &&
1298
+ !this.can_move_to(
1299
+ {size_x: wgd.size_x, size_y: wgd.size_y}, wgd.col, wgd.row)
1300
+ ) {
1301
+ /*if(!$el.hasClass('.disp_ad')){
1302
+ $el.remove();
1303
+ return false;
1304
+ }*/
1305
+ wgd = this.next_position(wgd.size_x, wgd.size_y);
1306
+ wgd.el = $el;
1307
+ $el.attr({
1308
+ 'data-col': wgd.col,
1309
+ 'data-row': wgd.row,
1310
+ 'data-sizex': wgd.size_x,
1311
+ 'data-sizey': wgd.size_y
1312
+ });
1313
+ }
1314
+
1315
+ // attach Coord object to player data-coord attribute
1316
+ $el.data('coords', $el.coords());
1317
+
1318
+ // Extend Coord object with grid position info
1319
+ $el.data('coords').grid = wgd;
1320
+
1321
+ this.add_to_gridmap(wgd, $el);
1322
+
1323
+ return this;
1324
+ };
1325
+
1326
+
1327
+ /**
1328
+ * Update in the mapped array of positions the value of cells represented by
1329
+ * the grid coords object passed in the `grid_data` param.
1330
+ *
1331
+ * @param {Object} grid_data The grid coords object representing the cells
1332
+ * to update in the mapped array.
1333
+ * @param {HTMLElement|Boolean} value Pass `false` or the jQuery wrapped
1334
+ * HTMLElement, depends if you want to delete an existing position or add
1335
+ * a new one.
1336
+ * @method update_widget_position
1337
+ * @return {Class} Returns the instance of the Gridster Class.
1338
+ */
1339
+ fn.update_widget_position = function(grid_data, value) {
1340
+ this.for_each_cell_occupied(grid_data, function(col, row) {
1341
+ if (!this.gridmap[col]) { return this; }
1342
+ this.gridmap[col][row] = value;
1343
+ });
1344
+ return this;
1345
+ };
1346
+
1347
+
1348
+ /**
1349
+ * Remove a widget from the mapped array of positions.
1350
+ *
1351
+ * @method remove_from_gridmap
1352
+ * @param {Object} grid_data The grid coords object representing the cells
1353
+ * to update in the mapped array.
1354
+ * @return {Class} Returns the instance of the Gridster Class.
1355
+ */
1356
+ fn.remove_from_gridmap = function(grid_data) {
1357
+ return this.update_widget_position(grid_data, false);
1358
+ };
1359
+
1360
+
1361
+ /**
1362
+ * Add a widget to the mapped array of positions.
1363
+ *
1364
+ * @method add_to_gridmap
1365
+ * @param {Object} grid_data The grid coords object representing the cells
1366
+ * to update in the mapped array.
1367
+ * @param {HTMLElement|Boolean} value The value to set in the specified
1368
+ * position .
1369
+ * @return {Class} Returns the instance of the Gridster Class.
1370
+ */
1371
+ fn.add_to_gridmap = function(grid_data, value) {
1372
+ this.update_widget_position(grid_data, value || grid_data.el);
1373
+ /*if (grid_data.el) {
1374
+ var $widgets = this.widgets_below(grid_data.el);
1375
+ $widgets.each($.proxy(function(i, widget) {
1376
+ console.log("from_add_to_gridmap");
1377
+ this.move_widget_up( $(widget));
1378
+ }, this));
1379
+ } */
1380
+ };
1381
+
1382
+
1383
+ /**
1384
+ * Make widgets draggable.
1385
+ *
1386
+ * @uses Draggable
1387
+ * @method draggable
1388
+ * @return {Class} Returns the instance of the Gridster Class.
1389
+ */
1390
+ fn.draggable = function() {
1391
+ var self = this;
1392
+ var draggable_options = $.extend(true, {}, this.options.draggable, {
1393
+ offset_left: this.options.widget_margins[0],
1394
+ start: function(event, ui) {
1395
+ self.$widgets.filter('.player-revert')
1396
+ .removeClass('player-revert');
1397
+
1398
+ self.$player = $(this);
1399
+ self.$helper = self.options.draggable.helper === 'clone' ?
1400
+ $(ui.helper) : self.$player;
1401
+ self.helper = !self.$helper.is(self.$player);
1402
+
1403
+ self.on_start_drag.call(self, event, ui);
1404
+ self.$el.trigger('gridster:dragstart');
1405
+ },
1406
+ stop: function(event, ui) {
1407
+ self.on_stop_drag.call(self, event, ui);
1408
+ self.$el.trigger('gridster:dragstop');
1409
+ },
1410
+ drag: throttle(function(event, ui) {
1411
+ self.on_drag.call(self, event, ui);
1412
+ self.$el.trigger('gridster:drag');
1413
+ }, 60)
1414
+ });
1415
+
1416
+ this.drag_api = this.$el.dragg(draggable_options).data('drag');
1417
+ return this;
1418
+ };
1419
+
1420
+
1421
+ /**
1422
+ * This function is executed when the player begins to be dragged.
1423
+ *
1424
+ * @method on_start_drag
1425
+ * @param {Event} The original browser event
1426
+ * @param {Object} A prepared ui object.
1427
+ */
1428
+ fn.on_start_drag = function(event, ui) {
1429
+
1430
+ this.$helper.add(this.$player).add(this.$wrapper).addClass('dragging');
1431
+
1432
+ this.$player.addClass('player');
1433
+ this.player_grid_data = this.$player.coords().grid;
1434
+ this.placeholder_grid_data = $.extend({}, this.player_grid_data);
1435
+
1436
+ //set new grid height along the dragging period
1437
+ this.$el.css('height', this.$el.height() +
1438
+ (this.player_grid_data.size_y * this.min_widget_height));
1439
+
1440
+ var colliders = this.faux_grid;
1441
+ var coords = this.$player.data('coords').coords;
1442
+
1443
+ this.cells_occupied_by_player = this.get_cells_occupied(
1444
+ this.player_grid_data);
1445
+ this.cells_occupied_by_placeholder = this.get_cells_occupied(
1446
+ this.placeholder_grid_data);
1447
+
1448
+ this.last_cols = [];
1449
+ this.last_rows = [];
1450
+
1451
+
1452
+ // see jquery.collision.js
1453
+ this.collision_api = this.$helper.collision(
1454
+ colliders, this.options.collision);
1455
+
1456
+ this.$preview_holder = $('<li />', {
1457
+ 'class': 'preview-holder',
1458
+ 'data-row': this.$player.attr('data-row'),
1459
+ 'data-col': this.$player.attr('data-col'),
1460
+ css: {
1461
+ width: coords.width,
1462
+ height: coords.height
1463
+ }
1464
+ }).appendTo(this.$el);
1465
+
1466
+ if (this.options.draggable.start) {
1467
+ this.options.draggable.start.call(this, event, ui);
1468
+ }
1469
+ };
1470
+
1471
+
1472
+ /**
1473
+ * This function is executed when the player is being dragged.
1474
+ *
1475
+ * @method on_drag
1476
+ * @param {Event} The original browser event
1477
+ * @param {Object} A prepared ui object.
1478
+ */
1479
+ fn.on_drag = function(event, ui) {
1480
+ //break if dragstop has been fired
1481
+ if (this.$player === null) {
1482
+ return false;
1483
+ }
1484
+
1485
+ var abs_offset = {
1486
+ left: ui.position.left + this.baseX,
1487
+ top: ui.position.top + this.baseY
1488
+ };
1489
+
1490
+ this.colliders_data = this.collision_api.get_closest_colliders(
1491
+ abs_offset);
1492
+
1493
+ this.on_overlapped_column_change(
1494
+ this.on_start_overlapping_column,
1495
+ this.on_stop_overlapping_column
1496
+ );
1497
+
1498
+ this.on_overlapped_row_change(
1499
+ this.on_start_overlapping_row,
1500
+ this.on_stop_overlapping_row
1501
+ );
1502
+
1503
+ if (this.helper && this.$player) {
1504
+ this.$player.css({
1505
+ 'left': ui.position.left,
1506
+ 'top': ui.position.top
1507
+ });
1508
+ }
1509
+
1510
+ if (this.options.draggable.drag) {
1511
+ this.options.draggable.drag.call(this, event, ui);
1512
+ }
1513
+ };
1514
+
1515
+ /**
1516
+ * This function is executed when the player stops being dragged.
1517
+ *
1518
+ * @method on_stop_drag
1519
+ * @param {Event} The original browser event
1520
+ * @param {Object} A prepared ui object.
1521
+ */
1522
+ fn.on_stop_drag = function(event, ui) {
1523
+ this.$helper.add(this.$player).add(this.$wrapper)
1524
+ .removeClass('dragging');
1525
+
1526
+ ui.position.left = ui.position.left + this.baseX;
1527
+ ui.position.top = ui.position.top + this.baseY;
1528
+ this.colliders_data = this.collision_api.get_closest_colliders(ui.position);
1529
+
1530
+ this.on_overlapped_column_change(
1531
+ this.on_start_overlapping_column,
1532
+ this.on_stop_overlapping_column
1533
+ );
1534
+
1535
+ this.on_overlapped_row_change(
1536
+ this.on_start_overlapping_row,
1537
+ this.on_stop_overlapping_row
1538
+ );
1539
+
1540
+ this.$player.addClass('player-revert').removeClass('player')
1541
+ .attr({
1542
+ 'data-col': this.placeholder_grid_data.col,
1543
+ 'data-row': this.placeholder_grid_data.row
1544
+ }).css({
1545
+ 'left': '',
1546
+ 'top': ''
1547
+ });
1548
+
1549
+ this.$changed = this.$changed.add(this.$player);
1550
+
1551
+ this.cells_occupied_by_player = this.get_cells_occupied(
1552
+ this.placeholder_grid_data);
1553
+ this.set_cells_player_occupies(
1554
+ this.placeholder_grid_data.col, this.placeholder_grid_data.row);
1555
+
1556
+ this.$player.coords().grid.row = this.placeholder_grid_data.row;
1557
+ this.$player.coords().grid.col = this.placeholder_grid_data.col;
1558
+
1559
+ if (this.options.draggable.stop) {
1560
+ this.options.draggable.stop.call(this, event, ui);
1561
+ }
1562
+
1563
+ this.$preview_holder.remove();
1564
+
1565
+ this.$player = null;
1566
+ this.$helper = null;
1567
+ this.placeholder_grid_data = {};
1568
+ this.player_grid_data = {};
1569
+ this.cells_occupied_by_placeholder = {};
1570
+ this.cells_occupied_by_player = {};
1571
+ this.w_queue = {};
1572
+
1573
+ this.set_dom_grid_height();
1574
+ };
1575
+
1576
+
1577
+ /**
1578
+ * Executes the callbacks passed as arguments when a column begins to be
1579
+ * overlapped or stops being overlapped.
1580
+ *
1581
+ * @param {Function} start_callback Function executed when a new column
1582
+ * begins to be overlapped. The column is passed as first argument.
1583
+ * @param {Function} stop_callback Function executed when a column stops
1584
+ * being overlapped. The column is passed as first argument.
1585
+ * @method on_overlapped_column_change
1586
+ * @return {Class} Returns the instance of the Gridster Class.
1587
+ */
1588
+ fn.on_overlapped_column_change = function(start_callback, stop_callback) {
1589
+ if (!this.colliders_data.length) {
1590
+ return;
1591
+ }
1592
+ var cols = this.get_targeted_columns(
1593
+ this.colliders_data[0].el.data.col);
1594
+
1595
+ var last_n_cols = this.last_cols.length;
1596
+ var n_cols = cols.length;
1597
+ var i;
1598
+
1599
+ for (i = 0; i < n_cols; i++) {
1600
+ if ($.inArray(cols[i], this.last_cols) === -1) {
1601
+ (start_callback || $.noop).call(this, cols[i]);
1602
+ }
1603
+ }
1604
+
1605
+ for (i = 0; i< last_n_cols; i++) {
1606
+ if ($.inArray(this.last_cols[i], cols) === -1) {
1607
+ (stop_callback || $.noop).call(this, this.last_cols[i]);
1608
+ }
1609
+ }
1610
+
1611
+ this.last_cols = cols;
1612
+
1613
+ return this;
1614
+ };
1615
+
1616
+
1617
+ /**
1618
+ * Executes the callbacks passed as arguments when a row starts to be
1619
+ * overlapped or stops being overlapped.
1620
+ *
1621
+ * @param {Function} start_callback Function executed when a new row begins
1622
+ * to be overlapped. The row is passed as first argument.
1623
+ * @param {Function} stop_callback Function executed when a row stops being
1624
+ * overlapped. The row is passed as first argument.
1625
+ * @method on_overlapped_row_change
1626
+ * @return {Class} Returns the instance of the Gridster Class.
1627
+ */
1628
+ fn.on_overlapped_row_change = function(start_callback, end_callback) {
1629
+ if (!this.colliders_data.length) {
1630
+ return;
1631
+ }
1632
+ var rows = this.get_targeted_rows(this.colliders_data[0].el.data.row);
1633
+ var last_n_rows = this.last_rows.length;
1634
+ var n_rows = rows.length;
1635
+ var i;
1636
+
1637
+ for (i = 0; i < n_rows; i++) {
1638
+ if ($.inArray(rows[i], this.last_rows) === -1) {
1639
+ (start_callback || $.noop).call(this, rows[i]);
1640
+ }
1641
+ }
1642
+
1643
+ for (i = 0; i < last_n_rows; i++) {
1644
+ if ($.inArray(this.last_rows[i], rows) === -1) {
1645
+ (end_callback || $.noop).call(this, this.last_rows[i]);
1646
+ }
1647
+ }
1648
+
1649
+ this.last_rows = rows;
1650
+ };
1651
+
1652
+
1653
+ /**
1654
+ * Sets the current position of the player
1655
+ *
1656
+ * @param {Function} start_callback Function executed when a new row begins
1657
+ * to be overlapped. The row is passed as first argument.
1658
+ * @param {Function} stop_callback Function executed when a row stops being
1659
+ * overlapped. The row is passed as first argument.
1660
+ * @method set_player
1661
+ * @return {Class} Returns the instance of the Gridster Class.
1662
+ */
1663
+ fn.set_player = function(col, row, no_player) {
1664
+ var self = this;
1665
+ var swap = false;
1666
+ if (!no_player) {
1667
+ this.empty_cells_player_occupies();
1668
+ }
1669
+ var cell = !no_player ? self.colliders_data[0].el.data : {col: col};
1670
+ var to_col = cell.col;
1671
+ var to_row = cell.row || row;
1672
+
1673
+ this.player_grid_data = {
1674
+ col: to_col,
1675
+ row: to_row,
1676
+ size_y : this.player_grid_data.size_y,
1677
+ size_x : this.player_grid_data.size_x
1678
+ };
1679
+
1680
+ this.cells_occupied_by_player = this.get_cells_occupied(
1681
+ this.player_grid_data);
1682
+
1683
+ //Added placeholder for more advanced movement.
1684
+ this.cells_occupied_by_placeholder = this.get_cells_occupied(
1685
+ this.placeholder_grid_data);
1686
+
1687
+ var $overlapped_widgets = this.get_widgets_overlapped(
1688
+ this.player_grid_data);
1689
+
1690
+ var player_size_y = this.player_grid_data.size_y;
1691
+ var player_size_x = this.player_grid_data.size_x;
1692
+ var placeholder_cells = this.cells_occupied_by_placeholder;
1693
+ var $gr = this;
1694
+
1695
+
1696
+ //Queue Swaps
1697
+ $overlapped_widgets.each($.proxy(function(i, w){
1698
+ var $w = $(w);
1699
+ var wgd = $w.coords().grid;
1700
+
1701
+ // Ensure all values are in integer format
1702
+ wgd.col = parseInt(wgd.col);
1703
+ wgd.row = parseInt(wgd.row);
1704
+ wgd.size_x = parseInt(wgd.size_x);
1705
+ wgd.size_y = parseInt(wgd.size_y);
1706
+ player_size_x = parseInt(player_size_x);
1707
+ player_size_y = parseInt(player_size_y);
1708
+
1709
+ var outside_col = placeholder_cells.cols[0]+player_size_x-1;
1710
+ var outside_row = placeholder_cells.rows[0]+player_size_y-1;
1711
+ if ($w.hasClass($gr.options.static_class)){
1712
+ //next iteration
1713
+ return true;
1714
+ }
1715
+ if(wgd.size_x <= player_size_x && wgd.size_y <= player_size_y){
1716
+ if(!$gr.is_swap_occupied(placeholder_cells.cols[0], wgd.row, wgd.size_x, wgd.size_y) && !$gr.is_player_in(placeholder_cells.cols[0], wgd.row) && !$gr.is_in_queue(placeholder_cells.cols[0], wgd.row, $w)){
1717
+ swap = $gr.queue_widget(placeholder_cells.cols[0], wgd.row, $w);
1718
+ }
1719
+ else if(!$gr.is_swap_occupied(outside_col, wgd.row, wgd.size_x, wgd.size_y) && !$gr.is_player_in(outside_col, wgd.row) && !$gr.is_in_queue(outside_col, wgd.row, $w)){
1720
+ swap = $gr.queue_widget(outside_col, wgd.row, $w);
1721
+ }
1722
+ else if(!$gr.is_swap_occupied(wgd.col, placeholder_cells.rows[0], wgd.size_x, wgd.size_y) && !$gr.is_player_in(wgd.col, placeholder_cells.rows[0]) && !$gr.is_in_queue(wgd.col, placeholder_cells.rows[0], $w)){
1723
+ swap = $gr.queue_widget(wgd.col, placeholder_cells.rows[0], $w);
1724
+ }
1725
+ else if(!$gr.is_swap_occupied(wgd.col, outside_row, wgd.size_x, wgd.size_y) && !$gr.is_player_in(wgd.col, outside_row) && !$gr.is_in_queue(wgd.col, outside_row, $w)){
1726
+ swap = $gr.queue_widget(wgd.col, outside_row, $w);
1727
+ }
1728
+ else if(!$gr.is_swap_occupied(placeholder_cells.cols[0],placeholder_cells.rows[0], wgd.size_x, wgd.size_y) && !$gr.is_player_in(placeholder_cells.cols[0],placeholder_cells.rows[0]) && !$gr.is_in_queue(placeholder_cells.cols[0],placeholder_cells.rows[0], $w)){
1729
+ swap = $gr.queue_widget(placeholder_cells.cols[0], placeholder_cells.rows[0], $w);
1730
+ } else {
1731
+ //in one last attempt we check for any other empty spaces
1732
+ for (var c = 0; c < player_size_x; c++){
1733
+ for (var r = 0; r < player_size_y; r++){
1734
+ var colc = placeholder_cells.cols[0]+c;
1735
+ var rowc = placeholder_cells.rows[0]+r;
1736
+ if (!$gr.is_swap_occupied(colc,rowc, wgd.size_x, wgd.size_y) && !$gr.is_player_in(colc,rowc) && !$gr.is_in_queue(colc, rowc, $w)){
1737
+ swap = $gr.queue_widget(colc, rowc, $w);
1738
+ c = player_size_x;
1739
+ break;
1740
+ }
1741
+ }
1742
+ }
1743
+
1744
+ }
1745
+ } else if ($gr.options.shift_larger_widgets_down && !swap) {
1746
+ $overlapped_widgets.each($.proxy(function(i, w){
1747
+ var $w = $(w);
1748
+ var wgd = $w.coords().grid;
1749
+
1750
+ if($gr.can_go_down($w)){
1751
+ $gr.move_widget_down($w, $gr.player_grid_data.size_y);
1752
+ $gr.set_placeholder(to_col, to_row);
1753
+ }
1754
+ }));
1755
+ }
1756
+
1757
+ $gr.clean_up_changed();
1758
+ }));
1759
+
1760
+
1761
+ /* To show queued items in console
1762
+ for(var key in this.w_queue){
1763
+ console.log("key " +key);
1764
+ console.log(this.w_queue[key]);
1765
+ }
1766
+ */
1767
+
1768
+ //Move queued widgets
1769
+ if(swap && this.can_placeholder_be_set(to_col, to_row, player_size_x, player_size_y)){
1770
+ for(var key in this.w_queue){
1771
+ var col = parseInt(key.split("_")[0]);
1772
+ var row = parseInt(key.split("_")[1]);
1773
+ if (this.w_queue[key] != "full"){
1774
+ this.new_move_widget_to(this.w_queue[key], col, row);
1775
+ }
1776
+ }
1777
+ this.set_placeholder(to_col, to_row);
1778
+ }
1779
+
1780
+ /* if there is not widgets overlapping in the new player position,
1781
+ * update the new placeholder position. */
1782
+ if (!$overlapped_widgets.length) {
1783
+ var pp = this.can_go_player_up(this.player_grid_data);
1784
+ if (pp !== false) {
1785
+ to_row = pp;
1786
+ }
1787
+ if(this.can_placeholder_be_set(to_col, to_row, player_size_x, player_size_y)){
1788
+ this.set_placeholder(to_col, to_row);
1789
+ }
1790
+ }
1791
+
1792
+ this.w_queue = {};
1793
+
1794
+ return {
1795
+ col: to_col,
1796
+ row: to_row
1797
+ };
1798
+ };
1799
+
1800
+
1801
+ fn.is_swap_occupied = function(col, row, w_size_x, w_size_y) {
1802
+ var occupied = false;
1803
+ for (var c = 0; c < w_size_x; c++){
1804
+ for (var r = 0; r < w_size_y; r++){
1805
+ var colc = col + c;
1806
+ var rowc = row + r;
1807
+ var key = colc+"_"+rowc;
1808
+ if(this.is_occupied(colc,rowc)){
1809
+ occupied = true;
1810
+ } else if(key in this.w_queue){
1811
+ if(this.w_queue[key] == "full"){
1812
+ occupied = true;
1813
+ continue;
1814
+ }
1815
+ $tw = this.w_queue[key];
1816
+ tgd = $tw.coords().grid;
1817
+ //remove queued items if no longer under player.
1818
+ if(!this.is_widget_under_player(tgd.col,tgd.row)){
1819
+ delete this.w_queue[key];
1820
+ }
1821
+ }
1822
+ if(rowc > parseInt(this.options.max_rows)){
1823
+ occupied = true;
1824
+ }
1825
+ if(colc > parseInt(this.options.max_cols)){
1826
+ occupied = true;
1827
+ }
1828
+ if (this.is_player_in(colc,rowc)){
1829
+ occupied = true;
1830
+ }
1831
+ }
1832
+ }
1833
+
1834
+ return occupied;
1835
+ }
1836
+
1837
+ fn.can_placeholder_be_set = function(col, row, player_size_x, player_size_y){
1838
+ var can_set = true;
1839
+ for (var c = 0; c < player_size_x; c++){
1840
+ for (var r = 0; r < player_size_y; r++){
1841
+ var colc = col + c;
1842
+ var rowc = row + r;
1843
+ var key = colc+"_"+rowc;
1844
+ var $tw = this.is_widget(colc, rowc);
1845
+ //if this space is occupied and not queued for move.
1846
+ if(rowc > parseInt(this.options.max_rows)){
1847
+ can_set = false;
1848
+ }
1849
+ if(colc > parseInt(this.options.max_cols)){
1850
+ can_set = false;
1851
+ }
1852
+ if(this.is_occupied(colc,rowc) && !this.is_widget_queued_and_can_move($tw)){
1853
+ can_set = false;
1854
+ }
1855
+ }
1856
+ }
1857
+ return can_set;
1858
+ }
1859
+
1860
+ fn.queue_widget = function(col, row, $widget){
1861
+ var $w = $widget
1862
+ var wgd = $w.coords().grid;
1863
+ var primary_key = col+"_"+row;
1864
+ if (primary_key in this.w_queue){
1865
+ return false;
1866
+ }
1867
+
1868
+ this.w_queue[primary_key] = $w;
1869
+
1870
+ for (var c = 0; c < wgd.size_x; c++){
1871
+ for (var r = 0; r < wgd.size_y; r++){
1872
+ var colc = col + c;
1873
+ var rowc = row + r;
1874
+ var key = colc+"_"+rowc;
1875
+ if (key == primary_key){
1876
+ continue;
1877
+ }
1878
+ this.w_queue[key] = "full";
1879
+ }
1880
+ }
1881
+
1882
+ return true;
1883
+ }
1884
+
1885
+ fn.is_widget_queued_and_can_move = function($widget){
1886
+ var queued = false;
1887
+ if ($widget === false){
1888
+ return false;
1889
+ }
1890
+
1891
+ for(var key in this.w_queue){
1892
+ if(this.w_queue[key] == "full"){
1893
+ continue;
1894
+ }
1895
+ if(this.w_queue[key].attr("data-col") == $widget.attr("data-col") && this.w_queue[key].attr("data-row") == $widget.attr("data-row")){
1896
+ queued = true;
1897
+ //test whole space
1898
+ var $w = this.w_queue[key];
1899
+ var dcol = parseInt(key.split("_")[0]);
1900
+ var drow = parseInt(key.split("_")[1]);
1901
+ var wgd = $w.coords().grid;
1902
+
1903
+ for (var c = 0; c < wgd.size_x; c++){
1904
+ for (var r = 0; r < wgd.size_y; r++){
1905
+ var colc = dcol + c;
1906
+ var rowc = drow + r;
1907
+ if (this.is_player_in(colc,rowc)){
1908
+ queued = false;
1909
+ }
1910
+
1911
+ }
1912
+ }
1913
+
1914
+ }
1915
+ }
1916
+
1917
+ return queued
1918
+ }
1919
+
1920
+ fn.is_in_queue = function(col,row, $widget){
1921
+ var queued = false;
1922
+ var key = col+"_"+row;
1923
+
1924
+ if ((key in this.w_queue)){
1925
+ if (this.w_queue[key] == "full"){
1926
+ queued = true;
1927
+ } else {
1928
+ $tw = this.w_queue[key];
1929
+ tgd = $tw.coords().grid;
1930
+ if(!this.is_widget_under_player(tgd.col,tgd.row)){
1931
+ delete this.w_queue[key]
1932
+ queued = false;
1933
+ } else if(this.w_queue[key].attr("data-col") == $widget.attr("data-col") && this.w_queue[key].attr("data-row") == $widget.attr("data-row")) {
1934
+ delete this.w_queue[key]
1935
+ queued = false;
1936
+ } else {
1937
+ queued = true;
1938
+ }
1939
+ }
1940
+ }
1941
+
1942
+ return queued;
1943
+ }
1944
+
1945
+
1946
+ /**
1947
+ * See which of the widgets in the $widgets param collection can go to
1948
+ * a upper row and which not.
1949
+ *
1950
+ * @method widgets_contraints
1951
+ * @param {HTMLElements} $widgets A jQuery wrapped collection of
1952
+ * HTMLElements.
1953
+ * @return {Array} Returns a literal Object with two keys: `can_go_up` &
1954
+ * `can_not_go_up`. Each contains a set of HTMLElements.
1955
+ */
1956
+ fn.widgets_constraints = function($widgets) {
1957
+ var $widgets_can_go_up = $([]);
1958
+ var $widgets_can_not_go_up;
1959
+ var wgd_can_go_up = [];
1960
+ var wgd_can_not_go_up = [];
1961
+
1962
+ $widgets.each($.proxy(function(i, w) {
1963
+ var $w = $(w);
1964
+ var wgd = $w.coords().grid;
1965
+ if (this.can_go_widget_up(wgd)) {
1966
+ $widgets_can_go_up = $widgets_can_go_up.add($w);
1967
+ wgd_can_go_up.push(wgd);
1968
+ }else{
1969
+ wgd_can_not_go_up.push(wgd);
1970
+ }
1971
+ }, this));
1972
+
1973
+ $widgets_can_not_go_up = $widgets.not($widgets_can_go_up);
1974
+
1975
+ return {
1976
+ can_go_up: this.sort_by_row_asc(wgd_can_go_up),
1977
+ can_not_go_up: this.sort_by_row_desc(wgd_can_not_go_up)
1978
+ };
1979
+ };
1980
+
1981
+
1982
+ /**
1983
+ * Sorts an Array of grid coords objects (representing the grid coords of
1984
+ * each widget) in ascending way.
1985
+ *
1986
+ * @method sort_by_row_asc
1987
+ * @param {Array} widgets Array of grid coords objects
1988
+ * @return {Array} Returns the array sorted.
1989
+ */
1990
+ fn.sort_by_row_asc = function(widgets) {
1991
+ widgets = widgets.sort(function(a, b) {
1992
+ if (!a.row) {
1993
+ a = $(a).coords().grid;
1994
+ b = $(b).coords().grid;
1995
+ }
1996
+
1997
+ if (a.row > b.row) {
1998
+ return 1;
1999
+ }
2000
+ return -1;
2001
+ });
2002
+
2003
+ return widgets;
2004
+ };
2005
+
2006
+
2007
+ /**
2008
+ * Sorts an Array of grid coords objects (representing the grid coords of
2009
+ * each widget) placing first the empty cells upper left.
2010
+ *
2011
+ * @method sort_by_row_and_col_asc
2012
+ * @param {Array} widgets Array of grid coords objects
2013
+ * @return {Array} Returns the array sorted.
2014
+ */
2015
+ fn.sort_by_row_and_col_asc = function(widgets) {
2016
+ widgets = widgets.sort(function(a, b) {
2017
+ if (a.row > b.row || a.row === b.row && a.col > b.col) {
2018
+ return 1;
2019
+ }
2020
+ return -1;
2021
+ });
2022
+
2023
+ return widgets;
2024
+ };
2025
+
2026
+
2027
+ /**
2028
+ * Sorts an Array of grid coords objects by column (representing the grid
2029
+ * coords of each widget) in ascending way.
2030
+ *
2031
+ * @method sort_by_col_asc
2032
+ * @param {Array} widgets Array of grid coords objects
2033
+ * @return {Array} Returns the array sorted.
2034
+ */
2035
+ fn.sort_by_col_asc = function(widgets) {
2036
+ widgets = widgets.sort(function(a, b) {
2037
+ if (a.col > b.col) {
2038
+ return 1;
2039
+ }
2040
+ return -1;
2041
+ });
2042
+
2043
+ return widgets;
2044
+ };
2045
+
2046
+
2047
+ /**
2048
+ * Sorts an Array of grid coords objects (representing the grid coords of
2049
+ * each widget) in descending way.
2050
+ *
2051
+ * @method sort_by_row_desc
2052
+ * @param {Array} widgets Array of grid coords objects
2053
+ * @return {Array} Returns the array sorted.
2054
+ */
2055
+ fn.sort_by_row_desc = function(widgets) {
2056
+ widgets = widgets.sort(function(a, b) {
2057
+ if (a.row + a.size_y < b.row + b.size_y) {
2058
+ return 1;
2059
+ }
2060
+ return -1;
2061
+ });
2062
+ return widgets;
2063
+ };
2064
+
2065
+
2066
+ /**
2067
+ * Sorts an Array of grid coords objects (representing the grid coords of
2068
+ * each widget) in descending way.
2069
+
2070
+ * Depreciated.
2071
+ *
2072
+ * @method manage_movements
2073
+ * @param {HTMLElements} $widgets A jQuery collection of HTMLElements
2074
+ * representing the widgets you want to move.
2075
+ * @param {Number} to_col The column to which we want to move the widgets.
2076
+ * @param {Number} to_row The row to which we want to move the widgets.
2077
+ * @return {Class} Returns the instance of the Gridster Class.
2078
+ */
2079
+ fn.manage_movements = function($widgets, to_col, to_row) {
2080
+ $.each($widgets, $.proxy(function(i, w) {
2081
+ var wgd = w;
2082
+ var $w = wgd.el;
2083
+
2084
+ var can_go_widget_up = this.can_go_widget_up(wgd);
2085
+
2086
+ if (can_go_widget_up) {
2087
+ //target CAN go up
2088
+ //so move widget up
2089
+ this.move_widget_to($w, can_go_widget_up);
2090
+ this.set_placeholder(to_col, can_go_widget_up + wgd.size_y);
2091
+
2092
+ } else {
2093
+ //target can't go up
2094
+ var can_go_player_up = this.can_go_player_up(
2095
+ this.player_grid_data);
2096
+
2097
+ if (!can_go_player_up) {
2098
+ // target can't go up
2099
+ // player cant't go up
2100
+ // so we need to move widget down to a position that dont
2101
+ // overlaps player
2102
+ var y = (to_row + this.player_grid_data.size_y) - wgd.row;
2103
+ if (this.can_go_down($w)){
2104
+ console.log("In Move Down!")
2105
+ this.move_widget_down($w, y);
2106
+ this.set_placeholder(to_col, to_row);
2107
+ }
2108
+ }
2109
+ }
2110
+ }, this));
2111
+
2112
+ return this;
2113
+ };
2114
+
2115
+ /**
2116
+ * Determines if there is a widget in the row and col given. Or if the
2117
+ * HTMLElement passed as first argument is the player.
2118
+ *
2119
+ * @method is_player
2120
+ * @param {Number|HTMLElement} col_or_el A jQuery wrapped collection of
2121
+ * HTMLElements.
2122
+ * @param {Number} [row] The column to which we want to move the widgets.
2123
+ * @return {Boolean} Returns true or false.
2124
+ */
2125
+ fn.is_player = function(col_or_el, row) {
2126
+ if (row && !this.gridmap[col_or_el]) { return false; }
2127
+ var $w = row ? this.gridmap[col_or_el][row] : col_or_el;
2128
+ return $w && ($w.is(this.$player) || $w.is(this.$helper));
2129
+ };
2130
+
2131
+
2132
+ /**
2133
+ * Determines if the widget that is being dragged is currently over the row
2134
+ * and col given.
2135
+ *
2136
+ * @method is_player_in
2137
+ * @param {Number} col The column to check.
2138
+ * @param {Number} row The row to check.
2139
+ * @return {Boolean} Returns true or false.
2140
+ */
2141
+ fn.is_player_in = function(col, row) {
2142
+ var c = this.cells_occupied_by_player || {};
2143
+ return $.inArray(col, c.cols) >= 0 && $.inArray(row, c.rows) >= 0;
2144
+ };
2145
+
2146
+
2147
+ /**
2148
+ * Determines if the placeholder is currently over the row and col given.
2149
+ *
2150
+ * @method is_placeholder_in
2151
+ * @param {Number} col The column to check.
2152
+ * @param {Number} row The row to check.
2153
+ * @return {Boolean} Returns true or false.
2154
+ */
2155
+ fn.is_placeholder_in = function(col, row) {
2156
+ var c = this.cells_occupied_by_placeholder || {};
2157
+ return this.is_placeholder_in_col(col) && $.inArray(row, c.rows) >= 0;
2158
+ };
2159
+
2160
+
2161
+ /**
2162
+ * Determines if the placeholder is currently over the column given.
2163
+ *
2164
+ * @method is_placeholder_in_col
2165
+ * @param {Number} col The column to check.
2166
+ * @return {Boolean} Returns true or false.
2167
+ */
2168
+ fn.is_placeholder_in_col = function(col) {
2169
+ var c = this.cells_occupied_by_placeholder || [];
2170
+ return $.inArray(col, c.cols) >= 0;
2171
+ };
2172
+
2173
+
2174
+ /**
2175
+ * Determines if the cell represented by col and row params is empty.
2176
+ *
2177
+ * @method is_empty
2178
+ * @param {Number} col The column to check.
2179
+ * @param {Number} row The row to check.
2180
+ * @return {Boolean} Returns true or false.
2181
+ */
2182
+ fn.is_empty = function(col, row) {
2183
+ if (typeof this.gridmap[col] !== 'undefined' &&
2184
+ typeof this.gridmap[col][row] !== 'undefined' &&
2185
+ this.gridmap[col][row] === false
2186
+ ) {
2187
+ return true;
2188
+ }
2189
+ return false;
2190
+ };
2191
+
2192
+
2193
+ /**
2194
+ * Determines if the cell represented by col and row params is occupied.
2195
+ *
2196
+ * @method is_occupied
2197
+ * @param {Number} col The column to check.
2198
+ * @param {Number} row The row to check.
2199
+ * @return {Boolean} Returns true or false.
2200
+ */
2201
+ fn.is_occupied = function(col, row) {
2202
+ if (!this.gridmap[col]) {
2203
+ return false;
2204
+ }
2205
+
2206
+ if (this.gridmap[col][row]) {
2207
+ return true;
2208
+ }
2209
+ return false;
2210
+ };
2211
+
2212
+
2213
+ /**
2214
+ * Determines if there is a widget in the cell represented by col/row params.
2215
+ *
2216
+ * @method is_widget
2217
+ * @param {Number} col The column to check.
2218
+ * @param {Number} row The row to check.
2219
+ * @return {Boolean|HTMLElement} Returns false if there is no widget,
2220
+ * else returns the jQuery HTMLElement
2221
+ */
2222
+ fn.is_widget = function(col, row) {
2223
+ var cell = this.gridmap[col];
2224
+ if (!cell) {
2225
+ return false;
2226
+ }
2227
+
2228
+ cell = cell[row];
2229
+
2230
+ if (cell) {
2231
+ return cell;
2232
+ }
2233
+
2234
+ return false;
2235
+ };
2236
+
2237
+ /**
2238
+ * Determines if widget is supposed to be static.
2239
+ * @method is_static
2240
+ * @param {Number} col The column to check.
2241
+ * @param {Number} row The row to check.
2242
+ * @return {Boolean} Returns true if widget exists and has static class,
2243
+ * else returns false
2244
+ */
2245
+
2246
+ fn.is_static = function(col, row) {
2247
+ var cell = this.gridmap[col];
2248
+ if (!cell) {
2249
+ return false;
2250
+ }
2251
+
2252
+ cell = cell[row];
2253
+
2254
+ if (cell) {
2255
+ if(cell.hasClass(this.options.static_class)){
2256
+ return true;
2257
+ }
2258
+ }
2259
+
2260
+ return false;
2261
+ };
2262
+
2263
+
2264
+ /**
2265
+ * Determines if there is a widget in the cell represented by col/row
2266
+ * params and if this is under the widget that is being dragged.
2267
+ *
2268
+ * @method is_widget_under_player
2269
+ * @param {Number} col The column to check.
2270
+ * @param {Number} row The row to check.
2271
+ * @return {Boolean} Returns true or false.
2272
+ */
2273
+ fn.is_widget_under_player = function(col, row) {
2274
+ if (this.is_widget(col, row)) {
2275
+ return this.is_player_in(col, row);
2276
+ }
2277
+ return false;
2278
+ };
2279
+
2280
+
2281
+ /**
2282
+ * Get widgets overlapping with the player or with the object passed
2283
+ * representing the grid cells.
2284
+ *
2285
+ * @method get_widgets_under_player
2286
+ * @return {HTMLElement} Returns a jQuery collection of HTMLElements
2287
+ */
2288
+ fn.get_widgets_under_player = function(cells) {
2289
+ cells || (cells = this.cells_occupied_by_player || {cols: [], rows: []});
2290
+ var $widgets = $([]);
2291
+
2292
+ $.each(cells.cols, $.proxy(function(i, col) {
2293
+ $.each(cells.rows, $.proxy(function(i, row) {
2294
+ if(this.is_widget(col, row)) {
2295
+ $widgets = $widgets.add(this.gridmap[col][row]);
2296
+ }
2297
+ }, this));
2298
+ }, this));
2299
+
2300
+ return $widgets;
2301
+ };
2302
+
2303
+
2304
+ /**
2305
+ * Put placeholder at the row and column specified.
2306
+ *
2307
+ * @method set_placeholder
2308
+ * @param {Number} col The column to which we want to move the
2309
+ * placeholder.
2310
+ * @param {Number} row The row to which we want to move the
2311
+ * placeholder.
2312
+ * @return {Class} Returns the instance of the Gridster Class.
2313
+ */
2314
+ fn.set_placeholder = function(col, row) {
2315
+ var phgd = $.extend({}, this.placeholder_grid_data);
2316
+ var $nexts = this.widgets_below({
2317
+ col: phgd.col,
2318
+ row: phgd.row,
2319
+ size_y: phgd.size_y,
2320
+ size_x: phgd.size_x
2321
+ });
2322
+
2323
+ // Prevents widgets go out of the grid
2324
+ var right_col = (col + parseInt(phgd.size_x) - 1);
2325
+ if (right_col > this.cols) {
2326
+ col = col - (right_col - col);
2327
+ }
2328
+
2329
+ var moved_down = this.placeholder_grid_data.row < row;
2330
+ var changed_column = this.placeholder_grid_data.col !== col;
2331
+
2332
+ this.placeholder_grid_data.col = col;
2333
+ this.placeholder_grid_data.row = row;
2334
+
2335
+ this.cells_occupied_by_placeholder = this.get_cells_occupied(
2336
+ this.placeholder_grid_data);
2337
+
2338
+ this.$preview_holder.attr({
2339
+ 'data-row' : row,
2340
+ 'data-col' : col
2341
+ });
2342
+
2343
+ if (moved_down || changed_column) {
2344
+ $nexts.each($.proxy(function(i, widget) {
2345
+ //Make sure widget is at it's topmost position
2346
+ $w = $(widget);
2347
+ wgd = $w.coords().grid;
2348
+
2349
+ var can_go_widget_up = this.can_go_widget_up(wgd);
2350
+
2351
+ if (can_go_widget_up) {
2352
+ this.move_widget_to($w, can_go_widget_up);
2353
+ }
2354
+
2355
+ }, this));
2356
+ }
2357
+
2358
+
2359
+ var $widgets_under_ph = this.get_widgets_under_player(this.cells_occupied_by_placeholder);
2360
+ if ($widgets_under_ph.length) {
2361
+ $widgets_under_ph.each($.proxy(function(i, widget) {
2362
+ var $w = $(widget);
2363
+ this.move_widget_down(
2364
+ $w, row + phgd.size_y - $w.data('coords').grid.row);
2365
+ }, this));
2366
+ }
2367
+
2368
+ };
2369
+
2370
+
2371
+ /**
2372
+ * Determines whether the player can move to a position above.
2373
+ *
2374
+ * @method can_go_player_up
2375
+ * @param {Object} widget_grid_data The actual grid coords object of the
2376
+ * player.
2377
+ * @return {Number|Boolean} If the player can be moved to an upper row
2378
+ * returns the row number, else returns false.
2379
+ */
2380
+ fn.can_go_player_up = function(widget_grid_data) {
2381
+ var p_bottom_row = widget_grid_data.row + widget_grid_data.size_y - 1;
2382
+ var result = true;
2383
+ var upper_rows = [];
2384
+ var min_row = 10000;
2385
+ var $widgets_under_player = this.get_widgets_under_player();
2386
+
2387
+ /* generate an array with columns as index and array with upper rows
2388
+ * empty as value */
2389
+ this.for_each_column_occupied(widget_grid_data, function(tcol) {
2390
+ var grid_col = this.gridmap[tcol];
2391
+ var r = p_bottom_row + 1;
2392
+ upper_rows[tcol] = [];
2393
+
2394
+ while (--r > 0) {
2395
+ if (this.is_empty(tcol, r) || this.is_player(tcol, r) ||
2396
+ this.is_widget(tcol, r) &&
2397
+ grid_col[r].is($widgets_under_player)
2398
+ ) {
2399
+ upper_rows[tcol].push(r);
2400
+ min_row = r < min_row ? r : min_row;
2401
+ }else{
2402
+ break;
2403
+ }
2404
+ }
2405
+
2406
+ if (upper_rows[tcol].length === 0) {
2407
+ result = false;
2408
+ return true; //break
2409
+ }
2410
+
2411
+ upper_rows[tcol].sort();
2412
+ });
2413
+
2414
+ if (!result) { return false; }
2415
+
2416
+ return this.get_valid_rows(widget_grid_data, upper_rows, min_row);
2417
+ };
2418
+
2419
+
2420
+ /**
2421
+ * Determines whether a widget can move to a position above.
2422
+ *
2423
+ * @method can_go_widget_up
2424
+ * @param {Object} widget_grid_data The actual grid coords object of the
2425
+ * widget we want to check.
2426
+ * @return {Number|Boolean} If the widget can be moved to an upper row
2427
+ * returns the row number, else returns false.
2428
+ */
2429
+ fn.can_go_widget_up = function(widget_grid_data) {
2430
+ var p_bottom_row = widget_grid_data.row + widget_grid_data.size_y - 1;
2431
+ var result = true;
2432
+ var upper_rows = [];
2433
+ var min_row = 10000;
2434
+
2435
+ /* generate an array with columns as index and array with topmost rows
2436
+ * empty as value */
2437
+ this.for_each_column_occupied(widget_grid_data, function(tcol) {
2438
+ var grid_col = this.gridmap[tcol];
2439
+ upper_rows[tcol] = [];
2440
+
2441
+ var r = p_bottom_row + 1;
2442
+ // iterate over each row
2443
+ while (--r > 0) {
2444
+ if (this.is_widget(tcol, r) && !this.is_player_in(tcol, r)) {
2445
+ if (!grid_col[r].is(widget_grid_data.el)) {
2446
+ break;
2447
+ }
2448
+ }
2449
+
2450
+ if (!this.is_player(tcol, r) &&
2451
+ !this.is_placeholder_in(tcol, r) &&
2452
+ !this.is_player_in(tcol, r)) {
2453
+ upper_rows[tcol].push(r);
2454
+ }
2455
+
2456
+ if (r < min_row) {
2457
+ min_row = r;
2458
+ }
2459
+ }
2460
+
2461
+ if (upper_rows[tcol].length === 0) {
2462
+ result = false;
2463
+ return true; //break
2464
+ }
2465
+
2466
+ upper_rows[tcol].sort();
2467
+ });
2468
+
2469
+ if (!result) { return false; }
2470
+
2471
+ return this.get_valid_rows(widget_grid_data, upper_rows, min_row);
2472
+ };
2473
+
2474
+
2475
+ /**
2476
+ * Search a valid row for the widget represented by `widget_grid_data' in
2477
+ * the `upper_rows` array. Iteration starts from row specified in `min_row`.
2478
+ *
2479
+ * @method get_valid_rows
2480
+ * @param {Object} widget_grid_data The actual grid coords object of the
2481
+ * player.
2482
+ * @param {Array} upper_rows An array with columns as index and arrays
2483
+ * of valid rows as values.
2484
+ * @param {Number} min_row The upper row from which the iteration will start.
2485
+ * @return {Number|Boolean} Returns the upper row valid from the `upper_rows`
2486
+ * for the widget in question.
2487
+ */
2488
+ fn.get_valid_rows = function(widget_grid_data, upper_rows, min_row) {
2489
+ var p_top_row = widget_grid_data.row;
2490
+ var p_bottom_row = widget_grid_data.row + widget_grid_data.size_y - 1;
2491
+ var size_y = widget_grid_data.size_y;
2492
+ var r = min_row - 1;
2493
+ var valid_rows = [];
2494
+
2495
+ while (++r <= p_bottom_row ) {
2496
+ var common = true;
2497
+ $.each(upper_rows, function(col, rows) {
2498
+ if ($.isArray(rows) && $.inArray(r, rows) === -1) {
2499
+ common = false;
2500
+ }
2501
+ });
2502
+
2503
+ if (common === true) {
2504
+ valid_rows.push(r);
2505
+ if (valid_rows.length === size_y) {
2506
+ break;
2507
+ }
2508
+ }
2509
+ }
2510
+
2511
+ var new_row = false;
2512
+ if (size_y === 1) {
2513
+ if (valid_rows[0] !== p_top_row) {
2514
+ new_row = valid_rows[0] || false;
2515
+ }
2516
+ }else{
2517
+ if (valid_rows[0] !== p_top_row) {
2518
+ new_row = this.get_consecutive_numbers_index(
2519
+ valid_rows, size_y);
2520
+ }
2521
+ }
2522
+
2523
+ return new_row;
2524
+ };
2525
+
2526
+
2527
+ fn.get_consecutive_numbers_index = function(arr, size_y) {
2528
+ var max = arr.length;
2529
+ var result = [];
2530
+ var first = true;
2531
+ var prev = -1; // or null?
2532
+
2533
+ for (var i=0; i < max; i++) {
2534
+ if (first || arr[i] === prev + 1) {
2535
+ result.push(i);
2536
+ if (result.length === size_y) {
2537
+ break;
2538
+ }
2539
+ first = false;
2540
+ }else{
2541
+ result = [];
2542
+ first = true;
2543
+ }
2544
+
2545
+ prev = arr[i];
2546
+ }
2547
+
2548
+ return result.length >= size_y ? arr[result[0]] : false;
2549
+ };
2550
+
2551
+
2552
+ /**
2553
+ * Get widgets overlapping with the player.
2554
+ *
2555
+ * @method get_widgets_overlapped
2556
+ * @return {HTMLElements} Returns a jQuery collection of HTMLElements.
2557
+ */
2558
+ fn.get_widgets_overlapped = function() {
2559
+ var $w;
2560
+ var $widgets = $([]);
2561
+ var used = [];
2562
+ var rows_from_bottom = this.cells_occupied_by_player.rows.slice(0);
2563
+ rows_from_bottom.reverse();
2564
+
2565
+ $.each(this.cells_occupied_by_player.cols, $.proxy(function(i, col) {
2566
+ $.each(rows_from_bottom, $.proxy(function(i, row) {
2567
+ // if there is a widget in the player position
2568
+ if (!this.gridmap[col]) { return true; } //next iteration
2569
+ var $w = this.gridmap[col][row];
2570
+ if (this.is_occupied(col, row) && !this.is_player($w) &&
2571
+ $.inArray($w, used) === -1
2572
+ ) {
2573
+ $widgets = $widgets.add($w);
2574
+ used.push($w);
2575
+ }
2576
+
2577
+ }, this));
2578
+ }, this));
2579
+
2580
+ return $widgets;
2581
+ };
2582
+
2583
+
2584
+ /**
2585
+ * This callback is executed when the player begins to collide with a column.
2586
+ *
2587
+ * @method on_start_overlapping_column
2588
+ * @param {Number} col The collided column.
2589
+ * @return {HTMLElements} Returns a jQuery collection of HTMLElements.
2590
+ */
2591
+ fn.on_start_overlapping_column = function(col) {
2592
+ this.set_player(col, false);
2593
+ };
2594
+
2595
+
2596
+ /**
2597
+ * A callback executed when the player begins to collide with a row.
2598
+ *
2599
+ * @method on_start_overlapping_row
2600
+ * @param {Number} col The collided row.
2601
+ * @return {HTMLElements} Returns a jQuery collection of HTMLElements.
2602
+ */
2603
+ fn.on_start_overlapping_row = function(row) {
2604
+ this.set_player(false, row);
2605
+ };
2606
+
2607
+
2608
+ /**
2609
+ * A callback executed when the the player ends to collide with a column.
2610
+ *
2611
+ * @method on_stop_overlapping_column
2612
+ * @param {Number} col The collided row.
2613
+ * @return {HTMLElements} Returns a jQuery collection of HTMLElements.
2614
+ */
2615
+ fn.on_stop_overlapping_column = function(col) {
2616
+ //this.set_player(col, false);
2617
+ var self = this;
2618
+ if(this.options.shift_larger_widgets_down){
2619
+ this.for_each_widget_below(col, this.cells_occupied_by_player.rows[0],
2620
+ function(tcol, trow) {
2621
+ self.move_widget_up(this, self.player_grid_data.size_y);
2622
+ });
2623
+ }
2624
+ };
2625
+
2626
+
2627
+ /**
2628
+ * This callback is executed when the player ends to collide with a row.
2629
+ *
2630
+ * @method on_stop_overlapping_row
2631
+ * @param {Number} row The collided row.
2632
+ * @return {HTMLElements} Returns a jQuery collection of HTMLElements.
2633
+ */
2634
+ fn.on_stop_overlapping_row = function(row) {
2635
+ //this.set_player(false, row);
2636
+ var self = this;
2637
+ var cols = this.cells_occupied_by_player.cols;
2638
+ if(this.options.shift_larger_widgets_down){
2639
+ for (var c = 0, cl = cols.length; c < cl; c++) {
2640
+ this.for_each_widget_below(cols[c], row, function(tcol, trow) {
2641
+ console.log("from_on_stop_overlapping_row");
2642
+ self.move_widget_up(this, self.player_grid_data.size_y);
2643
+ });
2644
+ }
2645
+ }
2646
+ };
2647
+
2648
+ //Not yet part of api - DM.
2649
+ fn.new_move_widget_to = function($widget, col, row){
2650
+ var self = this;
2651
+ var widget_grid_data = $widget.coords().grid;
2652
+
2653
+ this.remove_from_gridmap(widget_grid_data);
2654
+ widget_grid_data.row = row;
2655
+ widget_grid_data.col = col;
2656
+
2657
+ this.add_to_gridmap(widget_grid_data);
2658
+ $widget.attr('data-row', row);
2659
+ $widget.attr('data-col', col);
2660
+ this.update_widget_position(widget_grid_data, $widget);
2661
+ this.$changed = this.$changed.add($widget);
2662
+
2663
+ return this;
2664
+ }
2665
+
2666
+
2667
+ /**
2668
+ * Move a widget to a specific row. The cell or cells must be empty.
2669
+ * If the widget has widgets below, all of these widgets will be moved also
2670
+ * if they can.
2671
+ *
2672
+ * @method move_widget_to
2673
+ * @param {HTMLElement} $widget The jQuery wrapped HTMLElement of the
2674
+ * widget is going to be moved.
2675
+ * @return {Class} Returns the instance of the Gridster Class.
2676
+ */
2677
+ fn.move_widget_to = function($widget, row) {
2678
+ var self = this;
2679
+ var widget_grid_data = $widget.coords().grid;
2680
+ var diff = row - widget_grid_data.row;
2681
+ var $next_widgets = this.widgets_below($widget);
2682
+
2683
+ var can_move_to_new_cell = this.can_move_to(
2684
+ widget_grid_data, widget_grid_data.col, row, $widget);
2685
+
2686
+ if (can_move_to_new_cell === false) {
2687
+ return false;
2688
+ }
2689
+
2690
+ this.remove_from_gridmap(widget_grid_data);
2691
+ widget_grid_data.row = row;
2692
+ this.add_to_gridmap(widget_grid_data);
2693
+ $widget.attr('data-row', row);
2694
+ this.$changed = this.$changed.add($widget);
2695
+
2696
+
2697
+ $next_widgets.each(function(i, widget) {
2698
+ var $w = $(widget);
2699
+ var wgd = $w.coords().grid;
2700
+ var can_go_up = self.can_go_widget_up(wgd);
2701
+ if (can_go_up && can_go_up !== wgd.row) {
2702
+ self.move_widget_to($w, can_go_up);
2703
+ }
2704
+ });
2705
+
2706
+ return this;
2707
+ };
2708
+
2709
+
2710
+ /**
2711
+ * Move up the specified widget and all below it.
2712
+ *
2713
+ * @method move_widget_up
2714
+ * @param {HTMLElement} $widget The widget you want to move.
2715
+ * @param {Number} [y_units] The number of cells that the widget has to move.
2716
+ * @return {Class} Returns the instance of the Gridster Class.
2717
+ */
2718
+ fn.move_widget_up = function($widget, y_units) {
2719
+ var el_grid_data = $widget.coords().grid;
2720
+ var actual_row = el_grid_data.row;
2721
+ var moved = [];
2722
+ var can_go_up = true;
2723
+ y_units || (y_units = 1);
2724
+
2725
+ if (!this.can_go_up($widget)) { return false; } //break;
2726
+
2727
+ this.for_each_column_occupied(el_grid_data, function(col) {
2728
+ // can_go_up
2729
+ if ($.inArray($widget, moved) === -1) {
2730
+ var widget_grid_data = $widget.coords().grid;
2731
+ var next_row = actual_row - y_units;
2732
+ next_row = this.can_go_up_to_row(
2733
+ widget_grid_data, col, next_row);
2734
+
2735
+ if (!next_row) {
2736
+ return true;
2737
+ }
2738
+
2739
+ var $next_widgets = this.widgets_below($widget);
2740
+
2741
+ this.remove_from_gridmap(widget_grid_data);
2742
+ widget_grid_data.row = next_row;
2743
+ this.add_to_gridmap(widget_grid_data);
2744
+ $widget.attr('data-row', widget_grid_data.row);
2745
+ this.$changed = this.$changed.add($widget);
2746
+
2747
+ moved.push($widget);
2748
+
2749
+ /* $next_widgets.each($.proxy(function(i, widget) {
2750
+ console.log("from_within_move_widget_up");
2751
+ this.move_widget_up($(widget), y_units);
2752
+ }, this)); */
2753
+ }
2754
+ });
2755
+
2756
+ };
2757
+
2758
+
2759
+ /**
2760
+ * Move down the specified widget and all below it.
2761
+ *
2762
+ * @method move_widget_down
2763
+ * @param {HTMLElement} $widget The jQuery object representing the widget
2764
+ * you want to move.
2765
+ * @param {Number} The number of cells that the widget has to move.
2766
+ * @return {Class} Returns the instance of the Gridster Class.
2767
+ */
2768
+ fn.move_widget_down = function($widget, y_units) {
2769
+ var el_grid_data = $widget.coords().grid;
2770
+ var actual_row = el_grid_data.row;
2771
+ var moved = [];
2772
+ var y_diff = y_units;
2773
+
2774
+ if (!$widget) { return false; }
2775
+
2776
+ if ($.inArray($widget, moved) === -1) {
2777
+
2778
+ var widget_grid_data = $widget.coords().grid;
2779
+ var next_row = actual_row + y_units;
2780
+ var $next_widgets = this.widgets_below($widget);
2781
+
2782
+ this.remove_from_gridmap(widget_grid_data);
2783
+
2784
+ $next_widgets.each($.proxy(function(i, widget) {
2785
+ var $w = $(widget);
2786
+ var wd = $w.coords().grid;
2787
+ var tmp_y = this.displacement_diff(
2788
+ wd, widget_grid_data, y_diff);
2789
+
2790
+ if (tmp_y > 0) {
2791
+ this.move_widget_down($w, tmp_y);
2792
+ }
2793
+ }, this));
2794
+
2795
+ widget_grid_data.row = next_row;
2796
+ this.update_widget_position(widget_grid_data, $widget);
2797
+ $widget.attr('data-row', widget_grid_data.row);
2798
+ this.$changed = this.$changed.add($widget);
2799
+
2800
+ moved.push($widget);
2801
+ }
2802
+ };
2803
+
2804
+
2805
+ /**
2806
+ * Check if the widget can move to the specified row, else returns the
2807
+ * upper row possible.
2808
+ *
2809
+ * @method can_go_up_to_row
2810
+ * @param {Number} widget_grid_data The current grid coords object of the
2811
+ * widget.
2812
+ * @param {Number} col The target column.
2813
+ * @param {Number} row The target row.
2814
+ * @return {Boolean|Number} Returns the row number if the widget can move
2815
+ * to the target position, else returns false.
2816
+ */
2817
+ fn.can_go_up_to_row = function(widget_grid_data, col, row) {
2818
+ var ga = this.gridmap;
2819
+ var result = true;
2820
+ var urc = []; // upper_rows_in_columns
2821
+ var actual_row = widget_grid_data.row;
2822
+ var r;
2823
+
2824
+ /* generate an array with columns as index and array with
2825
+ * upper rows empty in the column */
2826
+ this.for_each_column_occupied(widget_grid_data, function(tcol) {
2827
+ var grid_col = ga[tcol];
2828
+ urc[tcol] = [];
2829
+
2830
+ r = actual_row;
2831
+ while (r--) {
2832
+ if (this.is_empty(tcol, r) &&
2833
+ !this.is_placeholder_in(tcol, r)
2834
+ ) {
2835
+ urc[tcol].push(r);
2836
+ }else{
2837
+ break;
2838
+ }
2839
+ }
2840
+
2841
+ if (!urc[tcol].length) {
2842
+ result = false;
2843
+ return true;
2844
+ }
2845
+
2846
+ });
2847
+
2848
+ if (!result) { return false; }
2849
+
2850
+ /* get common rows starting from upper position in all the columns
2851
+ * that widget occupies */
2852
+ r = row;
2853
+ for (r = 1; r < actual_row; r++) {
2854
+ var common = true;
2855
+
2856
+ for (var uc = 0, ucl = urc.length; uc < ucl; uc++) {
2857
+ if (urc[uc] && $.inArray(r, urc[uc]) === -1) {
2858
+ common = false;
2859
+ }
2860
+ }
2861
+
2862
+ if (common === true) {
2863
+ result = r;
2864
+ break;
2865
+ }
2866
+ }
2867
+
2868
+ return result;
2869
+ };
2870
+
2871
+
2872
+ fn.displacement_diff = function(widget_grid_data, parent_bgd, y_units) {
2873
+ var actual_row = widget_grid_data.row;
2874
+ var diffs = [];
2875
+ var parent_max_y = parent_bgd.row + parent_bgd.size_y;
2876
+
2877
+ this.for_each_column_occupied(widget_grid_data, function(col) {
2878
+ var temp_y_units = 0;
2879
+
2880
+ for (var r = parent_max_y; r < actual_row; r++) {
2881
+ if (this.is_empty(col, r)) {
2882
+ temp_y_units = temp_y_units + 1;
2883
+ }
2884
+ }
2885
+
2886
+ diffs.push(temp_y_units);
2887
+ });
2888
+
2889
+ var max_diff = Math.max.apply(Math, diffs);
2890
+ y_units = (y_units - max_diff);
2891
+
2892
+ return y_units > 0 ? y_units : 0;
2893
+ };
2894
+
2895
+
2896
+ /**
2897
+ * Get widgets below a widget.
2898
+ *
2899
+ * @method widgets_below
2900
+ * @param {HTMLElement} $el The jQuery wrapped HTMLElement.
2901
+ * @return {HTMLElements} A jQuery collection of HTMLElements.
2902
+ */
2903
+ fn.widgets_below = function($el) {
2904
+ var el_grid_data = $.isPlainObject($el) ? $el : $el.coords().grid;
2905
+ var self = this;
2906
+ var ga = this.gridmap;
2907
+ var next_row = el_grid_data.row + el_grid_data.size_y - 1;
2908
+ var $nexts = $([]);
2909
+
2910
+ this.for_each_column_occupied(el_grid_data, function(col) {
2911
+ self.for_each_widget_below(col, next_row, function(tcol, trow) {
2912
+ if (!self.is_player(this) && $.inArray(this, $nexts) === -1) {
2913
+ $nexts = $nexts.add(this);
2914
+ return true; // break
2915
+ }
2916
+ });
2917
+ });
2918
+
2919
+ return this.sort_by_row_asc($nexts);
2920
+ };
2921
+
2922
+
2923
+ /**
2924
+ * Update the array of mapped positions with the new player position.
2925
+ *
2926
+ * @method set_cells_player_occupies
2927
+ * @param {Number} col The new player col.
2928
+ * @param {Number} col The new player row.
2929
+ * @return {Class} Returns the instance of the Gridster Class.
2930
+ */
2931
+ fn.set_cells_player_occupies = function(col, row) {
2932
+ this.remove_from_gridmap(this.placeholder_grid_data);
2933
+ this.placeholder_grid_data.col = col;
2934
+ this.placeholder_grid_data.row = row;
2935
+ this.add_to_gridmap(this.placeholder_grid_data, this.$player);
2936
+ return this;
2937
+ };
2938
+
2939
+
2940
+ /**
2941
+ * Remove from the array of mapped positions the reference to the player.
2942
+ *
2943
+ * @method empty_cells_player_occupies
2944
+ * @return {Class} Returns the instance of the Gridster Class.
2945
+ */
2946
+ fn.empty_cells_player_occupies = function() {
2947
+ this.remove_from_gridmap(this.placeholder_grid_data);
2948
+ return this;
2949
+ };
2950
+
2951
+ fn.can_go_down = function($el) {
2952
+ var can_go_down = true;
2953
+ var $gr = this;
2954
+
2955
+ if ($el.hasClass(this.options.static_class)){
2956
+ can_go_down = false;
2957
+ }
2958
+
2959
+ this.widgets_below($el).each(function(){
2960
+ if ($(this).hasClass($gr.options.static_class)){
2961
+ can_go_down = false;
2962
+ }
2963
+ })
2964
+
2965
+ return can_go_down;
2966
+ }
2967
+
2968
+
2969
+ fn.can_go_up = function($el) {
2970
+ var el_grid_data = $el.coords().grid;
2971
+ var initial_row = el_grid_data.row;
2972
+ var prev_row = initial_row - 1;
2973
+ var ga = this.gridmap;
2974
+ var upper_rows_by_column = [];
2975
+
2976
+ var result = true;
2977
+ if (initial_row === 1) { return false; }
2978
+
2979
+ this.for_each_column_occupied(el_grid_data, function(col) {
2980
+ var $w = this.is_widget(col, prev_row);
2981
+
2982
+ if (this.is_occupied(col, prev_row) ||
2983
+ this.is_player(col, prev_row) ||
2984
+ this.is_placeholder_in(col, prev_row) ||
2985
+ this.is_player_in(col, prev_row)
2986
+ ) {
2987
+ result = false;
2988
+ return true; //break
2989
+ }
2990
+ });
2991
+
2992
+ return result;
2993
+ };
2994
+
2995
+
2996
+
2997
+ /**
2998
+ * Check if it's possible to move a widget to a specific col/row. It takes
2999
+ * into account the dimensions (`size_y` and `size_x` attrs. of the grid
3000
+ * coords object) the widget occupies.
3001
+ *
3002
+ * @method can_move_to
3003
+ * @param {Object} widget_grid_data The grid coords object that represents
3004
+ * the widget.
3005
+ * @param {Object} col The col to check.
3006
+ * @param {Object} row The row to check.
3007
+ * @param {Number} [max_row] The max row allowed.
3008
+ * @return {Boolean} Returns true if all cells are empty, else return false.
3009
+ */
3010
+ fn.can_move_to = function(widget_grid_data, col, row, max_row) {
3011
+ var ga = this.gridmap;
3012
+ var $w = widget_grid_data.el;
3013
+ var future_wd = {
3014
+ size_y: widget_grid_data.size_y,
3015
+ size_x: widget_grid_data.size_x,
3016
+ col: col,
3017
+ row: row
3018
+ };
3019
+ var result = true;
3020
+
3021
+ //Prevents widgets go out of the grid
3022
+ var right_col = col + widget_grid_data.size_x - 1;
3023
+ if (right_col > this.cols) {
3024
+ return false;
3025
+ }
3026
+
3027
+ if (max_row && max_row < row + widget_grid_data.size_y - 1) {
3028
+ return false;
3029
+ }
3030
+
3031
+ this.for_each_cell_occupied(future_wd, function(tcol, trow) {
3032
+ var $tw = this.is_widget(tcol, trow);
3033
+ if ($tw && (!widget_grid_data.el || $tw.is($w))) {
3034
+ result = false;
3035
+ }
3036
+ });
3037
+
3038
+ return result;
3039
+ };
3040
+
3041
+
3042
+ /**
3043
+ * Given the leftmost column returns all columns that are overlapping
3044
+ * with the player.
3045
+ *
3046
+ * @method get_targeted_columns
3047
+ * @param {Number} [from_col] The leftmost column.
3048
+ * @return {Array} Returns an array with column numbers.
3049
+ */
3050
+ fn.get_targeted_columns = function(from_col) {
3051
+ var max = (from_col || this.player_grid_data.col) +
3052
+ (this.player_grid_data.size_x - 1);
3053
+ var cols = [];
3054
+ for (var col = from_col; col <= max; col++) {
3055
+ cols.push(col);
3056
+ }
3057
+ return cols;
3058
+ };
3059
+
3060
+
3061
+ /**
3062
+ * Given the upper row returns all rows that are overlapping with the player.
3063
+ *
3064
+ * @method get_targeted_rows
3065
+ * @param {Number} [from_row] The upper row.
3066
+ * @return {Array} Returns an array with row numbers.
3067
+ */
3068
+ fn.get_targeted_rows = function(from_row) {
3069
+ var max = (from_row || this.player_grid_data.row) +
3070
+ (this.player_grid_data.size_y - 1);
3071
+ var rows = [];
3072
+ for (var row = from_row; row <= max; row++) {
3073
+ rows.push(row);
3074
+ }
3075
+ return rows;
3076
+ };
3077
+
3078
+ /**
3079
+ * Get all columns and rows that a widget occupies.
3080
+ *
3081
+ * @method get_cells_occupied
3082
+ * @param {Object} el_grid_data The grid coords object of the widget.
3083
+ * @return {Object} Returns an object like `{ cols: [], rows: []}`.
3084
+ */
3085
+ fn.get_cells_occupied = function(el_grid_data) {
3086
+ var cells = { cols: [], rows: []};
3087
+ var i;
3088
+ if (arguments[1] instanceof jQuery) {
3089
+ el_grid_data = arguments[1].coords().grid;
3090
+ }
3091
+
3092
+ for (i = 0; i < el_grid_data.size_x; i++) {
3093
+ var col = el_grid_data.col + i;
3094
+ cells.cols.push(col);
3095
+ }
3096
+
3097
+ for (i = 0; i < el_grid_data.size_y; i++) {
3098
+ var row = el_grid_data.row + i;
3099
+ cells.rows.push(row);
3100
+ }
3101
+
3102
+ return cells;
3103
+ };
3104
+
3105
+
3106
+ /**
3107
+ * Iterate over the cells occupied by a widget executing a function for
3108
+ * each one.
3109
+ *
3110
+ * @method for_each_cell_occupied
3111
+ * @param {Object} el_grid_data The grid coords object that represents the
3112
+ * widget.
3113
+ * @param {Function} callback The function to execute on each column
3114
+ * iteration. Column and row are passed as arguments.
3115
+ * @return {Class} Returns the instance of the Gridster Class.
3116
+ */
3117
+ fn.for_each_cell_occupied = function(grid_data, callback) {
3118
+ this.for_each_column_occupied(grid_data, function(col) {
3119
+ this.for_each_row_occupied(grid_data, function(row) {
3120
+ callback.call(this, col, row);
3121
+ });
3122
+ });
3123
+ return this;
3124
+ };
3125
+
3126
+
3127
+ /**
3128
+ * Iterate over the columns occupied by a widget executing a function for
3129
+ * each one.
3130
+ *
3131
+ * @method for_each_column_occupied
3132
+ * @param {Object} el_grid_data The grid coords object that represents
3133
+ * the widget.
3134
+ * @param {Function} callback The function to execute on each column
3135
+ * iteration. The column number is passed as first argument.
3136
+ * @return {Class} Returns the instance of the Gridster Class.
3137
+ */
3138
+ fn.for_each_column_occupied = function(el_grid_data, callback) {
3139
+ for (var i = 0; i < el_grid_data.size_x; i++) {
3140
+ var col = el_grid_data.col + i;
3141
+ callback.call(this, col, el_grid_data);
3142
+ }
3143
+ };
3144
+
3145
+
3146
+ /**
3147
+ * Iterate over the rows occupied by a widget executing a function for
3148
+ * each one.
3149
+ *
3150
+ * @method for_each_row_occupied
3151
+ * @param {Object} el_grid_data The grid coords object that represents
3152
+ * the widget.
3153
+ * @param {Function} callback The function to execute on each column
3154
+ * iteration. The row number is passed as first argument.
3155
+ * @return {Class} Returns the instance of the Gridster Class.
3156
+ */
3157
+ fn.for_each_row_occupied = function(el_grid_data, callback) {
3158
+ for (var i = 0; i < el_grid_data.size_y; i++) {
3159
+ var row = el_grid_data.row + i;
3160
+ callback.call(this, row, el_grid_data);
3161
+ }
3162
+ };
3163
+
3164
+ fn.clean_up_changed = function(){
3165
+ $gr = this;
3166
+ $gr.$changed.each(function(){
3167
+ if($gr.options.shift_larger_widgets_down){
3168
+ $gr.move_widget_up($(this));
3169
+ }
3170
+ });
3171
+ }
3172
+
3173
+
3174
+
3175
+ fn._traversing_widgets = function(type, direction, col, row, callback) {
3176
+ var ga = this.gridmap;
3177
+ if (!ga[col]) { return; }
3178
+
3179
+ var cr, max;
3180
+ var action = type + '/' + direction;
3181
+ if (arguments[2] instanceof jQuery) {
3182
+ var el_grid_data = arguments[2].coords().grid;
3183
+ col = el_grid_data.col;
3184
+ row = el_grid_data.row;
3185
+ callback = arguments[3];
3186
+ }
3187
+ var matched = [];
3188
+ var trow = row;
3189
+
3190
+
3191
+ var methods = {
3192
+ 'for_each/above': function() {
3193
+ while (trow--) {
3194
+ if (trow > 0 && this.is_widget(col, trow) &&
3195
+ $.inArray(ga[col][trow], matched) === -1
3196
+ ) {
3197
+ cr = callback.call(ga[col][trow], col, trow);
3198
+ matched.push(ga[col][trow]);
3199
+ if (cr) { break; }
3200
+ }
3201
+ }
3202
+ },
3203
+ 'for_each/below': function() {
3204
+ for (trow = row + 1, max = ga[col].length; trow < max; trow++) {
3205
+ if (this.is_widget(col, trow) &&
3206
+ $.inArray(ga[col][trow], matched) === -1
3207
+ ) {
3208
+ cr = callback.call(ga[col][trow], col, trow);
3209
+ matched.push(ga[col][trow]);
3210
+ //break was causing problems, leaving for testing.
3211
+ //if (cr) { break; }
3212
+ }
3213
+ }
3214
+ }
3215
+ };
3216
+
3217
+ if (methods[action]) {
3218
+ methods[action].call(this);
3219
+ }
3220
+ };
3221
+
3222
+
3223
+ /**
3224
+ * Iterate over each widget above the column and row specified.
3225
+ *
3226
+ * @method for_each_widget_above
3227
+ * @param {Number} col The column to start iterating.
3228
+ * @param {Number} row The row to start iterating.
3229
+ * @param {Function} callback The function to execute on each widget
3230
+ * iteration. The value of `this` inside the function is the jQuery
3231
+ * wrapped HTMLElement.
3232
+ * @return {Class} Returns the instance of the Gridster Class.
3233
+ */
3234
+ fn.for_each_widget_above = function(col, row, callback) {
3235
+ this._traversing_widgets('for_each', 'above', col, row, callback);
3236
+ return this;
3237
+ };
3238
+
3239
+
3240
+ /**
3241
+ * Iterate over each widget below the column and row specified.
3242
+ *
3243
+ * @method for_each_widget_below
3244
+ * @param {Number} col The column to start iterating.
3245
+ * @param {Number} row The row to start iterating.
3246
+ * @param {Function} callback The function to execute on each widget
3247
+ * iteration. The value of `this` inside the function is the jQuery wrapped
3248
+ * HTMLElement.
3249
+ * @return {Class} Returns the instance of the Gridster Class.
3250
+ */
3251
+ fn.for_each_widget_below = function(col, row, callback) {
3252
+ this._traversing_widgets('for_each', 'below', col, row, callback);
3253
+ return this;
3254
+ };
3255
+
3256
+
3257
+ /**
3258
+ * Returns the highest occupied cell in the grid.
3259
+ *
3260
+ * @method get_highest_occupied_cell
3261
+ * @return {Object} Returns an object with `col` and `row` numbers.
3262
+ */
3263
+ fn.get_highest_occupied_cell = function() {
3264
+ var r;
3265
+ var gm = this.gridmap;
3266
+ var rows = [];
3267
+ var row_in_col = [];
3268
+ for (var c = gm.length - 1; c >= 1; c--) {
3269
+ for (r = gm[c].length - 1; r >= 1; r--) {
3270
+ if (this.is_widget(c, r)) {
3271
+ rows.push(r);
3272
+ row_in_col[r] = c;
3273
+ break;
3274
+ }
3275
+ }
3276
+ }
3277
+
3278
+ var highest_row = Math.max.apply(Math, rows);
3279
+
3280
+ this.highest_occupied_cell = {
3281
+ col: row_in_col[highest_row],
3282
+ row: highest_row
3283
+ };
3284
+
3285
+ return this.highest_occupied_cell;
3286
+ };
3287
+
3288
+
3289
+ fn.get_widgets_from = function(col, row) {
3290
+ var ga = this.gridmap;
3291
+ var $widgets = $();
3292
+
3293
+ if (col) {
3294
+ $widgets = $widgets.add(
3295
+ this.$widgets.filter(function() {
3296
+ var tcol = $(this).attr('data-col');
3297
+ return (tcol === col || tcol > col);
3298
+ })
3299
+ );
3300
+ }
3301
+
3302
+ if (row) {
3303
+ $widgets = $widgets.add(
3304
+ this.$widgets.filter(function() {
3305
+ var trow = $(this).attr('data-row');
3306
+ return (trow === row || trow > row);
3307
+ })
3308
+ );
3309
+ }
3310
+
3311
+ return $widgets;
3312
+ };
3313
+
3314
+
3315
+ /**
3316
+ * Set the current height of the parent grid.
3317
+ *
3318
+ * @method set_dom_grid_height
3319
+ * @return {Object} Returns the instance of the Gridster class.
3320
+ */
3321
+ fn.set_dom_grid_height = function() {
3322
+ var r = this.get_highest_occupied_cell().row;
3323
+ this.$el.css('height', r * this.min_widget_height);
3324
+ return this;
3325
+ };
3326
+
3327
+
3328
+ /**
3329
+ * It generates the neccessary styles to position the widgets.
3330
+ *
3331
+ * @method generate_stylesheet
3332
+ * @param {Number} rows Number of columns.
3333
+ * @param {Number} cols Number of rows.
3334
+ * @return {Object} Returns the instance of the Gridster class.
3335
+ */
3336
+ fn.generate_stylesheet = function(opts) {
3337
+ var styles = '';
3338
+ var max_size_x = this.options.max_size_x;
3339
+ var max_rows = 0;
3340
+ var max_cols = 0;
3341
+ var i;
3342
+ var rules;
3343
+
3344
+ opts || (opts = {});
3345
+ opts.cols || (opts.cols = this.cols);
3346
+ opts.rows || (opts.rows = this.rows);
3347
+ opts.namespace || (opts.namespace = this.options.namespace);
3348
+ opts.widget_base_dimensions ||
3349
+ (opts.widget_base_dimensions = this.options.widget_base_dimensions);
3350
+ opts.widget_margins ||
3351
+ (opts.widget_margins = this.options.widget_margins);
3352
+ opts.min_widget_width = (opts.widget_margins[0] * 2) +
3353
+ opts.widget_base_dimensions[0];
3354
+ opts.min_widget_height = (opts.widget_margins[1] * 2) +
3355
+ opts.widget_base_dimensions[1];
3356
+
3357
+ // don't duplicate stylesheets for the same configuration
3358
+ var serialized_opts = $.param(opts);
3359
+ if ($.inArray(serialized_opts, Gridster.generated_stylesheets) >= 0) {
3360
+ return false;
3361
+ }
3362
+
3363
+ Gridster.generated_stylesheets.push(serialized_opts);
3364
+
3365
+ /* generate CSS styles for cols */
3366
+ for (i = opts.cols; i >= 0; i--) {
3367
+ styles += (opts.namespace + ' [data-col="'+ (i + 1) + '"] { left:' +
3368
+ ((i * opts.widget_base_dimensions[0]) +
3369
+ (i * opts.widget_margins[0]) +
3370
+ ((i + 1) * opts.widget_margins[0])) + 'px;} ');
3371
+ }
3372
+
3373
+ /* generate CSS styles for rows */
3374
+ for (i = opts.rows; i >= 0; i--) {
3375
+ styles += (opts.namespace + ' [data-row="' + (i + 1) + '"] { top:' +
3376
+ ((i * opts.widget_base_dimensions[1]) +
3377
+ (i * opts.widget_margins[1]) +
3378
+ ((i + 1) * opts.widget_margins[1]) ) + 'px;} ');
3379
+ }
3380
+
3381
+ for (var y = 1; y <= opts.rows; y++) {
3382
+ styles += (opts.namespace + ' [data-sizey="' + y + '"] { height:' +
3383
+ (y * opts.widget_base_dimensions[1] +
3384
+ (y - 1) * (opts.widget_margins[1] * 2)) + 'px;}');
3385
+ }
3386
+
3387
+ for (var x = 1; x <= max_size_x; x++) {
3388
+ styles += (opts.namespace + ' [data-sizex="' + x + '"] { width:' +
3389
+ (x * opts.widget_base_dimensions[0] +
3390
+ (x - 1) * (opts.widget_margins[0] * 2)) + 'px;}');
3391
+ }
3392
+
3393
+ return this.add_style_tag(styles);
3394
+ };
3395
+
3396
+
3397
+ /**
3398
+ * Injects the given CSS as string to the head of the document.
3399
+ *
3400
+ * @method add_style_tag
3401
+ * @param {String} css The styles to apply.
3402
+ * @return {Object} Returns the instance of the Gridster class.
3403
+ */
3404
+ fn.add_style_tag = function(css) {
3405
+ var d = document;
3406
+ var tag = d.createElement('style');
3407
+
3408
+ d.getElementsByTagName('head')[0].appendChild(tag);
3409
+ tag.setAttribute('type', 'text/css');
3410
+
3411
+ if (tag.styleSheet) {
3412
+ tag.styleSheet.cssText = css;
3413
+ }else{
3414
+ tag.appendChild(document.createTextNode(css));
3415
+ }
3416
+ return this;
3417
+ };
3418
+
3419
+
3420
+ /**
3421
+ * Generates a faux grid to collide with it when a widget is dragged and
3422
+ * detect row or column that we want to go.
3423
+ *
3424
+ * @method generate_faux_grid
3425
+ * @param {Number} rows Number of columns.
3426
+ * @param {Number} cols Number of rows.
3427
+ * @return {Object} Returns the instance of the Gridster class.
3428
+ */
3429
+ fn.generate_faux_grid = function(rows, cols) {
3430
+ this.faux_grid = [];
3431
+ this.gridmap = [];
3432
+ var col;
3433
+ var row;
3434
+ for (col = cols; col > 0; col--) {
3435
+ this.gridmap[col] = [];
3436
+ for (row = rows; row > 0; row--) {
3437
+ this.add_faux_cell(row, col);
3438
+ }
3439
+ }
3440
+ return this;
3441
+ };
3442
+
3443
+
3444
+ /**
3445
+ * Add cell to the faux grid.
3446
+ *
3447
+ * @method add_faux_cell
3448
+ * @param {Number} row The row for the new faux cell.
3449
+ * @param {Number} col The col for the new faux cell.
3450
+ * @return {Object} Returns the instance of the Gridster class.
3451
+ */
3452
+ fn.add_faux_cell = function(row, col) {
3453
+ var coords = $({
3454
+ left: this.baseX + ((col - 1) * this.min_widget_width),
3455
+ top: this.baseY + (row -1) * this.min_widget_height,
3456
+ width: this.min_widget_width,
3457
+ height: this.min_widget_height,
3458
+ col: col,
3459
+ row: row,
3460
+ original_col: col,
3461
+ original_row: row
3462
+ }).coords();
3463
+
3464
+ if (!$.isArray(this.gridmap[col])) {
3465
+ this.gridmap[col] = [];
3466
+ }
3467
+
3468
+ this.gridmap[col][row] = false;
3469
+ this.faux_grid.push(coords);
3470
+
3471
+ return this;
3472
+ };
3473
+
3474
+
3475
+ /**
3476
+ * Add rows to the faux grid.
3477
+ *
3478
+ * @method add_faux_rows
3479
+ * @param {Number} rows The number of rows you want to add to the faux grid.
3480
+ * @return {Object} Returns the instance of the Gridster class.
3481
+ */
3482
+ fn.add_faux_rows = function(rows) {
3483
+ var actual_rows = this.rows;
3484
+ var max_rows = actual_rows + (rows || 1);
3485
+
3486
+ for (var r = max_rows; r > actual_rows; r--) {
3487
+ for (var c = this.cols; c >= 1; c--) {
3488
+ this.add_faux_cell(r, c);
3489
+ }
3490
+ }
3491
+
3492
+ this.rows = max_rows;
3493
+
3494
+ if (this.options.autogenerate_stylesheet) {
3495
+ this.generate_stylesheet();
3496
+ }
3497
+
3498
+ return this;
3499
+ };
3500
+
3501
+ /**
3502
+ * Add cols to the faux grid.
3503
+ *
3504
+ * @method add_faux_cols
3505
+ * @param {Number} cols The number of cols you want to add to the faux grid.
3506
+ * @return {Object} Returns the instance of the Gridster class.
3507
+ */
3508
+ fn.add_faux_cols = function(cols) {
3509
+ var actual_cols = this.cols;
3510
+ var max_cols = actual_cols + (cols || 1);
3511
+
3512
+ for (var c = actual_cols; c < max_cols; c++) {
3513
+ for (var r = this.rows; r >= 1; r--) {
3514
+ this.add_faux_cell(r, c);
3515
+ }
3516
+ }
3517
+
3518
+ this.cols = max_cols;
3519
+
3520
+ if (this.options.autogenerate_stylesheet) {
3521
+ this.generate_stylesheet();
3522
+ }
3523
+
3524
+ return this;
3525
+ };
3526
+
3527
+
3528
+ /**
3529
+ * Recalculates the offsets for the faux grid. You need to use it when
3530
+ * the browser is resized.
3531
+ *
3532
+ * @method recalculate_faux_grid
3533
+ * @return {Object} Returns the instance of the Gridster class.
3534
+ */
3535
+ fn.recalculate_faux_grid = function() {
3536
+ var aw = this.$wrapper.width();
3537
+ this.baseX = ($(window).width() - aw) / 2;
3538
+ this.baseY = this.$wrapper.offset().top;
3539
+
3540
+ $.each(this.faux_grid, $.proxy(function(i, coords) {
3541
+ this.faux_grid[i] = coords.update({
3542
+ left: this.baseX + (coords.data.col -1) * this.min_widget_width,
3543
+ top: this.baseY + (coords.data.row -1) * this.min_widget_height
3544
+ });
3545
+
3546
+ }, this));
3547
+
3548
+ return this;
3549
+ };
3550
+
3551
+
3552
+ /**
3553
+ * Get all widgets in the DOM and register them.
3554
+ *
3555
+ * @method get_widgets_from_DOM
3556
+ * @return {Object} Returns the instance of the Gridster class.
3557
+ */
3558
+ fn.get_widgets_from_DOM = function() {
3559
+ this.$widgets.each($.proxy(function(i, widget) {
3560
+ this.register_widget($(widget));
3561
+ }, this));
3562
+ return this;
3563
+ };
3564
+
3565
+
3566
+ /**
3567
+ * Calculate columns and rows to be set based on the configuration
3568
+ * parameters, grid dimensions, etc ...
3569
+ *
3570
+ * @method generate_grid_and_stylesheet
3571
+ * @return {Object} Returns the instance of the Gridster class.
3572
+ */
3573
+ fn.generate_grid_and_stylesheet = function() {
3574
+ var aw = this.$wrapper.width();
3575
+ var ah = this.$wrapper.height();
3576
+
3577
+ var cols = Math.floor(aw / this.min_widget_width) +
3578
+ this.options.extra_cols;
3579
+
3580
+ var actual_cols = this.$widgets.map(function() {
3581
+ return $(this).attr('data-col');
3582
+ });
3583
+ actual_cols = Array.prototype.slice.call(actual_cols, 0);
3584
+ //needed to pass tests with phantomjs
3585
+ actual_cols.length || (actual_cols = [0]);
3586
+
3587
+ var min_cols = Math.max.apply(Math, actual_cols);
3588
+
3589
+ // get all rows that could be occupied by the current widgets
3590
+ var max_rows = this.options.extra_rows;
3591
+ this.$widgets.each(function(i, w) {
3592
+ max_rows += (+$(w).attr('data-sizey'));
3593
+ });
3594
+
3595
+ this.cols = Math.max(min_cols, cols, this.options.min_cols);
3596
+ //this.rows = Math.max(max_rows, this.options.min_rows);
3597
+ this.rows = this.options.max_rows;
3598
+
3599
+ this.baseX = ($(window).width() - aw) / 2;
3600
+ this.baseY = this.$wrapper.offset().top;
3601
+
3602
+ if (this.options.autogenerate_stylesheet) {
3603
+ this.generate_stylesheet();
3604
+ }
3605
+
3606
+ return this.generate_faux_grid(this.rows, this.cols);
3607
+ };
3608
+
3609
+
3610
+ //jQuery adapter
3611
+ $.fn.gridster = function(options) {
3612
+ return this.each(function() {
3613
+ if (!$(this).data('gridster')) {
3614
+ $(this).data('gridster', new Gridster( this, options ));
3615
+ }
3616
+ });
3617
+ };
3618
+
3619
+ $.Gridster = fn;
3620
+
3621
+ }(jQuery, window, document));