Merge pull request #159 from foodcoop-rostock/improve-delivery-workflow-further-stupid
Move client-side sorting into separate javascript file.
This commit is contained in:
commit
b81472d91b
5 changed files with 205 additions and 88 deletions
|
@ -11,6 +11,7 @@
|
||||||
//= require rails.validations
|
//= require rails.validations
|
||||||
//= require_self
|
//= require_self
|
||||||
//= require ordering
|
//= require ordering
|
||||||
|
//= require stupidtable
|
||||||
|
|
||||||
// allow touch devices to work on click events
|
// allow touch devices to work on click events
|
||||||
// http://stackoverflow.com/a/16221066
|
// http://stackoverflow.com/a/16221066
|
||||||
|
@ -25,34 +26,6 @@ $.fn.extend({
|
||||||
}()),
|
}()),
|
||||||
});
|
});
|
||||||
|
|
||||||
// function for sorting DOM elements
|
|
||||||
$.fn.sorter = (function(){
|
|
||||||
// Thanks to James Padolsey and Avi Deitcher
|
|
||||||
// http://james.padolsey.com/javascript/sorting-elements-with-jquery/#comment-29400
|
|
||||||
var sort = [].sort;
|
|
||||||
|
|
||||||
return function(comparator, getSortable) {
|
|
||||||
getSortable = getSortable || function(){return this;};
|
|
||||||
|
|
||||||
var sorted = sort.call(this, comparator); // sort all elements in memory
|
|
||||||
var prevElmt = null;
|
|
||||||
for(i=sorted.length-1; i>=0; --i) { // loop starting from last
|
|
||||||
var criterionElmt = sorted[i];
|
|
||||||
var curElmt = ( 'function' === typeof getSortable ) ? ( getSortable.call(criterionElmt) ) : ( criterionElmt );
|
|
||||||
var parent = curElmt.parentNode;
|
|
||||||
if (!prevElmt) {
|
|
||||||
parent.appendChild(curElmt); // place last element to the end
|
|
||||||
} else {
|
|
||||||
parent.insertBefore(curElmt, prevElmt); // move each element before the previous one
|
|
||||||
}
|
|
||||||
prevElmt = curElmt;
|
|
||||||
}
|
|
||||||
return sorted;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
// Load following statements, when DOM is ready
|
// Load following statements, when DOM is ready
|
||||||
$(function() {
|
$(function() {
|
||||||
|
|
||||||
|
@ -144,53 +117,13 @@ $(function() {
|
||||||
// Use bootstrap datepicker for dateinput
|
// Use bootstrap datepicker for dateinput
|
||||||
$('.datepicker').datepicker({format: 'yyyy-mm-dd', language: I18n.locale});
|
$('.datepicker').datepicker({format: 'yyyy-mm-dd', language: I18n.locale});
|
||||||
|
|
||||||
// Init table sorting
|
// See stupidtable.js for initialization of local table sorting
|
||||||
var myTriggerers = $('.dom-sort-triggerer');
|
|
||||||
myTriggerers.wrapInner('<a href="#" class="dom-sort-link"></a>');
|
|
||||||
$('.dom-sort-link', myTriggerers).click(function(e) {sortTable(e); e.preventDefault();});
|
|
||||||
// A title attribute ('sort ascending/descending') would be nice here.
|
|
||||||
// However, for a tiny detail like that it is not worth to translate everything:
|
|
||||||
// http://foodsoft.51229.x6.nabble.com/How-to-I18n-content-which-is-dynamically-loaded-via-javascript-assets-td75.html#a76
|
|
||||||
|
|
||||||
// Sort tables with a default sort
|
|
||||||
$('.dom-sort-link', '.default-sort').trigger('click');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// compare two elements interpreted as text
|
// retrigger last local table sorting
|
||||||
function compareText(a, b) {
|
function updateSort(table) {
|
||||||
return $.trim(a.textContent).toLowerCase() < $.trim(b.textContent).toLowerCase() ? -1 : 1;
|
$('.sorting-asc, .sorting-desc', table).toggleClass('.sorting-asc .sorting-desc')
|
||||||
}
|
.removeData('sort-dir').trigger('click'); // CAUTION: removing data field of plugin
|
||||||
|
|
||||||
// wrapper for $.fn.sorter (see above) for sorting tables
|
|
||||||
function sortTable(e) {
|
|
||||||
var sortLink = $(e.currentTarget);
|
|
||||||
|
|
||||||
var sign = ( sortLink.hasClass('sortup') ) ? ( -1 ) : ( 1 );
|
|
||||||
|
|
||||||
var options = sortLink.closest('.dom-sort-triggerer').data();
|
|
||||||
var sortCriterion = options.sortCriterion; // class name of (usually td) elements which define the order
|
|
||||||
var compareFunction = options.compareFunction; // function to compare two element contents for ordering
|
|
||||||
var sortElement = options.sortElement; // name of function which returns the movable element (default: 'thisParent')
|
|
||||||
var myTable = sortLink.closest('table'); // table to sort
|
|
||||||
|
|
||||||
sortElement = ( 'undefined' === typeof sortElement ) ? ( function() {return this.parentNode;} ) : ( window[sortElement] ); // is this dirty?
|
|
||||||
|
|
||||||
$('.' + sortCriterion, myTable).sorter(
|
|
||||||
function(a, b) {
|
|
||||||
return sign*window[compareFunction](a, b); // again dirty?
|
|
||||||
},
|
|
||||||
sortElement
|
|
||||||
);
|
|
||||||
|
|
||||||
$('.dom-sort-link', myTable).removeClass('sortup sortdown');
|
|
||||||
sortLink.addClass(
|
|
||||||
( sign == 1 ) ? ( 'sortup' ) : ( 'sortdown' )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// retrigger last sort function for elements in a context (e.g. after DOM update)
|
|
||||||
function updateSort(context) {
|
|
||||||
$('.sortup, .sortdown', context).toggleClass('sortup sortdown').trigger('click');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// gives the row an yellow background
|
// gives the row an yellow background
|
||||||
|
|
186
app/assets/javascripts/stupidtable.js
Normal file
186
app/assets/javascripts/stupidtable.js
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
// Stupid jQuery table plugin.
|
||||||
|
|
||||||
|
// Call on a table
|
||||||
|
// sortFns: Sort functions for your datatypes.
|
||||||
|
(function($) {
|
||||||
|
|
||||||
|
$.fn.stupidtable = function(sortFns) {
|
||||||
|
return this.each(function() {
|
||||||
|
var $table = $(this);
|
||||||
|
sortFns = sortFns || {};
|
||||||
|
|
||||||
|
// ==================================================== //
|
||||||
|
// Utility functions //
|
||||||
|
// ==================================================== //
|
||||||
|
|
||||||
|
// Merge sort functions with some default sort functions.
|
||||||
|
sortFns = $.extend({}, {
|
||||||
|
"int": function(a, b) {
|
||||||
|
return parseInt(a, 10) - parseInt(b, 10);
|
||||||
|
},
|
||||||
|
"float": function(a, b) {
|
||||||
|
return parseFloat(a) - parseFloat(b);
|
||||||
|
},
|
||||||
|
"string": function(a, b) {
|
||||||
|
if (a < b) return -1;
|
||||||
|
if (a > b) return +1;
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
"string-ins": function(a, b) {
|
||||||
|
a = a.toLowerCase();
|
||||||
|
b = b.toLowerCase();
|
||||||
|
if (a < b) return -1;
|
||||||
|
if (a > b) return +1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}, sortFns);
|
||||||
|
|
||||||
|
// Return the resulting indexes of a sort so we can apply
|
||||||
|
// this result elsewhere. This returns an array of index numbers.
|
||||||
|
// return[0] = x means "arr's 0th element is now at x"
|
||||||
|
var sort_map = function(arr, sort_function, reverse_column) {
|
||||||
|
var map = [];
|
||||||
|
var index = 0;
|
||||||
|
if (reverse_column) {
|
||||||
|
for (var i = arr.length-1; i >= 0; i--) {
|
||||||
|
map.push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var sorted = arr.slice(0).sort(sort_function);
|
||||||
|
for (var i=0; i<arr.length; i++) {
|
||||||
|
index = $.inArray(arr[i], sorted);
|
||||||
|
|
||||||
|
// If this index is already in the map, look for the next index.
|
||||||
|
// This handles the case of duplicate entries.
|
||||||
|
while ($.inArray(index, map) != -1) {
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
map.push(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Apply a sort map to the array.
|
||||||
|
var apply_sort_map = function(arr, map) {
|
||||||
|
var clone = arr.slice(0),
|
||||||
|
newIndex = 0;
|
||||||
|
for (var i=0; i<map.length; i++) {
|
||||||
|
newIndex = map[i];
|
||||||
|
clone[newIndex] = arr[i];
|
||||||
|
}
|
||||||
|
return clone;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================================================== //
|
||||||
|
// Begin execution! //
|
||||||
|
// ==================================================== //
|
||||||
|
|
||||||
|
// Do sorting when THs are clicked
|
||||||
|
$table.on("click", "th", function() {
|
||||||
|
var trs = $table.children("tbody").children("tr");
|
||||||
|
var $this = $(this);
|
||||||
|
var th_index = 0;
|
||||||
|
var dir = $.fn.stupidtable.dir;
|
||||||
|
|
||||||
|
$table.find("th").slice(0, $this.index()).each(function() {
|
||||||
|
var cols = $(this).attr("colspan") || 1;
|
||||||
|
th_index += parseInt(cols,10);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Determine (and/or reverse) sorting direction, default `asc`
|
||||||
|
var sort_dir = $this.data("sort-dir") === dir.ASC ? dir.DESC : dir.ASC;
|
||||||
|
|
||||||
|
// Choose appropriate sorting function. If we're sorting descending, check
|
||||||
|
// for a `data-sort-desc` attribute.
|
||||||
|
if ( sort_dir == dir.DESC )
|
||||||
|
var type = $this.data("sort-desc") || $this.data("sort") || null;
|
||||||
|
else
|
||||||
|
var type = $this.data("sort") || null;
|
||||||
|
|
||||||
|
// Prevent sorting if no type defined
|
||||||
|
if (type === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger `beforetablesort` event that calling scripts can hook into;
|
||||||
|
// pass parameters for sorted column index and sorting direction
|
||||||
|
$table.trigger("beforetablesort", {column: th_index, direction: sort_dir});
|
||||||
|
// More reliable method of forcing a redraw
|
||||||
|
$table.css("display");
|
||||||
|
|
||||||
|
// Run sorting asynchronously on a timout to force browser redraw after
|
||||||
|
// `beforetablesort` callback. Also avoids locking up the browser too much.
|
||||||
|
setTimeout(function() {
|
||||||
|
// Gather the elements for this column
|
||||||
|
var column = [];
|
||||||
|
var sortMethod = sortFns[type];
|
||||||
|
|
||||||
|
// Push either the value of the `data-order-by` attribute if specified
|
||||||
|
// or just the text() value in this column to column[] for comparison.
|
||||||
|
trs.each(function(index,tr) {
|
||||||
|
var $e = $(tr).children().eq(th_index);
|
||||||
|
var sort_val = $e.data("sort-value");
|
||||||
|
var order_by = typeof(sort_val) !== "undefined" ? sort_val : $e.text();
|
||||||
|
column.push(order_by);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create the sort map. This column having a sort-dir implies it was
|
||||||
|
// the last column sorted. As long as no data-sort-desc is specified,
|
||||||
|
// we're free to just reverse the column.
|
||||||
|
var reverse_column = !!$this.data("sort-dir") && !$this.data("sort-desc");
|
||||||
|
var theMap = sort_map(column, sortMethod, reverse_column);
|
||||||
|
|
||||||
|
// Reset siblings
|
||||||
|
$table.find("th").data("sort-dir", null).removeClass("sorting-desc sorting-asc");
|
||||||
|
$this.data("sort-dir", sort_dir).addClass("sorting-"+sort_dir);
|
||||||
|
|
||||||
|
// Replace the content of tbody with the sortedTRs. Strangely (and
|
||||||
|
// conveniently!) enough, .append accomplishes this for us.
|
||||||
|
var sortedTRs = $(apply_sort_map(trs, theMap));
|
||||||
|
$table.children("tbody").append(sortedTRs);
|
||||||
|
|
||||||
|
// Trigger `aftertablesort` event. Similar to `beforetablesort`
|
||||||
|
$table.trigger("aftertablesort", {column: th_index, direction: sort_dir});
|
||||||
|
// More reliable method of forcing a redraw
|
||||||
|
$table.css("display");
|
||||||
|
}, 10);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Enum containing sorting directions
|
||||||
|
$.fn.stupidtable.dir = {ASC: "asc", DESC: "desc"};
|
||||||
|
|
||||||
|
})(jQuery);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// own additions for automatic initialization of table sorting
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
$(function() {
|
||||||
|
var stupidtables = $('table.stupidtable');
|
||||||
|
|
||||||
|
if(stupidtables.length) {
|
||||||
|
// Add pseudo links just for matching foodsoft style
|
||||||
|
$('th[data-sort]', stupidtables).wrapInner('<a href="#" class="stupidlink"></a>');
|
||||||
|
$('.stupidlink', stupidtables).on('click', function(e) {e.preventDefault();});
|
||||||
|
|
||||||
|
// Init stupidtable sorting
|
||||||
|
stupidtables.stupidtable();
|
||||||
|
|
||||||
|
// Update class of sort link after sort to match foodsoft style
|
||||||
|
stupidtables.on('aftertablesort', function(e, data) {
|
||||||
|
// Ignore data and use the updated classes in DOM
|
||||||
|
var stupidthead = $('thead', this);
|
||||||
|
$('a.stupidlink', stupidthead).removeClass('sortup sortdown');
|
||||||
|
$('th.sorting-asc a.stupidlink', stupidthead).addClass('sortup');
|
||||||
|
$('th.sorting-desc a.stupidlink', stupidthead).addClass('sortdown');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sort tables with a default sort
|
||||||
|
$('.default-sort', stupidtables).trigger('click');
|
||||||
|
}
|
||||||
|
});
|
|
@ -94,11 +94,10 @@
|
||||||
= f.association :supplier, :as => :hidden
|
= f.association :supplier, :as => :hidden
|
||||||
|
|
||||||
%h2= t '.title_select_stock_articles'
|
%h2= t '.title_select_stock_articles'
|
||||||
%table.table.table-hover#stock_articles_for_adding
|
%table#stock_articles_for_adding.table.table-hover.stupidtable
|
||||||
%thead
|
%thead
|
||||||
%tr
|
%tr
|
||||||
%th.dom-sort-triggerer.default-sort{:data => {'compare-function' => 'compareText', 'sort-criterion' => 'sort-by-name'}}
|
%th.default-sort{:data => {:sort => 'string'}}= t '.article'
|
||||||
= t '.article'
|
|
||||||
%th= t '.price'
|
%th= t '.price'
|
||||||
%th= t '.unit'
|
%th= t '.unit'
|
||||||
%th= t '.category'
|
%th= t '.category'
|
||||||
|
@ -115,11 +114,10 @@
|
||||||
= render :partial => 'stock_article_for_adding', :locals => {:article => article}
|
= render :partial => 'stock_article_for_adding', :locals => {:article => article}
|
||||||
|
|
||||||
%h2= t '.title_fill_quantities'
|
%h2= t '.title_fill_quantities'
|
||||||
%table.table#stock_changes
|
%table.table#stock_changes.stupidtable
|
||||||
%thead
|
%thead
|
||||||
%tr
|
%tr
|
||||||
%th.dom-sort-triggerer.default-sort{:data => {'compare-function' => 'compareText', 'sort-criterion' => 'sort-by-name'}}
|
%th.default-sort{:data => {:sort => 'string'}}= t '.article'
|
||||||
= t '.article'
|
|
||||||
%th= t '.price'
|
%th= t '.price'
|
||||||
%th= t '.unit'
|
%th= t '.unit'
|
||||||
%th= t '.quantity'
|
%th= t '.quantity'
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
- css_class = ( @delivery and @delivery.includes_article? article ) ? ( 'unavailable' ) : ( false )
|
- css_class = ( @delivery and @delivery.includes_article? article ) ? ( 'unavailable' ) : ( false )
|
||||||
%tr{:id => "stock_article_#{article.id}", :class => css_class}
|
%tr{:id => "stock_article_#{article.id}", :class => css_class}
|
||||||
%td.sort-by-name= article.name
|
%td= article.name
|
||||||
%td{:data => {:toggle => :tooltip, :title => render(:partial => 'shared/article_price_info', :locals => {:article => article})}}= number_to_currency article.price
|
%td{:data => {:toggle => :tooltip, :title => render(:partial => 'shared/article_price_info', :locals => {:article => article})}}= number_to_currency article.price
|
||||||
%td= article.unit
|
%td= article.unit
|
||||||
%td.sort-by-category= article.article_category.name
|
%td= article.article_category.name
|
||||||
%td
|
%td
|
||||||
= link_to t('.action_edit'), edit_stock_article_supplier_deliveries_path(@supplier, :stock_article_id => article.id), remote: true, class: 'btn btn-mini'
|
= link_to t('.action_edit'), edit_stock_article_supplier_deliveries_path(@supplier, :stock_article_id => article.id), remote: true, class: 'btn btn-mini'
|
||||||
= link_to t('.action_other_price'), copy_stock_article_supplier_deliveries_path(@supplier, :old_stock_article_id => article.id), remote: true, class: 'btn btn-mini'
|
= link_to t('.action_other_price'), copy_stock_article_supplier_deliveries_path(@supplier, :old_stock_article_id => article.id), remote: true, class: 'btn btn-mini'
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
- stock_change = f.object
|
- stock_change = f.object
|
||||||
- stock_article = stock_change.stock_article
|
- stock_article = stock_change.stock_article
|
||||||
%tr{:id => "stock_change_stock_article_#{stock_article.id}", :data => {:id => stock_article.id}}
|
%tr{:id => "stock_change_stock_article_#{stock_article.id}", :data => {:id => stock_article.id}}
|
||||||
%td.sort-by-name
|
%td
|
||||||
%span.stock_article_name= stock_change.stock_article.name
|
%span.stock_article_name= stock_change.stock_article.name
|
||||||
= f.association :stock_article, :as => :hidden
|
= f.association :stock_article, :as => :hidden
|
||||||
%td.price{:data => {:toggle => :tooltip, :title => render(:partial => 'shared/article_price_info', :locals => {:article => stock_article})}}= number_to_currency stock_article.price
|
%td.price{:data => {:toggle => :tooltip, :title => render(:partial => 'shared/article_price_info', :locals => {:article => stock_article})}}= number_to_currency stock_article.price
|
||||||
|
|
Loading…
Reference in a new issue