(function (scope, _) { var Utils = { is_intercepted: function (a, b) { return !(a.x + a.width <= b.x || b.x + b.width <= a.x || a.y + a.height <= b.y || b.y + b.height <= a.y); }, sort: function (nodes, dir, width) { width = width || _.chain(nodes).map(function (node) { return node.x + node.width; }).max().value(); dir = dir != -1 ? 1 : -1; return _.sortBy(nodes, function (n) { return dir * (n.x + n.y * width); }); }, create_stylesheet: function () { var style = document.createElement("style"); // style.setAttribute("media", "screen") // style.setAttribute("media", "only screen and (max-width : 1024px)") // WebKit hack :( style.appendChild(document.createTextNode("")); document.head.appendChild(style); return style.sheet; } }; var id_seq = 0; var GridStackEngine = function (width, onchange, float, height, items) { this.width = width; this.float = float || false; this.height = height || 0; this.nodes = items || []; this.onchange = onchange || function () {}; }; GridStackEngine.prototype._fix_collisions = function (node) { this._sort_nodes(-1); while (true) { var collision_node = _.find(this.nodes, function (n) { return n != node && Utils.is_intercepted(n, node); }, this); if (typeof collision_node == 'undefined') { return; } this.move_node(collision_node, collision_node.x, node.y + node.height, collision_node.width, collision_node.height, true); } }; GridStackEngine.prototype._sort_nodes = function (dir) { this.nodes = Utils.sort(this.nodes, dir, this.width); }; GridStackEngine.prototype._pack_nodes = function () { this._sort_nodes(); if (this.float) { _.each(this.nodes, function (n, i) { if (n._updating || typeof n._orig_y == 'undefined' || n.y == n._orig_y) return; var collision_node = _.chain(this.nodes) .find(function (bn) { return n != bn && Utils.is_intercepted({x: n.x, y: n._orig_y, width: n.width, height: n.height}, bn); }) .value(); if (!collision_node) { n._dirty = true; n.y = n._orig_y; } }, this); } else { _.each(this.nodes, function (n, i) { while (n.y > 0) { var new_y = n.y - 1; var can_be_moved = i == 0; if (i > 0) { var collision_node = _.chain(this.nodes) .first(i) .find(function (bn) { return Utils.is_intercepted({x: n.x, y: new_y, width: n.width, height: n.height}, bn); }) .value(); can_be_moved = typeof collision_node == 'undefined'; } if (!can_be_moved) { break; } n._dirty = n.y != new_y; n.y = new_y; } }, this); } }; GridStackEngine.prototype._prepare_node = function (node, resizing) { node = _.defaults(node || {}, {width: 1, height: 1, x: 0, y: 0 }); node.x = parseInt('' + node.x); node.y = parseInt('' + node.y); node.width = parseInt('' + node.width); node.height = parseInt('' + node.height); node.auto_position = node.auto_position || false; node.no_resize = node.no_resize || false; node.no_move = node.no_move || false; if (node.width > this.width) { node.width = this.width; } else if (node.width < 1) { node.width = 1; } if (node.height < 1) { node.height = 1; } if (node.x < 0) { node.x = 0; } if (node.x + node.width > this.width) { if (resizing) { node.width = this.width - node.x; } else { node.x = this.width - node.width; } } if (node.y < 0) { node.y = 0; } return node; }; GridStackEngine.prototype._notify = function () { var deleted_nodes = Array.prototype.slice.call(arguments, 1).concat(this.get_dirty_nodes()); deleted_nodes = deleted_nodes.concat(this.get_dirty_nodes()); this.onchange(deleted_nodes); }; GridStackEngine.prototype.clean_nodes = function () { _.each(this.nodes, function (n) {n._dirty = false }); }; GridStackEngine.prototype.get_dirty_nodes = function () { return _.filter(this.nodes, function (n) { return n._dirty; }); }; GridStackEngine.prototype.add_node = function(node) { node = this._prepare_node(node); if (typeof node.max_width != 'undefined') node.width = Math.min(node.width, node.max_width); if (typeof node.max_height != 'undefined') node.height = Math.min(node.height, node.max_height); if (typeof node.min_width != 'undefined') node.width = Math.max(node.width, node.min_width); if (typeof node.min_height != 'undefined') node.height = Math.max(node.height, node.min_height); node._id = ++id_seq; node._dirty = true; if (node.auto_position) { this._sort_nodes(); for (var i = 0; ; ++i) { var x = i % this.width, y = Math.floor(i / this.width); if (x + node.width > this.width) { continue; } if (!_.find(this.nodes, function (n) { return Utils.is_intercepted({x: x, y: y, width: node.width, height: node.height}, n); })) { node.x = x; node.y = y; break; } } } this.nodes.push(node); this._fix_collisions(node); this._pack_nodes(); this._notify(); return node; }; GridStackEngine.prototype.remove_node = function (node) { node._id = null; this.nodes = _.without(this.nodes, node); this._pack_nodes(); this._notify(node); }; GridStackEngine.prototype.can_move_node = function (node, x, y, width, height) { if (!this.height) return true; var cloned_node; var clone = new GridStackEngine( this.width, null, this.float, 0, _.map(this.nodes, function (n) { if (n == node) { cloned_node = $.extend({}, n); return cloned_node; } return $.extend({}, n) })); clone.move_node(cloned_node, x, y, width, height); return clone.get_grid_height() <= this.height; }; GridStackEngine.prototype.can_be_placed_with_respect_to_height = function (node) { if (!this.height) return true; var clone = new GridStackEngine( this.width, null, this.float, 0, _.map(this.nodes, function (n) { return $.extend({}, n) })); clone.add_node(node); return clone.get_grid_height() <= this.height; }; GridStackEngine.prototype.move_node = function (node, x, y, width, height, no_pack) { if (typeof x != 'number') x = node.x; if (typeof y != 'number') y = node.y; if (typeof width != 'number') width = node.width; if (typeof height != 'number') height = node.height; if (typeof node.max_width != 'undefined') width = Math.min(width, node.max_width); if (typeof node.max_height != 'undefined') height = Math.min(height, node.max_height); if (typeof node.min_width != 'undefined') width = Math.max(width, node.min_width); if (typeof node.min_height != 'undefined') height = Math.max(height, node.min_height); if (node.x == x && node.y == y && node.width == width && node.height == height) { return node; } var resizing = node.width != width; node._dirty = true; node.x = x; node.y = y; node.width = width; node.height = height; node = this._prepare_node(node, resizing); this._fix_collisions(node); if (!no_pack) { this._pack_nodes(); this._notify(); } return node; }; GridStackEngine.prototype.get_grid_height = function () { return _.reduce(this.nodes, function (memo, n) { return Math.max(memo, n.y + n.height); }, 0); }; GridStackEngine.prototype.begin_update = function (node) { _.each(this.nodes, function (n) { n._orig_y = n.y; }); node._updating = true; }; GridStackEngine.prototype.end_update = function () { var n = _.find(this.nodes, function (n) { return n._updating; }); if (n) { n._updating = false; } }; var GridStack = function (el, opts) { var self = this, one_column_mode; this.container = $(el); this.opts = _.defaults(opts || {}, { width: parseInt(this.container.attr('data-gs-width')) || 12, height: parseInt(this.container.attr('data-gs-height')) || 0, item_class: 'grid-stack-item', placeholder_class: 'grid-stack-placeholder', handle: '.grid-stack-item-content', cell_height: 60, vertical_margin: 20, auto: true, min_width: 768, float: false, _class: 'grid-stack-' + (Math.random() * 10000).toFixed(0), animate: Boolean(this.container.attr('data-gs-animate')) || false }); this.container.addClass(this.opts._class); this._styles = Utils.create_stylesheet(); this._styles._max = 0; this.grid = new GridStackEngine(this.opts.width, function (nodes) { var max_height = 0; _.each(nodes, function (n) { if (n._id == null) { n.el.remove(); } else { n.el .attr('data-gs-x', n.x) .attr('data-gs-y', n.y) .attr('data-gs-width', n.width) .attr('data-gs-height', n.height); max_height = Math.max(max_height, n.y + n.height); } }); max_height += 10; if (max_height > self._styles._max) { for (var i = self._styles._max; i < max_height; ++i) { var css; css = '.' + self.opts._class + ' .' + self.opts.item_class + '[data-gs-height="' + (i + 1) + '"] { height: ' + (self.opts.cell_height * (i + 1) + self.opts.vertical_margin * i) + 'px; }'; self._styles.insertRule(css, i); css = '.' + self.opts._class + ' .' + self.opts.item_class + '[data-gs-y="' + (i) + '"] { top: ' + (self.opts.cell_height * i + self.opts.vertical_margin * i) + 'px; }'; self._styles.insertRule(css, i); } self._styles._max = max_height; } }, this.opts.float, this.opts.height); if (this.opts.auto) { this.container.find('.' + this.opts.item_class).each(function (index, el) { self._prepare_element(el); }); } this.set_animation(this.opts.animate); this.placeholder = $('