├── .gitignore ├── .travis.yml ├── Contents └── bootstrap-sortable.css ├── README.md ├── Scripts ├── bootstrap-sortable.js └── moment.min.js ├── Tests ├── avoidFlippingOrder.html ├── avoidFlippingOrder.js ├── disableSortingOnRows.html ├── disableSortingOnRows.js ├── emptyRowsSorting.html ├── emptyRowsSorting.js ├── eventsRaising.html ├── eventsRaising.js ├── initializeSortingValues.html ├── initializeSortingValues.js ├── rowSpan.html ├── rowSpan.js ├── tableInsideTable.html └── tableInsideTable.js ├── bower.json ├── karma.conf.js ├── license.md └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | npm-debug.log 3 | bower_components/* -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "4.2" 4 | -------------------------------------------------------------------------------- /Contents/bootstrap-sortable.css: -------------------------------------------------------------------------------- 1 | /** 2 | * adding sorting ability to HTML tables with Bootstrap styling 3 | * @summary HTML tables sorting ability 4 | * @version 2.0.0 5 | * @requires tinysort, moment.js, jQuery 6 | * @license MIT 7 | * @author Matus Brlit (drvic10k) 8 | * @copyright Matus Brlit (drvic10k), bootstrap-sortable contributors 9 | */ 10 | 11 | table.sortable span.sign { 12 | display: block; 13 | position: absolute; 14 | top: 50%; 15 | right: 5px; 16 | font-size: 12px; 17 | margin-top: -10px; 18 | color: #bfbfc1; 19 | } 20 | 21 | table.sortable th:after { 22 | display: block; 23 | position: absolute; 24 | top: 50%; 25 | right: 5px; 26 | font-size: 12px; 27 | margin-top: -10px; 28 | color: #bfbfc1; 29 | } 30 | 31 | table.sortable th.arrow:after { 32 | content: ''; 33 | } 34 | 35 | table.sortable span.arrow, span.reversed, th.arrow.down:after, th.reversedarrow.down:after, th.arrow.up:after, th.reversedarrow.up:after { 36 | border-style: solid; 37 | border-width: 5px; 38 | font-size: 0; 39 | border-color: #ccc transparent transparent transparent; 40 | line-height: 0; 41 | height: 0; 42 | width: 0; 43 | margin-top: -2px; 44 | } 45 | 46 | table.sortable span.arrow.up, th.arrow.up:after { 47 | border-color: transparent transparent #ccc transparent; 48 | margin-top: -7px; 49 | } 50 | 51 | table.sortable span.reversed, th.reversedarrow.down:after { 52 | border-color: transparent transparent #ccc transparent; 53 | margin-top: -7px; 54 | } 55 | 56 | table.sortable span.reversed.up, th.reversedarrow.up:after { 57 | border-color: #ccc transparent transparent transparent; 58 | margin-top: -2px; 59 | } 60 | 61 | table.sortable span.az:before, th.az.down:after { 62 | content: "a .. z"; 63 | } 64 | 65 | table.sortable span.az.up:before, th.az.up:after { 66 | content: "z .. a"; 67 | } 68 | 69 | table.sortable th.az.nosort:after, th.AZ.nosort:after, th._19.nosort:after, th.month.nosort:after { 70 | content: ".."; 71 | } 72 | 73 | table.sortable span.AZ:before, th.AZ.down:after { 74 | content: "A .. Z"; 75 | } 76 | 77 | table.sortable span.AZ.up:before, th.AZ.up:after { 78 | content: "Z .. A"; 79 | } 80 | 81 | table.sortable span._19:before, th._19.down:after { 82 | content: "1 .. 9"; 83 | } 84 | 85 | table.sortable span._19.up:before, th._19.up:after { 86 | content: "9 .. 1"; 87 | } 88 | 89 | table.sortable span.month:before, th.month.down:after { 90 | content: "jan .. dec"; 91 | } 92 | 93 | table.sortable span.month.up:before, th.month.up:after { 94 | content: "dec .. jan"; 95 | } 96 | 97 | table.sortable>thead th:not([data-defaultsort=disabled]) { 98 | cursor: pointer; 99 | position: relative; 100 | top: 0; 101 | left: 0; 102 | } 103 | 104 | table.sortable>thead th:hover:not([data-defaultsort=disabled]) { 105 | background: #efefef; 106 | } 107 | 108 | table.sortable>thead th div.mozilla { 109 | position: relative; 110 | } 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/drvic10k/bootstrap-sortable.svg?branch=master)](https://travis-ci.org/drvic10k/bootstrap-sortable) 2 | 3 | If you like the plugin, you can send tips here 4 | 5 | [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=M8EBWZL2QHN7Y¤cy_code=EUR&source=url) 6 | 7 | bootstrap-sortable 8 | ================== 9 | adding sorting ability to HTML tables with Bootstrap styling 10 | Current version: 1.12.0 / 2.1.0 11 | 12 | Sorting provided by [TinySort](https://github.com/Sjeiti/TinySort). 13 | Date parsing provided by [moment.js](https://github.com/timrwood/moment/). 14 | 15 | Available for download with NuGet, search for `bootstrap-sortable`. 16 | Working [demo](http://drvic10k.github.io/bootstrap-sortable/). 17 | 18 | For compatibility issues with older browsers and possible workarounds, please look into the [issues list](https://github.com/drvic10k/bootstrap-sortable/issues?labels=compatibility&page=1&state=closed). 19 | 20 | In version 2, the parameters to `$.bootstrapSortable` function were changed to single Object. For basic compatibility, you can still use single Boolean parameter (applyLast), other parameters are passed as properties of the Object. 21 | 22 | #### Dependencies: 23 | You should add the provided "moment.js" library, or get yourself a copy from http://momentjs.com. 24 | 25 | #### Basic usage: 26 | 27 | Add references to bootstrap-sortable.css and bootstrap-sortable.js to your page. Add class "sortable" to your bootstrap table. 28 | HTML table has to be properly formated, using ``, `` and `` elements. 29 | 30 | You can disable sorting for a column by using `data-defaultsort='disabled'` attribute in the `` element. 31 | 32 | You can disable sorting for a row by using `data-disablesort='true'` attribute in the `` element. 33 | 34 | When you add table rows or whole table from client side, use `$.bootstrapSortable({ applyLast: true })` function to add sortability to parts/tables that were not present at document.ready. 35 | Use optional paramater `applyLast=true` if you want to preserve the last used sorting. 36 | 37 | After sorting a column, the table triggers an event `sorted`. 38 | 39 | #### Cell with `colspan` and multiple header rows: 40 | When you have multiple header rows, all header cells in the same column can be used to sort that column. 41 | 42 | Cells with `colspan` can also be used for sorting. When not specified, the first column of the colspan will be used for sorting. You can override this by using `data-mainsort` attribute. (Use zero-based index as the value, `data-mainsort='1'` will sort the second column of the span.) 43 | 44 | If this cell is in the last row of the header, the sorting will be done according to this cell. If there is another row below, the cell in this row will be used. (i.e. the sorting sign, default-sort setting...) 45 | 46 | ##### !BREAKING CHANGE! 47 | This changes the previous behaviour, where it only worked if the `colspan` cell was not in the last row and the `mainsort` had to be set on the cell in the next row. Now the `mainsort` is set on the `colspan` cell. 48 | 49 | #### Sorting direction signs: 50 | You can choose the sign that show the sort direcion. Default is the arrow pointing towards the higher value. 51 | 52 | This proved to be counterintuitive for some, so you can change it to opposite using the second parameter: `$.bootstrapSortable({ sign: 'reversed' })`. 53 | 54 | Other possible values are `'az'`, `'AZ'`, `'_19'`, `'month'`. (See [demo](http://drvic10k.github.io/bootstrap-sortable/index.html) to see how it looks.) 55 | 56 | You can set individual signs for each column using `data-defaultsign` attribute in the `` element. 57 | 58 | ##### Alternative way of styling the sorting signs: 59 | Set `data-defaultsign='nospan'` and set a `class` on `` elements. Then the sorting signs will be shown using `:after` pseudoelement on ``. This can be seen on the first column in the demo page. 60 | For this purpose, sorted column headers have classes `up`, `down` and `nosort` respectively and all previous styles are supported. 61 | You can also do your own styling in `css`. 62 | 63 | #### Optional attributes: 64 | 65 | You can preset one column to be sorted when table is loaded using `data-defaultsort` attribute: 66 | ```html 67 | Column 1 68 | Column 2 69 | Column 3 70 | ``` 71 | 72 | To change the initial direction when sorting a column for the first time, use the `data-firstsort` attribute: 73 | ```html 74 | Column 1 75 | Column 2 76 | Column 3 77 | ``` 78 | 79 | You can change the value that is used for sorting for each `` using `data-value` attribute: 80 | ```html 81 | 5,45 82 | ``` 83 | 84 | You can sort dates, even if they are in different formats. Specify date formats according to moment.js documentation. This only works if you include any version of Moment.js. If Moment.js is not found it will fallback to the usual sorting. 85 | ```html 86 | 2013-06-24 87 | 26 June 2013 88 | ``` 89 | -------------------------------------------------------------------------------- /Scripts/bootstrap-sortable.js: -------------------------------------------------------------------------------- 1 | /** 2 | * adding sorting ability to HTML tables with Bootstrap styling 3 | * @summary HTML tables sorting ability 4 | * @version 2.0.1 5 | * @requires tinysort, moment.js, jQuery 6 | * @license MIT 7 | * @author Matus Brlit (drvic10k) 8 | * @copyright Matus Brlit (drvic10k), bootstrap-sortable contributors 9 | */ 10 | 11 | /** 12 | * TinySort is a small script that sorts HTML elements. It sorts by text- or attribute value, or by that of one of it's children. 13 | * @summary A nodeElement sorting script. 14 | * @version 2.3.6 15 | * @license MIT 16 | * @author Ron Valstar 17 | * @copyright Ron Valstar 18 | * @namespace tinysort 19 | */ 20 | !function (e, t) { "use strict"; function r() { return t } "function" == typeof define && define.amd ? define("tinysort", r) : e.tinysort = t }(this, function () { "use strict"; function e(e, n) { function s() { 0 === arguments.length ? v({}) : t(arguments, function (e) { v(x(e) ? { selector: e } : e) }), d = $.length } function v(e) { var t = !!e.selector, n = t && ":" === e.selector[0], o = r(e || {}, m); $.push(r({ hasSelector: t, hasAttr: !(o.attr === l || "" === o.attr), hasData: o.data !== l, hasFilter: n, sortReturnNumber: "asc" === o.order ? 1 : -1 }, o)) } function S() { t(e, function (e, t) { M ? M !== e.parentNode && (k = !1) : M = e.parentNode; var r = $[0], n = r.hasFilter, o = r.selector, a = !o || n && e.matchesSelector(o) || o && e.querySelector(o), l = a ? R : V, s = { elm: e, pos: t, posn: l.length }; B.push(s), l.push(s) }), D = R.slice(0) } function y(e, t, r) { for (var n = r(e.toString()), o = r(t.toString()), a = 0; n[a] && o[a]; a++) if (n[a] !== o[a]) { var l = Number(n[a]), s = Number(o[a]); return l == n[a] && s == o[a] ? l - s : n[a] > o[a] ? 1 : -1 } return n.length - o.length } function N(e) { for (var t, r, n = [], o = 0, a = -1, l = 0; t = (r = e.charAt(o++)).charCodeAt(0) ;) { var s = 46 == t || t >= 48 && 57 >= t; s !== l && (n[++a] = "", l = s), n[a] += r } return n } function C(e, r) { var n = 0; for (0 !== p && (p = 0) ; 0 === n && d > p;) { var l = $[p], s = l.ignoreDashes ? f : u; if (t(h, function (e) { var t = e.prepare; t && t(l) }), l.sortFunction) n = l.sortFunction(e, r); else if ("rand" == l.order) n = Math.random() < .5 ? 1 : -1; else { var c = a, g = w(e, l), m = w(r, l), v = "" === g || g === o, S = "" === m || m === o; if (g === m) n = 0; else if (l.emptyEnd && (v || S)) n = v && S ? 0 : v ? 1 : -1; else { if (!l.forceStrings) { var C = x(g) ? g && g.match(s) : a, b = x(m) ? m && m.match(s) : a; if (C && b) { var A = g.substr(0, g.length - C[0].length), F = m.substr(0, m.length - b[0].length); A == F && (c = !a, g = i(C[0]), m = i(b[0])) } } n = g === o || m === o ? 0 : l.natural && (isNaN(g) || isNaN(m)) ? y(g, m, N) : m > g ? -1 : g > m ? 1 : 0 } } t(h, function (e) { var t = e.sort; t && (n = t(l, c, g, m, n)) }), n *= l.sortReturnNumber, 0 === n && p++ } return 0 === n && (n = e.pos > r.pos ? 1 : -1), n } function b() { var e = R.length === B.length; if (k && e) O ? R.forEach(function (e, t) { e.elm.style.order = t }) : M ? M.appendChild(A()) : console.warn("parentNode has been removed"); else { var t = $[0], r = t.place, n = "org" === r, o = "start" === r, a = "end" === r, l = "first" === r, s = "last" === r; if (n) R.forEach(F), R.forEach(function (e, t) { E(D[t], e.elm) }); else if (o || a) { var c = D[o ? 0 : D.length - 1], i = c && c.elm.parentNode, u = i && (o && i.firstChild || i.lastChild); u && (u !== c.elm && (c = { elm: u }), F(c), a && i.appendChild(c.ghost), E(c, A())) } else if (l || s) { var f = D[l ? 0 : D.length - 1]; E(F(f), A()) } } } function A() { return R.forEach(function (e) { q.appendChild(e.elm) }), q } function F(e) { var t = e.elm, r = c.createElement("div"); return e.ghost = r, t.parentNode.insertBefore(r, t), e } function E(e, t) { var r = e.ghost, n = r.parentNode; n.insertBefore(t, r), n.removeChild(r), delete e.ghost } function w(e, t) { var r, n = e.elm; return t.selector && (t.hasFilter ? n.matchesSelector(t.selector) || (n = l) : n = n.querySelector(t.selector)), t.hasAttr ? r = n.getAttribute(t.attr) : t.useVal ? r = n.value || n.getAttribute("value") : t.hasData ? r = n.getAttribute("data-" + t.data) : n && (r = n.textContent), x(r) && (t.cases || (r = r.toLowerCase()), r = r.replace(/\s+/g, " ")), null === r && (r = g), r } function x(e) { return "string" == typeof e } x(e) && (e = c.querySelectorAll(e)), 0 === e.length && console.warn("No elements to sort"); var D, M, q = c.createDocumentFragment(), B = [], R = [], V = [], $ = [], k = !0, z = e.length && e[0].parentNode, L = z.rootNode !== document, O = e.length && (n === o || n.useFlex !== !1) && !L && -1 !== getComputedStyle(z, null).display.indexOf("flex"); return s.apply(l, Array.prototype.slice.call(arguments, 1)), S(), R.sort(C), b(), R.map(function (e) { return e.elm }) } function t(e, t) { for (var r, n = e.length, o = n; o--;) r = n - o - 1, t(e[r], r) } function r(e, t, r) { for (var n in t) (r || e[n] === o) && (e[n] = t[n]); return e } function n(e, t, r) { h.push({ prepare: e, sort: t, sortBy: r }) } var o, a = !1, l = null, s = window, c = s.document, i = parseFloat, u = /(-?\d+\.?\d*)\s*$/g, f = /(\d+\.?\d*)\s*$/g, h = [], d = 0, p = 0, g = String.fromCharCode(4095), m = { selector: l, order: "asc", attr: l, data: l, useVal: a, place: "org", returns: a, cases: a, natural: a, forceStrings: a, ignoreDashes: a, sortFunction: l, useFlex: a, emptyEnd: a }; return s.Element && function (e) { e.matchesSelector = e.matchesSelector || e.mozMatchesSelector || e.msMatchesSelector || e.oMatchesSelector || e.webkitMatchesSelector || function (e) { for (var t = this, r = (t.parentNode || t.document).querySelectorAll(e), n = -1; r[++n] && r[n] != t;); return !!r[n] } }(Element.prototype), r(n, { loop: t }), r(e, { plugin: n, defaults: m }) }()); 21 | 22 | (function (global, factory) { 23 | if (typeof define === 'function' && define.amd) { 24 | define(['jquery', 'tinysort', 'moment'], factory); 25 | } else { 26 | factory(global.jQuery, global.tinysort, global.moment || undefined); 27 | } 28 | })(this, function ($, tinysort, moment) { 29 | 30 | var $document = $(document), 31 | signClass, 32 | sortEngine, 33 | emptyEnd; 34 | 35 | $.bootstrapSortable = function (options) { 36 | if (options == undefined) { 37 | initialize({}); 38 | } 39 | else if (options.constructor === Boolean) { 40 | initialize({ applyLast: options }); 41 | } 42 | else if (options.sortingHeader !== undefined) { 43 | sortByColumn(options.sortingHeader); 44 | } 45 | else { 46 | initialize(options); 47 | } 48 | }; 49 | 50 | function initialize(options) { 51 | // Check if moment.js is available 52 | var momentJsAvailable = (typeof moment !== 'undefined'); 53 | 54 | // Set class based on sign parameter 55 | signClass = !options.sign ? "arrow" : options.sign; 56 | 57 | // Set sorting algorithm 58 | if (options.customSort == 'default') 59 | options.customSort = defaultSortEngine; 60 | sortEngine = options.customSort || sortEngine || defaultSortEngine; 61 | 62 | emptyEnd = options.emptyEnd; 63 | 64 | // Set attributes needed for sorting 65 | $('table.sortable').each(function () { 66 | var $this = $(this); 67 | var applyLast = (options.applyLast === true); 68 | $this.find('span.sign').remove(); 69 | 70 | // Add placeholder cells for colspans 71 | $this.find('> thead [colspan]').each(function () { 72 | var colspan = parseFloat($(this).attr('colspan')); 73 | for (var i = 1; i < colspan; i++) { 74 | $(this).after(''); 75 | } 76 | }); 77 | 78 | // Add placeholder cells for rowspans in header 79 | $this.find('> thead [rowspan]').each(function () { 80 | var $cell = $(this); 81 | var rowspan = parseFloat($cell.attr('rowspan')); 82 | for (var i = 1; i < rowspan; i++) { 83 | var parentRow = $cell.parent('tr'); 84 | var nextRow = parentRow.next('tr'); 85 | var index = parentRow.children().index($cell); 86 | nextRow.children().eq(index).before(''); 87 | } 88 | }); 89 | 90 | // Set indexes to header cells 91 | $this.find('> thead tr').each(function (rowIndex) { 92 | $(this).find('th').each(function (columnIndex) { 93 | var $header = $(this); 94 | $header.addClass('nosort').removeClass('up down'); 95 | $header.attr('data-sortcolumn', columnIndex); 96 | $header.attr('data-sortkey', columnIndex + '-' + rowIndex); 97 | }); 98 | }); 99 | 100 | // Cleanup placeholder cells 101 | $this.find('> thead .rowspan-compensate, .colspan-compensate').remove(); 102 | 103 | // Initialize sorting values specified in header 104 | $this.find('th').each(function () { 105 | var $header = $(this); 106 | if ($header.attr('data-dateformat') !== undefined && momentJsAvailable) { 107 | var colNumber = parseFloat($header.attr('data-sortcolumn')); 108 | $this.find('td:nth-child(' + (colNumber + 1) + ')').each(function () { 109 | var $cell = $(this); 110 | $cell.attr('data-value', moment($cell.text(), $header.attr('data-dateformat')).format('YYYY/MM/DD/HH/mm/ss')); 111 | }); 112 | } 113 | else if ($header.attr('data-valueprovider') !== undefined) { 114 | var colNumber = parseFloat($header.attr('data-sortcolumn')); 115 | $this.find('td:nth-child(' + (colNumber + 1) + ')').each(function () { 116 | var $cell = $(this); 117 | $cell.attr('data-value', new RegExp($header.attr('data-valueprovider')).exec($cell.text())[0]); 118 | }); 119 | } 120 | }); 121 | 122 | // Initialize sorting values 123 | $this.find('td').each(function () { 124 | var $cell = $(this); 125 | if ($cell.attr('data-dateformat') !== undefined && momentJsAvailable) { 126 | $cell.attr('data-value', moment($cell.text(), $cell.attr('data-dateformat')).format('YYYY/MM/DD/HH/mm/ss')); 127 | } 128 | else if ($cell.attr('data-valueprovider') !== undefined) { 129 | $cell.attr('data-value', new RegExp($cell.attr('data-valueprovider')).exec($cell.text())[0]); 130 | } 131 | else { 132 | $cell.attr('data-value') === undefined && $cell.attr('data-value', $cell.text()); 133 | } 134 | }); 135 | 136 | var context = lookupSortContext($this), 137 | bsSort = context.bsSort; 138 | 139 | $this.find('> thead th[data-defaultsort!="disabled"]').each(function (index) { 140 | var $header = $(this); 141 | var $sortTable = $header.closest('table.sortable'); 142 | $header.data('sortTable', $sortTable); 143 | var sortKey = $header.attr('data-sortkey'); 144 | var thisLastSort = applyLast ? context.lastSort : -1; 145 | bsSort[sortKey] = applyLast ? bsSort[sortKey] : $header.attr('data-defaultsort'); 146 | if (bsSort[sortKey] !== undefined && (applyLast === (sortKey === thisLastSort))) { 147 | bsSort[sortKey] = bsSort[sortKey] === 'asc' ? 'desc' : 'asc'; 148 | doSort($header, $sortTable); 149 | } 150 | }); 151 | }); 152 | } 153 | 154 | // Clean up placeholder cells for rowspans in body 155 | function removeRowspanPlaceholders(table) { 156 | table.find('> tbody [rowspan-group]').each(function () { 157 | var $this = $(this); 158 | var id = $this.attr('rowspan-group'); 159 | var parentRow = $this.parent('tr'); 160 | var index = parentRow.children().index($this); 161 | 162 | while(true) { 163 | var nextRow = parentRow.next('tr'); 164 | if (!nextRow.is('tr')) 165 | break; 166 | var nextCell = nextRow.children().eq(index); 167 | 168 | if (nextCell.attr('rowspan-group') === id) { 169 | var rowspan = parseFloat($this.attr('rowspan')) || 1; 170 | $this.attr('rowspan', rowspan + 1); 171 | nextCell.remove(); 172 | } else { 173 | break; 174 | } 175 | parentRow = nextRow; 176 | } 177 | }); 178 | } 179 | 180 | // Add placeholder cells for rowspans in body 181 | function addRowspanPlaceholders(table) { 182 | table.find('> tbody [rowspan]').each(function () { 183 | var $cell = $(this); 184 | var rowspan = parseFloat($cell.attr('rowspan')); 185 | $cell.removeAttr('rowspan'); 186 | var rowSpanId = $cell.attr('rowspan-group') || guid(); 187 | $cell.attr('rowspan-group', rowSpanId); 188 | $cell.attr('rowspan-value', rowspan); 189 | var parentRow = $cell.parent('tr'); 190 | var index = parentRow.children().index($cell); 191 | for (var i = 1; i < rowspan; i++) { 192 | var compemnsationCell = $cell.clone(false); 193 | var nextRow = parentRow.next('tr'); 194 | nextRow.children().eq(index).before(compemnsationCell); 195 | parentRow = nextRow; 196 | } 197 | }); 198 | } 199 | 200 | // Add click event to table header 201 | $document.on('click', 'table.sortable>thead th[data-defaultsort!="disabled"]', function (e) { 202 | sortByColumn(this); 203 | }); 204 | 205 | // element is the header of the column to sort (the clicked header) 206 | function sortByColumn(element) { 207 | var $this = $(element), $table = $this.data('sortTable') || $this.closest('table.sortable'); 208 | doSort($this, $table); 209 | } 210 | 211 | // Look up sorting data appropriate for the specified table (jQuery element). 212 | // This allows multiple tables on one page without collisions. 213 | function lookupSortContext($table) { 214 | var context = $table.data("bootstrap-sortable-context"); 215 | if (context === undefined) { 216 | context = { bsSort: [], lastSort: undefined }; 217 | $table.find('> thead th[data-defaultsort!="disabled"]').each(function (index) { 218 | var $this = $(this); 219 | var sortKey = $this.attr('data-sortkey'); 220 | context.bsSort[sortKey] = $this.attr('data-defaultsort'); 221 | if (context.bsSort[sortKey] !== undefined) { 222 | context.lastSort = sortKey; 223 | } 224 | }); 225 | $table.data("bootstrap-sortable-context", context); 226 | } 227 | return context; 228 | } 229 | 230 | function defaultSortEngine(rows, sortingParams) { 231 | tinysort(rows, sortingParams); 232 | } 233 | 234 | // Sorting mechanism separated 235 | function doSort($this, $table) { 236 | $table.trigger('before-sort'); 237 | 238 | addRowspanPlaceholders($table); 239 | 240 | var sortColumn = parseFloat($this.attr('data-sortcolumn')), 241 | context = lookupSortContext($table), 242 | bsSort = context.bsSort; 243 | 244 | var colspan = $this.attr('colspan'); 245 | if (colspan) { 246 | var mainSort = parseFloat($this.data('mainsort')) || 0; 247 | var rowIndex = parseFloat($this.data('sortkey').split('-').pop()); 248 | 249 | // If there is one more row in header, delve deeper 250 | if ($table.find('> thead tr').length - 1 > rowIndex) { 251 | doSort($table.find('[data-sortkey="' + (sortColumn + mainSort) + '-' + (rowIndex + 1) + '"]'), $table); 252 | return; 253 | } 254 | // Otherwise, just adjust the sortColumn 255 | sortColumn = sortColumn + mainSort; 256 | } 257 | 258 | var localSignClass = $this.attr('data-defaultsign') || signClass; 259 | 260 | // update arrow icon 261 | $table.find('> thead th').each(function () { 262 | $(this).removeClass('up').removeClass('down').addClass('nosort'); 263 | }); 264 | 265 | if ($.browser.mozilla) { 266 | var moz_arrow = $table.find('> thead div.mozilla'); 267 | if (moz_arrow !== undefined) { 268 | moz_arrow.find('.sign').remove(); 269 | moz_arrow.parent().html(moz_arrow.html()); 270 | } 271 | $this.wrapInner('
'); 272 | $this.children().eq(0).append(''); 273 | } 274 | else { 275 | $table.find('> thead span.sign').remove(); 276 | $this.append(''); 277 | } 278 | 279 | // sort direction 280 | var sortKey = $this.attr('data-sortkey'); 281 | var initialDirection = $this.attr('data-firstsort') !== 'desc' ? 'desc' : 'asc'; 282 | 283 | var newDirection = (bsSort[sortKey] || initialDirection); 284 | if (context.lastSort === sortKey || bsSort[sortKey] === undefined) { 285 | newDirection = newDirection === 'asc' ? 'desc' : 'asc'; 286 | } 287 | bsSort[sortKey] = newDirection; 288 | context.lastSort = sortKey; 289 | 290 | if (bsSort[sortKey] === 'desc') { 291 | $this.find('span.sign').addClass('up'); 292 | $this.addClass('up').removeClass('down nosort'); 293 | } else { 294 | $this.addClass('down').removeClass('up nosort'); 295 | } 296 | 297 | // remove rows that should not be sorted 298 | var rows = $table.children('tbody').children('tr'); 299 | var fixedRows = []; 300 | $(rows.filter('[data-disablesort="true"]').get().reverse()).each(function (index, fixedRow) { 301 | var $fixedRow = $(fixedRow); 302 | fixedRows.push({ index: rows.index($fixedRow), row: $fixedRow }); 303 | $fixedRow.remove(); 304 | }); 305 | 306 | // sort rows 307 | var rowsToSort = rows.not('[data-disablesort="true"]'); 308 | if (rowsToSort.length != 0) { 309 | var emptySorting = bsSort[sortKey] === 'asc' ? emptyEnd : false; 310 | sortEngine(rowsToSort, { emptyEnd: emptySorting, selector: 'td:nth-child(' + (sortColumn + 1) + ')', order: bsSort[sortKey], data: 'value' }); 311 | } 312 | 313 | // add back the fixed rows 314 | $(fixedRows.reverse()).each(function (index, row) { 315 | if (row.index === 0) { 316 | $table.children('tbody').prepend(row.row); 317 | } else { 318 | $table.children('tbody').children('tr').eq(row.index - 1).after(row.row); 319 | } 320 | }); 321 | 322 | // add class to sorted column cells 323 | $table.find('> tbody > tr > td.sorted,> thead th.sorted').removeClass('sorted'); 324 | rowsToSort.find('td:eq(' + sortColumn + ')').addClass('sorted'); 325 | $this.addClass('sorted'); 326 | 327 | removeRowspanPlaceholders($table); 328 | $table.trigger('sorted'); 329 | } 330 | 331 | // jQuery 1.9 removed this object 332 | if (!$.browser) { 333 | $.browser = { chrome: false, mozilla: false, opera: false, msie: false, safari: false }; 334 | var ua = navigator.userAgent; 335 | $.each($.browser, function (c) { 336 | $.browser[c] = ((new RegExp(c, 'i').test(ua))) ? true : false; 337 | if ($.browser.mozilla && c === 'mozilla') { $.browser.mozilla = ((new RegExp('firefox', 'i').test(ua))) ? true : false; } 338 | if ($.browser.chrome && c === 'safari') { $.browser.safari = false; } 339 | }); 340 | } 341 | 342 | function guid() { 343 | function s4() { 344 | return Math.floor((1 + Math.random()) * 0x10000) 345 | .toString(16) 346 | .substring(1); 347 | } 348 | return s4() + s4() + '-' + s4() + '-' + s4() + '-' + 349 | s4() + '-' + s4() + s4() + s4(); 350 | } 351 | 352 | // Initialise on DOM ready 353 | $($.bootstrapSortable); 354 | 355 | }); 356 | -------------------------------------------------------------------------------- /Scripts/moment.min.js: -------------------------------------------------------------------------------- 1 | //! moment.js 2 | //! version : 2.3.1 3 | //! authors : Tim Wood, Iskren Chernev, Moment.js contributors 4 | //! license : MIT 5 | //! momentjs.com 6 | (function(a){function b(a,b){return function(c){return i(a.call(this,c),b)}}function c(a,b){return function(c){return this.lang().ordinal(a.call(this,c),b)}}function d(){}function e(a){u(a),g(this,a)}function f(a){var b=o(a),c=b.year||0,d=b.month||0,e=b.week||0,f=b.day||0,g=b.hour||0,h=b.minute||0,i=b.second||0,j=b.millisecond||0;this._input=a,this._milliseconds=+j+1e3*i+6e4*h+36e5*g,this._days=+f+7*e,this._months=+d+12*c,this._data={},this._bubble()}function g(a,b){for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return b.hasOwnProperty("toString")&&(a.toString=b.toString),b.hasOwnProperty("valueOf")&&(a.valueOf=b.valueOf),a}function h(a){return 0>a?Math.ceil(a):Math.floor(a)}function i(a,b){for(var c=a+"";c.lengthd;d++)(c&&a[d]!==b[d]||!c&&q(a[d])!==q(b[d]))&&g++;return g+f}function n(a){if(a){var b=a.toLowerCase().replace(/(.)s$/,"$1");a=Jb[a]||Kb[b]||b}return a}function o(a){var b,c,d={};for(c in a)a.hasOwnProperty(c)&&(b=n(c),b&&(d[b]=a[c]));return d}function p(b){var c,d;if(0===b.indexOf("week"))c=7,d="day";else{if(0!==b.indexOf("month"))return;c=12,d="month"}bb[b]=function(e,f){var g,h,i=bb.fn._lang[b],j=[];if("number"==typeof e&&(f=e,e=a),h=function(a){var b=bb().utc().set(d,a);return i.call(bb.fn._lang,b,e||"")},null!=f)return h(f);for(g=0;c>g;g++)j.push(h(g));return j}}function q(a){var b=+a,c=0;return 0!==b&&isFinite(b)&&(c=b>=0?Math.floor(b):Math.ceil(b)),c}function r(a,b){return new Date(Date.UTC(a,b+1,0)).getUTCDate()}function s(a){return t(a)?366:365}function t(a){return 0===a%4&&0!==a%100||0===a%400}function u(a){var b;a._a&&-2===a._pf.overflow&&(b=a._a[gb]<0||a._a[gb]>11?gb:a._a[hb]<1||a._a[hb]>r(a._a[fb],a._a[gb])?hb:a._a[ib]<0||a._a[ib]>23?ib:a._a[jb]<0||a._a[jb]>59?jb:a._a[kb]<0||a._a[kb]>59?kb:a._a[lb]<0||a._a[lb]>999?lb:-1,a._pf._overflowDayOfYear&&(fb>b||b>hb)&&(b=hb),a._pf.overflow=b)}function v(a){a._pf={empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1}}function w(a){return null==a._isValid&&(a._isValid=!isNaN(a._d.getTime())&&a._pf.overflow<0&&!a._pf.empty&&!a._pf.invalidMonth&&!a._pf.nullInput&&!a._pf.invalidFormat&&!a._pf.userInvalidated,a._strict&&(a._isValid=a._isValid&&0===a._pf.charsLeftOver&&0===a._pf.unusedTokens.length)),a._isValid}function x(a){return a?a.toLowerCase().replace("_","-"):a}function y(a,b){return b.abbr=a,mb[a]||(mb[a]=new d),mb[a].set(b),mb[a]}function z(a){delete mb[a]}function A(a){var b,c,d,e,f=0,g=function(a){if(!mb[a]&&nb)try{require("./lang/"+a)}catch(b){}return mb[a]};if(!a)return bb.fn._lang;if(!k(a)){if(c=g(a))return c;a=[a]}for(;f0;){if(c=g(e.slice(0,b).join("-")))return c;if(d&&d.length>=b&&m(e,d,!0)>=b-1)break;b--}f++}return bb.fn._lang}function B(a){return a.match(/\[[\s\S]/)?a.replace(/^\[|\]$/g,""):a.replace(/\\/g,"")}function C(a){var b,c,d=a.match(rb);for(b=0,c=d.length;c>b;b++)d[b]=Ob[d[b]]?Ob[d[b]]:B(d[b]);return function(e){var f="";for(b=0;c>b;b++)f+=d[b]instanceof Function?d[b].call(e,a):d[b];return f}}function D(a,b){return a.isValid()?(b=E(b,a.lang()),Lb[b]||(Lb[b]=C(b)),Lb[b](a)):a.lang().invalidDate()}function E(a,b){function c(a){return b.longDateFormat(a)||a}var d=5;for(sb.lastIndex=0;d>=0&&sb.test(a);)a=a.replace(sb,c),sb.lastIndex=0,d-=1;return a}function F(a,b){var c;switch(a){case"DDDD":return vb;case"YYYY":case"GGGG":case"gggg":return wb;case"YYYYY":case"GGGGG":case"ggggg":return xb;case"S":case"SS":case"SSS":case"DDD":return ub;case"MMM":case"MMMM":case"dd":case"ddd":case"dddd":return yb;case"a":case"A":return A(b._l)._meridiemParse;case"X":return Bb;case"Z":case"ZZ":return zb;case"T":return Ab;case"MM":case"DD":case"YY":case"GG":case"gg":case"HH":case"hh":case"mm":case"ss":case"M":case"D":case"d":case"H":case"h":case"m":case"s":case"w":case"ww":case"W":case"WW":case"e":case"E":return tb;default:return c=new RegExp(N(M(a.replace("\\","")),"i"))}}function G(a){var b=(zb.exec(a)||[])[0],c=(b+"").match(Gb)||["-",0,0],d=+(60*c[1])+q(c[2]);return"+"===c[0]?-d:d}function H(a,b,c){var d,e=c._a;switch(a){case"M":case"MM":null!=b&&(e[gb]=q(b)-1);break;case"MMM":case"MMMM":d=A(c._l).monthsParse(b),null!=d?e[gb]=d:c._pf.invalidMonth=b;break;case"D":case"DD":null!=b&&(e[hb]=q(b));break;case"DDD":case"DDDD":null!=b&&(c._dayOfYear=q(b));break;case"YY":e[fb]=q(b)+(q(b)>68?1900:2e3);break;case"YYYY":case"YYYYY":e[fb]=q(b);break;case"a":case"A":c._isPm=A(c._l).isPM(b);break;case"H":case"HH":case"h":case"hh":e[ib]=q(b);break;case"m":case"mm":e[jb]=q(b);break;case"s":case"ss":e[kb]=q(b);break;case"S":case"SS":case"SSS":e[lb]=q(1e3*("0."+b));break;case"X":c._d=new Date(1e3*parseFloat(b));break;case"Z":case"ZZ":c._useUTC=!0,c._tzm=G(b);break;case"w":case"ww":case"W":case"WW":case"d":case"dd":case"ddd":case"dddd":case"e":case"E":a=a.substr(0,1);case"gg":case"gggg":case"GG":case"GGGG":case"GGGGG":a=a.substr(0,2),b&&(c._w=c._w||{},c._w[a]=b)}}function I(a){var b,c,d,e,f,g,h,i,j,k,l=[];if(!a._d){for(d=K(a),a._w&&null==a._a[hb]&&null==a._a[gb]&&(f=function(b){return b?b.length<3?parseInt(b,10)>68?"19"+b:"20"+b:b:null==a._a[fb]?bb().weekYear():a._a[fb]},g=a._w,null!=g.GG||null!=g.W||null!=g.E?h=X(f(g.GG),g.W||1,g.E,4,1):(i=A(a._l),j=null!=g.d?T(g.d,i):null!=g.e?parseInt(g.e,10)+i._week.dow:0,k=parseInt(g.w,10)||1,null!=g.d&&js(e)&&(a._pf._overflowDayOfYear=!0),c=S(e,0,a._dayOfYear),a._a[gb]=c.getUTCMonth(),a._a[hb]=c.getUTCDate()),b=0;3>b&&null==a._a[b];++b)a._a[b]=l[b]=d[b];for(;7>b;b++)a._a[b]=l[b]=null==a._a[b]?2===b?1:0:a._a[b];l[ib]+=q((a._tzm||0)/60),l[jb]+=q((a._tzm||0)%60),a._d=(a._useUTC?S:R).apply(null,l)}}function J(a){var b;a._d||(b=o(a._i),a._a=[b.year,b.month,b.day,b.hour,b.minute,b.second,b.millisecond],I(a))}function K(a){var b=new Date;return a._useUTC?[b.getUTCFullYear(),b.getUTCMonth(),b.getUTCDate()]:[b.getFullYear(),b.getMonth(),b.getDate()]}function L(a){a._a=[],a._pf.empty=!0;var b,c,d,e,f,g=A(a._l),h=""+a._i,i=h.length,j=0;for(d=E(a._f,g).match(rb)||[],b=0;b0&&a._pf.unusedInput.push(f),h=h.slice(h.indexOf(c)+c.length),j+=c.length),Ob[e]?(c?a._pf.empty=!1:a._pf.unusedTokens.push(e),H(e,c,a)):a._strict&&!c&&a._pf.unusedTokens.push(e);a._pf.charsLeftOver=i-j,h.length>0&&a._pf.unusedInput.push(h),a._isPm&&a._a[ib]<12&&(a._a[ib]+=12),a._isPm===!1&&12===a._a[ib]&&(a._a[ib]=0),I(a),u(a)}function M(a){return a.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(a,b,c,d,e){return b||c||d||e})}function N(a){return a.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function O(a){var b,c,d,e,f;if(0===a._f.length)return a._pf.invalidFormat=!0,a._d=new Date(0/0),void 0;for(e=0;ef)&&(d=f,c=b));g(a,c||b)}function P(a){var b,c=a._i,d=Cb.exec(c);if(d){for(b=4;b>0;b--)if(d[b]){a._f=Eb[b-1]+(d[6]||" ");break}for(b=0;4>b;b++)if(Fb[b][1].exec(c)){a._f+=Fb[b][0];break}zb.exec(c)&&(a._f+=" Z"),L(a)}else a._d=new Date(c)}function Q(b){var c=b._i,d=ob.exec(c);c===a?b._d=new Date:d?b._d=new Date(+d[1]):"string"==typeof c?P(b):k(c)?(b._a=c.slice(0),I(b)):l(c)?b._d=new Date(+c):"object"==typeof c?J(b):b._d=new Date(c)}function R(a,b,c,d,e,f,g){var h=new Date(a,b,c,d,e,f,g);return 1970>a&&h.setFullYear(a),h}function S(a){var b=new Date(Date.UTC.apply(null,arguments));return 1970>a&&b.setUTCFullYear(a),b}function T(a,b){if("string"==typeof a)if(isNaN(a)){if(a=b.weekdaysParse(a),"number"!=typeof a)return null}else a=parseInt(a,10);return a}function U(a,b,c,d,e){return e.relativeTime(b||1,!!c,a,d)}function V(a,b,c){var d=eb(Math.abs(a)/1e3),e=eb(d/60),f=eb(e/60),g=eb(f/24),h=eb(g/365),i=45>d&&["s",d]||1===e&&["m"]||45>e&&["mm",e]||1===f&&["h"]||22>f&&["hh",f]||1===g&&["d"]||25>=g&&["dd",g]||45>=g&&["M"]||345>g&&["MM",eb(g/30)]||1===h&&["y"]||["yy",h];return i[2]=b,i[3]=a>0,i[4]=c,U.apply({},i)}function W(a,b,c){var d,e=c-b,f=c-a.day();return f>e&&(f-=7),e-7>f&&(f+=7),d=bb(a).add("d",f),{week:Math.ceil(d.dayOfYear()/7),year:d.year()}}function X(a,b,c,d,e){var f,g,h=new Date(Date.UTC(a,0)).getUTCDay();return c=null!=c?c:e,f=e-h+(h>d?7:0),g=7*(b-1)+(c-e)+f+1,{year:g>0?a:a-1,dayOfYear:g>0?g:s(a-1)+g}}function Y(a){var b=a._i,c=a._f;return"undefined"==typeof a._pf&&v(a),null===b?bb.invalid({nullInput:!0}):("string"==typeof b&&(a._i=b=A().preparse(b)),bb.isMoment(b)?(a=g({},b),a._d=new Date(+b._d)):c?k(c)?O(a):L(a):Q(a),new e(a))}function Z(a,b){bb.fn[a]=bb.fn[a+"s"]=function(a){var c=this._isUTC?"UTC":"";return null!=a?(this._d["set"+c+b](a),bb.updateOffset(this),this):this._d["get"+c+b]()}}function $(a){bb.duration.fn[a]=function(){return this._data[a]}}function _(a,b){bb.duration.fn["as"+a]=function(){return+this/b}}function ab(){"undefined"==typeof ender&&(this.moment=bb)}for(var bb,cb,db="2.3.1",eb=Math.round,fb=0,gb=1,hb=2,ib=3,jb=4,kb=5,lb=6,mb={},nb="undefined"!=typeof module&&module.exports,ob=/^\/?Date\((\-?\d+)/i,pb=/(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/,qb=/^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/,rb=/(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|SS?S?|X|zz?|ZZ?|.)/g,sb=/(\[[^\[]*\])|(\\)?(LT|LL?L?L?|l{1,4})/g,tb=/\d\d?/,ub=/\d{1,3}/,vb=/\d{3}/,wb=/\d{1,4}/,xb=/[+\-]?\d{1,6}/,yb=/[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i,zb=/Z|[\+\-]\d\d:?\d\d/i,Ab=/T/i,Bb=/[\+\-]?\d+(\.\d{1,3})?/,Cb=/^\s*\d{4}-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d\d?\d?)?)?)?)?([\+\-]\d\d:?\d\d)?)?$/,Db="YYYY-MM-DDTHH:mm:ssZ",Eb=["YYYY-MM-DD","GGGG-[W]WW","GGGG-[W]WW-E","YYYY-DDD"],Fb=[["HH:mm:ss.S",/(T| )\d\d:\d\d:\d\d\.\d{1,3}/],["HH:mm:ss",/(T| )\d\d:\d\d:\d\d/],["HH:mm",/(T| )\d\d:\d\d/],["HH",/(T| )\d\d/]],Gb=/([\+\-]|\d\d)/gi,Hb="Date|Hours|Minutes|Seconds|Milliseconds".split("|"),Ib={Milliseconds:1,Seconds:1e3,Minutes:6e4,Hours:36e5,Days:864e5,Months:2592e6,Years:31536e6},Jb={ms:"millisecond",s:"second",m:"minute",h:"hour",d:"day",D:"date",w:"week",W:"isoWeek",M:"month",y:"year",DDD:"dayOfYear",e:"weekday",E:"isoWeekday",gg:"weekYear",GG:"isoWeekYear"},Kb={dayofyear:"dayOfYear",isoweekday:"isoWeekday",isoweek:"isoWeek",weekyear:"weekYear",isoweekyear:"isoWeekYear"},Lb={},Mb="DDD w W M D d".split(" "),Nb="M D H h m s w W".split(" "),Ob={M:function(){return this.month()+1},MMM:function(a){return this.lang().monthsShort(this,a)},MMMM:function(a){return this.lang().months(this,a)},D:function(){return this.date()},DDD:function(){return this.dayOfYear()},d:function(){return this.day()},dd:function(a){return this.lang().weekdaysMin(this,a)},ddd:function(a){return this.lang().weekdaysShort(this,a)},dddd:function(a){return this.lang().weekdays(this,a)},w:function(){return this.week()},W:function(){return this.isoWeek()},YY:function(){return i(this.year()%100,2)},YYYY:function(){return i(this.year(),4)},YYYYY:function(){return i(this.year(),5)},gg:function(){return i(this.weekYear()%100,2)},gggg:function(){return this.weekYear()},ggggg:function(){return i(this.weekYear(),5)},GG:function(){return i(this.isoWeekYear()%100,2)},GGGG:function(){return this.isoWeekYear()},GGGGG:function(){return i(this.isoWeekYear(),5)},e:function(){return this.weekday()},E:function(){return this.isoWeekday()},a:function(){return this.lang().meridiem(this.hours(),this.minutes(),!0)},A:function(){return this.lang().meridiem(this.hours(),this.minutes(),!1)},H:function(){return this.hours()},h:function(){return this.hours()%12||12},m:function(){return this.minutes()},s:function(){return this.seconds()},S:function(){return q(this.milliseconds()/100)},SS:function(){return i(q(this.milliseconds()/10),2)},SSS:function(){return i(this.milliseconds(),3)},Z:function(){var a=-this.zone(),b="+";return 0>a&&(a=-a,b="-"),b+i(q(a/60),2)+":"+i(q(a)%60,2)},ZZ:function(){var a=-this.zone(),b="+";return 0>a&&(a=-a,b="-"),b+i(q(10*a/6),4)},z:function(){return this.zoneAbbr()},zz:function(){return this.zoneName()},X:function(){return this.unix()}},Pb=["months","monthsShort","weekdays","weekdaysShort","weekdaysMin"];Mb.length;)cb=Mb.pop(),Ob[cb+"o"]=c(Ob[cb],cb);for(;Nb.length;)cb=Nb.pop(),Ob[cb+cb]=b(Ob[cb],2);for(Ob.DDDD=b(Ob.DDD,3),g(d.prototype,{set:function(a){var b,c;for(c in a)b=a[c],"function"==typeof b?this[c]=b:this["_"+c]=b},_months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),months:function(a){return this._months[a.month()]},_monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),monthsShort:function(a){return this._monthsShort[a.month()]},monthsParse:function(a){var b,c,d;for(this._monthsParse||(this._monthsParse=[]),b=0;12>b;b++)if(this._monthsParse[b]||(c=bb.utc([2e3,b]),d="^"+this.months(c,"")+"|^"+this.monthsShort(c,""),this._monthsParse[b]=new RegExp(d.replace(".",""),"i")),this._monthsParse[b].test(a))return b},_weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdays:function(a){return this._weekdays[a.day()]},_weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysShort:function(a){return this._weekdaysShort[a.day()]},_weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),weekdaysMin:function(a){return this._weekdaysMin[a.day()]},weekdaysParse:function(a){var b,c,d;for(this._weekdaysParse||(this._weekdaysParse=[]),b=0;7>b;b++)if(this._weekdaysParse[b]||(c=bb([2e3,1]).day(b),d="^"+this.weekdays(c,"")+"|^"+this.weekdaysShort(c,"")+"|^"+this.weekdaysMin(c,""),this._weekdaysParse[b]=new RegExp(d.replace(".",""),"i")),this._weekdaysParse[b].test(a))return b},_longDateFormat:{LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D YYYY",LLL:"MMMM D YYYY LT",LLLL:"dddd, MMMM D YYYY LT"},longDateFormat:function(a){var b=this._longDateFormat[a];return!b&&this._longDateFormat[a.toUpperCase()]&&(b=this._longDateFormat[a.toUpperCase()].replace(/MMMM|MM|DD|dddd/g,function(a){return a.slice(1)}),this._longDateFormat[a]=b),b},isPM:function(a){return"p"===(a+"").toLowerCase().charAt(0)},_meridiemParse:/[ap]\.?m?\.?/i,meridiem:function(a,b,c){return a>11?c?"pm":"PM":c?"am":"AM"},_calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},calendar:function(a,b){var c=this._calendar[a];return"function"==typeof c?c.apply(b):c},_relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},relativeTime:function(a,b,c,d){var e=this._relativeTime[c];return"function"==typeof e?e(a,b,c,d):e.replace(/%d/i,a)},pastFuture:function(a,b){var c=this._relativeTime[a>0?"future":"past"];return"function"==typeof c?c(b):c.replace(/%s/i,b)},ordinal:function(a){return this._ordinal.replace("%d",a)},_ordinal:"%d",preparse:function(a){return a},postformat:function(a){return a},week:function(a){return W(a,this._week.dow,this._week.doy).week},_week:{dow:0,doy:6},_invalidDate:"Invalid date",invalidDate:function(){return this._invalidDate}}),bb=function(b,c,d,e){return"boolean"==typeof d&&(e=d,d=a),Y({_i:b,_f:c,_l:d,_strict:e,_isUTC:!1})},bb.utc=function(b,c,d,e){var f;return"boolean"==typeof d&&(e=d,d=a),f=Y({_useUTC:!0,_isUTC:!0,_l:d,_i:b,_f:c,_strict:e}).utc()},bb.unix=function(a){return bb(1e3*a)},bb.duration=function(a,b){var c,d,e,g=bb.isDuration(a),h="number"==typeof a,i=g?a._input:h?{}:a,j=null;return h?b?i[b]=a:i.milliseconds=a:(j=pb.exec(a))?(c="-"===j[1]?-1:1,i={y:0,d:q(j[hb])*c,h:q(j[ib])*c,m:q(j[jb])*c,s:q(j[kb])*c,ms:q(j[lb])*c}):(j=qb.exec(a))&&(c="-"===j[1]?-1:1,e=function(a){var b=a&&parseFloat(a.replace(",","."));return(isNaN(b)?0:b)*c},i={y:e(j[2]),M:e(j[3]),d:e(j[4]),h:e(j[5]),m:e(j[6]),s:e(j[7]),w:e(j[8])}),d=new f(i),g&&a.hasOwnProperty("_lang")&&(d._lang=a._lang),d},bb.version=db,bb.defaultFormat=Db,bb.updateOffset=function(){},bb.lang=function(a,b){var c;return a?(b?y(x(a),b):null===b?(z(a),a="en"):mb[a]||A(a),c=bb.duration.fn._lang=bb.fn._lang=A(a),c._abbr):bb.fn._lang._abbr},bb.langData=function(a){return a&&a._lang&&a._lang._abbr&&(a=a._lang._abbr),A(a)},bb.isMoment=function(a){return a instanceof e},bb.isDuration=function(a){return a instanceof f},cb=Pb.length-1;cb>=0;--cb)p(Pb[cb]);for(bb.normalizeUnits=function(a){return n(a)},bb.invalid=function(a){var b=bb.utc(0/0);return null!=a?g(b._pf,a):b._pf.userInvalidated=!0,b},bb.parseZone=function(a){return bb(a).parseZone()},g(bb.fn=e.prototype,{clone:function(){return bb(this)},valueOf:function(){return+this._d+6e4*(this._offset||0)},unix:function(){return Math.floor(+this/1e3)},toString:function(){return this.clone().lang("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},toDate:function(){return this._offset?new Date(+this):this._d},toISOString:function(){return D(bb(this).utc(),"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]")},toArray:function(){var a=this;return[a.year(),a.month(),a.date(),a.hours(),a.minutes(),a.seconds(),a.milliseconds()]},isValid:function(){return w(this)},isDSTShifted:function(){return this._a?this.isValid()&&m(this._a,(this._isUTC?bb.utc(this._a):bb(this._a)).toArray())>0:!1},parsingFlags:function(){return g({},this._pf)},invalidAt:function(){return this._pf.overflow},utc:function(){return this.zone(0)},local:function(){return this.zone(0),this._isUTC=!1,this},format:function(a){var b=D(this,a||bb.defaultFormat);return this.lang().postformat(b)},add:function(a,b){var c;return c="string"==typeof a?bb.duration(+b,a):bb.duration(a,b),j(this,c,1),this},subtract:function(a,b){var c;return c="string"==typeof a?bb.duration(+b,a):bb.duration(a,b),j(this,c,-1),this},diff:function(a,b,c){var d,e,f=this._isUTC?bb(a).zone(this._offset||0):bb(a).local(),g=6e4*(this.zone()-f.zone());return b=n(b),"year"===b||"month"===b?(d=432e5*(this.daysInMonth()+f.daysInMonth()),e=12*(this.year()-f.year())+(this.month()-f.month()),e+=(this-bb(this).startOf("month")-(f-bb(f).startOf("month")))/d,e-=6e4*(this.zone()-bb(this).startOf("month").zone()-(f.zone()-bb(f).startOf("month").zone()))/d,"year"===b&&(e/=12)):(d=this-f,e="second"===b?d/1e3:"minute"===b?d/6e4:"hour"===b?d/36e5:"day"===b?(d-g)/864e5:"week"===b?(d-g)/6048e5:d),c?e:h(e)},from:function(a,b){return bb.duration(this.diff(a)).lang(this.lang()._abbr).humanize(!b)},fromNow:function(a){return this.from(bb(),a)},calendar:function(){var a=this.diff(bb().zone(this.zone()).startOf("day"),"days",!0),b=-6>a?"sameElse":-1>a?"lastWeek":0>a?"lastDay":1>a?"sameDay":2>a?"nextDay":7>a?"nextWeek":"sameElse";return this.format(this.lang().calendar(b,this))},isLeapYear:function(){return t(this.year())},isDST:function(){return this.zone()+bb(a).startOf(b)},isBefore:function(a,b){return b="undefined"!=typeof b?b:"millisecond",+this.clone().startOf(b)<+bb(a).startOf(b)},isSame:function(a,b){return b="undefined"!=typeof b?b:"millisecond",+this.clone().startOf(b)===+bb(a).startOf(b)},min:function(a){return a=bb.apply(null,arguments),this>a?this:a},max:function(a){return a=bb.apply(null,arguments),a>this?this:a},zone:function(a){var b=this._offset||0;return null==a?this._isUTC?b:this._d.getTimezoneOffset():("string"==typeof a&&(a=G(a)),Math.abs(a)<16&&(a=60*a),this._offset=a,this._isUTC=!0,b!==a&&j(this,bb.duration(b-a,"m"),1,!0),this)},zoneAbbr:function(){return this._isUTC?"UTC":""},zoneName:function(){return this._isUTC?"Coordinated Universal Time":""},parseZone:function(){return"string"==typeof this._i&&this.zone(this._i),this},hasAlignedHourOffset:function(a){return a=a?bb(a).zone():0,0===(this.zone()-a)%60},daysInMonth:function(){return r(this.year(),this.month())},dayOfYear:function(a){var b=eb((bb(this).startOf("day")-bb(this).startOf("year"))/864e5)+1;return null==a?b:this.add("d",a-b)},weekYear:function(a){var b=W(this,this.lang()._week.dow,this.lang()._week.doy).year;return null==a?b:this.add("y",a-b)},isoWeekYear:function(a){var b=W(this,1,4).year;return null==a?b:this.add("y",a-b)},week:function(a){var b=this.lang().week(this);return null==a?b:this.add("d",7*(a-b))},isoWeek:function(a){var b=W(this,1,4).week;return null==a?b:this.add("d",7*(a-b))},weekday:function(a){var b=(this.day()+7-this.lang()._week.dow)%7;return null==a?b:this.add("d",a-b)},isoWeekday:function(a){return null==a?this.day()||7:this.day(this.day()%7?a:a-7)},get:function(a){return a=n(a),this[a]()},set:function(a,b){return a=n(a),"function"==typeof this[a]&&this[a](b),this},lang:function(b){return b===a?this._lang:(this._lang=A(b),this)}}),cb=0;cb 2 | 3 | 4 | Bootstrap Sortable Tables 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
h1h2
az
by
cx
dw
33 | 34 | 35 | -------------------------------------------------------------------------------- /Tests/avoidFlippingOrder.js: -------------------------------------------------------------------------------- 1 | jasmine.getFixtures().fixturesPath = 'base/Tests/'; 2 | 3 | describe('Avoid flipping order', function () { 4 | beforeEach(function () { 5 | jasmine.getFixtures().load('avoidFlippingOrder.html'); 6 | $.bootstrapSortable(); 7 | }); 8 | 9 | it("Initially 'a' is first", function () { 10 | var index = $('#az').index('tbody tr'); 11 | expect(index).toBe(0); 12 | }); 13 | 14 | it("Click h2, 'w' is first", function () { 15 | $.bootstrapSortable({ sortingHeader: $('#h2') }); 16 | var index = $('#dw').index('tbody tr'); 17 | expect(index).toBe(0); 18 | }); 19 | 20 | it("Click h2 twice, 'a' is first", function () { 21 | $.bootstrapSortable({ sortingHeader: $('#h2') }); 22 | $.bootstrapSortable({ sortingHeader: $('#h2') }); 23 | var index = $('#az').index('tbody tr'); 24 | expect(index).toBe(0); 25 | }); 26 | 27 | it("Click h2 and h1, 'a' is first", function () { 28 | $.bootstrapSortable({ sortingHeader: $('#h2') }); 29 | $.bootstrapSortable({ sortingHeader: $('#h1') }); 30 | var index = $('#az').index('tbody tr'); 31 | expect(index).toBe(0); 32 | }); 33 | }) -------------------------------------------------------------------------------- /Tests/disableSortingOnRows.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Bootstrap Sortable Tables 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
a
b
c
z
28 | 29 | 30 | -------------------------------------------------------------------------------- /Tests/disableSortingOnRows.js: -------------------------------------------------------------------------------- 1 | jasmine.getFixtures().fixturesPath = 'base/Tests/'; 2 | 3 | describe('Disable sorting on rows', function () { 4 | beforeEach(function () { 5 | jasmine.getFixtures().load('disableSortingOnRows.html'); 6 | $.bootstrapSortable(); 7 | }); 8 | 9 | it("First row stays first", function () { 10 | var index = $('#first').index('tbody tr td'); 11 | expect(index).toBe(0); 12 | }); 13 | 14 | it("Last row stays last", function () { 15 | var index = $('#last').index('tbody tr td'); 16 | expect(index).toBe(3); 17 | }); 18 | 19 | it("Middle rows are ordered", function () { 20 | var index = $('#b').index('tbody tr td'); 21 | expect(index).toBe(2); 22 | }); 23 | }) -------------------------------------------------------------------------------- /Tests/emptyRowsSorting.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Bootstrap Sortable Tables 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
a
z
31 | 32 | 33 | -------------------------------------------------------------------------------- /Tests/emptyRowsSorting.js: -------------------------------------------------------------------------------- 1 | jasmine.getFixtures().fixturesPath = 'base/Tests/'; 2 | 3 | describe('Empty rows sorting', function () { 4 | 5 | describe('Empty rows are sorted at the end { emptyEnd: true }', function () { 6 | beforeEach(function () { 7 | jasmine.getFixtures().load('emptyRowsSorting.html'); 8 | $.bootstrapSortable({ emptyEnd: true }); 9 | }); 10 | 11 | it("Sort ascending", function () { 12 | var header = $('#header'); 13 | $.bootstrapSortable({ sortingHeader: header }); 14 | var indexA = $('#a').index('tbody tr td'); 15 | var indexZ = $('#z').index('tbody tr td'); 16 | expect(indexA).toBe(0); 17 | expect(indexZ).toBe(1); 18 | }); 19 | 20 | it("Sort descending", function () { 21 | $.bootstrapSortable({ sortingHeader: header }); 22 | $.bootstrapSortable({ sortingHeader: header }); 23 | var indexZ = $('#z').index('tbody tr td'); 24 | var indexA = $('#a').index('tbody tr td'); 25 | expect(indexZ).toBe(0); 26 | expect(indexA).toBe(1); 27 | }); 28 | }); 29 | 30 | describe('Empty rows are sorted as default (beginning of the alphabet)', function () { 31 | beforeEach(function () { 32 | jasmine.getFixtures().load('emptyRowsSorting.html'); 33 | $.bootstrapSortable(); 34 | }); 35 | 36 | it("Sort ascending", function () { 37 | var header = $('#header'); 38 | $.bootstrapSortable({ sortingHeader: header }); 39 | var indexA = $('#a').index('tbody tr td'); 40 | var indexZ = $('#z').index('tbody tr td'); 41 | expect(indexA).toBe(3); 42 | expect(indexZ).toBe(4); 43 | }); 44 | 45 | it("Sort descending", function () { 46 | $.bootstrapSortable({ sortingHeader: header }); 47 | $.bootstrapSortable({ sortingHeader: header }); 48 | var indexA = $('#a').index('tbody tr td'); 49 | var indexZ = $('#z').index('tbody tr td'); 50 | expect(indexZ).toBe(0); 51 | expect(indexA).toBe(1); 52 | }); 53 | }); 54 | }); -------------------------------------------------------------------------------- /Tests/eventsRaising.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Bootstrap Sortable Tables 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
h1h2
az
by
cx
dw
33 | 34 | 35 | -------------------------------------------------------------------------------- /Tests/eventsRaising.js: -------------------------------------------------------------------------------- 1 | jasmine.getFixtures().fixturesPath = 'base/Tests/'; 2 | 3 | describe('Events raising', function () { 4 | beforeEach(function () { 5 | jasmine.getFixtures().load('eventsRaising.html'); 6 | }); 7 | 8 | it("before-sort is raised on initialization", function () { 9 | var table = $('table.sortable'); 10 | var wasRaised = false; 11 | table.on('before-sort', function() { 12 | wasRaised = true; 13 | }); 14 | $.bootstrapSortable(); 15 | expect(wasRaised).toBe(true); 16 | }); 17 | 18 | it("sorted is raised on initialization", function () { 19 | var table = $('table.sortable'); 20 | var wasRaised = false; 21 | table.on('sorted', function () { 22 | wasRaised = true; 23 | }); 24 | $.bootstrapSortable(); 25 | expect(wasRaised).toBe(true); 26 | }); 27 | 28 | it("before-sort is raised on manual sorting", function () { 29 | var table = $('table.sortable'); 30 | var wasRaised = false; 31 | $.bootstrapSortable(); 32 | table.on('before-sort', function () { 33 | wasRaised = true; 34 | }); 35 | $.bootstrapSortable({ sortingHeader: $('#h2') }); 36 | expect(wasRaised).toBe(true); 37 | }); 38 | 39 | it("sorted is raised on manual sorting", function () { 40 | var table = $('table.sortable'); 41 | var wasRaised = false; 42 | $.bootstrapSortable(); 43 | table.on('sorted', function () { 44 | wasRaised = true; 45 | }); 46 | $.bootstrapSortable({ sortingHeader: $('#h2') }); 47 | expect(wasRaised).toBe(true); 48 | }); 49 | }) -------------------------------------------------------------------------------- /Tests/initializeSortingValues.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Bootstrap Sortable Tables 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
22/3/2012direct valuedirect value10%22/3/201210%
29 | 30 | 31 | -------------------------------------------------------------------------------- /Tests/initializeSortingValues.js: -------------------------------------------------------------------------------- 1 | jasmine.getFixtures().fixturesPath = 'base/Tests/'; 2 | 3 | describe('Initializing sorting values', function () { 4 | beforeEach(function () { 5 | jasmine.getFixtures().load('initializeSortingValues.html'); 6 | $.bootstrapSortable(); 7 | }); 8 | 9 | it("Date value", function () { 10 | var cell = $('#date'); 11 | expect(cell).toHaveData('value', '2012/03/22/00/00/00'); 12 | }); 13 | 14 | it("Automatic value", function () { 15 | var cell = $('#auto'); 16 | expect(cell).toHaveData('value', 'direct value'); 17 | }); 18 | 19 | it("Specified value", function () { 20 | var cell = $('#specified'); 21 | expect(cell).toHaveData('value', 'specified value'); 22 | }); 23 | 24 | it("Regex value", function () { 25 | var cell = $('#regex'); 26 | expect(cell).toHaveData('value', 10); 27 | }); 28 | 29 | describe('Format in header', function() { 30 | it("Correctly applied date format", function () { 31 | var cell = $('#dateHeader'); 32 | expect(cell).toHaveData('value', '2012/03/22/00/00/00'); 33 | }); 34 | 35 | it("Correctly applied regex value", function () { 36 | var cell = $('#regexHeader'); 37 | expect(cell).toHaveData('value', 10); 38 | }); 39 | }); 40 | }) -------------------------------------------------------------------------------- /Tests/rowSpan.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Bootstrap Sortable Tables 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
column acolumn bcolumn c
cell a1cell b1cell c1
cell b2cell c2
cell b3cell c3
cell a4cell b4cell c4
cell a5cell b5cell c5
41 | 42 | 43 | -------------------------------------------------------------------------------- /Tests/rowSpan.js: -------------------------------------------------------------------------------- 1 | jasmine.getFixtures().fixturesPath = 'base/Tests/'; 2 | 3 | describe('Sorting cells with rowspan', function() { 4 | beforeEach(function() { 5 | jasmine.getFixtures().load('rowSpan.html'); 6 | $.bootstrapSortable(); 7 | }); 8 | 9 | it("Sort 1st column, cells are correct in first row", function() { 10 | $.bootstrapSortable({ sortingHeader: $('#ha') }); 11 | $.bootstrapSortable({ sortingHeader: $('#ha') }); 12 | 13 | expect($('#a1').index('#row1 td')).toBe(0); 14 | expect($('#b1').index('#row1 td')).toBe(1); 15 | expect($('#c1').index('#row1 td')).toBe(2); 16 | }); 17 | 18 | it("Sort 1st column, cells are correct in second row", function () { 19 | $.bootstrapSortable({ sortingHeader: $('#ha') }); 20 | $.bootstrapSortable({ sortingHeader: $('#ha') }); 21 | 22 | expect($('#b2').index('#row2 td')).toBe(0); 23 | expect($('#c2').index('#row2 td')).toBe(1); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /Tests/tableInsideTable.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Bootstrap Sortable Tables 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 68 | 69 | 70 |
head1head2head3head4
2hi2015/04/085.234
5yeah2013/03/16548.2154
1test2005/10/241.547
37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 |
head1head2head3head4
2hi2015/04/085.234
5yeah2013/03/16548.2154
1test2005/10/241.547
67 |
71 | 72 | 73 | -------------------------------------------------------------------------------- /Tests/tableInsideTable.js: -------------------------------------------------------------------------------- 1 | jasmine.getFixtures().fixturesPath = 'base/Tests/'; 2 | 3 | describe('Table inside table sorting', function() { 4 | beforeEach(function() { 5 | jasmine.getFixtures().load('tableInsideTable.html'); 6 | $.bootstrapSortable(); 7 | }); 8 | 9 | it("Outer table has correct order of rows", function() { 10 | var index = $('#outerFirst').index('#outer > tr'); 11 | expect(index).toBe(1); 12 | }); 13 | 14 | it("Inner table has correct order of rows", function() { 15 | var index = $('#innerFirst').index('#inner > tr'); 16 | expect(index).toBe(0); 17 | }); 18 | 19 | it("Inner table header keeps sorted class after outer sorting", function() { 20 | var innerHeader = $('#innerh1'); 21 | expect(innerHeader.hasClass('sorted')).toBe(true); 22 | 23 | $.bootstrapSortable({ sortingHeader: $('#outerh2') }); 24 | expect(innerHeader.hasClass('sorted')).toBe(true); 25 | }); 26 | 27 | it("Inner table header keeps sorted icon after outer sorting", function() { 28 | var innerHeader = $('#innerh1'); 29 | expect(innerHeader.children('.sign').length).toBe(1); 30 | 31 | $.bootstrapSortable({ sortingHeader: $('#outerh2') }); 32 | expect(innerHeader.children('.sign').length).toBe(1); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bootstrap-sortable", 3 | "version": "2.0.0", 4 | "homepage": "https://github.com/drvic10k/bootstrap-sortable", 5 | "authors": [ 6 | "Matus Brlit " 7 | ], 8 | "description": "Adding sorting ability to HTML tables", 9 | "main": "Scripts/bootstrap-sortable.js", 10 | "keywords": [ 11 | "sort table", "sortable", "sortable table", "bootstrap sortable", "bootstrap-sortable" 12 | ], 13 | "license": "MIT", 14 | "ignore": [ 15 | "Scripts/moment.min.js" 16 | ], 17 | "dependencies": { 18 | "jquery": ">= 1.9.0", 19 | "moment": ">= 2.3.1" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function (config) { 2 | var configuration = { 3 | 4 | // base path that will be used to resolve all patterns (eg. files, exclude) 5 | basePath: '', 6 | 7 | 8 | // frameworks to use 9 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 10 | frameworks: ['jasmine'], 11 | 12 | 13 | // list of files / patterns to load in the browser 14 | files: [ 15 | 'node_modules/jquery/dist/jquery.js', 16 | 'node_modules/jasmine-jquery/lib/jasmine-jquery.js', 17 | 'node_modules/jasmine2-custom-message/jasmine2-custom-message.js', 18 | { pattern: 'Tests/*.html', included: true }, 19 | { pattern: 'Tests/*.js', included: true }, 20 | 'Scripts/moment.min.js', 21 | 'Scripts/bootstrap-sortable.js' 22 | ], 23 | 24 | 25 | // list of files to exclude 26 | exclude: [ 27 | ], 28 | 29 | 30 | // preprocess matching files before serving them to the browser 31 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 32 | preprocessors: { 33 | }, 34 | 35 | 36 | // test results reporter to use 37 | // possible values: 'dots', 'progress' 38 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 39 | reporters: ['mocha'], 40 | 41 | 42 | // enable / disable colors in the output (reporters and logs) 43 | colors: true, 44 | 45 | 46 | // level of logging 47 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 48 | logLevel: config.LOG_INFO, 49 | 50 | 51 | // enable / disable watching file and executing tests whenever any file changes 52 | autoWatch: false, 53 | 54 | 55 | // start these browsers 56 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 57 | browsers: ['PhantomJS'], 58 | 59 | // Continuous Integration mode 60 | // if true, Karma captures browsers, runs the tests and exits 61 | singleRun: true 62 | }; 63 | 64 | config.set(configuration); 65 | } 66 | 67 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Matúš Brliť (drvic10k), bootstrap-sortable contributors 2 | 3 | Copyright (c) 2011-2013 Tim Wood, Iskren Chernev, Moment.js contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bootstrap-sortable", 3 | "version": "1.11.2", 4 | "description": "adding sorting ability to HTML tables", 5 | "main": "Scripts/bootstrap-sortable.js", 6 | "scripts": { 7 | "test": "karma start karma.conf.js --single-run" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/drvic10k/bootstrap-sortable.git" 12 | }, 13 | "author": "Matus Brlit ", 14 | "license": "MIT", 15 | "bugs": { 16 | "url": "https://github.com/drvic10k/bootstrap-sortable/issues" 17 | }, 18 | "homepage": "https://github.com/drvic10k/bootstrap-sortable#readme", 19 | "devDependencies": { 20 | "grunt": "^0.4.5", 21 | "jasmine-core": "^2.4.1", 22 | "jasmine-jquery": "^2.1.1", 23 | "jasmine2-custom-message": "^0.8.0", 24 | "jquery": "^2.1.4", 25 | "karma": "^0.13.16", 26 | "karma-jasmine": "^0.3.6", 27 | "karma-mocha-reporter": "^1.1.3", 28 | "karma-phantomjs-launcher": "^0.2.2", 29 | "phantomjs": "^1.9.19" 30 | } 31 | } 32 | --------------------------------------------------------------------------------