Mercurial > nebulaweb3
comparison default/node_modules/tablesaw/src/tables.sortable.js @ 0:1d038bc9b3d2 default tip
Up:default
author | Liny <dev@neowd.com> |
---|---|
date | Sat, 31 May 2025 09:21:51 +0800 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:1d038bc9b3d2 |
---|---|
1 /* | |
2 * tablesaw: A set of plugins for responsive tables | |
3 * Sortable column headers | |
4 * Copyright (c) 2013 Filament Group, Inc. | |
5 * MIT License | |
6 */ | |
7 | |
8 (function() { | |
9 function getSortValue(cell) { | |
10 var text = []; | |
11 $(cell.childNodes).each(function() { | |
12 var $el = $(this); | |
13 if ($el.is("input, select")) { | |
14 text.push($el.val()); | |
15 } else if ($el.is(".tablesaw-cell-label")) { | |
16 } else { | |
17 text.push(($el.text() || "").replace(/^\s+|\s+$/g, "")); | |
18 } | |
19 }); | |
20 | |
21 return text.join(""); | |
22 } | |
23 | |
24 var pluginName = "tablesaw-sortable", | |
25 initSelector = "table[data-" + pluginName + "]", | |
26 sortableSwitchSelector = "[data-" + pluginName + "-switch]", | |
27 attrs = { | |
28 sortCol: "data-tablesaw-sortable-col", | |
29 defaultCol: "data-tablesaw-sortable-default-col", | |
30 numericCol: "data-tablesaw-sortable-numeric", | |
31 subRow: "data-tablesaw-subrow", | |
32 ignoreRow: "data-tablesaw-ignorerow" | |
33 }, | |
34 classes = { | |
35 head: pluginName + "-head", | |
36 ascend: pluginName + "-ascending", | |
37 descend: pluginName + "-descending", | |
38 switcher: pluginName + "-switch", | |
39 tableToolbar: "tablesaw-bar-section", | |
40 sortButton: pluginName + "-btn" | |
41 }, | |
42 methods = { | |
43 _create: function(o) { | |
44 return $(this).each(function() { | |
45 var init = $(this).data(pluginName + "-init"); | |
46 if (init) { | |
47 return false; | |
48 } | |
49 $(this) | |
50 .data(pluginName + "-init", true) | |
51 .trigger("beforecreate." + pluginName) | |
52 [pluginName]("_init", o) | |
53 .trigger("create." + pluginName); | |
54 }); | |
55 }, | |
56 _init: function() { | |
57 var el = $(this); | |
58 var tblsaw = el.data("tablesaw"); | |
59 var heads; | |
60 var $switcher; | |
61 | |
62 function addClassToHeads(h) { | |
63 $.each(h, function(i, v) { | |
64 $(v).addClass(classes.head); | |
65 }); | |
66 } | |
67 | |
68 function makeHeadsActionable(h, fn) { | |
69 $.each(h, function(i, col) { | |
70 var b = $("<button class='" + classes.sortButton + "'/>"); | |
71 b.on("click", { col: col }, fn); | |
72 $(col) | |
73 .wrapInner(b) | |
74 .find("button") | |
75 .append("<span class='tablesaw-sortable-arrow'>"); | |
76 }); | |
77 } | |
78 | |
79 function clearOthers(headcells) { | |
80 $.each(headcells, function(i, v) { | |
81 var col = $(v); | |
82 col.removeAttr(attrs.defaultCol); | |
83 col.removeClass(classes.ascend); | |
84 col.removeClass(classes.descend); | |
85 }); | |
86 } | |
87 | |
88 function headsOnAction(e) { | |
89 if ($(e.target).is("a[href]")) { | |
90 return; | |
91 } | |
92 | |
93 e.stopPropagation(); | |
94 var headCell = $(e.target).closest("[" + attrs.sortCol + "]"), | |
95 v = e.data.col, | |
96 newSortValue = heads.index(headCell[0]); | |
97 | |
98 clearOthers( | |
99 headCell | |
100 .closest("thead") | |
101 .find("th") | |
102 .filter(function() { | |
103 return this !== headCell[0]; | |
104 }) | |
105 ); | |
106 if (headCell.is("." + classes.descend) || !headCell.is("." + classes.ascend)) { | |
107 el[pluginName]("sortBy", v, true); | |
108 newSortValue += "_asc"; | |
109 } else { | |
110 el[pluginName]("sortBy", v); | |
111 newSortValue += "_desc"; | |
112 } | |
113 if ($switcher) { | |
114 $switcher | |
115 .find("select") | |
116 .val(newSortValue) | |
117 .trigger("refresh"); | |
118 } | |
119 | |
120 e.preventDefault(); | |
121 } | |
122 | |
123 function handleDefault(heads) { | |
124 $.each(heads, function(idx, el) { | |
125 var $el = $(el); | |
126 if ($el.is("[" + attrs.defaultCol + "]")) { | |
127 if (!$el.is("." + classes.descend)) { | |
128 $el.addClass(classes.ascend); | |
129 } | |
130 } | |
131 }); | |
132 } | |
133 | |
134 function addSwitcher(heads) { | |
135 $switcher = $("<div>") | |
136 .addClass(classes.switcher) | |
137 .addClass(classes.tableToolbar); | |
138 | |
139 var html = ["<label>" + Tablesaw.i18n.sort + ":"]; | |
140 | |
141 // TODO next major version: remove .btn | |
142 html.push('<span class="btn tablesaw-btn"><select>'); | |
143 heads.each(function(j) { | |
144 var $t = $(this); | |
145 var isDefaultCol = $t.is("[" + attrs.defaultCol + "]"); | |
146 var isDescending = $t.is("." + classes.descend); | |
147 | |
148 var hasNumericAttribute = $t.is("[" + attrs.numericCol + "]"); | |
149 var numericCount = 0; | |
150 // Check only the first four rows to see if the column is numbers. | |
151 var numericCountMax = 5; | |
152 | |
153 $(this.cells.slice(0, numericCountMax)).each(function() { | |
154 if (!isNaN(parseInt(getSortValue(this), 10))) { | |
155 numericCount++; | |
156 } | |
157 }); | |
158 var isNumeric = numericCount === numericCountMax; | |
159 if (!hasNumericAttribute) { | |
160 $t.attr(attrs.numericCol, isNumeric ? "" : "false"); | |
161 } | |
162 | |
163 html.push( | |
164 "<option" + | |
165 (isDefaultCol && !isDescending ? " selected" : "") + | |
166 ' value="' + | |
167 j + | |
168 '_asc">' + | |
169 $t.text() + | |
170 " " + | |
171 (isNumeric ? "↑" : "(A-Z)") + | |
172 "</option>" | |
173 ); | |
174 html.push( | |
175 "<option" + | |
176 (isDefaultCol && isDescending ? " selected" : "") + | |
177 ' value="' + | |
178 j + | |
179 '_desc">' + | |
180 $t.text() + | |
181 " " + | |
182 (isNumeric ? "↓" : "(Z-A)") + | |
183 "</option>" | |
184 ); | |
185 }); | |
186 html.push("</select></span></label>"); | |
187 | |
188 $switcher.html(html.join("")); | |
189 | |
190 var $firstChild = tblsaw.$toolbar.children().eq(0); | |
191 if ($firstChild.length) { | |
192 $switcher.insertBefore($firstChild); | |
193 } else { | |
194 $switcher.appendTo(tblsaw.$toolbar); | |
195 } | |
196 $switcher.find(".tablesaw-btn").tablesawbtn(); | |
197 $switcher.find("select").on("change", function() { | |
198 var val = $(this) | |
199 .val() | |
200 .split("_"), | |
201 head = heads.eq(val[0]); | |
202 | |
203 clearOthers(head.siblings()); | |
204 el[pluginName]("sortBy", head.get(0), val[1] === "asc"); | |
205 }); | |
206 } | |
207 | |
208 el.addClass(pluginName); | |
209 | |
210 heads = el | |
211 .children() | |
212 .filter("thead") | |
213 .find("th[" + attrs.sortCol + "]"); | |
214 | |
215 addClassToHeads(heads); | |
216 makeHeadsActionable(heads, headsOnAction); | |
217 handleDefault(heads); | |
218 | |
219 if (el.is(sortableSwitchSelector)) { | |
220 addSwitcher(heads); | |
221 } | |
222 }, | |
223 sortRows: function(rows, colNum, ascending, col, tbody) { | |
224 function convertCells(cellArr, belongingToTbody) { | |
225 var cells = []; | |
226 $.each(cellArr, function(i, cell) { | |
227 var row = cell.parentNode; | |
228 var $row = $(row); | |
229 // next row is a subrow | |
230 var subrows = []; | |
231 var $next = $row.next(); | |
232 while ($next.is("[" + attrs.subRow + "]")) { | |
233 subrows.push($next[0]); | |
234 $next = $next.next(); | |
235 } | |
236 | |
237 var tbody = row.parentNode; | |
238 | |
239 // current row is a subrow | |
240 if ($row.is("[" + attrs.subRow + "]")) { | |
241 } else if (tbody === belongingToTbody) { | |
242 cells.push({ | |
243 element: cell, | |
244 cell: getSortValue(cell), | |
245 row: row, | |
246 subrows: subrows.length ? subrows : null, | |
247 ignored: $row.is("[" + attrs.ignoreRow + "]") | |
248 }); | |
249 } | |
250 }); | |
251 return cells; | |
252 } | |
253 | |
254 function getSortFxn(ascending, forceNumeric) { | |
255 var fn, | |
256 regex = /[^\-\+\d\.]/g; | |
257 if (ascending) { | |
258 fn = function(a, b) { | |
259 if (a.ignored || b.ignored) { | |
260 return 0; | |
261 } | |
262 if (forceNumeric) { | |
263 return ( | |
264 parseFloat(a.cell.replace(regex, "")) - parseFloat(b.cell.replace(regex, "")) | |
265 ); | |
266 } else { | |
267 return a.cell.toLowerCase() > b.cell.toLowerCase() ? 1 : -1; | |
268 } | |
269 }; | |
270 } else { | |
271 fn = function(a, b) { | |
272 if (a.ignored || b.ignored) { | |
273 return 0; | |
274 } | |
275 if (forceNumeric) { | |
276 return ( | |
277 parseFloat(b.cell.replace(regex, "")) - parseFloat(a.cell.replace(regex, "")) | |
278 ); | |
279 } else { | |
280 return a.cell.toLowerCase() < b.cell.toLowerCase() ? 1 : -1; | |
281 } | |
282 }; | |
283 } | |
284 return fn; | |
285 } | |
286 | |
287 function convertToRows(sorted) { | |
288 var newRows = [], | |
289 i, | |
290 l; | |
291 for (i = 0, l = sorted.length; i < l; i++) { | |
292 newRows.push(sorted[i].row); | |
293 if (sorted[i].subrows) { | |
294 newRows.push(sorted[i].subrows); | |
295 } | |
296 } | |
297 return newRows; | |
298 } | |
299 | |
300 var fn; | |
301 var sorted; | |
302 var cells = convertCells(col.cells, tbody); | |
303 | |
304 var customFn = $(col).data("tablesaw-sort"); | |
305 | |
306 fn = | |
307 (customFn && typeof customFn === "function" ? customFn(ascending) : false) || | |
308 getSortFxn( | |
309 ascending, | |
310 $(col).is("[" + attrs.numericCol + "]") && | |
311 !$(col).is("[" + attrs.numericCol + '="false"]') | |
312 ); | |
313 | |
314 sorted = cells.sort(fn); | |
315 | |
316 rows = convertToRows(sorted); | |
317 | |
318 return rows; | |
319 }, | |
320 makeColDefault: function(col, a) { | |
321 var c = $(col); | |
322 c.attr(attrs.defaultCol, "true"); | |
323 if (a) { | |
324 c.removeClass(classes.descend); | |
325 c.addClass(classes.ascend); | |
326 } else { | |
327 c.removeClass(classes.ascend); | |
328 c.addClass(classes.descend); | |
329 } | |
330 }, | |
331 sortBy: function(col, ascending) { | |
332 var el = $(this); | |
333 var colNum; | |
334 var tbl = el.data("tablesaw"); | |
335 tbl.$tbody.each(function() { | |
336 var tbody = this; | |
337 var $tbody = $(this); | |
338 var rows = tbl.getBodyRows(tbody); | |
339 var sortedRows; | |
340 var map = tbl.headerMapping[0]; | |
341 var j, k; | |
342 | |
343 // find the column number that we’re sorting | |
344 for (j = 0, k = map.length; j < k; j++) { | |
345 if (map[j] === col) { | |
346 colNum = j; | |
347 break; | |
348 } | |
349 } | |
350 | |
351 sortedRows = el[pluginName]("sortRows", rows, colNum, ascending, col, tbody); | |
352 | |
353 // replace Table rows | |
354 for (j = 0, k = sortedRows.length; j < k; j++) { | |
355 $tbody.append(sortedRows[j]); | |
356 } | |
357 }); | |
358 | |
359 el[pluginName]("makeColDefault", col, ascending); | |
360 | |
361 el.trigger("tablesaw-sorted"); | |
362 } | |
363 }; | |
364 | |
365 // Collection method. | |
366 $.fn[pluginName] = function(arrg) { | |
367 var args = Array.prototype.slice.call(arguments, 1), | |
368 returnVal; | |
369 | |
370 // if it's a method | |
371 if (arrg && typeof arrg === "string") { | |
372 returnVal = $.fn[pluginName].prototype[arrg].apply(this[0], args); | |
373 return typeof returnVal !== "undefined" ? returnVal : $(this); | |
374 } | |
375 // check init | |
376 if (!$(this).data(pluginName + "-active")) { | |
377 $(this).data(pluginName + "-active", true); | |
378 $.fn[pluginName].prototype._create.call(this, arrg); | |
379 } | |
380 return $(this); | |
381 }; | |
382 // add methods | |
383 $.extend($.fn[pluginName].prototype, methods); | |
384 | |
385 $(document).on(Tablesaw.events.create, function(e, Tablesaw) { | |
386 if (Tablesaw.$table.is(initSelector)) { | |
387 Tablesaw.$table[pluginName](); | |
388 } | |
389 }); | |
390 | |
391 // TODO OOP this and add to Tablesaw object | |
392 })(); |