vendor: 56,647 bytes, lines 1-2371
1/*! Tablesaw - v3.1.2 - 2019-03-19 2* https://github.com/filamentgroup/tablesaw 3* Copyright (c) 2019 Filament Group; Licensed MIT */ 4/*! Shoestring - v2.0.0 - 2017-02-14 5* http://github.com/filamentgroup/shoestring/ 6* Copyright (c) 2017 Scott Jehl, Filament Group, Inc; Licensed MIT & GPLv2 */ 7(function( factory ) { 8 if( typeof define === 'function' && define.amd ) { 9 // AMD. Register as an anonymous module. 10 define( [ 'shoestring' ], factory ); 11 } else if (typeof module === 'object' && module.exports) { 12 // Node/CommonJS 13 module.exports = factory(); 14 } else { 15 // Browser globals 16 factory(); 17 } 18}(function () { 19 var win = typeof window !== "undefined" ? window : this; 20 var doc = win.document; 21 22 23 /** 24 * The shoestring object constructor. 25 * 26 * @param {string,object} prim The selector to find or element to wrap. 27 * @param {object} sec The context in which to match the `prim` selector. 28 * @returns shoestring 29 * @this window 30 */ 31 function shoestring( prim, sec ){ 32 var pType = typeof( prim ), 33 ret = [], 34 sel; 35 36 // return an empty shoestring object 37 if( !prim ){ 38 return new Shoestring( ret ); 39 } 40 41 // ready calls 42 if( prim.call ){ 43 return shoestring.ready( prim ); 44 } 45 46 // handle re-wrapping shoestring objects 47 if( prim.constructor === Shoestring && !sec ){ 48 return prim; 49 } 50 51 // if string starting with <, make html 52 if( pType === "string" && prim.indexOf( "<" ) === 0 ){ 53 var dfrag = doc.createElement( "div" ); 54 55 dfrag.innerHTML = prim; 56 57 // TODO depends on children (circular) 58 return shoestring( dfrag ).children().each(function(){ 59 dfrag.removeChild( this ); 60 }); 61 } 62 63 // if string, it's a selector, use qsa 64 if( pType === "string" ){ 65 if( sec ){ 66 return shoestring( sec ).find( prim ); 67 } 68 69 sel = doc.querySelectorAll( prim ); 70 71 return new Shoestring( sel, prim ); 72 } 73 74 // array like objects or node lists 75 if( Object.prototype.toString.call( pType ) === '[object Array]' || 76 (win.NodeList && prim instanceof win.NodeList) ){ 77 78 return new Shoestring( prim, prim ); 79 } 80 81 // if it's an array, use all the elements 82 if( prim.constructor === Array ){ 83 return new Shoestring( prim, prim ); 84 } 85 86 // otherwise assume it's an object the we want at an index 87 return new Shoestring( [prim], prim ); 88 } 89 90 var Shoestring = function( ret, prim ) { 91 this.length = 0; 92 this.selector = prim; 93 shoestring.merge(this, ret); 94 }; 95 96 // TODO only required for tests 97 Shoestring.prototype.reverse = [].reverse; 98 99 // For adding element set methods 100 shoestring.fn = Shoestring.prototype; 101 102 shoestring.Shoestring = Shoestring; 103 104 // For extending objects 105 // TODO move to separate module when we use prototypes 106 shoestring.extend = function( first, second ){ 107 for( var i in second ){ 108 if( second.hasOwnProperty( i ) ){ 109 first[ i ] = second[ i ]; 110 } 111 } 112 113 return first; 114 }; 115 116 // taken directly from jQuery 117 shoestring.merge = function( first, second ) { 118 var len, j, i; 119 120 len = +second.length, 121 j = 0, 122 i = first.length; 123 124 for ( ; j < len; j++ ) { 125 first[ i++ ] = second[ j ]; 126 } 127 128 first.length = i; 129 130 return first; 131 }; 132 133 // expose 134 win.shoestring = shoestring; 135 136 137 138 /** 139 * Iterates over `shoestring` collections. 140 * 141 * @param {function} callback The callback to be invoked on each element and index 142 * @return shoestring 143 * @this shoestring 144 */ 145 shoestring.fn.each = function( callback ){ 146 return shoestring.each( this, callback ); 147 }; 148 149 shoestring.each = function( collection, callback ) { 150 var val; 151 for( var i = 0, il = collection.length; i < il; i++ ){ 152 val = callback.call( collection[i], i, collection[i] ); 153 if( val === false ){ 154 break; 155 } 156 } 157 158 return collection; 159 }; 160 161 162 163 /** 164 * Check for array membership. 165 * 166 * @param {object} needle The thing to find. 167 * @param {object} haystack The thing to find the needle in. 168 * @return {boolean} 169 * @this window 170 */ 171 shoestring.inArray = function( needle, haystack ){ 172 var isin = -1; 173 for( var i = 0, il = haystack.length; i < il; i++ ){ 174 if( haystack.hasOwnProperty( i ) && haystack[ i ] === needle ){ 175 isin = i; 176 } 177 } 178 return isin; 179 }; 180 181 182 183 /** 184 * Bind callbacks to be run when the DOM is "ready". 185 * 186 * @param {function} fn The callback to be run 187 * @return shoestring 188 * @this shoestring 189 */ 190 shoestring.ready = function( fn ){ 191 if( ready && fn ){ 192 fn.call( doc ); 193 } 194 else if( fn ){ 195 readyQueue.push( fn ); 196 } 197 else { 198 runReady(); 199 } 200 201 return [doc]; 202 }; 203 204 // TODO necessary? 205 shoestring.fn.ready = function( fn ){ 206 shoestring.ready( fn ); 207 return this; 208 }; 209 210 // Empty and exec the ready queue 211 var ready = false, 212 readyQueue = [], 213 runReady = function(){ 214 if( !ready ){ 215 while( readyQueue.length ){ 216 readyQueue.shift().call( doc ); 217 } 218 ready = true; 219 } 220 }; 221 222 // If DOM is already ready at exec time, depends on the browser. 223 // From: https://github.com/mobify/mobifyjs/blob/526841be5509e28fc949038021799e4223479f8d/src/capture.js#L128 224 if (doc.attachEvent ? doc.readyState === "complete" : doc.readyState !== "loading") { 225 runReady(); 226 } else { 227 doc.addEventListener( "DOMContentLoaded", runReady, false ); 228 doc.addEventListener( "readystatechange", runReady, false ); 229 win.addEventListener( "load", runReady, false ); 230 } 231 232 233 234 /** 235 * Checks the current set of elements against the selector, if one matches return `true`. 236 * 237 * @param {string} selector The selector to check. 238 * @return {boolean} 239 * @this {shoestring} 240 */ 241 shoestring.fn.is = function( selector ){ 242 var ret = false, self = this, parents, check; 243 244 // assume a dom element 245 if( typeof selector !== "string" ){ 246 // array-like, ie shoestring objects or element arrays 247 if( selector.length && selector[0] ){ 248 check = selector; 249 } else { 250 check = [selector]; 251 } 252 253 return _checkElements(this, check); 254 } 255 256 parents = this.parent(); 257 258 if( !parents.length ){ 259 parents = shoestring( doc ); 260 } 261 262 parents.each(function( i, e ) { 263 var children; 264 265 children = e.querySelectorAll( selector ); 266 267 ret = _checkElements( self, children ); 268 }); 269 270 return ret; 271 }; 272 273 function _checkElements(needles, haystack){ 274 var ret = false; 275 276 needles.each(function() { 277 var j = 0; 278 279 while( j < haystack.length ){ 280 if( this === haystack[j] ){ 281 ret = true; 282 } 283 284 j++; 285 } 286 }); 287 288 return ret; 289 } 290 291 292 293 /** 294 * Get data attached to the first element or set data values on all elements in the current set. 295 * 296 * @param {string} name The data attribute name. 297 * @param {any} value The value assigned to the data attribute. 298 * @return {any|shoestring} 299 * @this shoestring 300 */ 301 shoestring.fn.data = function( name, value ){ 302 if( name !== undefined ){ 303 if( value !== undefined ){ 304 return this.each(function(){ 305 if( !this.shoestringData ){ 306 this.shoestringData = {}; 307 } 308 309 this.shoestringData[ name ] = value; 310 }); 311 } 312 else { 313 if( this[ 0 ] ) { 314 if( this[ 0 ].shoestringData ) { 315 return this[ 0 ].shoestringData[ name ]; 316 } 317 } 318 } 319 } 320 else { 321 return this[ 0 ] ? this[ 0 ].shoestringData || {} : undefined; 322 } 323 }; 324 325 326 /** 327 * Remove data associated with `name` or all the data, for each element in the current set. 328 * 329 * @param {string} name The data attribute name. 330 * @return shoestring 331 * @this shoestring 332 */ 333 shoestring.fn.removeData = function( name ){ 334 return this.each(function(){ 335 if( name !== undefined && this.shoestringData ){ 336 this.shoestringData[ name ] = undefined; 337 delete this.shoestringData[ name ]; 338 } else { 339 this[ 0 ].shoestringData = {}; 340 } 341 }); 342 }; 343 344 345 346 /** 347 * An alias for the `shoestring` constructor. 348 */ 349 win.$ = shoestring; 350 351 352 353 /** 354 * Add a class to each DOM element in the set of elements. 355 * 356 * @param {string} className The name of the class to be added. 357 * @return shoestring 358 * @this shoestring 359 */ 360 shoestring.fn.addClass = function( className ){ 361 var classes = className.replace(/^\s+|\s+$/g, '').split( " " ); 362 363 return this.each(function(){ 364 for( var i = 0, il = classes.length; i < il; i++ ){ 365 if( this.className !== undefined && 366 (this.className === "" || 367 !this.className.match( new RegExp( "(^|\\s)" + classes[ i ] + "($|\\s)"))) ){ 368 this.className += " " + classes[ i ]; 369 } 370 } 371 }); 372 }; 373 374 375 376 /** 377 * Add elements matching the selector to the current set. 378 * 379 * @param {string} selector The selector for the elements to add from the DOM 380 * @return shoestring 381 * @this shoestring 382 */ 383 shoestring.fn.add = function( selector ){ 384 var ret = []; 385 this.each(function(){ 386 ret.push( this ); 387 }); 388 389 shoestring( selector ).each(function(){ 390 ret.push( this ); 391 }); 392 393 return shoestring( ret ); 394 }; 395 396 397 398 /** 399 * Insert an element or HTML string as the last child of each element in the set. 400 * 401 * @param {string|HTMLElement} fragment The HTML or HTMLElement to insert. 402 * @return shoestring 403 * @this shoestring 404 */ 405 shoestring.fn.append = function( fragment ){ 406 if( typeof( fragment ) === "string" || fragment.nodeType !== undefined ){ 407 fragment = shoestring( fragment ); 408 } 409 410 return this.each(function( i ){ 411 for( var j = 0, jl = fragment.length; j < jl; j++ ){ 412 this.appendChild( i > 0 ? fragment[ j ].cloneNode( true ) : fragment[ j ] ); 413 } 414 }); 415 }; 416 417 418 419 /** 420 * Insert the current set as the last child of the elements matching the selector. 421 * 422 * @param {string} selector The selector after which to append the current set. 423 * @return shoestring 424 * @this shoestring 425 */ 426 shoestring.fn.appendTo = function( selector ){ 427 return this.each(function(){ 428 shoestring( selector ).append( this ); 429 }); 430 }; 431 432 433 434 /** 435 * Get the value of the first element of the set or set the value of all the elements in the set. 436 * 437 * @param {string} name The attribute name. 438 * @param {string} value The new value for the attribute. 439 * @return {shoestring|string|undefined} 440 * @this {shoestring} 441 */ 442 shoestring.fn.attr = function( name, value ){ 443 var nameStr = typeof( name ) === "string"; 444 445 if( value !== undefined || !nameStr ){ 446 return this.each(function(){ 447 if( nameStr ){ 448 this.setAttribute( name, value ); 449 } else { 450 for( var i in name ){ 451 if( name.hasOwnProperty( i ) ){ 452 this.setAttribute( i, name[ i ] ); 453 } 454 } 455 } 456 }); 457 } else { 458 return this[ 0 ] ? this[ 0 ].getAttribute( name ) : undefined; 459 } 460 }; 461 462 463 464 /** 465 * Insert an element or HTML string before each element in the current set. 466 * 467 * @param {string|HTMLElement} fragment The HTML or HTMLElement to insert. 468 * @return shoestring 469 * @this shoestring 470 */ 471 shoestring.fn.before = function( fragment ){ 472 if( typeof( fragment ) === "string" || fragment.nodeType !== undefined ){ 473 fragment = shoestring( fragment ); 474 } 475 476 return this.each(function( i ){ 477 for( var j = 0, jl = fragment.length; j < jl; j++ ){ 478 this.parentNode.insertBefore( i > 0 ? fragment[ j ].cloneNode( true ) : fragment[ j ], this ); 479 } 480 }); 481 }; 482 483 484 485 /** 486 * Get the children of the current collection. 487 * @return shoestring 488 * @this shoestring 489 */ 490 shoestring.fn.children = function(){ 491 var ret = [], 492 childs, 493 j; 494 this.each(function(){ 495 childs = this.children; 496 j = -1; 497 498 while( j++ < childs.length-1 ){ 499 if( shoestring.inArray( childs[ j ], ret ) === -1 ){ 500 ret.push( childs[ j ] ); 501 } 502 } 503 }); 504 return shoestring(ret); 505 }; 506 507 508 509 /** 510 * Find an element matching the selector in the set of the current element and its parents. 511 * 512 * @param {string} selector The selector used to identify the target element. 513 * @return shoestring 514 * @this shoestring 515 */ 516 shoestring.fn.closest = function( selector ){ 517 var ret = []; 518 519 if( !selector ){ 520 return shoestring( ret ); 521 } 522 523 this.each(function(){ 524 var element, $self = shoestring( element = this ); 525 526 if( $self.is(selector) ){ 527 ret.push( this ); 528 return; 529 } 530 531 while( element.parentElement ) { 532 if( shoestring(element.parentElement).is(selector) ){ 533 ret.push( element.parentElement ); 534 break; 535 } 536 537 element = element.parentElement; 538 } 539 }); 540 541 return shoestring( ret ); 542 }; 543 544 545 546 shoestring.cssExceptions = { 547 'float': [ 'cssFloat' ] 548 }; 549 550 551 552 (function() { 553 var cssExceptions = shoestring.cssExceptions; 554 555 // IE8 uses marginRight instead of margin-right 556 function convertPropertyName( str ) { 557 return str.replace( /\-([A-Za-z])/g, function ( match, character ) { 558 return character.toUpperCase(); 559 }); 560 } 561 562 function _getStyle( element, property ) { 563 return win.getComputedStyle( element, null ).getPropertyValue( property ); 564 } 565 566 var vendorPrefixes = [ '', '-webkit-', '-ms-', '-moz-', '-o-', '-khtml-' ]; 567 568 /** 569 * Private function for getting the computed style of an element. 570 * 571 * **NOTE** Please use the [css](../css.js.html) method instead. 572 * 573 * @method _getStyle 574 * @param {HTMLElement} element The element we want the style property for. 575 * @param {string} property The css property we want the style for. 576 */ 577 shoestring._getStyle = function( element, property ) { 578 var convert, value, j, k; 579 580 if( cssExceptions[ property ] ) { 581 for( j = 0, k = cssExceptions[ property ].length; j < k; j++ ) { 582 value = _getStyle( element, cssExceptions[ property ][ j ] ); 583 584 if( value ) { 585 return value; 586 } 587 } 588 } 589 590 for( j = 0, k = vendorPrefixes.length; j < k; j++ ) { 591 convert = convertPropertyName( vendorPrefixes[ j ] + property ); 592 593 // VendorprefixKeyName || key-name 594 value = _getStyle( element, convert ); 595 596 if( convert !== property ) { 597 value = value || _getStyle( element, property ); 598 } 599 600 if( vendorPrefixes[ j ] ) { 601 // -vendorprefix-key-name 602 value = value || _getStyle( element, vendorPrefixes[ j ] + property ); 603 } 604 605 if( value ) { 606 return value; 607 } 608 } 609 610 return undefined; 611 }; 612 })(); 613 614 615 616 (function() { 617 var cssExceptions = shoestring.cssExceptions; 618 619 // IE8 uses marginRight instead of margin-right 620 function convertPropertyName( str ) { 621 return str.replace( /\-([A-Za-z])/g, function ( match, character ) { 622 return character.toUpperCase(); 623 }); 624 } 625 626 /** 627 * Private function for setting the style of an element. 628 * 629 * **NOTE** Please use the [css](../css.js.html) method instead. 630 * 631 * @method _setStyle 632 * @param {HTMLElement} element The element we want to style. 633 * @param {string} property The property being used to style the element. 634 * @param {string} value The css value for the style property. 635 */ 636 shoestring._setStyle = function( element, property, value ) { 637 var convertedProperty = convertPropertyName(property); 638 639 element.style[ property ] = value; 640 641 if( convertedProperty !== property ) { 642 element.style[ convertedProperty ] = value; 643 } 644 645 if( cssExceptions[ property ] ) { 646 for( var j = 0, k = cssExceptions[ property ].length; j<k; j++ ) { 647 element.style[ cssExceptions[ property ][ j ] ] = value; 648 } 649 } 650 }; 651 })(); 652 653 654 655 /** 656 * Get the compute style property of the first element or set the value of a style property 657 * on all elements in the set. 658 * 659 * @method _setStyle 660 * @param {string} property The property being used to style the element. 661 * @param {string|undefined} value The css value for the style property. 662 * @return {string|shoestring} 663 * @this shoestring 664 */ 665 shoestring.fn.css = function( property, value ){ 666 if( !this[0] ){ 667 return; 668 } 669 670 if( typeof property === "object" ) { 671 return this.each(function() { 672 for( var key in property ) { 673 if( property.hasOwnProperty( key ) ) { 674 shoestring._setStyle( this, key, property[key] ); 675 } 676 } 677 }); 678 } else { 679 // assignment else retrieve first 680 if( value !== undefined ){ 681 return this.each(function(){ 682 shoestring._setStyle( this, property, value ); 683 }); 684 } 685 686 return shoestring._getStyle( this[0], property ); 687 } 688 }; 689 690 691 692 /** 693 * Returns the indexed element wrapped in a new `shoestring` object. 694 * 695 * @param {integer} index The index of the element to wrap and return. 696 * @return shoestring 697 * @this shoestring 698 */ 699 shoestring.fn.eq = function( index ){ 700 if( this[index] ){ 701 return shoestring( this[index] ); 702 } 703 704 return shoestring([]); 705 }; 706 707 708 709 /** 710 * Filter out the current set if they do *not* match the passed selector or 711 * the supplied callback returns false 712 * 713 * @param {string,function} selector The selector or boolean return value callback used to filter the elements. 714 * @return shoestring 715 * @this shoestring 716 */ 717 shoestring.fn.filter = function( selector ){ 718 var ret = []; 719 720 this.each(function( index ){ 721 var wsel; 722 723 if( typeof selector === 'function' ) { 724 if( selector.call( this, index ) !== false ) { 725 ret.push( this ); 726 } 727 } else { 728 if( !this.parentNode ){ 729 var context = shoestring( doc.createDocumentFragment() ); 730 731 context[ 0 ].appendChild( this ); 732 wsel = shoestring( selector, context ); 733 } else { 734 wsel = shoestring( selector, this.parentNode ); 735 } 736 737 if( shoestring.inArray( this, wsel ) > -1 ){ 738 ret.push( this ); 739 } 740 } 741 }); 742 743 return shoestring( ret ); 744 }; 745 746 747 748 /** 749 * Find descendant elements of the current collection. 750 * 751 * @param {string} selector The selector used to find the children 752 * @return shoestring 753 * @this shoestring 754 */ 755 shoestring.fn.find = function( selector ){ 756 var ret = [], 757 finds; 758 this.each(function(){ 759 finds = this.querySelectorAll( selector ); 760 761 for( var i = 0, il = finds.length; i < il; i++ ){ 762 ret = ret.concat( finds[i] ); 763 } 764 }); 765 return shoestring( ret ); 766 }; 767 768 769 770 /** 771 * Returns the first element of the set wrapped in a new `shoestring` object. 772 * 773 * @return shoestring 774 * @this shoestring 775 */ 776 shoestring.fn.first = function(){ 777 return this.eq( 0 ); 778 }; 779 780 781 782 /** 783 * Returns the raw DOM node at the passed index. 784 * 785 * @param {integer} index The index of the element to wrap and return. 786 * @return {HTMLElement|undefined|array} 787 * @this shoestring 788 */ 789 shoestring.fn.get = function( index ){ 790 791 // return an array of elements if index is undefined 792 if( index === undefined ){ 793 var elements = []; 794 795 for( var i = 0; i < this.length; i++ ){ 796 elements.push( this[ i ] ); 797 } 798 799 return elements; 800 } else { 801 return this[ index ]; 802 } 803 }; 804 805 806 807 var set = function( html ){ 808 if( typeof html === "string" || typeof html === "number" ){ 809 return this.each(function(){ 810 this.innerHTML = "" + html; 811 }); 812 } else { 813 var h = ""; 814 if( typeof html.length !== "undefined" ){ 815 for( var i = 0, l = html.length; i < l; i++ ){ 816 h += html[i].outerHTML; 817 } 818 } else { 819 h = html.outerHTML; 820 } 821 return this.each(function(){ 822 this.innerHTML = h; 823 }); 824 } 825 }; 826 /** 827 * Gets or sets the `innerHTML` from all the elements in the set. 828 * 829 * @param {string|undefined} html The html to assign 830 * @return {string|shoestring} 831 * @this shoestring 832 */ 833 shoestring.fn.html = function( html ){ 834 if( typeof html !== "undefined" ){ 835 return set.call( this, html ); 836 } else { // get 837 var pile = ""; 838 839 this.each(function(){ 840 pile += this.innerHTML; 841 }); 842 843 return pile; 844 } 845 }; 846 847 848 849 (function() { 850 function _getIndex( set, test ) { 851 var i, result, element; 852 853 for( i = result = 0; i < set.length; i++ ) { 854 element = set.item ? set.item(i) : set[i]; 855 856 if( test(element) ){ 857 return result; 858 } 859 860 // ignore text nodes, etc 861 // NOTE may need to be more permissive 862 if( element.nodeType === 1 ){ 863 result++; 864 } 865 } 866 867 return -1; 868 } 869 870 /** 871 * Find the index in the current set for the passed selector. 872 * Without a selector it returns the index of the first node within the array of its siblings. 873 * 874 * @param {string|undefined} selector The selector used to search for the index. 875 * @return {integer} 876 * @this {shoestring} 877 */ 878 shoestring.fn.index = function( selector ){ 879 var self, children; 880 881 self = this; 882 883 // no arg? check the children, otherwise check each element that matches 884 if( selector === undefined ){ 885 children = ( ( this[ 0 ] && this[0].parentNode ) || doc.documentElement).childNodes; 886 887 // check if the element matches the first of the set 888 return _getIndex(children, function( element ) { 889 return self[0] === element; 890 }); 891 } else { 892 893 // check if the element matches the first selected node from the parent 894 return _getIndex(self, function( element ) { 895 return element === (shoestring( selector, element.parentNode )[ 0 ]); 896 }); 897 } 898 }; 899 })(); 900 901 902 903 /** 904 * Insert the current set before the elements matching the selector. 905 * 906 * @param {string} selector The selector before which to insert the current set. 907 * @return shoestring 908 * @this shoestring 909 */ 910 shoestring.fn.insertBefore = function( selector ){ 911 return this.each(function(){ 912 shoestring( selector ).before( this ); 913 }); 914 }; 915 916 917 918 /** 919 * Returns the last element of the set wrapped in a new `shoestring` object. 920 * 921 * @return shoestring 922 * @this shoestring 923 */ 924 shoestring.fn.last = function(){ 925 return this.eq( this.length - 1 ); 926 }; 927 928 929 930 /** 931 * Returns a `shoestring` object with the set of siblings of each element in the original set. 932 * 933 * @return shoestring 934 * @this shoestring 935 */ 936 shoestring.fn.next = function(){ 937 938 var result = []; 939 940 // TODO need to implement map 941 this.each(function() { 942 var children, item, found; 943 944 // get the child nodes for this member of the set 945 children = shoestring( this.parentNode )[0].childNodes; 946 947 for( var i = 0; i < children.length; i++ ){ 948 item = children.item( i ); 949 950 // found the item we needed (found) which means current item value is 951 // the next node in the list, as long as it's viable grab it 952 // NOTE may need to be more permissive 953 if( found && item.nodeType === 1 ){ 954 result.push( item ); 955 break; 956 } 957 958 // find the current item and mark it as found 959 if( item === this ){ 960 found = true; 961 } 962 } 963 }); 964 965 return shoestring( result ); 966 }; 967 968 969 970 /** 971 * Removes elements from the current set. 972 * 973 * @param {string} selector The selector to use when removing the elements. 974 * @return shoestring 975 * @this shoestring 976 */ 977 shoestring.fn.not = function( selector ){ 978 var ret = []; 979 980 this.each(function(){ 981 var found = shoestring( selector, this.parentNode ); 982 983 if( shoestring.inArray(this, found) === -1 ){ 984 ret.push( this ); 985 } 986 }); 987 988 return shoestring( ret ); 989 }; 990 991 992 993 /** 994 * Returns the set of first parents for each element in the current set. 995 * 996 * @return shoestring 997 * @this shoestring 998 */ 999 shoestring.fn.parent = function(){ 1000 var ret = [], 1001 parent; 1002 1003 this.each(function(){ 1004 // no parent node, assume top level 1005 // jQuery parent: return the document object for <html> or the parent node if it exists 1006 parent = (this === doc.documentElement ? doc : this.parentNode); 1007 1008 // if there is a parent and it's not a document fragment 1009 if( parent && parent.nodeType !== 11 ){ 1010 ret.push( parent ); 1011 } 1012 }); 1013 1014 return shoestring(ret); 1015 }; 1016 1017 1018 1019 /** 1020 * Add an HTML string or element before the children of each element in the current set. 1021 * 1022 * @param {string|HTMLElement} fragment The HTML string or element to add. 1023 * @return shoestring 1024 * @this shoestring 1025 */ 1026 shoestring.fn.prepend = function( fragment ){ 1027 if( typeof( fragment ) === "string" || fragment.nodeType !== undefined ){ 1028 fragment = shoestring( fragment ); 1029 } 1030 1031 return this.each(function( i ){ 1032 1033 for( var j = 0, jl = fragment.length; j < jl; j++ ){ 1034 var insertEl = i > 0 ? fragment[ j ].cloneNode( true ) : fragment[ j ]; 1035 if ( this.firstChild ){ 1036 this.insertBefore( insertEl, this.firstChild ); 1037 } else { 1038 this.appendChild( insertEl ); 1039 } 1040 } 1041 }); 1042 }; 1043 1044 1045 1046 /** 1047 * Returns a `shoestring` object with the set of *one* siblingx before each element in the original set. 1048 * 1049 * @return shoestring 1050 * @this shoestring 1051 */ 1052 shoestring.fn.prev = function(){ 1053 1054 var result = []; 1055 1056 // TODO need to implement map 1057 this.each(function() { 1058 var children, item, found; 1059 1060 // get the child nodes for this member of the set 1061 children = shoestring( this.parentNode )[0].childNodes; 1062 1063 for( var i = children.length -1; i >= 0; i-- ){ 1064 item = children.item( i ); 1065 1066 // found the item we needed (found) which means current item value is 1067 // the next node in the list, as long as it's viable grab it 1068 // NOTE may need to be more permissive 1069 if( found && item.nodeType === 1 ){ 1070 result.push( item ); 1071 break; 1072 } 1073 1074 // find the current item and mark it as found 1075 if( item === this ){ 1076 found = true; 1077 } 1078 } 1079 }); 1080 1081 return shoestring( result ); 1082 }; 1083 1084 1085 1086 /** 1087 * Returns a `shoestring` object with the set of *all* siblings before each element in the original set. 1088 * 1089 * @return shoestring 1090 * @this shoestring 1091 */ 1092 shoestring.fn.prevAll = function(){ 1093 1094 var result = []; 1095 1096 this.each(function() { 1097 var $previous = shoestring( this ).prev(); 1098 1099 while( $previous.length ){ 1100 result.push( $previous[0] ); 1101 $previous = $previous.prev(); 1102 } 1103 }); 1104 1105 return shoestring( result ); 1106 }; 1107 1108 1109 1110 /** 1111 * Remove an attribute from each element in the current set. 1112 * 1113 * @param {string} name The name of the attribute. 1114 * @return shoestring 1115 * @this shoestring 1116 */ 1117 shoestring.fn.removeAttr = function( name ){ 1118 return this.each(function(){ 1119 this.removeAttribute( name ); 1120 }); 1121 }; 1122 1123 1124 1125 /** 1126 * Remove a class from each DOM element in the set of elements. 1127 * 1128 * @param {string} className The name of the class to be removed. 1129 * @return shoestring 1130 * @this shoestring 1131 */ 1132 shoestring.fn.removeClass = function( cname ){ 1133 var classes = cname.replace(/^\s+|\s+$/g, '').split( " " ); 1134 1135 return this.each(function(){ 1136 var newClassName, regex; 1137 1138 for( var i = 0, il = classes.length; i < il; i++ ){ 1139 if( this.className !== undefined ){ 1140 regex = new RegExp( "(^|\\s)" + classes[ i ] + "($|\\s)", "gmi" ); 1141 newClassName = this.className.replace( regex, " " ); 1142 1143 this.className = newClassName.replace(/^\s+|\s+$/g, ''); 1144 } 1145 } 1146 }); 1147 }; 1148 1149 1150 1151 /** 1152 * Remove the current set of elements from the DOM. 1153 * 1154 * @return shoestring 1155 * @this shoestring 1156 */ 1157 shoestring.fn.remove = function(){ 1158 return this.each(function(){ 1159 if( this.parentNode ) { 1160 this.parentNode.removeChild( this ); 1161 } 1162 }); 1163 }; 1164 1165 1166 1167 /** 1168 * Replace each element in the current set with that argument HTML string or HTMLElement. 1169 * 1170 * @param {string|HTMLElement} fragment The value to assign. 1171 * @return shoestring 1172 * @this shoestring 1173 */ 1174 shoestring.fn.replaceWith = function( fragment ){ 1175 if( typeof( fragment ) === "string" ){ 1176 fragment = shoestring( fragment ); 1177 } 1178 1179 var ret = []; 1180 1181 if( fragment.length > 1 ){ 1182 fragment = fragment.reverse(); 1183 } 1184 this.each(function( i ){ 1185 var clone = this.cloneNode( true ), 1186 insertEl; 1187 ret.push( clone ); 1188 1189 // If there is no parentNode, this is pointless, drop it. 1190 if( !this.parentNode ){ return; } 1191 1192 if( fragment.length === 1 ){ 1193 insertEl = i > 0 ? fragment[ 0 ].cloneNode( true ) : fragment[ 0 ]; 1194 this.parentNode.replaceChild( insertEl, this ); 1195 } else { 1196 for( var j = 0, jl = fragment.length; j < jl; j++ ){ 1197 insertEl = i > 0 ? fragment[ j ].cloneNode( true ) : fragment[ j ]; 1198 this.parentNode.insertBefore( insertEl, this.nextSibling ); 1199 } 1200 this.parentNode.removeChild( this ); 1201 } 1202 }); 1203 1204 return shoestring( ret ); 1205 }; 1206 1207 1208 1209 /** 1210 * Get all of the sibling elements for each element in the current set. 1211 * 1212 * @return shoestring 1213 * @this shoestring 1214 */ 1215 shoestring.fn.siblings = function(){ 1216 1217 if( !this.length ) { 1218 return shoestring( [] ); 1219 } 1220 1221 var sibs = [], el = this[ 0 ].parentNode.firstChild; 1222 1223 do { 1224 if( el.nodeType === 1 && el !== this[ 0 ] ) { 1225 sibs.push( el ); 1226 } 1227 1228 el = el.nextSibling; 1229 } while( el ); 1230 1231 return shoestring( sibs ); 1232 }; 1233 1234 1235 1236 var getText = function( elem ){ 1237 var node, 1238 ret = "", 1239 i = 0, 1240 nodeType = elem.nodeType; 1241 1242 if ( !nodeType ) { 1243 // If no nodeType, this is expected to be an array 1244 while ( (node = elem[i++]) ) { 1245 // Do not traverse comment nodes 1246 ret += getText( node ); 1247 } 1248 } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { 1249 // Use textContent for elements 1250 // innerText usage removed for consistency of new lines (jQuery #11153) 1251 if ( typeof elem.textContent === "string" ) { 1252 return elem.textContent; 1253 } else { 1254 // Traverse its children 1255 for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { 1256 ret += getText( elem ); 1257 } 1258 } 1259 } else if ( nodeType === 3 || nodeType === 4 ) { 1260 return elem.nodeValue; 1261 } 1262 // Do not include comment or processing instruction nodes 1263 1264 return ret; 1265 }; 1266 1267 /** 1268 * Recursively retrieve the text content of the each element in the current set. 1269 * 1270 * @return shoestring 1271 * @this shoestring 1272 */ 1273 shoestring.fn.text = function() { 1274 1275 return getText( this ); 1276 }; 1277 1278 1279 1280 1281 /** 1282 * Get the value of the first element or set the value of all elements in the current set. 1283 * 1284 * @param {string} value The value to set. 1285 * @return shoestring 1286 * @this shoestring 1287 */ 1288 shoestring.fn.val = function( value ){ 1289 var el; 1290 if( value !== undefined ){ 1291 return this.each(function(){ 1292 if( this.tagName === "SELECT" ){ 1293 var optionSet, option, 1294 options = this.options, 1295 values = [], 1296 i = options.length, 1297 newIndex; 1298 1299 values[0] = value; 1300 while ( i-- ) { 1301 option = options[ i ]; 1302 if ( (option.selected = shoestring.inArray( option.value, values ) >= 0) ) { 1303 optionSet = true; 1304 newIndex = i; 1305 } 1306 } 1307 // force browsers to behave consistently when non-matching value is set 1308 if ( !optionSet ) { 1309 this.selectedIndex = -1; 1310 } else { 1311 this.selectedIndex = newIndex; 1312 } 1313 } else { 1314 this.value = value; 1315 } 1316 }); 1317 } else { 1318 el = this[0]; 1319 1320 if( el.tagName === "SELECT" ){ 1321 if( el.selectedIndex < 0 ){ return ""; } 1322 return el.options[ el.selectedIndex ].value; 1323 } else { 1324 return el.value; 1325 } 1326 } 1327 }; 1328 1329 1330 1331 /** 1332 * Private function for setting/getting the offset property for height/width. 1333 * 1334 * **NOTE** Please use the [width](width.js.html) or [height](height.js.html) methods instead. 1335 * 1336 * @param {shoestring} set The set of elements. 1337 * @param {string} name The string "height" or "width". 1338 * @param {float|undefined} value The value to assign. 1339 * @return shoestring 1340 * @this window 1341 */ 1342 shoestring._dimension = function( set, name, value ){ 1343 var offsetName; 1344 1345 if( value === undefined ){ 1346 offsetName = name.replace(/^[a-z]/, function( letter ) { 1347 return letter.toUpperCase(); 1348 }); 1349 1350 return set[ 0 ][ "offset" + offsetName ]; 1351 } else { 1352 // support integer values as pixels 1353 value = typeof value === "string" ? value : value + "px"; 1354 1355 return set.each(function(){ 1356 this.style[ name ] = value; 1357 }); 1358 } 1359 }; 1360 1361 1362 1363 /** 1364 * Gets the width value of the first element or sets the width for the whole set. 1365 * 1366 * @param {float|undefined} value The value to assign. 1367 * @return shoestring 1368 * @this shoestring 1369 */ 1370 shoestring.fn.width = function( value ){ 1371 return shoestring._dimension( this, "width", value ); 1372 }; 1373 1374 1375 1376 /** 1377 * Wraps the child elements in the provided HTML. 1378 * 1379 * @param {string} html The wrapping HTML. 1380 * @return shoestring 1381 * @this shoestring 1382 */ 1383 shoestring.fn.wrapInner = function( html ){ 1384 return this.each(function(){ 1385 var inH = this.innerHTML; 1386 1387 this.innerHTML = ""; 1388 shoestring( this ).append( shoestring( html ).html( inH ) ); 1389 }); 1390 }; 1391 1392 1393 1394 function initEventCache( el, evt ) { 1395 if ( !el.shoestringData ) { 1396 el.shoestringData = {}; 1397 } 1398 if ( !el.shoestringData.events ) { 1399 el.shoestringData.events = {}; 1400 } 1401 if ( !el.shoestringData.loop ) { 1402 el.shoestringData.loop = {}; 1403 } 1404 if ( !el.shoestringData.events[ evt ] ) { 1405 el.shoestringData.events[ evt ] = []; 1406 } 1407 } 1408 1409 function addToEventCache( el, evt, eventInfo ) { 1410 var obj = {}; 1411 obj.isCustomEvent = eventInfo.isCustomEvent; 1412 obj.callback = eventInfo.callfunc; 1413 obj.originalCallback = eventInfo.originalCallback; 1414 obj.namespace = eventInfo.namespace; 1415 1416 el.shoestringData.events[ evt ].push( obj ); 1417 1418 if( eventInfo.customEventLoop ) { 1419 el.shoestringData.loop[ evt ] = eventInfo.customEventLoop; 1420 } 1421 } 1422 1423 /** 1424 * Bind a callback to an event for the currrent set of elements. 1425 * 1426 * @param {string} evt The event(s) to watch for. 1427 * @param {object,function} data Data to be included with each event or the callback. 1428 * @param {function} originalCallback Callback to be invoked when data is define.d. 1429 * @return shoestring 1430 * @this shoestring 1431 */ 1432 shoestring.fn.bind = function( evt, data, originalCallback ){ 1433 1434 if( typeof data === "function" ){ 1435 originalCallback = data; 1436 data = null; 1437 } 1438 1439 var evts = evt.split( " " ); 1440 1441 // NOTE the `triggeredElement` is purely for custom events from IE 1442 function encasedCallback( e, namespace, triggeredElement ){ 1443 var result; 1444 1445 if( e._namespace && e._namespace !== namespace ) { 1446 return; 1447 } 1448 1449 e.data = data; 1450 e.namespace = e._namespace; 1451 1452 var returnTrue = function(){ 1453 return true; 1454 }; 1455 1456 e.isDefaultPrevented = function(){ 1457 return false; 1458 }; 1459 1460 var originalPreventDefault = e.preventDefault; 1461 var preventDefaultConstructor = function(){ 1462 if( originalPreventDefault ) { 1463 return function(){ 1464 e.isDefaultPrevented = returnTrue; 1465 originalPreventDefault.call(e); 1466 }; 1467 } else { 1468 return function(){ 1469 e.isDefaultPrevented = returnTrue; 1470 e.returnValue = false; 1471 }; 1472 } 1473 }; 1474 1475 // thanks https://github.com/jonathantneal/EventListener 1476 e.target = triggeredElement || e.target || e.srcElement; 1477 e.preventDefault = preventDefaultConstructor(); 1478 e.stopPropagation = e.stopPropagation || function () { 1479 e.cancelBubble = true; 1480 }; 1481 1482 result = originalCallback.apply(this, [ e ].concat( e._args ) ); 1483 1484 if( result === false ){ 1485 e.preventDefault(); 1486 e.stopPropagation(); 1487 } 1488 1489 return result; 1490 } 1491 1492 return this.each(function(){ 1493 var domEventCallback, 1494 customEventCallback, 1495 customEventLoop, 1496 oEl = this; 1497 1498 for( var i = 0, il = evts.length; i < il; i++ ){ 1499 var split = evts[ i ].split( "." ), 1500 evt = split[ 0 ], 1501 namespace = split.length > 0 ? split[ 1 ] : null; 1502 1503 domEventCallback = function( originalEvent ) { 1504 if( oEl.ssEventTrigger ) { 1505 originalEvent._namespace = oEl.ssEventTrigger._namespace; 1506 originalEvent._args = oEl.ssEventTrigger._args; 1507 1508 oEl.ssEventTrigger = null; 1509 } 1510 return encasedCallback.call( oEl, originalEvent, namespace ); 1511 }; 1512 customEventCallback = null; 1513 customEventLoop = null; 1514 1515 initEventCache( this, evt ); 1516 1517 this.addEventListener( evt, domEventCallback, false ); 1518 1519 addToEventCache( this, evt, { 1520 callfunc: customEventCallback || domEventCallback, 1521 isCustomEvent: !!customEventCallback, 1522 customEventLoop: customEventLoop, 1523 originalCallback: originalCallback, 1524 namespace: namespace 1525 }); 1526 } 1527 }); 1528 }; 1529 1530 shoestring.fn.on = shoestring.fn.bind; 1531 1532 1533 1534 1535 /** 1536 * Unbind a previous bound callback for an event. 1537 * 1538 * @param {string} event The event(s) the callback was bound to.. 1539 * @param {function} callback Callback to unbind. 1540 * @return shoestring 1541 * @this shoestring 1542 */ 1543 shoestring.fn.unbind = function( event, callback ){ 1544 1545 1546 var evts = event ? event.split( " " ) : []; 1547 1548 return this.each(function(){ 1549 if( !this.shoestringData || !this.shoestringData.events ) { 1550 return; 1551 } 1552 1553 if( !evts.length ) { 1554 unbindAll.call( this ); 1555 } else { 1556 var split, evt, namespace; 1557 for( var i = 0, il = evts.length; i < il; i++ ){ 1558 split = evts[ i ].split( "." ), 1559 evt = split[ 0 ], 1560 namespace = split.length > 0 ? split[ 1 ] : null; 1561 1562 if( evt ) { 1563 unbind.call( this, evt, namespace, callback ); 1564 } else { 1565 unbindAll.call( this, namespace, callback ); 1566 } 1567 } 1568 } 1569 }); 1570 }; 1571 1572 function unbind( evt, namespace, callback ) { 1573 var bound = this.shoestringData.events[ evt ]; 1574 if( !(bound && bound.length) ) { 1575 return; 1576 } 1577 1578 var matched = [], j, jl; 1579 for( j = 0, jl = bound.length; j < jl; j++ ) { 1580 if( !namespace || namespace === bound[ j ].namespace ) { 1581 if( callback === undefined || callback === bound[ j ].originalCallback ) { 1582 this.removeEventListener( evt, bound[ j ].callback, false ); 1583 matched.push( j ); 1584 } 1585 } 1586 } 1587 1588 for( j = 0, jl = matched.length; j < jl; j++ ) { 1589 this.shoestringData.events[ evt ].splice( j, 1 ); 1590 } 1591 } 1592 1593 function unbindAll( namespace, callback ) { 1594 for( var evtKey in this.shoestringData.events ) { 1595 unbind.call( this, evtKey, namespace, callback ); 1596 } 1597 } 1598 1599 shoestring.fn.off = shoestring.fn.unbind; 1600 1601 1602 /** 1603 * Bind a callback to an event for the currrent set of elements, unbind after one occurence. 1604 * 1605 * @param {string} event The event(s) to watch for. 1606 * @param {function} callback Callback to invoke on the event. 1607 * @return shoestring 1608 * @this shoestring 1609 */ 1610 shoestring.fn.one = function( event, callback ){ 1611 var evts = event.split( " " ); 1612 1613 return this.each(function(){ 1614 var thisevt, cbs = {}, $t = shoestring( this ); 1615 1616 for( var i = 0, il = evts.length; i < il; i++ ){ 1617 thisevt = evts[ i ]; 1618 1619 cbs[ thisevt ] = function( e ){ 1620 var $t = shoestring( this ); 1621 1622 for( var j in cbs ) { 1623 $t.unbind( j, cbs[ j ] ); 1624 } 1625 1626 return callback.apply( this, [ e ].concat( e._args ) ); 1627 }; 1628 1629 $t.bind( thisevt, cbs[ thisevt ] ); 1630 } 1631 }); 1632 }; 1633 1634 1635 1636 /** 1637 * Trigger an event on the first element in the set, no bubbling, no defaults. 1638 * 1639 * @param {string} event The event(s) to trigger. 1640 * @param {object} args Arguments to append to callback invocations. 1641 * @return shoestring 1642 * @this shoestring 1643 */ 1644 shoestring.fn.triggerHandler = function( event, args ){ 1645 var e = event.split( " " )[ 0 ], 1646 el = this[ 0 ], 1647 ret; 1648 1649 // See this.fireEvent( 'on' + evts[ i ], document.createEventObject() ); instead of click() etc in trigger. 1650 if( doc.createEvent && el.shoestringData && el.shoestringData.events && el.shoestringData.events[ e ] ){ 1651 var bindings = el.shoestringData.events[ e ]; 1652 for (var i in bindings ){ 1653 if( bindings.hasOwnProperty( i ) ){ 1654 event = doc.createEvent( "Event" ); 1655 event.initEvent( e, true, true ); 1656 event._args = args; 1657 args.unshift( event ); 1658 1659 ret = bindings[ i ].originalCallback.apply( event.target, args ); 1660 } 1661 } 1662 } 1663 1664 return ret; 1665 }; 1666 1667 1668 1669 /** 1670 * Trigger an event on each of the DOM elements in the current set. 1671 * 1672 * @param {string} event The event(s) to trigger. 1673 * @param {object} args Arguments to append to callback invocations. 1674 * @return shoestring 1675 * @this shoestring 1676 */ 1677 shoestring.fn.trigger = function( event, args ){ 1678 var evts = event.split( " " ); 1679 1680 return this.each(function(){ 1681 var split, evt, namespace; 1682 for( var i = 0, il = evts.length; i < il; i++ ){ 1683 split = evts[ i ].split( "." ), 1684 evt = split[ 0 ], 1685 namespace = split.length > 0 ? split[ 1 ] : null; 1686 1687 if( evt === "click" ){ 1688 if( this.tagName === "INPUT" && this.type === "checkbox" && this.click ){ 1689 this.click(); 1690 return false; 1691 } 1692 } 1693 1694 if( doc.createEvent ){ 1695 var event = doc.createEvent( "Event" ); 1696 event.initEvent( evt, true, true ); 1697 event._args = args; 1698 event._namespace = namespace; 1699 1700 this.dispatchEvent( event ); 1701 } 1702 } 1703 }); 1704 }; 1705 1706 1707 1708 return shoestring; 1709})); 1710 1711(function (root, factory) { 1712 if (typeof define === 'function' && define.amd) { 1713 define(["shoestring"], function (shoestring) { 1714 return (root.Tablesaw = factory(shoestring, root)); 1715 }); 1716 } else if (typeof exports === 'object') { 1717 module.exports = factory(require('shoestring'), root); 1718 } else { 1719 root.Tablesaw = factory(shoestring, root); 1720 } 1721}(typeof window !== "undefined" ? window : this, function ($, window) { 1722 "use strict"; 1723 1724 var document = window.document; 1725// Account for Tablesaw being loaded either before or after the DOMContentLoaded event is fired. 1726var domContentLoadedTriggered = /complete|loaded/.test(document.readyState); 1727document.addEventListener("DOMContentLoaded", function() { 1728 domContentLoadedTriggered = true; 1729}); 1730 1731var Tablesaw = { 1732 i18n: { 1733 modeStack: "Stack", 1734 modeSwipe: "Swipe", 1735 modeToggle: "Toggle", 1736 modeSwitchColumnsAbbreviated: "Cols", 1737 modeSwitchColumns: "Columns", 1738 columnToggleButton: "Columns", 1739 columnToggleError: "No eligible columns.", 1740 sort: "Sort", 1741 swipePreviousColumn: "Previous column", 1742 swipeNextColumn: "Next column" 1743 }, 1744 // cut the mustard 1745 mustard: 1746 "head" in document && // IE9+, Firefox 4+, Safari 5.1+, Mobile Safari 4.1+, Opera 11.5+, Android 2.3+ 1747 (!window.blackberry || window.WebKitPoint) && // only WebKit Blackberry (OS 6+) 1748 !window.operamini, 1749 $: $, 1750 _init: function(element) { 1751 Tablesaw.$(element || document).trigger("enhance.tablesaw"); 1752 }, 1753 init: function(element) { 1754 // Account for Tablesaw being loaded either before or after the DOMContentLoaded event is fired. 1755 domContentLoadedTriggered = 1756 domContentLoadedTriggered || /complete|loaded/.test(document.readyState); 1757 if (!domContentLoadedTriggered) { 1758 if ("addEventListener" in document) { 1759 // Use raw DOMContentLoaded instead of shoestring (may have issues in Android 2.3, exhibited by stack table) 1760 document.addEventListener("DOMContentLoaded", function() { 1761 Tablesaw._init(element); 1762 }); 1763 } 1764 } else { 1765 Tablesaw._init(element); 1766 } 1767 } 1768}; 1769 1770$(document).on("enhance.tablesaw", function() { 1771 // Extend i18n config, if one exists. 1772 if (typeof TablesawConfig !== "undefined" && TablesawConfig.i18n) { 1773 Tablesaw.i18n = $.extend(Tablesaw.i18n, TablesawConfig.i18n || {}); 1774 } 1775 1776 Tablesaw.i18n.modes = [ 1777 Tablesaw.i18n.modeStack, 1778 Tablesaw.i18n.modeSwipe, 1779 Tablesaw.i18n.modeToggle 1780 ]; 1781}); 1782 1783if (Tablesaw.mustard) { 1784 $(document.documentElement).addClass("tablesaw-enhanced"); 1785} 1786 1787(function() { 1788 var pluginName = "tablesaw"; 1789 var classes = { 1790 toolbar: "tablesaw-bar" 1791 }; 1792 var events = { 1793 create: "tablesawcreate", 1794 destroy: "tablesawdestroy", 1795 refresh: "tablesawrefresh", 1796 resize: "tablesawresize" 1797 }; 1798 var defaultMode = "stack"; 1799 var initSelector = "table"; 1800 var initFilterSelector = "[data-tablesaw],[data-tablesaw-mode],[data-tablesaw-sortable]"; 1801 var defaultConfig = {}; 1802 1803 Tablesaw.events = events; 1804 1805 var Table = function(element) { 1806 if (!element) { 1807 throw new Error("Tablesaw requires an element."); 1808 } 1809 1810 this.table = element; 1811 this.$table = $(element); 1812 1813 // only one <thead> and <tfoot> are allowed, per the specification 1814 this.$thead = this.$table 1815 .children() 1816 .filter("thead") 1817 .eq(0); 1818 1819 // multiple <tbody> are allowed, per the specification 1820 this.$tbody = this.$table.children().filter("tbody"); 1821 1822 this.mode = this.$table.attr("data-tablesaw-mode") || defaultMode; 1823 1824 this.$toolbar = null; 1825 1826 this.attributes = { 1827 subrow: "data-tablesaw-subrow", 1828 ignorerow: "data-tablesaw-ignorerow" 1829 }; 1830 1831 this.init(); 1832 }; 1833 1834 Table.prototype.init = function() { 1835 if (!this.$thead.length) { 1836 throw new Error("tablesaw: a <thead> is required, but none was found."); 1837 } 1838 1839 if (!this.$thead.find("th").length) { 1840 throw new Error("tablesaw: no header cells found. Are you using <th> inside of <thead>?"); 1841 } 1842 1843 // assign an id if there is none 1844 if (!this.$table.attr("id")) { 1845 this.$table.attr("id", pluginName + "-" + Math.round(Math.random() * 10000)); 1846 } 1847 1848 this.createToolbar(); 1849 1850 this._initCells(); 1851 1852 this.$table.data(pluginName, this); 1853 1854 this.$table.trigger(events.create, [this]); 1855 }; 1856 1857 Table.prototype.getConfig = function(pluginSpecificConfig) { 1858 // shoestring extend doesnât support arbitrary args 1859 var configs = $.extend(defaultConfig, pluginSpecificConfig || {}); 1860 return $.extend(configs, typeof TablesawConfig !== "undefined" ? TablesawConfig : {}); 1861 }; 1862 1863 Table.prototype._getPrimaryHeaderRow = function() { 1864 return this._getHeaderRows().eq(0); 1865 }; 1866 1867 Table.prototype._getHeaderRows = function() { 1868 return this.$thead 1869 .children() 1870 .filter("tr") 1871 .filter(function() { 1872 return !$(this).is("[data-tablesaw-ignorerow]"); 1873 }); 1874 }; 1875 1876 Table.prototype._getRowIndex = function($row) { 1877 return $row.prevAll().length; 1878 }; 1879 1880 Table.prototype._getHeaderRowIndeces = function() { 1881 var self = this; 1882 var indeces = []; 1883 this._getHeaderRows().each(function() { 1884 indeces.push(self._getRowIndex($(this))); 1885 }); 1886 return indeces; 1887 }; 1888 1889 Table.prototype._getPrimaryHeaderCells = function($row) { 1890 return ($row || this._getPrimaryHeaderRow()).find("th"); 1891 }; 1892 1893 Table.prototype._$getCells = function(th) { 1894 var self = this; 1895 return $(th) 1896 .add(th.cells) 1897 .filter(function() { 1898 var $t = $(this); 1899 var $row = $t.parent(); 1900 var hasColspan = $t.is("[colspan]"); 1901 // no subrows or ignored rows (keep cells in ignored rows that do not have a colspan) 1902 return ( 1903 !$row.is("[" + self.attributes.subrow + "]") && 1904 (!$row.is("[" + self.attributes.ignorerow + "]") || !hasColspan) 1905 ); 1906 }); 1907 }; 1908 1909 Table.prototype._getVisibleColspan = function() { 1910 var colspan = 0; 1911 this._getPrimaryHeaderCells().each(function() { 1912 var $t = $(this); 1913 if ($t.css("display") !== "none") { 1914 colspan += parseInt($t.attr("colspan"), 10) || 1; 1915 } 1916 }); 1917 return colspan; 1918 }; 1919 1920 Table.prototype.getColspanForCell = function($cell) { 1921 var visibleColspan = this._getVisibleColspan(); 1922 var visibleSiblingColumns = 0; 1923 if ($cell.closest("tr").data("tablesaw-rowspanned")) { 1924 visibleSiblingColumns++; 1925 } 1926 1927 $cell.siblings().each(function() { 1928 var $t = $(this); 1929 var colColspan = parseInt($t.attr("colspan"), 10) || 1; 1930 1931 if ($t.css("display") !== "none") { 1932 visibleSiblingColumns += colColspan; 1933 } 1934 }); 1935 // console.log( $cell[ 0 ], visibleColspan, visibleSiblingColumns ); 1936 1937 return visibleColspan - visibleSiblingColumns; 1938 }; 1939 1940 Table.prototype.isCellInColumn = function(header, cell) { 1941 return $(header) 1942 .add(header.cells) 1943 .filter(function() { 1944 return this === cell; 1945 }).length; 1946 }; 1947 1948 Table.prototype.updateColspanCells = function(cls, header, userAction) { 1949 var self = this; 1950 var primaryHeaderRow = self._getPrimaryHeaderRow(); 1951 1952 // find persistent column rowspans 1953 this.$table.find("[rowspan][data-tablesaw-priority]").each(function() { 1954 var $t = $(this); 1955 if ($t.attr("data-tablesaw-priority") !== "persist") { 1956 return; 1957 } 1958 1959 var $row = $t.closest("tr"); 1960 var rowspan = parseInt($t.attr("rowspan"), 10); 1961 if (rowspan > 1) { 1962 $row = $row.next(); 1963 1964 $row.data("tablesaw-rowspanned", true); 1965 1966 rowspan--; 1967 } 1968 }); 1969 1970 this.$table 1971 .find("[colspan],[data-tablesaw-maxcolspan]") 1972 .filter(function() { 1973 // is not in primary header row 1974 return $(this).closest("tr")[0] !== primaryHeaderRow[0]; 1975 }) 1976 .each(function() { 1977 var $cell = $(this); 1978 1979 if (userAction === undefined || self.isCellInColumn(header, this)) { 1980 } else { 1981 // if is not a user action AND the cell is not in the updating column, kill it 1982 return; 1983 } 1984 1985 var colspan = self.getColspanForCell($cell); 1986 1987 if (cls && userAction !== undefined) { 1988 // console.log( colspan === 0 ? "addClass" : "removeClass", $cell ); 1989 $cell[colspan === 0 ? "addClass" : "removeClass"](cls); 1990 } 1991 1992 // cache original colspan 1993 var maxColspan = parseInt($cell.attr("data-tablesaw-maxcolspan"), 10); 1994 if (!maxColspan) { 1995 $cell.attr("data-tablesaw-maxcolspan", $cell.attr("colspan")); 1996 } else if (colspan > maxColspan) { 1997 colspan = maxColspan; 1998 } 1999 2000 // console.log( this, "setting colspan to ", colspan ); 2001 $cell.attr("colspan", colspan); 2002 }); 2003 }; 2004 2005 Table.prototype._findPrimaryHeadersForCell = function(cell) { 2006 var $headerRow = this._getPrimaryHeaderRow(); 2007 var headerRowIndex = this._getRowIndex($headerRow); 2008 var results = []; 2009 2010 for (var rowNumber = 0; rowNumber < this.headerMapping.length; rowNumber++) { 2011 if (rowNumber === headerRowIndex) { 2012 continue; 2013 } 2014 2015 for (var colNumber = 0; colNumber < this.headerMapping[rowNumber].length; colNumber++) { 2016 if (this.headerMapping[rowNumber][colNumber] === cell) { 2017 results.push(this.headerMapping[headerRowIndex][colNumber]); 2018 } 2019 } 2020 } 2021 2022 return results; 2023 }; 2024 2025 // used by init cells 2026 Table.prototype.getRows = function() { 2027 var self = this; 2028 return this.$table.find("tr").filter(function() { 2029 return $(this) 2030 .closest("table") 2031 .is(self.$table); 2032 }); 2033 }; 2034 2035 // used by sortable 2036 Table.prototype.getBodyRows = function(tbody) { 2037 return (tbody ? $(tbody) : this.$tbody).children().filter("tr"); 2038 }; 2039 2040 Table.prototype.getHeaderCellIndex = function(cell) { 2041 var lookup = this.headerMapping[0]; 2042 for (var colIndex = 0; colIndex < lookup.length; colIndex++) { 2043 if (lookup[colIndex] === cell) { 2044 return colIndex; 2045 } 2046 } 2047 2048 return -1; 2049 }; 2050 2051 Table.prototype._initCells = function() { 2052 // re-establish original colspans 2053 this.$table.find("[data-tablesaw-maxcolspan]").each(function() { 2054 var $t = $(this); 2055 $t.attr("colspan", $t.attr("data-tablesaw-maxcolspan")); 2056 }); 2057 2058 var $rows = this.getRows(); 2059 var columnLookup = []; 2060 2061 $rows.each(function(rowNumber) { 2062 columnLookup[rowNumber] = []; 2063 }); 2064 2065 $rows.each(function(rowNumber) { 2066 var coltally = 0; 2067 var $t = $(this); 2068 var children = $t.children(); 2069 2070 children.each(function() { 2071 var colspan = parseInt( 2072 this.getAttribute("data-tablesaw-maxcolspan") || this.getAttribute("colspan"), 2073 10 2074 ); 2075 var rowspan = parseInt(this.getAttribute("rowspan"), 10); 2076 2077 // set in a previous rowspan 2078 while (columnLookup[rowNumber][coltally]) { 2079 coltally++; 2080 } 2081 2082 columnLookup[rowNumber][coltally] = this; 2083 2084 // TODO? both colspan and rowspan 2085 if (colspan) { 2086 for (var k = 0; k < colspan - 1; k++) { 2087 coltally++; 2088 columnLookup[rowNumber][coltally] = this; 2089 } 2090 } 2091 if (rowspan) { 2092 for (var j = 1; j < rowspan; j++) { 2093 columnLookup[rowNumber + j][coltally] = this; 2094 } 2095 } 2096 2097 coltally++; 2098 }); 2099 }); 2100 2101 var headerRowIndeces = this._getHeaderRowIndeces(); 2102 for (var colNumber = 0; colNumber < columnLookup[0].length; colNumber++) { 2103 for (var headerIndex = 0, k = headerRowIndeces.length; headerIndex < k; headerIndex++) { 2104 var headerCol = columnLookup[headerRowIndeces[headerIndex]][colNumber]; 2105 2106 var rowNumber = headerRowIndeces[headerIndex]; 2107 var rowCell; 2108 2109 if (!headerCol.cells) { 2110 headerCol.cells = []; 2111 } 2112 2113 while (rowNumber < columnLookup.length) { 2114 rowCell = columnLookup[rowNumber][colNumber]; 2115 2116 if (headerCol !== rowCell) { 2117 headerCol.cells.push(rowCell); 2118 } 2119 2120 rowNumber++; 2121 } 2122 } 2123 } 2124 2125 this.headerMapping = columnLookup; 2126 }; 2127 2128 Table.prototype.refresh = function() { 2129 this._initCells(); 2130 2131 this.$table.trigger(events.refresh, [this]); 2132 }; 2133 2134 Table.prototype._getToolbarAnchor = function() { 2135 var $parent = this.$table.parent(); 2136 if ($parent.is(".tablesaw-overflow")) { 2137 return $parent; 2138 } 2139 return this.$table; 2140 }; 2141 2142 Table.prototype._getToolbar = function($anchor) { 2143 if (!$anchor) { 2144 $anchor = this._getToolbarAnchor(); 2145 } 2146 return $anchor.prev().filter("." + classes.toolbar); 2147 }; 2148 2149 Table.prototype.createToolbar = function() { 2150 // Insert the toolbar 2151 // TODO move this into a separate component 2152 var $anchor = this._getToolbarAnchor(); 2153 var $toolbar = this._getToolbar($anchor); 2154 if (!$toolbar.length) { 2155 $toolbar = $("<div>") 2156 .addClass(classes.toolbar) 2157 .insertBefore($anchor); 2158 } 2159 this.$toolbar = $toolbar; 2160 2161 if (this.mode) { 2162 this.$toolbar.addClass("tablesaw-mode-" + this.mode); 2163 } 2164 }; 2165 2166 Table.prototype.destroy = function() { 2167 // Donât remove the toolbar, just erase the classes on it. 2168 // Some of the table features are not yet destroy-friendly. 2169 this._getToolbar().each(function() { 2170 this.className = this.className.replace(/\btablesaw-mode\-\w*\b/gi, ""); 2171 }); 2172 2173 var tableId = this.$table.attr("id"); 2174 $(document).off("." + tableId); 2175 $(window).off("." + tableId); 2176 2177 // other plugins 2178 this.$table.trigger(events.destroy, [this]); 2179 2180 this.$table.removeData(pluginName); 2181 }; 2182 2183 // Collection method. 2184 $.fn[pluginName] = function() { 2185 return this.each(function() { 2186 var $t = $(this); 2187 2188 if ($t.data(pluginName)) { 2189 return; 2190 } 2191 2192 new Table(this); 2193 }); 2194 }; 2195 2196 var $doc = $(document); 2197 $doc.on("enhance.tablesaw", function(e) { 2198 // Cut the mustard 2199 if (Tablesaw.mustard) { 2200 var $target = $(e.target); 2201 if ($target.parent().length) { 2202 $target = $target.parent(); 2203 } 2204 2205 $target 2206 .find(initSelector) 2207 .filter(initFilterSelector) 2208 [pluginName](); 2209 } 2210 }); 2211 2212 // Avoid a resize during scroll: 2213 // Some Mobile devices trigger a resize during scroll (sometimes when 2214 // doing elastic stretch at the end of the document or from the 2215 // location bar hide) 2216 var isScrolling = false; 2217 var scrollTimeout; 2218 $doc.on("scroll.tablesaw", function() { 2219 isScrolling = true; 2220 2221 window.clearTimeout(scrollTimeout); 2222 scrollTimeout = window.setTimeout(function() { 2223 isScrolling = false; 2224 }, 300); // must be greater than the resize timeout below 2225 }); 2226 2227 var resizeTimeout; 2228 $(window).on("resize", function() { 2229 if (!isScrolling) { 2230 window.clearTimeout(resizeTimeout); 2231 resizeTimeout = window.setTimeout(function() { 2232 $doc.trigger(events.resize); 2233 }, 150); // must be less than the scrolling timeout above. 2234 } 2235 }); 2236 2237 Tablesaw.Table = Table; 2238})(); 2239 2240(function() { 2241 var classes = { 2242 stackTable: "tablesaw-stack", 2243 cellLabels: "tablesaw-cell-label", 2244 cellContentLabels: "tablesaw-cell-content" 2245 }; 2246 2247 var data = { 2248 key: "tablesaw-stack" 2249 }; 2250 2251 var attrs = { 2252 labelless: "data-tablesaw-no-labels", 2253 hideempty: "data-tablesaw-hide-empty" 2254 }; 2255 2256 var Stack = function(element, tablesaw) { 2257 this.tablesaw = tablesaw; 2258 this.$table = $(element); 2259 2260 this.labelless = this.$table.is("[" + attrs.labelless + "]"); 2261 this.hideempty = this.$table.is("[" + attrs.hideempty + "]"); 2262 2263 this.$table.data(data.key, this); 2264 }; 2265 2266 Stack.prototype.init = function() { 2267 this.$table.addClass(classes.stackTable); 2268 2269 if (this.labelless) { 2270 return; 2271 } 2272 2273 var self = this; 2274 2275 this.$table 2276 .find("th, td") 2277 .filter(function() { 2278 return !$(this).closest("thead").length; 2279 }) 2280 .filter(function() { 2281 return ( 2282 !$(this).is("[" + attrs.labelless + "]") && 2283 !$(this) 2284 .closest("tr") 2285 .is("[" + attrs.labelless + "]") && 2286 (!self.hideempty || !!$(this).html()) 2287 ); 2288 }) 2289 .each(function() { 2290 var $newHeader = $(document.createElement("b")).addClass(classes.cellLabels); 2291 var $cell = $(this); 2292 2293 $(self.tablesaw._findPrimaryHeadersForCell(this)).each(function(index) { 2294 var $header = $(this.cloneNode(true)); 2295 // TODO decouple from sortable better 2296 // Changed from .text() in https://github.com/filamentgroup/tablesaw/commit/b9c12a8f893ec192830ec3ba2d75f062642f935b 2297 // to preserve structural html in headers, like <a> 2298 var $sortableButton = $header.find(".tablesaw-sortable-btn"); 2299 $header.find(".tablesaw-sortable-arrow").remove(); 2300 2301 // TODO decouple from checkall better 2302 var $checkall = $header.find("[data-tablesaw-checkall]"); 2303 $checkall.closest("label").remove(); 2304 if ($checkall.length) { 2305 $newHeader = $([]); 2306 return; 2307 } 2308 2309 if (index > 0) { 2310 $newHeader.append(document.createTextNode(", ")); 2311 } 2312 2313 var parentNode = $sortableButton.length ? $sortableButton[0] : $header[0]; 2314 var el; 2315 while ((el = parentNode.firstChild)) { 2316 $newHeader[0].appendChild(el); 2317 } 2318 }); 2319 2320 if ($newHeader.length && !$cell.find("." + classes.cellContentLabels).length) { 2321 $cell.wrapInner("<span class='" + classes.cellContentLabels + "'></span>"); 2322 } 2323 2324 // Update if already exists. 2325 var $label = $cell.find("." + classes.cellLabels); 2326 if (!$label.length) { 2327 $cell.prepend(document.createTextNode(" ")); 2328 $cell.prepend($newHeader); 2329 } else { 2330 // only if changed 2331 $label.replaceWith($newHeader); 2332 } 2333 }); 2334 }; 2335 2336 Stack.prototype.destroy = function() { 2337 this.$table.removeClass(classes.stackTable); 2338 this.$table.find("." + classes.cellLabels).remove(); 2339 this.$table.find("." + classes.cellContentLabels).each(function() { 2340 $(this).replaceWith($(this.childNodes)); 2341 }); 2342 }; 2343 2344 // on tablecreate, init 2345 $(document) 2346 .on(Tablesaw.events.create, function(e, tablesaw) { 2347 if (tablesaw.mode === "stack") { 2348 var table = new Stack(tablesaw.table, tablesaw); 2349 table.init(); 2350 } 2351 }) 2352 .on(Tablesaw.events.refresh, function(e, tablesaw) { 2353 if (tablesaw.mode === "stack") { 2354 $(tablesaw.table) 2355 .data(data.key) 2356 .init(); 2357 } 2358 }) 2359 .on(Tablesaw.events.destroy, function(e, tablesaw) { 2360 if (tablesaw.mode === "stack") { 2361 $(tablesaw.table) 2362 .data(data.key) 2363 .destroy(); 2364 } 2365 }); 2366 2367 Tablesaw.Stack = Stack; 2368})(); 2369 2370 return Tablesaw; 2371}));
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.