vendor: 55,935 bytes, lines 1-2156
1/*! Tablesaw - v3.1.2 - 2019-03-19 2* https://github.com/filamentgroup/tablesaw 3* Copyright (c) 2019 Filament Group; Licensed MIT */ 4(function (root, factory) { 5 if (typeof define === 'function' && define.amd) { 6 define(["jquery"], function (jQuery) { 7 return (root.Tablesaw = factory(jQuery, root)); 8 }); 9 } else if (typeof exports === 'object') { 10 if( "document" in root ) { 11 module.exports = factory(require('jquery'), root); 12 } else { 13 // special jQuery case for CommonJS (pass in a window) 14 module.exports = factory(require('jquery')(root), root); 15 } 16 } else { 17 root.Tablesaw = factory(jQuery, root); 18 } 19}(typeof window !== "undefined" ? window : this, function ($, window) { 20 "use strict"; 21 22 var document = window.document; 23 24// Account for Tablesaw being loaded either before or after the DOMContentLoaded event is fired. 25var domContentLoadedTriggered = /complete|loaded/.test(document.readyState); 26document.addEventListener("DOMContentLoaded", function() { 27 domContentLoadedTriggered = true; 28}); 29 30var Tablesaw = { 31 i18n: { 32 modeStack: "Stack", 33 modeSwipe: "Swipe", 34 modeToggle: "Toggle", 35 modeSwitchColumnsAbbreviated: "Cols", 36 modeSwitchColumns: "Columns", 37 columnToggleButton: "Columns", 38 columnToggleError: "No eligible columns.", 39 sort: "Sort", 40 swipePreviousColumn: "Previous column", 41 swipeNextColumn: "Next column" 42 }, 43 // cut the mustard 44 mustard: 45 "head" in document && // IE9+, Firefox 4+, Safari 5.1+, Mobile Safari 4.1+, Opera 11.5+, Android 2.3+ 46 (!window.blackberry || window.WebKitPoint) && // only WebKit Blackberry (OS 6+) 47 !window.operamini, 48 $: $, 49 _init: function(element) { 50 Tablesaw.$(element || document).trigger("enhance.tablesaw"); 51 }, 52 init: function(element) { 53 // Account for Tablesaw being loaded either before or after the DOMContentLoaded event is fired. 54 domContentLoadedTriggered = 55 domContentLoadedTriggered || /complete|loaded/.test(document.readyState); 56 if (!domContentLoadedTriggered) { 57 if ("addEventListener" in document) { 58 // Use raw DOMContentLoaded instead of shoestring (may have issues in Android 2.3, exhibited by stack table) 59 document.addEventListener("DOMContentLoaded", function() { 60 Tablesaw._init(element); 61 }); 62 } 63 } else { 64 Tablesaw._init(element); 65 } 66 } 67}; 68 69$(document).on("enhance.tablesaw", function() { 70 // Extend i18n config, if one exists. 71 if (typeof TablesawConfig !== "undefined" && TablesawConfig.i18n) { 72 Tablesaw.i18n = $.extend(Tablesaw.i18n, TablesawConfig.i18n || {}); 73 } 74 75 Tablesaw.i18n.modes = [ 76 Tablesaw.i18n.modeStack, 77 Tablesaw.i18n.modeSwipe, 78 Tablesaw.i18n.modeToggle 79 ]; 80}); 81 82if (Tablesaw.mustard) { 83 $(document.documentElement).addClass("tablesaw-enhanced"); 84} 85 86(function() { 87 var pluginName = "tablesaw"; 88 var classes = { 89 toolbar: "tablesaw-bar" 90 }; 91 var events = { 92 create: "tablesawcreate", 93 destroy: "tablesawdestroy", 94 refresh: "tablesawrefresh", 95 resize: "tablesawresize" 96 }; 97 var defaultMode = "stack"; 98 var initSelector = "table"; 99 var initFilterSelector = "[data-tablesaw],[data-tablesaw-mode],[data-tablesaw-sortable]"; 100 var defaultConfig = {}; 101 102 Tablesaw.events = events; 103 104 var Table = function(element) { 105 if (!element) { 106 throw new Error("Tablesaw requires an element."); 107 } 108 109 this.table = element; 110 this.$table = $(element); 111 112 // only one <thead> and <tfoot> are allowed, per the specification 113 this.$thead = this.$table 114 .children() 115 .filter("thead") 116 .eq(0); 117 118 // multiple <tbody> are allowed, per the specification 119 this.$tbody = this.$table.children().filter("tbody"); 120 121 this.mode = this.$table.attr("data-tablesaw-mode") || defaultMode; 122 123 this.$toolbar = null; 124 125 this.attributes = { 126 subrow: "data-tablesaw-subrow", 127 ignorerow: "data-tablesaw-ignorerow" 128 }; 129 130 this.init(); 131 }; 132 133 Table.prototype.init = function() { 134 if (!this.$thead.length) { 135 throw new Error("tablesaw: a <thead> is required, but none was found."); 136 } 137 138 if (!this.$thead.find("th").length) { 139 throw new Error("tablesaw: no header cells found. Are you using <th> inside of <thead>?"); 140 } 141 142 // assign an id if there is none 143 if (!this.$table.attr("id")) { 144 this.$table.attr("id", pluginName + "-" + Math.round(Math.random() * 10000)); 145 } 146 147 this.createToolbar(); 148 149 this._initCells(); 150 151 this.$table.data(pluginName, this); 152 153 this.$table.trigger(events.create, [this]); 154 }; 155 156 Table.prototype.getConfig = function(pluginSpecificConfig) { 157 // shoestring extend doesnât support arbitrary args 158 var configs = $.extend(defaultConfig, pluginSpecificConfig || {}); 159 return $.extend(configs, typeof TablesawConfig !== "undefined" ? TablesawConfig : {}); 160 }; 161 162 Table.prototype._getPrimaryHeaderRow = function() { 163 return this._getHeaderRows().eq(0); 164 }; 165 166 Table.prototype._getHeaderRows = function() { 167 return this.$thead 168 .children() 169 .filter("tr") 170 .filter(function() { 171 return !$(this).is("[data-tablesaw-ignorerow]"); 172 }); 173 }; 174 175 Table.prototype._getRowIndex = function($row) { 176 return $row.prevAll().length; 177 }; 178 179 Table.prototype._getHeaderRowIndeces = function() { 180 var self = this; 181 var indeces = []; 182 this._getHeaderRows().each(function() { 183 indeces.push(self._getRowIndex($(this))); 184 }); 185 return indeces; 186 }; 187 188 Table.prototype._getPrimaryHeaderCells = function($row) { 189 return ($row || this._getPrimaryHeaderRow()).find("th"); 190 }; 191 192 Table.prototype._$getCells = function(th) { 193 var self = this; 194 return $(th) 195 .add(th.cells) 196 .filter(function() { 197 var $t = $(this); 198 var $row = $t.parent(); 199 var hasColspan = $t.is("[colspan]"); 200 // no subrows or ignored rows (keep cells in ignored rows that do not have a colspan) 201 return ( 202 !$row.is("[" + self.attributes.subrow + "]") && 203 (!$row.is("[" + self.attributes.ignorerow + "]") || !hasColspan) 204 ); 205 }); 206 }; 207 208 Table.prototype._getVisibleColspan = function() { 209 var colspan = 0; 210 this._getPrimaryHeaderCells().each(function() { 211 var $t = $(this); 212 if ($t.css("display") !== "none") { 213 colspan += parseInt($t.attr("colspan"), 10) || 1; 214 } 215 }); 216 return colspan; 217 }; 218 219 Table.prototype.getColspanForCell = function($cell) { 220 var visibleColspan = this._getVisibleColspan(); 221 var visibleSiblingColumns = 0; 222 if ($cell.closest("tr").data("tablesaw-rowspanned")) { 223 visibleSiblingColumns++; 224 } 225 226 $cell.siblings().each(function() { 227 var $t = $(this); 228 var colColspan = parseInt($t.attr("colspan"), 10) || 1; 229 230 if ($t.css("display") !== "none") { 231 visibleSiblingColumns += colColspan; 232 } 233 }); 234 // console.log( $cell[ 0 ], visibleColspan, visibleSiblingColumns ); 235 236 return visibleColspan - visibleSiblingColumns; 237 }; 238 239 Table.prototype.isCellInColumn = function(header, cell) { 240 return $(header) 241 .add(header.cells) 242 .filter(function() { 243 return this === cell; 244 }).length; 245 }; 246 247 Table.prototype.updateColspanCells = function(cls, header, userAction) { 248 var self = this; 249 var primaryHeaderRow = self._getPrimaryHeaderRow(); 250 251 // find persistent column rowspans 252 this.$table.find("[rowspan][data-tablesaw-priority]").each(function() { 253 var $t = $(this); 254 if ($t.attr("data-tablesaw-priority") !== "persist") { 255 return; 256 } 257 258 var $row = $t.closest("tr"); 259 var rowspan = parseInt($t.attr("rowspan"), 10); 260 if (rowspan > 1) { 261 $row = $row.next(); 262 263 $row.data("tablesaw-rowspanned", true); 264 265 rowspan--; 266 } 267 }); 268 269 this.$table 270 .find("[colspan],[data-tablesaw-maxcolspan]") 271 .filter(function() { 272 // is not in primary header row 273 return $(this).closest("tr")[0] !== primaryHeaderRow[0]; 274 }) 275 .each(function() { 276 var $cell = $(this); 277 278 if (userAction === undefined || self.isCellInColumn(header, this)) { 279 } else { 280 // if is not a user action AND the cell is not in the updating column, kill it 281 return; 282 } 283 284 var colspan = self.getColspanForCell($cell); 285 286 if (cls && userAction !== undefined) { 287 // console.log( colspan === 0 ? "addClass" : "removeClass", $cell ); 288 $cell[colspan === 0 ? "addClass" : "removeClass"](cls); 289 } 290 291 // cache original colspan 292 var maxColspan = parseInt($cell.attr("data-tablesaw-maxcolspan"), 10); 293 if (!maxColspan) { 294 $cell.attr("data-tablesaw-maxcolspan", $cell.attr("colspan")); 295 } else if (colspan > maxColspan) { 296 colspan = maxColspan; 297 } 298 299 // console.log( this, "setting colspan to ", colspan ); 300 $cell.attr("colspan", colspan); 301 }); 302 }; 303 304 Table.prototype._findPrimaryHeadersForCell = function(cell) { 305 var $headerRow = this._getPrimaryHeaderRow(); 306 var headerRowIndex = this._getRowIndex($headerRow); 307 var results = []; 308 309 for (var rowNumber = 0; rowNumber < this.headerMapping.length; rowNumber++) { 310 if (rowNumber === headerRowIndex) { 311 continue; 312 } 313 314 for (var colNumber = 0; colNumber < this.headerMapping[rowNumber].length; colNumber++) { 315 if (this.headerMapping[rowNumber][colNumber] === cell) { 316 results.push(this.headerMapping[headerRowIndex][colNumber]); 317 } 318 } 319 } 320 321 return results; 322 }; 323 324 // used by init cells 325 Table.prototype.getRows = function() { 326 var self = this; 327 return this.$table.find("tr").filter(function() { 328 return $(this) 329 .closest("table") 330 .is(self.$table); 331 }); 332 }; 333 334 // used by sortable 335 Table.prototype.getBodyRows = function(tbody) { 336 return (tbody ? $(tbody) : this.$tbody).children().filter("tr"); 337 }; 338 339 Table.prototype.getHeaderCellIndex = function(cell) { 340 var lookup = this.headerMapping[0]; 341 for (var colIndex = 0; colIndex < lookup.length; colIndex++) { 342 if (lookup[colIndex] === cell) { 343 return colIndex; 344 } 345 } 346 347 return -1; 348 }; 349 350 Table.prototype._initCells = function() { 351 // re-establish original colspans 352 this.$table.find("[data-tablesaw-maxcolspan]").each(function() { 353 var $t = $(this); 354 $t.attr("colspan", $t.attr("data-tablesaw-maxcolspan")); 355 }); 356 357 var $rows = this.getRows(); 358 var columnLookup = []; 359 360 $rows.each(function(rowNumber) { 361 columnLookup[rowNumber] = []; 362 }); 363 364 $rows.each(function(rowNumber) { 365 var coltally = 0; 366 var $t = $(this); 367 var children = $t.children(); 368 369 children.each(function() { 370 var colspan = parseInt( 371 this.getAttribute("data-tablesaw-maxcolspan") || this.getAttribute("colspan"), 372 10 373 ); 374 var rowspan = parseInt(this.getAttribute("rowspan"), 10); 375 376 // set in a previous rowspan 377 while (columnLookup[rowNumber][coltally]) { 378 coltally++; 379 } 380 381 columnLookup[rowNumber][coltally] = this; 382 383 // TODO? both colspan and rowspan 384 if (colspan) { 385 for (var k = 0; k < colspan - 1; k++) { 386 coltally++; 387 columnLookup[rowNumber][coltally] = this; 388 } 389 } 390 if (rowspan) { 391 for (var j = 1; j < rowspan; j++) { 392 columnLookup[rowNumber + j][coltally] = this; 393 } 394 } 395 396 coltally++; 397 }); 398 }); 399 400 var headerRowIndeces = this._getHeaderRowIndeces(); 401 for (var colNumber = 0; colNumber < columnLookup[0].length; colNumber++) { 402 for (var headerIndex = 0, k = headerRowIndeces.length; headerIndex < k; headerIndex++) { 403 var headerCol = columnLookup[headerRowIndeces[headerIndex]][colNumber]; 404 405 var rowNumber = headerRowIndeces[headerIndex]; 406 var rowCell; 407 408 if (!headerCol.cells) { 409 headerCol.cells = []; 410 } 411 412 while (rowNumber < columnLookup.length) { 413 rowCell = columnLookup[rowNumber][colNumber]; 414 415 if (headerCol !== rowCell) { 416 headerCol.cells.push(rowCell); 417 } 418 419 rowNumber++; 420 } 421 } 422 } 423 424 this.headerMapping = columnLookup; 425 }; 426 427 Table.prototype.refresh = function() { 428 this._initCells(); 429 430 this.$table.trigger(events.refresh, [this]); 431 }; 432 433 Table.prototype._getToolbarAnchor = function() { 434 var $parent = this.$table.parent(); 435 if ($parent.is(".tablesaw-overflow")) { 436 return $parent; 437 } 438 return this.$table; 439 }; 440 441 Table.prototype._getToolbar = function($anchor) { 442 if (!$anchor) { 443 $anchor = this._getToolbarAnchor(); 444 } 445 return $anchor.prev().filter("." + classes.toolbar); 446 }; 447 448 Table.prototype.createToolbar = function() { 449 // Insert the toolbar 450 // TODO move this into a separate component 451 var $anchor = this._getToolbarAnchor(); 452 var $toolbar = this._getToolbar($anchor); 453 if (!$toolbar.length) { 454 $toolbar = $("<div>") 455 .addClass(classes.toolbar) 456 .insertBefore($anchor); 457 } 458 this.$toolbar = $toolbar; 459 460 if (this.mode) { 461 this.$toolbar.addClass("tablesaw-mode-" + this.mode); 462 } 463 }; 464 465 Table.prototype.destroy = function() { 466 // Donât remove the toolbar, just erase the classes on it. 467 // Some of the table features are not yet destroy-friendly. 468 this._getToolbar().each(function() { 469 this.className = this.className.replace(/\btablesaw-mode\-\w*\b/gi, ""); 470 }); 471 472 var tableId = this.$table.attr("id"); 473 $(document).off("." + tableId); 474 $(window).off("." + tableId); 475 476 // other plugins 477 this.$table.trigger(events.destroy, [this]); 478 479 this.$table.removeData(pluginName); 480 }; 481 482 // Collection method. 483 $.fn[pluginName] = function() { 484 return this.each(function() { 485 var $t = $(this); 486 487 if ($t.data(pluginName)) { 488 return; 489 } 490 491 new Table(this); 492 }); 493 }; 494 495 var $doc = $(document); 496 $doc.on("enhance.tablesaw", function(e) { 497 // Cut the mustard 498 if (Tablesaw.mustard) { 499 var $target = $(e.target); 500 if ($target.parent().length) { 501 $target = $target.parent(); 502 } 503 504 $target 505 .find(initSelector) 506 .filter(initFilterSelector) 507 [pluginName](); 508 } 509 }); 510 511 // Avoid a resize during scroll: 512 // Some Mobile devices trigger a resize during scroll (sometimes when 513 // doing elastic stretch at the end of the document or from the 514 // location bar hide) 515 var isScrolling = false; 516 var scrollTimeout; 517 $doc.on("scroll.tablesaw", function() { 518 isScrolling = true; 519 520 window.clearTimeout(scrollTimeout); 521 scrollTimeout = window.setTimeout(function() { 522 isScrolling = false; 523 }, 300); // must be greater than the resize timeout below 524 }); 525 526 var resizeTimeout; 527 $(window).on("resize", function() { 528 if (!isScrolling) { 529 window.clearTimeout(resizeTimeout); 530 resizeTimeout = window.setTimeout(function() { 531 $doc.trigger(events.resize); 532 }, 150); // must be less than the scrolling timeout above. 533 } 534 }); 535 536 Tablesaw.Table = Table; 537})(); 538 539(function() { 540 var classes = { 541 stackTable: "tablesaw-stack", 542 cellLabels: "tablesaw-cell-label", 543 cellContentLabels: "tablesaw-cell-content" 544 }; 545 546 var data = { 547 key: "tablesaw-stack" 548 }; 549 550 var attrs = { 551 labelless: "data-tablesaw-no-labels", 552 hideempty: "data-tablesaw-hide-empty" 553 }; 554 555 var Stack = function(element, tablesaw) { 556 this.tablesaw = tablesaw; 557 this.$table = $(element); 558 559 this.labelless = this.$table.is("[" + attrs.labelless + "]"); 560 this.hideempty = this.$table.is("[" + attrs.hideempty + "]"); 561 562 this.$table.data(data.key, this); 563 }; 564 565 Stack.prototype.init = function() { 566 this.$table.addClass(classes.stackTable); 567 568 if (this.labelless) { 569 return; 570 } 571 572 var self = this; 573 574 this.$table 575 .find("th, td") 576 .filter(function() { 577 return !$(this).closest("thead").length; 578 }) 579 .filter(function() { 580 return ( 581 !$(this).is("[" + attrs.labelless + "]") && 582 !$(this) 583 .closest("tr") 584 .is("[" + attrs.labelless + "]") && 585 (!self.hideempty || !!$(this).html()) 586 ); 587 }) 588 .each(function() { 589 var $newHeader = $(document.createElement("b")).addClass(classes.cellLabels); 590 var $cell = $(this); 591 592 $(self.tablesaw._findPrimaryHeadersForCell(this)).each(function(index) { 593 var $header = $(this.cloneNode(true)); 594 // TODO decouple from sortable better 595 // Changed from .text() in https://github.com/filamentgroup/tablesaw/commit/b9c12a8f893ec192830ec3ba2d75f062642f935b 596 // to preserve structural html in headers, like <a> 597 var $sortableButton = $header.find(".tablesaw-sortable-btn"); 598 $header.find(".tablesaw-sortable-arrow").remove(); 599 600 // TODO decouple from checkall better 601 var $checkall = $header.find("[data-tablesaw-checkall]"); 602 $checkall.closest("label").remove(); 603 if ($checkall.length) { 604 $newHeader = $([]); 605 return; 606 } 607 608 if (index > 0) { 609 $newHeader.append(document.createTextNode(", ")); 610 } 611 612 var parentNode = $sortableButton.length ? $sortableButton[0] : $header[0]; 613 var el; 614 while ((el = parentNode.firstChild)) { 615 $newHeader[0].appendChild(el); 616 } 617 }); 618 619 if ($newHeader.length && !$cell.find("." + classes.cellContentLabels).length) { 620 $cell.wrapInner("<span class='" + classes.cellContentLabels + "'></span>"); 621 } 622 623 // Update if already exists. 624 var $label = $cell.find("." + classes.cellLabels); 625 if (!$label.length) { 626 $cell.prepend(document.createTextNode(" ")); 627 $cell.prepend($newHeader); 628 } else { 629 // only if changed 630 $label.replaceWith($newHeader); 631 } 632 }); 633 }; 634 635 Stack.prototype.destroy = function() { 636 this.$table.removeClass(classes.stackTable); 637 this.$table.find("." + classes.cellLabels).remove(); 638 this.$table.find("." + classes.cellContentLabels).each(function() { 639 $(this).replaceWith($(this.childNodes)); 640 }); 641 }; 642 643 // on tablecreate, init 644 $(document) 645 .on(Tablesaw.events.create, function(e, tablesaw) { 646 if (tablesaw.mode === "stack") { 647 var table = new Stack(tablesaw.table, tablesaw); 648 table.init(); 649 } 650 }) 651 .on(Tablesaw.events.refresh, function(e, tablesaw) { 652 if (tablesaw.mode === "stack") { 653 $(tablesaw.table) 654 .data(data.key) 655 .init(); 656 } 657 }) 658 .on(Tablesaw.events.destroy, function(e, tablesaw) { 659 if (tablesaw.mode === "stack") { 660 $(tablesaw.table) 661 .data(data.key) 662 .destroy(); 663 } 664 }); 665 666 Tablesaw.Stack = Stack; 667})(); 668 669(function() { 670 var pluginName = "tablesawbtn", 671 methods = { 672 _create: function() { 673 return $(this).each(function() { 674 $(this) 675 .trigger("beforecreate." + pluginName) 676 [pluginName]("_init") 677 .trigger("create." + pluginName); 678 }); 679 }, 680 _init: function() { 681 var oEl = $(this), 682 sel = this.getElementsByTagName("select")[0]; 683 684 if (sel) { 685 // TODO next major version: remove .btn-select 686 $(this) 687 .addClass("btn-select tablesaw-btn-select") 688 [pluginName]("_select", sel); 689 } 690 return oEl; 691 }, 692 _select: function(sel) { 693 var update = function(oEl, sel) { 694 var opts = $(sel).find("option"); 695 var label = document.createElement("span"); 696 var el; 697 var children; 698 var found = false; 699 700 label.setAttribute("aria-hidden", "true"); 701 label.innerHTML = " "; 702 703 opts.each(function() { 704 var opt = this; 705 if (opt.selected) { 706 label.innerHTML = opt.text; 707 } 708 }); 709 710 children = oEl.childNodes; 711 if (opts.length > 0) { 712 for (var i = 0, l = children.length; i < l; i++) { 713 el = children[i]; 714 715 if (el && el.nodeName.toUpperCase() === "SPAN") { 716 oEl.replaceChild(label, el); 717 found = true; 718 } 719 } 720 721 if (!found) { 722 oEl.insertBefore(label, oEl.firstChild); 723 } 724 } 725 }; 726 727 update(this, sel); 728 // todo should this be tablesawrefresh? 729 $(this).on("change refresh", function() { 730 update(this, sel); 731 }); 732 } 733 }; 734 735 // Collection method. 736 $.fn[pluginName] = function(arrg, a, b, c) { 737 return this.each(function() { 738 // if it's a method 739 if (arrg && typeof arrg === "string") { 740 return $.fn[pluginName].prototype[arrg].call(this, a, b, c); 741 } 742 743 // don't re-init 744 if ($(this).data(pluginName + "active")) { 745 return $(this); 746 } 747 748 $(this).data(pluginName + "active", true); 749 750 $.fn[pluginName].prototype._create.call(this); 751 }); 752 }; 753 754 // add methods 755 $.extend($.fn[pluginName].prototype, methods); 756 757 // TODO OOP this and add to Tablesaw object 758})(); 759 760(function() { 761 var data = { 762 key: "tablesaw-coltoggle" 763 }; 764 765 var ColumnToggle = function(element) { 766 this.$table = $(element); 767 768 if (!this.$table.length) { 769 return; 770 } 771 772 this.tablesaw = this.$table.data("tablesaw"); 773 774 this.attributes = { 775 btnTarget: "data-tablesaw-columntoggle-btn-target", 776 set: "data-tablesaw-columntoggle-set" 777 }; 778 779 this.classes = { 780 columnToggleTable: "tablesaw-columntoggle", 781 columnBtnContain: "tablesaw-columntoggle-btnwrap tablesaw-advance", 782 columnBtn: "tablesaw-columntoggle-btn tablesaw-nav-btn down", 783 popup: "tablesaw-columntoggle-popup", 784 priorityPrefix: "tablesaw-priority-" 785 }; 786 787 this.set = []; 788 this.$headers = this.tablesaw._getPrimaryHeaderCells(); 789 790 this.$table.data(data.key, this); 791 }; 792 793 // Column Toggle Sets (one column chooser can control multiple tables) 794 ColumnToggle.prototype.initSet = function() { 795 var set = this.$table.attr(this.attributes.set); 796 if (set) { 797 // Should not include the current table 798 var table = this.$table[0]; 799 this.set = $("table[" + this.attributes.set + "='" + set + "']") 800 .filter(function() { 801 return this !== table; 802 }) 803 .get(); 804 } 805 }; 806 807 ColumnToggle.prototype.init = function() { 808 if (!this.$table.length) { 809 return; 810 } 811 812 var tableId, 813 id, 814 $menuButton, 815 $popup, 816 $menu, 817 $btnContain, 818 self = this; 819 820 var cfg = this.tablesaw.getConfig({ 821 getColumnToggleLabelTemplate: function(text) { 822 return "<label><input type='checkbox' checked>" + text + "</label>"; 823 } 824 }); 825 826 this.$table.addClass(this.classes.columnToggleTable); 827 828 tableId = this.$table.attr("id"); 829 id = tableId + "-popup"; 830 $btnContain = $("<div class='" + this.classes.columnBtnContain + "'></div>"); 831 // TODO next major version: remove .btn 832 $menuButton = $( 833 "<a href='#" + 834 id + 835 "' class='btn tablesaw-btn btn-micro " + 836 this.classes.columnBtn + 837 "' data-popup-link>" + 838 "<span>" + 839 Tablesaw.i18n.columnToggleButton + 840 "</span></a>" 841 ); 842 $popup = $("<div class='" + this.classes.popup + "' id='" + id + "'></div>"); 843 $menu = $("<div class='tablesaw-btn-group'></div>"); 844 845 this.$popup = $popup; 846 847 var hasNonPersistentHeaders = false; 848 this.$headers.each(function() { 849 var $this = $(this), 850 priority = $this.attr("data-tablesaw-priority"), 851 $cells = self.tablesaw._$getCells(this); 852 853 if (priority && priority !== "persist") { 854 $cells.addClass(self.classes.priorityPrefix + priority); 855 856 $(cfg.getColumnToggleLabelTemplate($this.text())) 857 .appendTo($menu) 858 .find('input[type="checkbox"]') 859 .data("tablesaw-header", this); 860 861 hasNonPersistentHeaders = true; 862 } 863 }); 864 865 if (!hasNonPersistentHeaders) { 866 $menu.append("<label>" + Tablesaw.i18n.columnToggleError + "</label>"); 867 } 868 869 $menu.appendTo($popup); 870 871 function onToggleCheckboxChange(checkbox) { 872 var checked = checkbox.checked; 873 874 var header = self.getHeaderFromCheckbox(checkbox); 875 var $cells = self.tablesaw._$getCells(header); 876 877 $cells[!checked ? "addClass" : "removeClass"]("tablesaw-toggle-cellhidden"); 878 $cells[checked ? "addClass" : "removeClass"]("tablesaw-toggle-cellvisible"); 879 880 self.updateColspanCells(header, checked); 881 882 self.$table.trigger("tablesawcolumns"); 883 } 884 885 // bind change event listeners to inputs - TODO: move to a private method? 886 $menu.find('input[type="checkbox"]').on("change", function(e) { 887 onToggleCheckboxChange(e.target); 888 889 if (self.set.length) { 890 var index; 891 $(self.$popup) 892 .find("input[type='checkbox']") 893 .each(function(j) { 894 if (this === e.target) { 895 index = j; 896 return false; 897 } 898 }); 899 900 $(self.set).each(function() { 901 var checkbox = $(this) 902 .data(data.key) 903 .$popup.find("input[type='checkbox']") 904 .get(index); 905 if (checkbox) { 906 checkbox.checked = e.target.checked; 907 onToggleCheckboxChange(checkbox); 908 } 909 }); 910 } 911 }); 912 913 $menuButton.appendTo($btnContain); 914 915 // Use a different target than the toolbar 916 var $btnTarget = $(this.$table.attr(this.attributes.btnTarget)); 917 $btnContain.appendTo($btnTarget.length ? $btnTarget : this.tablesaw.$toolbar); 918 919 function closePopup(event) { 920 // Click came from inside the popup, ignore. 921 if (event && $(event.target).closest("." + self.classes.popup).length) { 922 return; 923 } 924 925 $(document).off("click." + tableId); 926 $menuButton.removeClass("up").addClass("down"); 927 $btnContain.removeClass("visible"); 928 } 929 930 var closeTimeout; 931 function openPopup() { 932 $btnContain.addClass("visible"); 933 $menuButton.removeClass("down").addClass("up"); 934 935 $(document).off("click." + tableId, closePopup); 936 937 window.clearTimeout(closeTimeout); 938 closeTimeout = window.setTimeout(function() { 939 $(document).on("click." + tableId, closePopup); 940 }, 15); 941 } 942 943 $menuButton.on("click.tablesaw", function(event) { 944 event.preventDefault(); 945 946 if (!$btnContain.is(".visible")) { 947 openPopup(); 948 } else { 949 closePopup(); 950 } 951 }); 952 953 $popup.appendTo($btnContain); 954 955 this.$menu = $menu; 956 957 // Fix for iOS not rendering shadows correctly when using `-webkit-overflow-scrolling` 958 var $overflow = this.$table.closest(".tablesaw-overflow"); 959 if ($overflow.css("-webkit-overflow-scrolling")) { 960 var timeout; 961 $overflow.on("scroll", function() { 962 var $div = $(this); 963 window.clearTimeout(timeout); 964 timeout = window.setTimeout(function() { 965 $div.css("-webkit-overflow-scrolling", "auto"); 966 window.setTimeout(function() { 967 $div.css("-webkit-overflow-scrolling", "touch"); 968 }, 0); 969 }, 100); 970 }); 971 } 972 973 $(window).on(Tablesaw.events.resize + "." + tableId, function() { 974 self.refreshToggle(); 975 }); 976 977 this.initSet(); 978 this.refreshToggle(); 979 }; 980 981 ColumnToggle.prototype.getHeaderFromCheckbox = function(checkbox) { 982 return $(checkbox).data("tablesaw-header"); 983 }; 984 985 ColumnToggle.prototype.refreshToggle = function() { 986 var self = this; 987 var invisibleColumns = 0; 988 this.$menu.find("input").each(function() { 989 var header = self.getHeaderFromCheckbox(this); 990 this.checked = 991 self.tablesaw 992 ._$getCells(header) 993 .eq(0) 994 .css("display") === "table-cell"; 995 }); 996 997 this.updateColspanCells(); 998 }; 999 1000 ColumnToggle.prototype.updateColspanCells = function(header, userAction) { 1001 this.tablesaw.updateColspanCells("tablesaw-toggle-cellhidden", header, userAction); 1002 }; 1003 1004 ColumnToggle.prototype.destroy = function() { 1005 this.$table.removeClass(this.classes.columnToggleTable); 1006 this.$table.find("th, td").each(function() { 1007 var $cell = $(this); 1008 $cell.removeClass("tablesaw-toggle-cellhidden").removeClass("tablesaw-toggle-cellvisible"); 1009 1010 this.className = this.className.replace(/\bui\-table\-priority\-\d\b/g, ""); 1011 }); 1012 }; 1013 1014 // on tablecreate, init 1015 $(document).on(Tablesaw.events.create, function(e, tablesaw) { 1016 if (tablesaw.mode === "columntoggle") { 1017 var table = new ColumnToggle(tablesaw.table); 1018 table.init(); 1019 } 1020 }); 1021 1022 $(document).on(Tablesaw.events.destroy, function(e, tablesaw) { 1023 if (tablesaw.mode === "columntoggle") { 1024 $(tablesaw.table) 1025 .data(data.key) 1026 .destroy(); 1027 } 1028 }); 1029 1030 $(document).on(Tablesaw.events.refresh, function(e, tablesaw) { 1031 if (tablesaw.mode === "columntoggle") { 1032 $(tablesaw.table) 1033 .data(data.key) 1034 .refreshToggle(); 1035 } 1036 }); 1037 1038 Tablesaw.ColumnToggle = ColumnToggle; 1039})(); 1040 1041(function() { 1042 function getSortValue(cell) { 1043 var text = []; 1044 $(cell.childNodes).each(function() { 1045 var $el = $(this); 1046 if ($el.is("input, select")) { 1047 text.push($el.val()); 1048 } else if ($el.is(".tablesaw-cell-label")) { 1049 } else { 1050 text.push(($el.text() || "").replace(/^\s+|\s+$/g, "")); 1051 } 1052 }); 1053 1054 return text.join(""); 1055 } 1056 1057 var pluginName = "tablesaw-sortable", 1058 initSelector = "table[data-" + pluginName + "]", 1059 sortableSwitchSelector = "[data-" + pluginName + "-switch]", 1060 attrs = { 1061 sortCol: "data-tablesaw-sortable-col", 1062 defaultCol: "data-tablesaw-sortable-default-col", 1063 numericCol: "data-tablesaw-sortable-numeric", 1064 subRow: "data-tablesaw-subrow", 1065 ignoreRow: "data-tablesaw-ignorerow" 1066 }, 1067 classes = { 1068 head: pluginName + "-head", 1069 ascend: pluginName + "-ascending", 1070 descend: pluginName + "-descending", 1071 switcher: pluginName + "-switch", 1072 tableToolbar: "tablesaw-bar-section", 1073 sortButton: pluginName + "-btn" 1074 }, 1075 methods = { 1076 _create: function(o) { 1077 return $(this).each(function() { 1078 var init = $(this).data(pluginName + "-init"); 1079 if (init) { 1080 return false; 1081 } 1082 $(this) 1083 .data(pluginName + "-init", true) 1084 .trigger("beforecreate." + pluginName) 1085 [pluginName]("_init", o) 1086 .trigger("create." + pluginName); 1087 }); 1088 }, 1089 _init: function() { 1090 var el = $(this); 1091 var tblsaw = el.data("tablesaw"); 1092 var heads; 1093 var $switcher; 1094 1095 function addClassToHeads(h) { 1096 $.each(h, function(i, v) { 1097 $(v).addClass(classes.head); 1098 }); 1099 } 1100 1101 function makeHeadsActionable(h, fn) { 1102 $.each(h, function(i, col) { 1103 var b = $("<button class='" + classes.sortButton + "'/>"); 1104 b.on("click", { col: col }, fn); 1105 $(col) 1106 .wrapInner(b) 1107 .find("button") 1108 .append("<span class='tablesaw-sortable-arrow'>"); 1109 }); 1110 } 1111 1112 function clearOthers(headcells) { 1113 $.each(headcells, function(i, v) { 1114 var col = $(v); 1115 col.removeAttr(attrs.defaultCol); 1116 col.removeClass(classes.ascend); 1117 col.removeClass(classes.descend); 1118 }); 1119 } 1120 1121 function headsOnAction(e) { 1122 if ($(e.target).is("a[href]")) { 1123 return; 1124 } 1125 1126 e.stopPropagation(); 1127 var headCell = $(e.target).closest("[" + attrs.sortCol + "]"), 1128 v = e.data.col, 1129 newSortValue = heads.index(headCell[0]); 1130 1131 clearOthers( 1132 headCell 1133 .closest("thead") 1134 .find("th") 1135 .filter(function() { 1136 return this !== headCell[0]; 1137 }) 1138 ); 1139 if (headCell.is("." + classes.descend) || !headCell.is("." + classes.ascend)) { 1140 el[pluginName]("sortBy", v, true); 1141 newSortValue += "_asc"; 1142 } else { 1143 el[pluginName]("sortBy", v); 1144 newSortValue += "_desc"; 1145 } 1146 if ($switcher) { 1147 $switcher 1148 .find("select") 1149 .val(newSortValue) 1150 .trigger("refresh"); 1151 } 1152 1153 e.preventDefault(); 1154 } 1155 1156 function handleDefault(heads) { 1157 $.each(heads, function(idx, el) { 1158 var $el = $(el); 1159 if ($el.is("[" + attrs.defaultCol + "]")) { 1160 if (!$el.is("." + classes.descend)) { 1161 $el.addClass(classes.ascend); 1162 } 1163 } 1164 }); 1165 } 1166 1167 function addSwitcher(heads) { 1168 $switcher = $("<div>") 1169 .addClass(classes.switcher) 1170 .addClass(classes.tableToolbar); 1171 1172 var html = ["<label>" + Tablesaw.i18n.sort + ":"]; 1173 1174 // TODO next major version: remove .btn 1175 html.push('<span class="btn tablesaw-btn"><select>'); 1176 heads.each(function(j) { 1177 var $t = $(this); 1178 var isDefaultCol = $t.is("[" + attrs.defaultCol + "]"); 1179 var isDescending = $t.is("." + classes.descend); 1180 1181 var hasNumericAttribute = $t.is("[" + attrs.numericCol + "]"); 1182 var numericCount = 0; 1183 // Check only the first four rows to see if the column is numbers. 1184 var numericCountMax = 5; 1185 1186 $(this.cells.slice(0, numericCountMax)).each(function() { 1187 if (!isNaN(parseInt(getSortValue(this), 10))) { 1188 numericCount++; 1189 } 1190 }); 1191 var isNumeric = numericCount === numericCountMax; 1192 if (!hasNumericAttribute) { 1193 $t.attr(attrs.numericCol, isNumeric ? "" : "false"); 1194 } 1195 1196 html.push( 1197 "<option" + 1198 (isDefaultCol && !isDescending ? " selected" : "") + 1199 ' value="' + 1200 j + 1201 '_asc">' + 1202 $t.text() + 1203 " " + 1204 (isNumeric ? "↑" : "(A-Z)") + 1205 "</option>" 1206 ); 1207 html.push( 1208 "<option" + 1209 (isDefaultCol && isDescending ? " selected" : "") + 1210 ' value="' + 1211 j + 1212 '_desc">' + 1213 $t.text() + 1214 " " + 1215 (isNumeric ? "↓" : "(Z-A)") + 1216 "</option>" 1217 ); 1218 }); 1219 html.push("</select></span></label>"); 1220 1221 $switcher.html(html.join("")); 1222 1223 var $firstChild = tblsaw.$toolbar.children().eq(0); 1224 if ($firstChild.length) { 1225 $switcher.insertBefore($firstChild); 1226 } else { 1227 $switcher.appendTo(tblsaw.$toolbar); 1228 } 1229 $switcher.find(".tablesaw-btn").tablesawbtn(); 1230 $switcher.find("select").on("change", function() { 1231 var val = $(this) 1232 .val() 1233 .split("_"), 1234 head = heads.eq(val[0]); 1235 1236 clearOthers(head.siblings()); 1237 el[pluginName]("sortBy", head.get(0), val[1] === "asc"); 1238 }); 1239 } 1240 1241 el.addClass(pluginName); 1242 1243 heads = el 1244 .children() 1245 .filter("thead") 1246 .find("th[" + attrs.sortCol + "]"); 1247 1248 addClassToHeads(heads); 1249 makeHeadsActionable(heads, headsOnAction); 1250 handleDefault(heads); 1251 1252 if (el.is(sortableSwitchSelector)) { 1253 addSwitcher(heads); 1254 } 1255 }, 1256 sortRows: function(rows, colNum, ascending, col, tbody) { 1257 function convertCells(cellArr, belongingToTbody) { 1258 var cells = []; 1259 $.each(cellArr, function(i, cell) { 1260 var row = cell.parentNode; 1261 var $row = $(row); 1262 // next row is a subrow 1263 var subrows = []; 1264 var $next = $row.next(); 1265 while ($next.is("[" + attrs.subRow + "]")) { 1266 subrows.push($next[0]); 1267 $next = $next.next(); 1268 } 1269 1270 var tbody = row.parentNode; 1271 1272 // current row is a subrow 1273 if ($row.is("[" + attrs.subRow + "]")) { 1274 } else if (tbody === belongingToTbody) { 1275 cells.push({ 1276 element: cell, 1277 cell: getSortValue(cell), 1278 row: row, 1279 subrows: subrows.length ? subrows : null, 1280 ignored: $row.is("[" + attrs.ignoreRow + "]") 1281 }); 1282 } 1283 }); 1284 return cells; 1285 } 1286 1287 function getSortFxn(ascending, forceNumeric) { 1288 var fn, 1289 regex = /[^\-\+\d\.]/g; 1290 if (ascending) { 1291 fn = function(a, b) { 1292 if (a.ignored || b.ignored) { 1293 return 0; 1294 } 1295 if (forceNumeric) { 1296 return ( 1297 parseFloat(a.cell.replace(regex, "")) - parseFloat(b.cell.replace(regex, "")) 1298 ); 1299 } else { 1300 return a.cell.toLowerCase() > b.cell.toLowerCase() ? 1 : -1; 1301 } 1302 }; 1303 } else { 1304 fn = function(a, b) { 1305 if (a.ignored || b.ignored) { 1306 return 0; 1307 } 1308 if (forceNumeric) { 1309 return ( 1310 parseFloat(b.cell.replace(regex, "")) - parseFloat(a.cell.replace(regex, "")) 1311 ); 1312 } else { 1313 return a.cell.toLowerCase() < b.cell.toLowerCase() ? 1 : -1; 1314 } 1315 }; 1316 } 1317 return fn; 1318 } 1319 1320 function convertToRows(sorted) { 1321 var newRows = [], 1322 i, 1323 l; 1324 for (i = 0, l = sorted.length; i < l; i++) { 1325 newRows.push(sorted[i].row); 1326 if (sorted[i].subrows) { 1327 newRows.push(sorted[i].subrows); 1328 } 1329 } 1330 return newRows; 1331 } 1332 1333 var fn; 1334 var sorted; 1335 var cells = convertCells(col.cells, tbody); 1336 1337 var customFn = $(col).data("tablesaw-sort"); 1338 1339 fn = 1340 (customFn && typeof customFn === "function" ? customFn(ascending) : false) || 1341 getSortFxn( 1342 ascending, 1343 $(col).is("[" + attrs.numericCol + "]") && 1344 !$(col).is("[" + attrs.numericCol + '="false"]') 1345 ); 1346 1347 sorted = cells.sort(fn); 1348 1349 rows = convertToRows(sorted); 1350 1351 return rows; 1352 }, 1353 makeColDefault: function(col, a) { 1354 var c = $(col); 1355 c.attr(attrs.defaultCol, "true"); 1356 if (a) { 1357 c.removeClass(classes.descend); 1358 c.addClass(classes.ascend); 1359 } else { 1360 c.removeClass(classes.ascend); 1361 c.addClass(classes.descend); 1362 } 1363 }, 1364 sortBy: function(col, ascending) { 1365 var el = $(this); 1366 var colNum; 1367 var tbl = el.data("tablesaw"); 1368 tbl.$tbody.each(function() { 1369 var tbody = this; 1370 var $tbody = $(this); 1371 var rows = tbl.getBodyRows(tbody); 1372 var sortedRows; 1373 var map = tbl.headerMapping[0]; 1374 var j, k; 1375 1376 // find the column number that weâre sorting 1377 for (j = 0, k = map.length; j < k; j++) { 1378 if (map[j] === col) { 1379 colNum = j; 1380 break; 1381 } 1382 } 1383 1384 sortedRows = el[pluginName]("sortRows", rows, colNum, ascending, col, tbody); 1385 1386 // replace Table rows 1387 for (j = 0, k = sortedRows.length; j < k; j++) { 1388 $tbody.append(sortedRows[j]); 1389 } 1390 }); 1391 1392 el[pluginName]("makeColDefault", col, ascending); 1393 1394 el.trigger("tablesaw-sorted"); 1395 } 1396 }; 1397 1398 // Collection method. 1399 $.fn[pluginName] = function(arrg) { 1400 var args = Array.prototype.slice.call(arguments, 1), 1401 returnVal; 1402 1403 // if it's a method 1404 if (arrg && typeof arrg === "string") { 1405 returnVal = $.fn[pluginName].prototype[arrg].apply(this[0], args); 1406 return typeof returnVal !== "undefined" ? returnVal : $(this); 1407 } 1408 // check init 1409 if (!$(this).data(pluginName + "-active")) { 1410 $(this).data(pluginName + "-active", true); 1411 $.fn[pluginName].prototype._create.call(this, arrg); 1412 } 1413 return $(this); 1414 }; 1415 // add methods 1416 $.extend($.fn[pluginName].prototype, methods); 1417 1418 $(document).on(Tablesaw.events.create, function(e, Tablesaw) { 1419 if (Tablesaw.$table.is(initSelector)) { 1420 Tablesaw.$table[pluginName](); 1421 } 1422 }); 1423 1424 // TODO OOP this and add to Tablesaw object 1425})(); 1426 1427(function() { 1428 var classes = { 1429 hideBtn: "disabled", 1430 persistWidths: "tablesaw-fix-persist", 1431 hiddenCol: "tablesaw-swipe-cellhidden", 1432 persistCol: "tablesaw-swipe-cellpersist", 1433 allColumnsVisible: "tablesaw-all-cols-visible" 1434 }; 1435 var attrs = { 1436 disableTouchEvents: "data-tablesaw-no-touch", 1437 ignorerow: "data-tablesaw-ignorerow", 1438 subrow: "data-tablesaw-subrow" 1439 }; 1440 1441 function createSwipeTable(tbl, $table) { 1442 var tblsaw = $table.data("tablesaw"); 1443 1444 var $btns = $("<div class='tablesaw-advance'></div>"); 1445 // TODO next major version: remove .btn 1446 var $prevBtn = $( 1447 "<a href='#' class='btn tablesaw-nav-btn tablesaw-btn btn-micro left'>" + 1448 Tablesaw.i18n.swipePreviousColumn + 1449 "</a>" 1450 ).appendTo($btns); 1451 // TODO next major version: remove .btn 1452 var $nextBtn = $( 1453 "<a href='#' class='btn tablesaw-nav-btn tablesaw-btn btn-micro right'>" + 1454 Tablesaw.i18n.swipeNextColumn + 1455 "</a>" 1456 ).appendTo($btns); 1457 1458 var $headerCells = tbl._getPrimaryHeaderCells(); 1459 var $headerCellsNoPersist = $headerCells.not('[data-tablesaw-priority="persist"]'); 1460 var headerWidths = []; 1461 var headerWidthsNoPersist = []; 1462 var $head = $(document.head || "head"); 1463 var tableId = $table.attr("id"); 1464 1465 if (!$headerCells.length) { 1466 throw new Error("tablesaw swipe: no header cells found."); 1467 } 1468 1469 $table.addClass("tablesaw-swipe"); 1470 1471 function initMinHeaderWidths() { 1472 $table.css({ 1473 width: "1px" 1474 }); 1475 1476 // remove any hidden columns 1477 $table.find("." + classes.hiddenCol).removeClass(classes.hiddenCol); 1478 1479 headerWidths = []; 1480 headerWidthsNoPersist = []; 1481 // Calculate initial widths 1482 $headerCells.each(function() { 1483 var width = this.offsetWidth; 1484 headerWidths.push(width); 1485 if (!isPersistent(this)) { 1486 headerWidthsNoPersist.push(width); 1487 } 1488 }); 1489 1490 // reset props 1491 $table.css({ 1492 width: "" 1493 }); 1494 } 1495 1496 initMinHeaderWidths(); 1497 1498 $btns.appendTo(tblsaw.$toolbar); 1499 1500 if (!tableId) { 1501 tableId = "tableswipe-" + Math.round(Math.random() * 10000); 1502 $table.attr("id", tableId); 1503 } 1504 1505 function showColumn(headerCell) { 1506 tblsaw._$getCells(headerCell).removeClass(classes.hiddenCol); 1507 } 1508 1509 function hideColumn(headerCell) { 1510 tblsaw._$getCells(headerCell).addClass(classes.hiddenCol); 1511 } 1512 1513 function persistColumn(headerCell) { 1514 tblsaw._$getCells(headerCell).addClass(classes.persistCol); 1515 } 1516 1517 function isPersistent(headerCell) { 1518 return $(headerCell).is('[data-tablesaw-priority="persist"]'); 1519 } 1520 1521 function unmaintainWidths() { 1522 $table.removeClass(classes.persistWidths); 1523 $("#" + tableId + "-persist").remove(); 1524 } 1525 1526 function maintainWidths() { 1527 var prefix = "#" + tableId + ".tablesaw-swipe "; 1528 var styles = []; 1529 var tableWidth = $table.width(); 1530 var tableWidthNoPersistantColumns = tableWidth; 1531 var hash = []; 1532 var newHash; 1533 1534 // save persistent column widths (as long as they take up less than 75% of table width) 1535 $headerCells.each(function(index) { 1536 var width; 1537 if (isPersistent(this)) { 1538 width = this.offsetWidth; 1539 tableWidthNoPersistantColumns -= width; 1540 1541 if (width < tableWidth * 0.75) { 1542 hash.push(index + "-" + width); 1543 styles.push( 1544 prefix + 1545 " ." + 1546 classes.persistCol + 1547 ":nth-child(" + 1548 (index + 1) + 1549 ") { width: " + 1550 width + 1551 "px; }" 1552 ); 1553 } 1554 } 1555 }); 1556 newHash = hash.join("_"); 1557 1558 if (styles.length) { 1559 $table.addClass(classes.persistWidths); 1560 var $style = $("#" + tableId + "-persist"); 1561 // If style element not yet added OR if the widths have changed 1562 if (!$style.length || $style.data("tablesaw-hash") !== newHash) { 1563 // Remove existing 1564 $style.remove(); 1565 1566 $("<style>" + styles.join("\n") + "</style>") 1567 .attr("id", tableId + "-persist") 1568 .data("tablesaw-hash", newHash) 1569 .appendTo($head); 1570 } 1571 } 1572 1573 return tableWidthNoPersistantColumns; 1574 } 1575 1576 function getNext() { 1577 var next = []; 1578 var checkFound; 1579 $headerCellsNoPersist.each(function(i) { 1580 var $t = $(this); 1581 var isHidden = $t.css("display") === "none" || $t.is("." + classes.hiddenCol); 1582 1583 if (!isHidden && !checkFound) { 1584 checkFound = true; 1585 next[0] = i; 1586 } else if (isHidden && checkFound) { 1587 next[1] = i; 1588 1589 return false; 1590 } 1591 }); 1592 1593 return next; 1594 } 1595 1596 function getPrev() { 1597 var next = getNext(); 1598 return [next[1] - 1, next[0] - 1]; 1599 } 1600 1601 function canNavigate(pair) { 1602 return pair[1] > -1 && pair[1] < $headerCellsNoPersist.length; 1603 } 1604 1605 function matchesMedia() { 1606 var matchMedia = $table.attr("data-tablesaw-swipe-media"); 1607 return !matchMedia || ("matchMedia" in window && window.matchMedia(matchMedia).matches); 1608 } 1609 1610 function fakeBreakpoints() { 1611 if (!matchesMedia()) { 1612 return; 1613 } 1614 1615 var containerWidth = $table.parent().width(), 1616 persist = [], 1617 sum = 0, 1618 sums = [], 1619 visibleNonPersistantCount = $headerCells.length; 1620 1621 $headerCells.each(function(index) { 1622 var $t = $(this), 1623 isPersist = $t.is('[data-tablesaw-priority="persist"]'); 1624 1625 persist.push(isPersist); 1626 sum += headerWidths[index]; 1627 sums.push(sum); 1628 1629 // is persistent or is hidden 1630 if (isPersist || sum > containerWidth) { 1631 visibleNonPersistantCount--; 1632 } 1633 }); 1634 1635 // We need at least one column to swipe. 1636 var needsNonPersistentColumn = visibleNonPersistantCount === 0; 1637 1638 $headerCells.each(function(index) { 1639 if (sums[index] > containerWidth) { 1640 hideColumn(this); 1641 } 1642 }); 1643 1644 var firstPersist = true; 1645 $headerCells.each(function(index) { 1646 if (persist[index]) { 1647 // for visual box-shadow 1648 persistColumn(this); 1649 1650 if (firstPersist) { 1651 tblsaw._$getCells(this).css("width", sums[index] + "px"); 1652 firstPersist = false; 1653 } 1654 return; 1655 } 1656 1657 if (sums[index] <= containerWidth || needsNonPersistentColumn) { 1658 needsNonPersistentColumn = false; 1659 showColumn(this); 1660 tblsaw.updateColspanCells(classes.hiddenCol, this, true); 1661 } 1662 }); 1663 1664 unmaintainWidths(); 1665 1666 $table.trigger("tablesawcolumns"); 1667 } 1668 1669 function goForward() { 1670 navigate(true); 1671 } 1672 function goBackward() { 1673 navigate(false); 1674 } 1675 1676 function navigate(isNavigateForward) { 1677 var pair; 1678 if (isNavigateForward) { 1679 pair = getNext(); 1680 } else { 1681 pair = getPrev(); 1682 } 1683 1684 if (canNavigate(pair)) { 1685 if (isNaN(pair[0])) { 1686 if (isNavigateForward) { 1687 pair[0] = 0; 1688 } else { 1689 pair[0] = $headerCellsNoPersist.length - 1; 1690 } 1691 } 1692 1693 var roomForColumnsWidth = maintainWidths(); 1694 var hideColumnIndex = pair[0]; 1695 var showColumnIndex = pair[1]; 1696 1697 // Hide one column, show one or more based on how much space was freed up 1698 var columnToShow; 1699 var columnToHide = $headerCellsNoPersist.get(hideColumnIndex); 1700 var wasAtLeastOneColumnShown = false; 1701 var atLeastOneColumnIsVisible = false; 1702 1703 hideColumn(columnToHide); 1704 tblsaw.updateColspanCells(classes.hiddenCol, columnToHide, true); 1705 1706 var columnIndex = hideColumnIndex + (isNavigateForward ? 1 : -1); 1707 while (columnIndex >= 0 && columnIndex < headerWidthsNoPersist.length) { 1708 roomForColumnsWidth -= headerWidthsNoPersist[columnIndex]; 1709 1710 var $columnToShow = $headerCellsNoPersist.eq(columnIndex); 1711 if ($columnToShow.is(".tablesaw-swipe-cellhidden")) { 1712 if (roomForColumnsWidth > 0) { 1713 columnToShow = $columnToShow.get(0); 1714 wasAtLeastOneColumnShown = true; 1715 atLeastOneColumnIsVisible = true; 1716 showColumn(columnToShow); 1717 tblsaw.updateColspanCells(classes.hiddenCol, columnToShow, false); 1718 } 1719 } else { 1720 atLeastOneColumnIsVisible = true; 1721 } 1722 1723 if (isNavigateForward) { 1724 columnIndex++; 1725 } else { 1726 columnIndex--; 1727 } 1728 } 1729 1730 if (!atLeastOneColumnIsVisible) { 1731 // if no columns are showing, at least show the first one we were aiming for. 1732 columnToShow = $headerCellsNoPersist.get(showColumnIndex); 1733 showColumn(columnToShow); 1734 tblsaw.updateColspanCells(classes.hiddenCol, columnToShow, false); 1735 } else if ( 1736 !wasAtLeastOneColumnShown && 1737 canNavigate(isNavigateForward ? getNext() : getPrev()) 1738 ) { 1739 // if our one new column was hidden but no new columns were shown, letâs navigate again automatically. 1740 navigate(isNavigateForward); 1741 } 1742 $table.trigger("tablesawcolumns"); 1743 } 1744 } 1745 1746 $prevBtn.add($nextBtn).on("click", function(e) { 1747 if (!!$(e.target).closest($nextBtn).length) { 1748 goForward(); 1749 } else { 1750 goBackward(); 1751 } 1752 e.preventDefault(); 1753 }); 1754 1755 function getCoord(event, key) { 1756 return (event.touches || event.originalEvent.touches)[0][key]; 1757 } 1758 1759 if (!$table.is("[" + attrs.disableTouchEvents + "]")) { 1760 $table.on("touchstart.swipetoggle", function(e) { 1761 var originX = getCoord(e, "pageX"); 1762 var originY = getCoord(e, "pageY"); 1763 var x; 1764 var y; 1765 var scrollTop = window.pageYOffset; 1766 1767 $(window).off(Tablesaw.events.resize, fakeBreakpoints); 1768 1769 $(this) 1770 .on("touchmove.swipetoggle", function(e) { 1771 x = getCoord(e, "pageX"); 1772 y = getCoord(e, "pageY"); 1773 }) 1774 .on("touchend.swipetoggle", function() { 1775 var cfg = tbl.getConfig({ 1776 swipeHorizontalThreshold: 30, 1777 swipeVerticalThreshold: 30 1778 }); 1779 1780 // This config code is a little awkward because shoestring doesnât support deep $.extend 1781 // Trying to work around when devs only override one of (not both) horizontalThreshold or 1782 // verticalThreshold in their TablesawConfig. 1783 // @TODO major version bump: remove cfg.swipe, move to just use the swipePrefix keys 1784 var verticalThreshold = cfg.swipe 1785 ? cfg.swipe.verticalThreshold 1786 : cfg.swipeVerticalThreshold; 1787 var horizontalThreshold = cfg.swipe 1788 ? cfg.swipe.horizontalThreshold 1789 : cfg.swipeHorizontalThreshold; 1790 1791 var isPageScrolled = Math.abs(window.pageYOffset - scrollTop) >= verticalThreshold; 1792 var isVerticalSwipe = Math.abs(y - originY) >= verticalThreshold; 1793 1794 if (!isVerticalSwipe && !isPageScrolled) { 1795 if (x - originX < -1 * horizontalThreshold) { 1796 goForward(); 1797 } 1798 if (x - originX > horizontalThreshold) { 1799 goBackward(); 1800 } 1801 } 1802 1803 window.setTimeout(function() { 1804 $(window).on(Tablesaw.events.resize, fakeBreakpoints); 1805 }, 300); 1806 1807 $(this).off("touchmove.swipetoggle touchend.swipetoggle"); 1808 }); 1809 }); 1810 } 1811 1812 $table 1813 .on("tablesawcolumns.swipetoggle", function() { 1814 var canGoPrev = canNavigate(getPrev()); 1815 var canGoNext = canNavigate(getNext()); 1816 $prevBtn[canGoPrev ? "removeClass" : "addClass"](classes.hideBtn); 1817 $nextBtn[canGoNext ? "removeClass" : "addClass"](classes.hideBtn); 1818 1819 tblsaw.$toolbar[!canGoPrev && !canGoNext ? "addClass" : "removeClass"]( 1820 classes.allColumnsVisible 1821 ); 1822 }) 1823 .on("tablesawnext.swipetoggle", function() { 1824 goForward(); 1825 }) 1826 .on("tablesawprev.swipetoggle", function() { 1827 goBackward(); 1828 }) 1829 .on(Tablesaw.events.destroy + ".swipetoggle", function() { 1830 var $t = $(this); 1831 1832 $t.removeClass("tablesaw-swipe"); 1833 tblsaw.$toolbar.find(".tablesaw-advance").remove(); 1834 $(window).off(Tablesaw.events.resize, fakeBreakpoints); 1835 1836 $t.off(".swipetoggle"); 1837 }) 1838 .on(Tablesaw.events.refresh, function() { 1839 unmaintainWidths(); 1840 initMinHeaderWidths(); 1841 fakeBreakpoints(); 1842 }); 1843 1844 fakeBreakpoints(); 1845 $(window).on(Tablesaw.events.resize, fakeBreakpoints); 1846 } 1847 1848 // on tablecreate, init 1849 $(document).on(Tablesaw.events.create, function(e, tablesaw) { 1850 if (tablesaw.mode === "swipe") { 1851 createSwipeTable(tablesaw, tablesaw.$table); 1852 } 1853 }); 1854 1855 // TODO OOP this and add to Tablesaw object 1856})(); 1857 1858(function() { 1859 var MiniMap = { 1860 attr: { 1861 init: "data-tablesaw-minimap" 1862 }, 1863 show: function(table) { 1864 var mq = table.getAttribute(MiniMap.attr.init); 1865 1866 if (mq === "") { 1867 // value-less but exists 1868 return true; 1869 } else if (mq && "matchMedia" in window) { 1870 // has a mq value 1871 return window.matchMedia(mq).matches; 1872 } 1873 1874 return false; 1875 } 1876 }; 1877 1878 function createMiniMap($table) { 1879 var tblsaw = $table.data("tablesaw"); 1880 var $btns = $('<div class="tablesaw-advance minimap">'); 1881 var $dotNav = $('<ul class="tablesaw-advance-dots">').appendTo($btns); 1882 var hideDot = "tablesaw-advance-dots-hide"; 1883 var $headerCells = $table.data("tablesaw")._getPrimaryHeaderCells(); 1884 1885 // populate dots 1886 $headerCells.each(function() { 1887 $dotNav.append("<li><i></i></li>"); 1888 }); 1889 1890 $btns.appendTo(tblsaw.$toolbar); 1891 1892 function showHideNav() { 1893 if (!MiniMap.show($table[0])) { 1894 $btns.css("display", "none"); 1895 return; 1896 } 1897 $btns.css("display", "block"); 1898 1899 // show/hide dots 1900 var dots = $dotNav.find("li").removeClass(hideDot); 1901 $table.find("thead th").each(function(i) { 1902 if ($(this).css("display") === "none") { 1903 dots.eq(i).addClass(hideDot); 1904 } 1905 }); 1906 } 1907 1908 // run on init and resize 1909 showHideNav(); 1910 $(window).on(Tablesaw.events.resize, showHideNav); 1911 1912 $table 1913 .on("tablesawcolumns.minimap", function() { 1914 showHideNav(); 1915 }) 1916 .on(Tablesaw.events.destroy + ".minimap", function() { 1917 var $t = $(this); 1918 1919 tblsaw.$toolbar.find(".tablesaw-advance").remove(); 1920 $(window).off(Tablesaw.events.resize, showHideNav); 1921 1922 $t.off(".minimap"); 1923 }); 1924 } 1925 1926 // on tablecreate, init 1927 $(document).on(Tablesaw.events.create, function(e, tablesaw) { 1928 if ( 1929 (tablesaw.mode === "swipe" || tablesaw.mode === "columntoggle") && 1930 tablesaw.$table.is("[ " + MiniMap.attr.init + "]") 1931 ) { 1932 createMiniMap(tablesaw.$table); 1933 } 1934 }); 1935 1936 // TODO OOP this better 1937 Tablesaw.MiniMap = MiniMap; 1938})(); 1939 1940(function() { 1941 var S = { 1942 selectors: { 1943 init: "table[data-tablesaw-mode-switch]" 1944 }, 1945 attributes: { 1946 excludeMode: "data-tablesaw-mode-exclude" 1947 }, 1948 classes: { 1949 main: "tablesaw-modeswitch", 1950 toolbar: "tablesaw-bar-section" 1951 }, 1952 modes: ["stack", "swipe", "columntoggle"], 1953 init: function(table) { 1954 var $table = $(table); 1955 var tblsaw = $table.data("tablesaw"); 1956 var ignoreMode = $table.attr(S.attributes.excludeMode); 1957 var $toolbar = tblsaw.$toolbar; 1958 var $switcher = $("<div>").addClass(S.classes.main + " " + S.classes.toolbar); 1959 1960 var html = [ 1961 '<label><span class="abbreviated">' + 1962 Tablesaw.i18n.modeSwitchColumnsAbbreviated + 1963 '</span><span class="longform">' + 1964 Tablesaw.i18n.modeSwitchColumns + 1965 "</span>:" 1966 ], 1967 dataMode = $table.attr("data-tablesaw-mode"), 1968 isSelected; 1969 1970 // TODO next major version: remove .btn 1971 html.push('<span class="btn tablesaw-btn"><select>'); 1972 for (var j = 0, k = S.modes.length; j < k; j++) { 1973 if (ignoreMode && ignoreMode.toLowerCase() === S.modes[j]) { 1974 continue; 1975 } 1976 1977 isSelected = dataMode === S.modes[j]; 1978 1979 html.push( 1980 "<option" + 1981 (isSelected ? " selected" : "") + 1982 ' value="' + 1983 S.modes[j] + 1984 '">' + 1985 Tablesaw.i18n.modes[j] + 1986 "</option>" 1987 ); 1988 } 1989 html.push("</select></span></label>"); 1990 1991 $switcher.html(html.join("")); 1992 1993 var $otherToolbarItems = $toolbar.find(".tablesaw-advance").eq(0); 1994 if ($otherToolbarItems.length) { 1995 $switcher.insertBefore($otherToolbarItems); 1996 } else { 1997 $switcher.appendTo($toolbar); 1998 } 1999 2000 $switcher.find(".tablesaw-btn").tablesawbtn(); 2001 $switcher.find("select").on("change", function(event) { 2002 return S.onModeChange.call(table, event, $(this).val()); 2003 }); 2004 }, 2005 onModeChange: function(event, val) { 2006 var $table = $(this); 2007 var tblsaw = $table.data("tablesaw"); 2008 var $switcher = tblsaw.$toolbar.find("." + S.classes.main); 2009 2010 $switcher.remove(); 2011 tblsaw.destroy(); 2012 2013 $table.attr("data-tablesaw-mode", val); 2014 $table.tablesaw(); 2015 } 2016 }; 2017 2018 $(document).on(Tablesaw.events.create, function(e, Tablesaw) { 2019 if (Tablesaw.$table.is(S.selectors.init)) { 2020 S.init(Tablesaw.table); 2021 } 2022 }); 2023 2024 // TODO OOP this and add to Tablesaw object 2025})(); 2026 2027(function() { 2028 var pluginName = "tablesawCheckAll"; 2029 2030 function CheckAll(tablesaw) { 2031 this.tablesaw = tablesaw; 2032 this.$table = tablesaw.$table; 2033 2034 this.attr = "data-tablesaw-checkall"; 2035 this.checkAllSelector = "[" + this.attr + "]"; 2036 this.forceCheckedSelector = "[" + this.attr + "-checked]"; 2037 this.forceUncheckedSelector = "[" + this.attr + "-unchecked]"; 2038 this.checkboxSelector = 'input[type="checkbox"]'; 2039 2040 this.$triggers = null; 2041 this.$checkboxes = null; 2042 2043 if (this.$table.data(pluginName)) { 2044 return; 2045 } 2046 this.$table.data(pluginName, this); 2047 this.init(); 2048 } 2049 2050 CheckAll.prototype._filterCells = function($checkboxes) { 2051 return $checkboxes 2052 .filter(function() { 2053 return !$(this) 2054 .closest("tr") 2055 .is("[data-tablesaw-subrow],[data-tablesaw-ignorerow]"); 2056 }) 2057 .find(this.checkboxSelector) 2058 .not(this.checkAllSelector); 2059 }; 2060 2061 // With buttons you can use a scoping selector like: data-tablesaw-checkall="#my-scoped-id input[type='checkbox']" 2062 CheckAll.prototype.getCheckboxesForButton = function(button) { 2063 return this._filterCells($($(button).attr(this.attr))); 2064 }; 2065 2066 CheckAll.prototype.getCheckboxesForCheckbox = function(checkbox) { 2067 return this._filterCells($($(checkbox).closest("th")[0].cells)); 2068 }; 2069 2070 CheckAll.prototype.init = function() { 2071 var self = this; 2072 this.$table.find(this.checkAllSelector).each(function() { 2073 var $trigger = $(this); 2074 if ($trigger.is(self.checkboxSelector)) { 2075 self.addCheckboxEvents(this); 2076 } else { 2077 self.addButtonEvents(this); 2078 } 2079 }); 2080 }; 2081 2082 CheckAll.prototype.addButtonEvents = function(trigger) { 2083 var self = this; 2084 2085 // Update body checkboxes when header checkbox is changed 2086 $(trigger).on("click", function(event) { 2087 event.preventDefault(); 2088 2089 var $checkboxes = self.getCheckboxesForButton(this); 2090 2091 var allChecked = true; 2092 $checkboxes.each(function() { 2093 if (!this.checked) { 2094 allChecked = false; 2095 } 2096 }); 2097 2098 var setChecked; 2099 if ($(this).is(self.forceCheckedSelector)) { 2100 setChecked = true; 2101 } else if ($(this).is(self.forceUncheckedSelector)) { 2102 setChecked = false; 2103 } else { 2104 setChecked = allChecked ? false : true; 2105 } 2106 2107 $checkboxes.each(function() { 2108 this.checked = setChecked; 2109 2110 $(this).trigger("change." + pluginName); 2111 }); 2112 }); 2113 }; 2114 2115 CheckAll.prototype.addCheckboxEvents = function(trigger) { 2116 var self = this; 2117 2118 // Update body checkboxes when header checkbox is changed 2119 $(trigger).on("change", function() { 2120 var setChecked = this.checked; 2121 2122 self.getCheckboxesForCheckbox(this).each(function() { 2123 this.checked = setChecked; 2124 }); 2125 }); 2126 2127 var $checkboxes = self.getCheckboxesForCheckbox(trigger); 2128 2129 // Update header checkbox when body checkboxes are changed 2130 $checkboxes.on("change." + pluginName, function() { 2131 var checkedCount = 0; 2132 $checkboxes.each(function() { 2133 if (this.checked) { 2134 checkedCount++; 2135 } 2136 }); 2137 2138 var allSelected = checkedCount === $checkboxes.length; 2139 2140 trigger.checked = allSelected; 2141 2142 // only indeterminate if some are selected (not all and not none) 2143 trigger.indeterminate = checkedCount !== 0 && !allSelected; 2144 }); 2145 }; 2146 2147 // on tablecreate, init 2148 $(document).on(Tablesaw.events.create, function(e, tablesaw) { 2149 new CheckAll(tablesaw); 2150 }); 2151 2152 Tablesaw.CheckAll = CheckAll; 2153})(); 2154 2155 return Tablesaw; 2156}));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.