/*! * sorttable * * @Version 1.0.3 * * Copyright (c) 2013, David Brink dbrink@gmail.com * Copyright (c) 2010, Andres Koetter * Dual licensed under the MIT (MIT-LICENSE.txt) * and GPL (GPL-LICENSE.txt) licenses. * * Inspired by the the dragtable from Dan Vanderkam (danvk.org/dragtable/) * Thanks to the jquery and jqueryui comitters * * Any comment, bug report, feature-request is welcome * Feel free to contact me. */ (function ($) { $.widget("extend.sorttable", $.ui.sortable, { widgetEventPrefix: "sorttable", options: { helper: "table", helperCells: 1 }, _table: null, _startIndex: 0, _endIndex: 0, _currentItemWidth: 0, _sortCells: null, _createHelper: function (event) { var o = this.options; if (o.helper == 'table') { // if using 'table' helper if (!this.setWidths) { this.setWidths = true; var items = this.items; var widths = []; for (i = 0; i < items.length; i++) { var item = $(items[i].item[0]); widths[i] = item.innerWidth() - parseInt(item.css('paddingLeft') || 0, 10) - parseInt(item.css('paddingRight') || 0, 10); } for (i = 0; i < items.length; i++) { var item = $(items[i].item[0]); item.width(widths[i]); } } return this._createHelperTable(event, this.currentItem); } else { // use default helper method return $.ui.sortable.prototype._createHelper.apply(this, [event]); } }, _createHelperTable: function (e, ui) { var o = this.options, helperCells = o.helperCells; var hc = ui.is('th') || ui.is('td') ? ui : ui.parents('th:first,td:first'); var index = hc.prevAll().length + 1 var hcWidth = hc.innerWidth() - parseInt(hc.css('paddingLeft') || 0, 10) - parseInt(hc.css('paddingRight') || 0, 10); // save sizes because tr:not(.ui-sortable)>td:nth-child(' + index + ')'); if (helperCellCount < 0) { cells = cells.slice(0, cells.length + helperCellCount); } else if (helperCellCount > 1) { cells = cells.slice(0, helperCellCount - 1); } } if (!helperCellCount && helperCells) { cells = cells.filter(helperCells); } cells.splice(0, 0, hc); // insert first cell this._sortCells = cells; var tableClone = table.clone().empty(); tableClone.css('position', 'absolute'); tableClone.css('width', 'auto'); tableClone.css('min-width', 'inherit'); tableClone.css('height', 'auto'); tableClone.css('min-height', 'inherit'); tableClone.attr('id', ''); for (i = 0; i < cells.length; i++) { var cell = cells[i]; if (!(cell instanceof jQuery)) { cell = $(cell); } var tr = cell.parents('tr:first'); var cellClone = cell.clone(); cellClone.width(hcWidth); var trClone = tr.clone().empty(); var trHeight = tr.innerHeight() - parseInt(tr.css('paddingTop') || 0, 10) - parseInt(tr.css('paddingBottom') || 0, 10); trClone.height(trHeight); cellClone.appendTo(trClone.appendTo(tableClone)); } tableClone.find().each(function () { this.attr('id', ''); // clear ids on cloned table }); table.before(tableClone); return tableClone; }, _createPlaceholder: function (that) { var self = that || this, o = self.options; // call base method $.ui.sortable.prototype._createPlaceholder.apply(this, arguments); if (o.helper == 'table') { var p = self.placeholder; // force width as border will prevent width from setting even though cell has no explicit width self.placeholder.width(self._currentItemWidth); var sortCells = self._sortCells; if (sortCells) { sortCells.hide(); if (sortCells.length > 1) { p.attr('rowSpan', sortCells.length); } } var rowSpan = self._placeholderRowSpan; if (rowSpan > 1) { p.attr('rowSpan', rowSpan); } } }, _swapNodes: function (a, b) { if (a && b) { var aparent = a.parentNode; var asibling = a.nextSibling === b ? a : a.nextSibling; b.parentNode.insertBefore(a, b); aparent.insertBefore(b, asibling); } }, // bubble the moved col left or right _bubbleCols: function () { var from = this._startIndex; var to = this._endIndex; /* Find children thead and tbody. * Only to process the immediate tr-children. Bugfix for inner tables */ var trs = this._table.children().find('> tr:not(.ui-sortable)'); if (from < to) { for (var i = from; i < to; i++) { var row1 = trs.find('>td:nth-child(' + i + '),>th:nth-child(' + i + ')'); var row2 = trs.find('>td:nth-child(' + (i + 1) + '),>th:nth-child(' + (i + 1) + ')') for (var j = 0; j < row1.length; j++) { this._swapNodes(row1[j], row2[j]); } } } else { for (var i = from; i > to; i--) { var row1 = trs.find('>td:nth-child(' + i + '),>th:nth-child(' + i + ')'); var row2 = trs.find('>td:nth-child(' + (i - 1) + '),>th:nth-child(' + (i - 1) + ')') for (var j = 0; j < row1.length; j++) { this._swapNodes(row1[j], row2[j]); } } } }, _rearrangeTableBackroundProcessing: function () { var _this = this; return function () { _this._bubbleCols(); }; }, _sortStart: function () { var _this = this; return function (e, ui) { _this._startIndex = ui.item.prevAll().length + 1; } }, _sortUpdate: function () { var _this = this; return function (e, ui) { _this._endIndex = ui.item.prevAll().length + 1; if (_this._startIndex != _this._endIndex) { // do reorganisation asynchronous // for chrome a little bit more than 1 ms because we want to force a rerender setTimeout(_this._rearrangeTableBackroundProcessing(), 50); } } }, _sortStop: function () { var _this = this; return function (e, ui) { if (_this._sortCells) { _this._sortCells.show(); } } }, _create: function () { var el = this.element; if (el.is('table')) { this._table = el; // reset element to first tr el = this.element = el.find('tr:first'); } else { // find parent table this._table = el.parents('table:first'); } this._startIndex = 0; this._endIndex = 0; // bind to start and update el.bind('sorttablestart', this._sortStart()); el.bind('sorttableupdate', this._sortUpdate()); el.bind('sorttablestop', this._sortStop()); $.ui.sortable.prototype._create.apply(this); }, destroy: function () { $.Widget.prototype.destroy.apply(this, arguments); // default destroy // now do other stuff particular to this widget } }); })(jQuery);