diff --git a/css/dragtable.mod.css b/css/dragtable.mod.css new file mode 100644 index 00000000..eea04bbf --- /dev/null +++ b/css/dragtable.mod.css @@ -0,0 +1,40 @@ +/* + * dragtable + * + * @Version 2.0.14 + * + * default css + * + */ +/*##### the dragtable stuff #####*/ +.dragtable-sortable { + list-style-type: none; margin: 0; padding: 0; -moz-user-select: none; +} +.dragtable-sortable li { + margin: 0; padding: 0; float: left; font-size: 1em; background: white; +} + +.dragtable-sortable th, .dragtable-sortable td{ + border-left: 0px; +} + +.dragtable-sortable li:first-child th, .dragtable-sortable li:first-child td { + border-left: 1px solid #CCC; +} + +.ui-sortable-helper { + opacity: 0.7;filter: alpha(opacity=70); +} +.ui-sortable-placeholder { + -moz-box-shadow: 4px 5px 4px #C6C6C6 inset; + -webkit-box-shadow: 4px 5px 4px #C6C6C6 inset; + box-shadow: 4px 5px 4px #C6C6C6 inset; + border-bottom: 1px solid #CCCCCC; + border-top: 1px solid #CCCCCC; + visibility: visible !important; + background: #EFEFEF !important; + visibility: visible !important; +} +.ui-sortable-placeholder * { + opacity: 0.0; visibility: hidden; +} diff --git a/js/extras/jquery.dragtable.mod.js b/js/extras/jquery.dragtable.mod.js new file mode 100644 index 00000000..32a4f23d --- /dev/null +++ b/js/extras/jquery.dragtable.mod.js @@ -0,0 +1,403 @@ +/*! + * dragtable + * + * @Version 2.0.14 + * + * Copyright (c) 2010-2013, Andres akottr@gmail.com + * 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. + */ + +/* TOKNOW: + * For IE7 you need this css rule: + * table { + * border-collapse: collapse; + * } + * Or take a clean reset.css (see http://meyerweb.com/eric/tools/css/reset/) + */ + +/* TODO: investigate + * Does not work properly with css rule: + * html { + * overflow: -moz-scrollbars-vertical; + * } + * Workaround: + * Fixing Firefox issues by scrolling down the page + * http://stackoverflow.com/questions/2451528/jquery-ui-sortable-scroll-helper-element-offset-firefox-issue + * + * var start = $.noop; + * var beforeStop = $.noop; + * if($.browser.mozilla) { + * var start = function (event, ui) { + * if( ui.helper !== undefined ) + * ui.helper.css('position','absolute').css('margin-top', $(window).scrollTop() ); + * } + * var beforeStop = function (event, ui) { + * if( ui.offset !== undefined ) + * ui.helper.css('margin-top', 0); + * } + * } + * + * and pass this as start and stop function to the sortable initialisation + * start: start, + * beforeStop: beforeStop + */ +/* + * Special thx to all pull requests comitters + */ + +(function($) { + $.widget("akottr.dragtable", { + options: { + revert: false, // smooth revert + dragHandle: '.table-handle', // handle for moving cols, if not exists the whole 'th' is the handle + maxMovingRows: 40, // 1 -> only header. 40 row should be enough, the rest is usually not in the viewport + excludeFooter: false, // excludes the footer row(s) while moving other columns. Make sense if there is a footer with a colspan. */ + onlyHeaderThreshold: 100, // TODO: not implemented yet, switch automatically between entire col moving / only header moving + dragaccept: null, // draggable cols -> default all + persistState: null, // url or function -> plug in your custom persistState function right here. function call is persistState(originalTable) + restoreState: null, // JSON-Object or function: some kind of experimental aka Quick-Hack TODO: do it better + exact: true, // removes pixels, so that the overlay table width fits exactly the original table width + clickDelay: 10, // ms to wait before rendering sortable list and delegating click event + containment: null, // @see http://api.jqueryui.com/sortable/#option-containment, use it if you want to move in 2 dimesnions (together with axis: null) + cursor: 'move', // @see http://api.jqueryui.com/sortable/#option-cursor + cursorAt: false, // @see http://api.jqueryui.com/sortable/#option-cursorAt + distance: 0, // @see http://api.jqueryui.com/sortable/#option-distance, for immediate feedback use "0" + tolerance: 'pointer', // @see http://api.jqueryui.com/sortable/#option-tolerance + axis: 'x', // @see http://api.jqueryui.com/sortable/#option-axis, Only vertical moving is allowed. Use 'x' or null. Use this in conjunction with the 'containment' setting + beforeStart: $.noop, // returning FALSE will stop the execution chain. + beforeMoving: $.noop, + beforeReorganize: $.noop, + beforeStop: $.noop + }, + originalTable: { + el: null, + selectedHandle: null, + sortOrder: null, + startIndex: 0, + endIndex: 0 + }, + sortableTable: { + el: $(), + selectedHandle: $(), + movingRow: $() + }, + persistState: function() { + var _this = this; + this.originalTable.el.find('th').each(function(i) { + if (this.id !== '') { + _this.originalTable.sortOrder[this.id] = i; + } + }); + $.ajax({ + url: this.options.persistState, + data: this.originalTable.sortOrder + }); + }, + /* + * persistObj looks like + * {'id1':'2','id3':'3','id2':'1'} + * table looks like + * | id2 | id1 | id3 | + */ + _restoreState: function(persistObj) { + for (var n in persistObj) { + this.originalTable.startIndex = $('#' + n).closest('th').prevAll().size() + 1; + this.originalTable.endIndex = parseInt(persistObj[n], 10) + 1; + this._bubbleCols(); + } + }, + // bubble the moved col left or right + _bubbleCols: function() { + var i, j, col1, col2; + var from = this.originalTable.startIndex; + var to = this.originalTable.endIndex; + /* Find children thead and tbody. + * Only to process the immediate tr-children. Bugfix for inner tables + */ + var thtb = this.originalTable.el.children(); + if (this.options.excludeFooter) { + thtb = thtb.not('tfoot'); + } + if (from < to) { + for (i = from; i < to; i++) { + col1 = thtb.find('> tr > td:nth-child(' + i + ')') + .add(thtb.find('> tr > th:nth-child(' + i + ')')); + col2 = thtb.find('> tr > td:nth-child(' + (i + 1) + ')') + .add(thtb.find('> tr > th:nth-child(' + (i + 1) + ')')); + for (j = 0; j < col1.length; j++) { + swapNodes(col1[j], col2[j]); + } + } + } else { + for (i = from; i > to; i--) { + col1 = thtb.find('> tr > td:nth-child(' + i + ')') + .add(thtb.find('> tr > th:nth-child(' + i + ')')); + col2 = thtb.find('> tr > td:nth-child(' + (i - 1) + ')') + .add(thtb.find('> tr > th:nth-child(' + (i - 1) + ')')); + for (j = 0; j < col1.length; j++) { + swapNodes(col1[j], col2[j]); + } + } + } + }, + _rearrangeTableBackroundProcessing: function() { + var _this = this; + return function() { + _this._bubbleCols(); + _this.options.beforeStop(_this.originalTable); + _this.sortableTable.el.remove(); + restoreTextSelection(); + // persist state if necessary + if (_this.options.persistState !== null) { + $.isFunction(_this.options.persistState) ? _this.options.persistState(_this.originalTable) : _this.persistState(); + } + }; + }, + _rearrangeTable: function() { + var _this = this; + return function() { + // remove handler-class -> handler is now finished + _this.originalTable.selectedHandle.removeClass('dragtable-handle-selected'); + // add disabled class -> reorgorganisation starts soon + _this.sortableTable.el.sortable("disable"); + _this.sortableTable.el.addClass('dragtable-disabled'); + _this.options.beforeReorganize(_this.originalTable, _this.sortableTable); + // do reorganisation asynchronous + // for chrome a little bit more than 1 ms because we want to force a rerender + _this.originalTable.endIndex = _this.sortableTable.movingRow.prevAll().size() + 1; + setTimeout(_this._rearrangeTableBackroundProcessing(), 50); + }; + }, + /* + * Disrupts the table. The original table stays the same. + * But on a layer above the original table we are constructing a list (ul > li) + * each li with a separate table representig a single col of the original table. + */ + _generateSortable: function(e) { + !e.cancelBubble && (e.cancelBubble = true); + var _this = this; + // table attributes + var attrs = this.originalTable.el[0].attributes; + var attrsString = ''; + for (var i = 0; i < attrs.length; i++) { + if (attrs[i].nodeValue && attrs[i].nodeName != 'id' && attrs[i].nodeName != 'width') { + attrsString += attrs[i].nodeName + '="' + attrs[i].nodeValue + '" '; + } + } + + // row attributes + var rowAttrsArr = []; + //compute height, special handling for ie needed :-( + var heightArr = []; + this.originalTable.el.find('tr').slice(0, this.options.maxMovingRows).each(function(i, v) { + // row attributes + var attrs = this.attributes; + var attrsString = ""; + for (var j = 0; j < attrs.length; j++) { + if (attrs[j].nodeValue && attrs[j].nodeName != 'id') { + attrsString += " " + attrs[j].nodeName + '="' + attrs[j].nodeValue + '"'; + } + } + rowAttrsArr.push(attrsString); + heightArr.push($(this).height()); + }); + + // compute width, no special handling for ie needed :-) + var widthArr = []; + // compute total width, needed for not wrapping around after the screen ends (floating) + var totalWidth = 0; + /* Find children thead and tbody. + * Only to process the immediate tr-children. Bugfix for inner tables + */ + var thtb = _this.originalTable.el.children(); + if (this.options.excludeFooter) { + thtb = thtb.not('tfoot'); + } + thtb.find('> tr > th').each(function(i, v) { + var w = $(this).outerWidth(); + widthArr.push(w); + totalWidth += w; + }); + if(_this.options.exact) { + var difference = totalWidth - _this.originalTable.el.outerWidth(); + widthArr[0] -= difference; + } + // one extra px on right and left side + totalWidth += 2 + + var sortableHtml = '