// Generated by CoffeeScript 1.4.0 (function() { (function($, window, document) { var Plugin, defaults, pluginName; pluginName = "shapeshift"; defaults = { selector: "*", enableDrag: true, enableCrossDrop: true, enableResize: true, enableTrash: false, align: "center", colWidth: null, columns: null, minColumns: 1, autoHeight: true, maxHeight: null, minHeight: 100, gutterX: 10, gutterY: 10, paddingX: 10, paddingY: 10, animated: true, animateOnInit: false, animationSpeed: 225, animationThreshold: 100, dragClone: false, deleteClone: true, dragRate: 100, dragWhitelist: "*", crossDropWhitelist: "*", cutoffStart: null, cutoffEnd: null, handle: false, cloneClass: "ss-cloned-child", activeClass: "ss-active-child", draggedClass: "ss-dragged-child", placeholderClass: "ss-placeholder-child", originalContainerClass: "ss-original-container", currentContainerClass: "ss-current-container", previousContainerClass: "ss-previous-container" }; Plugin = (function() { function Plugin(element, options) { this.element = element; this.options = $.extend({}, defaults, options); this.globals = {}; this.$container = $(element); if (this.errorCheck()) { this.init(); } } Plugin.prototype.errorCheck = function() { var $children, error_msg, errors, options; options = this.options; errors = false; error_msg = "Shapeshift ERROR:"; if (options.colWidth === null) { $children = this.$container.children(options.selector); if ($children.length === 0) { errors = true; console.error("" + error_msg + " option colWidth must be specified if Shapeshift is initialized with no active children."); } } return !errors; }; Plugin.prototype.init = function() { this.createEvents(); this.setGlobals(); this.setIdentifier(); this.setActiveChildren(); this.enableFeatures(); this.gridInit(); this.render(); return this.afterInit(); }; Plugin.prototype.createEvents = function() { var $container, options, _this = this; options = this.options; $container = this.$container; $container.off("ss-arrange").on("ss-arrange", function(e, trigger_drop_finished) { if (trigger_drop_finished == null) { trigger_drop_finished = false; } return _this.render(false, trigger_drop_finished); }); $container.off("ss-rearrange").on("ss-rearrange", function() { return _this.render(true); }); $container.off("ss-setTargetPosition").on("ss-setTargetPosition", function() { return _this.setTargetPosition(); }); $container.off("ss-destroy").on("ss-destroy", function() { return _this.destroy(); }); return $container.off("ss-shuffle").on("ss-shuffle", function() { return _this.shuffle(); }); }; Plugin.prototype.setGlobals = function() { this.globals.animated = this.options.animateOnInit; return this.globals.dragging = false; }; Plugin.prototype.afterInit = function() { return this.globals.animated = this.options.animated; }; Plugin.prototype.setIdentifier = function() { this.identifier = "shapeshifted_container_" + Math.random().toString(36).substring(7); return this.$container.addClass(this.identifier); }; Plugin.prototype.enableFeatures = function() { if (this.options.enableResize) { this.enableResize(); } if (this.options.enableDrag || this.options.enableCrossDrop) { return this.enableDragNDrop(); } }; Plugin.prototype.setActiveChildren = function() { var $children, active_child_class, colspan, columns, i, min_columns, options, total, _i, _j, _ref, _results; options = this.options; $children = this.$container.children(options.selector); active_child_class = options.activeClass; total = $children.length; for (i = _i = 0; 0 <= total ? _i < total : _i > total; i = 0 <= total ? ++_i : --_i) { $($children[i]).addClass(active_child_class); } this.setParsedChildren(); columns = options.columns; _results = []; for (i = _j = 0, _ref = this.parsedChildren.length; 0 <= _ref ? _j < _ref : _j > _ref; i = 0 <= _ref ? ++_j : --_j) { colspan = this.parsedChildren[i].colspan; min_columns = options.minColumns; if (colspan > columns && colspan > min_columns) { options.minColumns = colspan; _results.push(console.error("Shapeshift ERROR: There are child elements that have a larger colspan than the minimum columns set through options.\noptions.minColumns has been set to " + colspan)); } else { _results.push(void 0); } } return _results; }; Plugin.prototype.setParsedChildren = function() { var $child, $children, child, i, parsedChildren, total, _i; $children = this.$container.find("." + this.options.activeClass).filter(":visible"); total = $children.length; parsedChildren = []; for (i = _i = 0; 0 <= total ? _i < total : _i > total; i = 0 <= total ? ++_i : --_i) { $child = $($children[i]); child = { i: i, el: $child, colspan: parseInt($child.attr("data-ss-colspan")) || 1, height: $child.outerHeight() }; parsedChildren.push(child); } return this.parsedChildren = parsedChildren; }; Plugin.prototype.gridInit = function() { var fc_colspan, fc_width, first_child, gutter_x, single_width; gutter_x = this.options.gutterX; if (!(this.options.colWidth >= 1)) { first_child = this.parsedChildren[0]; fc_width = first_child.el.outerWidth(); fc_colspan = first_child.colspan; single_width = (fc_width - ((fc_colspan - 1) * gutter_x)) / fc_colspan; return this.globals.col_width = single_width + gutter_x; } else { return this.globals.col_width = this.options.colWidth + gutter_x; } }; Plugin.prototype.render = function(reparse, trigger_drop_finished) { if (reparse == null) { reparse = false; } if (reparse) { this.setActiveChildren(); } this.setGridColumns(); return this.arrange(false, trigger_drop_finished); }; Plugin.prototype.setGridColumns = function() { var actual_columns, children_count, col_width, colspan, columns, globals, grid_width, gutter_x, i, inner_width, minColumns, options, padding_x, _i, _ref; globals = this.globals; options = this.options; col_width = globals.col_width; gutter_x = options.gutterX; padding_x = options.paddingX; inner_width = this.$container.innerWidth() - (padding_x * 2); minColumns = options.minColumns; columns = options.columns || Math.floor((inner_width + gutter_x) / col_width); if (minColumns && minColumns > columns) { columns = minColumns; } globals.columns = columns; children_count = this.parsedChildren.length; if (columns > children_count) { actual_columns = 0; for (i = _i = 0, _ref = this.parsedChildren.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { colspan = this.parsedChildren[i].colspan; if (colspan + actual_columns <= columns) { actual_columns += colspan; } } columns = actual_columns; } globals.child_offset = padding_x; switch (options.align) { case "center": grid_width = (columns * col_width) - gutter_x; return globals.child_offset += (inner_width - grid_width) / 2; case "right": grid_width = (columns * col_width) - gutter_x; return globals.child_offset += inner_width - grid_width; } }; Plugin.prototype.arrange = function(reparse, trigger_drop_finished) { var $child, $container, animated, animation_speed, attributes, child_positions, container_height, dragged_class, globals, i, is_dragged_child, max_height, min_height, options, parsed_children, placeholder_class, total_children, _i; if (reparse) { this.setParsedChildren(); } globals = this.globals; options = this.options; $container = this.$container; child_positions = this.getPositions(); parsed_children = this.parsedChildren; total_children = parsed_children.length; animated = globals.animated && total_children <= options.animationThreshold; animation_speed = options.animationSpeed; dragged_class = options.draggedClass; for (i = _i = 0; 0 <= total_children ? _i < total_children : _i > total_children; i = 0 <= total_children ? ++_i : --_i) { $child = parsed_children[i].el; attributes = child_positions[i]; is_dragged_child = $child.hasClass(dragged_class); if (is_dragged_child) { placeholder_class = options.placeholderClass; $child = $child.siblings("." + placeholder_class); } if (animated && !is_dragged_child) { $child.stop(true, false).animate(attributes, animation_speed, function() {}); } else { $child.css(attributes); } } if (trigger_drop_finished) { if (animated) { setTimeout((function() { return $container.trigger("ss-drop-complete"); }), animation_speed); } else { $container.trigger("ss-drop-complete"); } } $container.trigger("ss-arranged"); if (options.autoHeight) { container_height = globals.container_height; max_height = options.maxHeight; min_height = options.minHeight; if (min_height && container_height < min_height) { container_height = min_height; } else if (max_height && container_height > max_height) { container_height = max_height; } return $container.height(container_height); } }; Plugin.prototype.getPositions = function(include_dragged) { var col_heights, determineMultiposition, determinePositions, dragged_class, globals, grid_height, gutter_y, i, options, padding_y, parsed_children, positions, recalculateSavedChildren, savePosition, saved_children, total_children, _i, _ref, _this = this; if (include_dragged == null) { include_dragged = true; } globals = this.globals; options = this.options; gutter_y = options.gutterY; padding_y = options.paddingY; dragged_class = options.draggedClass; parsed_children = this.parsedChildren; total_children = parsed_children.length; col_heights = []; for (i = _i = 0, _ref = globals.columns; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { col_heights.push(padding_y); } savePosition = function(child) { var col, colspan, j, offset_x, offset_y, _j, _results; col = child.col; colspan = child.colspan; offset_x = (child.col * globals.col_width) + globals.child_offset; offset_y = col_heights[col]; positions[child.i] = { left: offset_x, top: offset_y }; col_heights[col] += child.height + gutter_y; if (colspan >= 1) { _results = []; for (j = _j = 1; 1 <= colspan ? _j < colspan : _j > colspan; j = 1 <= colspan ? ++_j : --_j) { _results.push(col_heights[col + j] = col_heights[col]); } return _results; } }; determineMultiposition = function(child) { var chosen_col, col, colspan, height, kosher, next_height, offset, possible_col_heights, possible_cols, span, _j, _k; possible_cols = col_heights.length - child.colspan + 1; possible_col_heights = col_heights.slice(0).splice(0, possible_cols); chosen_col = void 0; for (offset = _j = 0; 0 <= possible_cols ? _j < possible_cols : _j > possible_cols; offset = 0 <= possible_cols ? ++_j : --_j) { col = _this.lowestCol(possible_col_heights, offset); colspan = child.colspan; height = col_heights[col]; kosher = true; for (span = _k = 1; 1 <= colspan ? _k < colspan : _k > colspan; span = 1 <= colspan ? ++_k : --_k) { next_height = col_heights[col + span]; if (height < next_height) { kosher = false; break; } } if (kosher) { chosen_col = col; break; } } return chosen_col; }; saved_children = []; recalculateSavedChildren = function() { var index, pop_i, saved_child, saved_i, to_pop, _j, _k, _ref1, _ref2, _results; to_pop = []; for (saved_i = _j = 0, _ref1 = saved_children.length; 0 <= _ref1 ? _j < _ref1 : _j > _ref1; saved_i = 0 <= _ref1 ? ++_j : --_j) { saved_child = saved_children[saved_i]; saved_child.col = determineMultiposition(saved_child); if (saved_child.col >= 0) { savePosition(saved_child); to_pop.push(saved_i); } } _results = []; for (pop_i = _k = _ref2 = to_pop.length - 1; _k >= 0; pop_i = _k += -1) { index = to_pop[pop_i]; _results.push(saved_children.splice(index, 1)); } return _results; }; positions = []; (determinePositions = function() { var child, _j, _results; _results = []; for (i = _j = 0; 0 <= total_children ? _j < total_children : _j > total_children; i = 0 <= total_children ? ++_j : --_j) { child = parsed_children[i]; if (!(!include_dragged && child.el.hasClass(dragged_class))) { if (child.colspan > 1) { child.col = determineMultiposition(child); } else { child.col = _this.lowestCol(col_heights); } if (child.col === void 0) { saved_children.push(child); } else { savePosition(child); } _results.push(recalculateSavedChildren()); } else { _results.push(void 0); } } return _results; })(); if (options.autoHeight) { grid_height = col_heights[this.highestCol(col_heights)] - gutter_y; globals.container_height = grid_height + padding_y; } return positions; }; Plugin.prototype.enableDragNDrop = function() { var $clone, $container, $placeholder, $selected, active_class, clone_class, current_container_class, delete_clone, drag_clone, drag_rate, drag_timeout, dragged_class, options, original_container_class, placeholder_class, previous_container_class, selected_offset_x, selected_offset_y, _this = this; options = this.options; $container = this.$container; active_class = options.activeClass; dragged_class = options.draggedClass; placeholder_class = options.placeholderClass; original_container_class = options.originalContainerClass; current_container_class = options.currentContainerClass; previous_container_class = options.previousContainerClass; delete_clone = options.deleteClone; drag_rate = options.dragRate; drag_clone = options.dragClone; clone_class = options.cloneClass; $selected = $placeholder = $clone = selected_offset_y = selected_offset_x = null; drag_timeout = false; if (options.enableDrag) { $container.children("." + active_class).filter(options.dragWhitelist).draggable({ addClasses: false, containment: 'document', handle: options.handle, zIndex: 9999, start: function(e, ui) { var selected_tag; _this.globals.dragging = true; $selected = $(e.target); if (drag_clone) { $clone = $selected.clone(false, false).insertBefore($selected).addClass(clone_class); } $selected.addClass(dragged_class); selected_tag = $selected.prop("tagName"); $placeholder = $("<" + selected_tag + " class='" + placeholder_class + "' style='height: " + ($selected.height()) + "px; width: " + ($selected.width()) + "px'>"); $selected.parent().addClass(original_container_class).addClass(current_container_class); selected_offset_y = $selected.outerHeight() / 2; return selected_offset_x = $selected.outerWidth() / 2; }, drag: function(e, ui) { if (!drag_timeout && !(drag_clone && delete_clone && $("." + current_container_class)[0] === $("." + original_container_class)[0])) { $placeholder.remove().appendTo("." + current_container_class); $("." + current_container_class).trigger("ss-setTargetPosition"); drag_timeout = true; window.setTimeout((function() { return drag_timeout = false; }), drag_rate); } ui.position.left = e.pageX - $selected.parent().offset().left - selected_offset_x; return ui.position.top = e.pageY - $selected.parent().offset().top - selected_offset_y; }, stop: function() { var $current_container, $original_container, $previous_container; _this.globals.dragging = false; $original_container = $("." + original_container_class); $current_container = $("." + current_container_class); $previous_container = $("." + previous_container_class); $selected.removeClass(dragged_class); $("." + placeholder_class).remove(); if (drag_clone) { if (delete_clone && $("." + current_container_class)[0] === $("." + original_container_class)[0]) { $clone.remove(); $("." + current_container_class).trigger("ss-rearrange"); } else { $clone.removeClass(clone_class); $original_container.shapeshift($original_container.data("plugin_shapeshift").options); $current_container.shapeshift($current_container.data("plugin_shapeshift").options); } } if ($original_container[0] === $current_container[0]) { $current_container.trigger("ss-rearranged", $selected); } else { $original_container.trigger("ss-removed", $selected); $current_container.trigger("ss-added", $selected); } $original_container.trigger("ss-arrange").removeClass(original_container_class); $current_container.trigger("ss-arrange", true).removeClass(current_container_class); $previous_container.trigger("ss-arrange").removeClass(previous_container_class); return $selected = $placeholder = null; } }); } if (options.enableCrossDrop) { return $container.droppable({ accept: options.crossDropWhitelist, tolerance: 'intersect', over: function(e) { $("." + previous_container_class).removeClass(previous_container_class); $("." + current_container_class).removeClass(current_container_class).addClass(previous_container_class); return $(e.target).addClass(current_container_class); }, drop: function(e, selected) { var $current_container, $original_container, $previous_container; if (_this.options.enableTrash) { $original_container = $("." + original_container_class); $current_container = $("." + current_container_class); $previous_container = $("." + previous_container_class); $selected = $(selected.helper); $current_container.trigger("ss-trashed", $selected); $selected.remove(); $original_container.trigger("ss-rearrange").removeClass(original_container_class); $current_container.trigger("ss-rearrange").removeClass(current_container_class); return $previous_container.trigger("ss-arrange").removeClass(previous_container_class); } } }); } }; Plugin.prototype.setTargetPosition = function() { var $selected, $start_container, $target, attributes, child_positions, cutoff_end, cutoff_start, distance, dragged_class, options, parsed_children, placeholder_class, position_i, previous_container_class, selected_x, selected_y, shortest_distance, target_position, total_positions, x_dist, y_dist, _i; options = this.options; if (!options.enableTrash) { dragged_class = options.draggedClass; $selected = $("." + dragged_class); $start_container = $selected.parent(); parsed_children = this.parsedChildren; child_positions = this.getPositions(false); total_positions = child_positions.length; selected_x = $selected.offset().left - $start_container.offset().left + (this.globals.col_width / 2); selected_y = $selected.offset().top - $start_container.offset().top + ($selected.height() / 2); shortest_distance = 9999999; target_position = 0; if (total_positions > 1) { cutoff_start = options.cutoffStart + 1 || 0; cutoff_end = options.cutoffEnd || total_positions; for (position_i = _i = cutoff_start; cutoff_start <= cutoff_end ? _i < cutoff_end : _i > cutoff_end; position_i = cutoff_start <= cutoff_end ? ++_i : --_i) { attributes = child_positions[position_i]; if (attributes) { y_dist = selected_x - attributes.left; x_dist = selected_y - attributes.top; if (y_dist > 0 && x_dist > 0) { distance = Math.sqrt((x_dist * x_dist) + (y_dist * y_dist)); if (distance < shortest_distance) { shortest_distance = distance; target_position = position_i; if (position_i === total_positions - 1) { if (y_dist > parsed_children[position_i].height / 2) { target_position++; } } } } } } if (target_position === parsed_children.length) { $target = parsed_children[target_position - 1].el; $selected.insertAfter($target); } else { $target = parsed_children[target_position].el; $selected.insertBefore($target); } } else { if (total_positions === 1) { attributes = child_positions[0]; if (attributes.left < selected_x) { this.$container.append($selected); } else { this.$container.prepend($selected); } } else { this.$container.append($selected); } } this.arrange(true); if ($start_container[0] !== $selected.parent()[0]) { previous_container_class = options.previousContainerClass; if ($("." + previous_container_class).data("plugin_shapeshift").options.enableCrossDrop == true) { return $("." + previous_container_class).trigger("ss-rearrange"); } else { return $("." + previous_container_class); } } } else { placeholder_class = this.options.placeholderClass; return $("." + placeholder_class).remove(); } }; Plugin.prototype.enableResize = function() { var animation_speed, binding, resizing, _this = this; animation_speed = this.options.animationSpeed; resizing = false; binding = "resize." + this.identifier; return $(window).on(binding, function() { if (!resizing) { resizing = true; setTimeout((function() { return _this.render(); }), animation_speed / 3); setTimeout((function() { return _this.render(); }), animation_speed / 3); return setTimeout(function() { resizing = false; return _this.render(); }, animation_speed / 3); } }); }; Plugin.prototype.shuffle = function() { var calculateShuffled; calculateShuffled = function(container, activeClass) { var shuffle; shuffle = function(arr) { var i, j, x; j = void 0; x = void 0; i = arr.length; while (i) { j = parseInt(Math.random() * i); x = arr[--i]; arr[i] = arr[j]; arr[j] = x; } return arr; }; return container.each(function() { var items; items = container.find("." + activeClass).filter(":visible"); if (items.length) { return container.html(shuffle(items)); } else { return this; } }); }; if (!this.globals.dragging) { calculateShuffled(this.$container, this.options.activeClass); this.enableFeatures(); return this.$container.trigger("ss-rearrange"); } }; Plugin.prototype.lowestCol = function(array, offset) { var augmented_array, i, length, _i; if (offset == null) { offset = 0; } length = array.length; augmented_array = []; for (i = _i = 0; 0 <= length ? _i < length : _i > length; i = 0 <= length ? ++_i : --_i) { augmented_array.push([array[i], i]); } augmented_array.sort(function(a, b) { var ret; ret = a[0] - b[0]; if (ret === 0) { ret = a[1] - b[1]; } return ret; }); return augmented_array[offset][1]; }; Plugin.prototype.highestCol = function(array) { return $.inArray(Math.max.apply(window, array), array); }; Plugin.prototype.destroy = function() { var $active_children, $container, active_class; $container = this.$container; $container.off("ss-arrange"); $container.off("ss-rearrange"); $container.off("ss-setTargetPosition"); $container.off("ss-destroy"); active_class = this.options.activeClass; $active_children = $container.find("." + active_class); if (this.options.enableDrag) { $active_children.draggable('destroy'); } if (this.options.enableCrossDrop) { $container.droppable('destroy'); } $active_children.removeClass(active_class); return $container.removeClass(this.identifier); }; return Plugin; })(); return $.fn[pluginName] = function(options) { return this.each(function() { var bound_indentifier, old_class, _ref, _ref1; old_class = (_ref = $(this).attr("class")) != null ? (_ref1 = _ref.match(/shapeshifted_container_\w+/)) != null ? _ref1[0] : void 0 : void 0; if (old_class) { bound_indentifier = "resize." + old_class; $(window).off(bound_indentifier); $(this).removeClass(old_class); } return $.data(this, "plugin_" + pluginName, new Plugin(this, options)); }); }; })(jQuery, window, document); }).call(this);