1/*! 2 * FooTable - Awesome Responsive Tables 3 * Version : 2.0.1.4 4 * http://fooplugins.com/plugins/footable-jquery/ 5 * 6 * Requires jQuery - http://jquery.com/ 7 * 8 * Copyright 2014 Steven Usher & Brad Vincent 9 * Released under the MIT license 10 * You are free to use FooTable in commercial projects as long as this copyright header is left intact. 11 * 12 * Date: 16 Feb 2014 13 */ 14(function ($, w, undefined) { 15 w.footable = { 16 options: { 17 delay: 100, // The number of millseconds to wait before triggering the react event 18 breakpoints: { // The different screen resolution breakpoints 19 phone: 480, 20 tablet: 1024 21 }, 22 parsers: { // The default parser to parse the value out of a cell (values are used in building up row detail) 23 alpha: function (cell) { 24 return $(cell).data('value') || $.trim($(cell).text()); 25 }, 26 numeric: function (cell) { 27 var val = $(cell).data('value') || $(cell).text().replace(/[^0-9.\-]/g, ''); 28 val = parseFloat(val); 29 if (isNaN(val)) val = 0; 30 return val; 31 } 32 }, 33 addRowToggle: true, 34 calculateWidthOverride: null, 35 toggleSelector: ' > tbody > tr:not(.footable-row-detail)', //the selector to show/hide the detail row 36 columnDataSelector: '> thead > tr:last-child > th, > thead > tr:last-child > td', //the selector used to find the column data in the thead 37 detailSeparator: ':', //the separator character used when building up the detail row 38 toggleHTMLElement: '<span />', // override this if you want to insert a click target rather than use a background image. 39 createGroupedDetail: function (data) { 40 var groups = { '_none': { 'name': null, 'data': [] } }; 41 for (var i = 0; i < data.length; i++) { 42 var groupid = data[i].group; 43 if (groupid !== null) { 44 if (!(groupid in groups)) 45 groups[groupid] = { 'name': data[i].groupName || data[i].group, 'data': [] }; 46 47 groups[groupid].data.push(data[i]); 48 } else { 49 groups._none.data.push(data[i]); 50 } 51 } 52 return groups; 53 }, 54 createDetail: function (element, data, createGroupedDetail, separatorChar, classes) { 55 /// <summary>This function is used by FooTable to generate the detail view seen when expanding a collapsed row.</summary> 56 /// <param name="element">This is the div that contains all the detail row information, anything could be added to it.</param> 57 /// <param name="data"> 58 /// This is an array of objects containing the cell information for the current row. 59 /// These objects look like the below: 60 /// obj = { 61 /// 'name': String, // The name of the column 62 /// 'value': Object, // The value parsed from the cell using the parsers. This could be a string, a number or whatever the parser outputs. 63 /// 'display': String, // This is the actual HTML from the cell, so if you have images etc you want moved this is the one to use and is the default value used. 64 /// 'group': String, // This is the identifier used in the data-group attribute of the column. 65 /// 'groupName': String // This is the actual name of the group the column belongs to. 66 /// } 67 /// </param> 68 /// <param name="createGroupedDetail">The grouping function to group the data</param> 69 /// <param name="separatorChar">The separator charactor used</param> 70 /// <param name="classes">The array of class names used to build up the detail row</param> 71 72 var groups = createGroupedDetail(data); 73 for (var group in groups) { 74 if (groups[group].data.length === 0) continue; 75 if (group !== '_none') element.append('<div class="' + classes.detailInnerGroup + '">' + groups[group].name + '</div>'); 76 77 for (var j = 0; j < groups[group].data.length; j++) { 78 var separator = (groups[group].data[j].name) ? separatorChar : ''; 79 element.append('<div class="' + classes.detailInnerRow + '"><div class="' + classes.detailInnerName + '">' + groups[group].data[j].name + separator + '</div><div class="' + classes.detailInnerValue + '">' + groups[group].data[j].display + '</div></div>'); 80 } 81 } 82 }, 83 classes: { 84 main: 'footable', 85 loading: 'footable-loading', 86 loaded: 'footable-loaded', 87 toggle: 'footable-toggle', 88 disabled: 'footable-disabled', 89 detail: 'footable-row-detail', 90 detailCell: 'footable-row-detail-cell', 91 detailInner: 'footable-row-detail-inner', 92 detailInnerRow: 'footable-row-detail-row', 93 detailInnerGroup: 'footable-row-detail-group', 94 detailInnerName: 'footable-row-detail-name', 95 detailInnerValue: 'footable-row-detail-value', 96 detailShow: 'footable-detail-show' 97 }, 98 triggers: { 99 initialize: 'footable_initialize', //trigger this event to force FooTable to reinitialize 100 resize: 'footable_resize', //trigger this event to force FooTable to resize 101 redraw: 'footable_redraw', //trigger this event to force FooTable to redraw 102 toggleRow: 'footable_toggle_row', //trigger this event to force FooTable to toggle a row 103 expandFirstRow: 'footable_expand_first_row', //trigger this event to force FooTable to expand the first row 104 expandAll: 'footable_expand_all', //trigger this event to force FooTable to expand all rows 105 collapseAll: 'footable_collapse_all' //trigger this event to force FooTable to collapse all rows 106 }, 107 events: { 108 alreadyInitialized: 'footable_already_initialized', //fires when the FooTable has already been initialized 109 initializing: 'footable_initializing', //fires before FooTable starts initializing 110 initialized: 'footable_initialized', //fires after FooTable has finished initializing 111 resizing: 'footable_resizing', //fires before FooTable resizes 112 resized: 'footable_resized', //fires after FooTable has resized 113 redrawn: 'footable_redrawn', //fires after FooTable has redrawn 114 breakpoint: 'footable_breakpoint', //fires inside the resize function, when a breakpoint is hit 115 columnData: 'footable_column_data', //fires when setting up column data. Plugins should use this event to capture their own info about a column 116 rowDetailUpdating: 'footable_row_detail_updating', //fires before a detail row is updated 117 rowDetailUpdated: 'footable_row_detail_updated', //fires when a detail row is being updated 118 rowCollapsed: 'footable_row_collapsed', //fires when a row is collapsed 119 rowExpanded: 'footable_row_expanded', //fires when a row is expanded 120 rowRemoved: 'footable_row_removed', //fires when a row is removed 121 reset: 'footable_reset' //fires when FooTable is reset 122 }, 123 debug: false, // Whether or not to log information to the console. 124 log: null 125 }, 126 127 version: { 128 major: 0, minor: 5, 129 toString: function () {
130 return w.footable.version.major + '.' + w.footable.version.minor; 131 }, 132 parse: function (str) { 133 version = /(\d+)\.?(\d+)?\.?(\d+)?/.exec(str); 134 return { 135 major: parseInt(version[1], 10) || 0, 136 minor: parseInt(version[2], 10) || 0, 137 patch: parseInt(version[3], 10) || 0 138 }; 139 } 140 }, 141 142 plugins: { 143 _validate: function (plugin) { 144 ///<summary>Simple validation of the <paramref name="plugin"/> to make sure any members called by FooTable actually exist.</summary> 145 ///<param name="plugin">The object defining the plugin, this should implement a string property called "name" and a function called "init".</param> 146 147 if (!$.isFunction(plugin)) { 148 if (w.footable.options.debug === true) console.error('Validation failed, expected type "function", received type "{0}".', typeof plugin); 149 return false; 150 } 151 var p = new plugin(); 152 if (typeof p['name'] !== 'string') { 153 if (w.footable.options.debug === true) console.error('Validation failed, plugin does not implement a string property called "name".', p); 154 return false; 155 } 156 if (!$.isFunction(p['init'])) { 157 if (w.footable.options.debug === true) console.error('Validation failed, plugin "' + p['name'] + '" does not implement a function called "init".', p); 158 return false; 159 } 160 if (w.footable.options.debug === true) console.log('Validation succeeded for plugin "' + p['name'] + '".', p); 161 return true; 162 }, 163 registered: [], // An array containing all registered plugins. 164 register: function (plugin, options) { 165 ///<summary>Registers a <paramref name="plugin"/> and its default <paramref name="options"/> with FooTable.</summary> 166 ///<param name="plugin">The plugin that should implement a string property called "name" and a function called "init".</param> 167 ///<param name="options">The default options to merge with the FooTable's base options.</param> 168 169 if (w.footable.plugins._validate(plugin)) { 170 w.footable.plugins.registered.push(plugin); 171 if (typeof options === 'object') $.extend(true, w.footable.options, options); 172 } 173 }, 174 load: function(instance){ 175 var loaded = [], registered, i; 176 for(i = 0; i < w.footable.plugins.registered.length; i++){ 177 try { 178 registered = w.footable.plugins.registered[i]; 179 loaded.push(new registered(instance)); 180 } catch (err) { 181 if (w.footable.options.debug === true) console.error(err); 182 } 183 } 184 return loaded; 185 }, 186 init: function (instance) { 187 ///<summary>Loops through all registered plugins and calls the "init" method supplying the current <paramref name="instance"/> of the FooTable as the first parameter.</summary> 188 ///<param name="instance">The current instance of the FooTable that the plugin is being initialized for.</param> 189 190 for (var i = 0; i < instance.plugins.length; i++) { 191 try { 192 instance.plugins[i]['init'](instance); 193 } catch (err) { 194 if (w.footable.options.debug === true) console.error(err); 195 } 196 } 197 } 198 } 199 }; 200 201 var instanceCount = 0; 202 203 $.fn.footable = function (options) { 204 ///<summary>The main constructor call to initialize the plugin using the supplied <paramref name="options"/>.</summary> 205 ///<param name="options"> 206 ///<para>A JSON object containing user defined options for the plugin to use. Any options not supplied will have a default value assigned.</para> 207 ///<para>Check the documentation or the default options object above for more information on available options.
207</para> 208 ///</param> 209 210 options = options || {}; 211 var o = $.extend(true, {}, w.footable.options, options); //merge user and default options 212 return this.each(function () { 213 instanceCount++; 214 var footable = new Footable(this, o, instanceCount); 215 $(this).data('footable', footable); 216 }); 217 }; 218 219 //helper for using timeouts 220 function Timer() { 221 ///<summary>Simple timer object created around a timeout.</summary> 222 var t = this; 223 t.id = null; 224 t.busy = false; 225 t.start = function (code, milliseconds) { 226 ///<summary>Starts the timer and waits the specified amount of <paramref name="milliseconds"/> before executing the supplied <paramref name="code"/>.</summary> 227 ///<param name="code">The code to execute once the timer runs out.</param> 228 ///<param name="milliseconds">The time in milliseconds to wait before executing the supplied <paramref name="code"/>.</param> 229 230 if (t.busy) { 231 return; 232 } 233 t.stop(); 234 t.id = setTimeout(function () { 235 code(); 236 t.id = null; 237 t.busy = false; 238 }, milliseconds); 239 t.busy = true; 240 }; 241 t.stop = function () { 242 ///<summary>Stops the timer if its runnning and resets it back to its starting state.</summary> 243 244 if (t.id !== null) { 245 clearTimeout(t.id); 246 t.id = null; 247 t.busy = false; 248 } 249 }; 250 } 251 252 function Footable(t, o, id) { 253 ///<summary>Inits a new instance of the plugin.</summary> 254 ///<param name="t">The main table element to apply this plugin to.</param> 255 ///<param name="o">The options supplied to the plugin. Check the defaults object to see all available options.</param> 256 ///<param name="id">The id to assign to this instance of the plugin.</param> 257 258 var ft = this; 259 ft.id = id; 260 ft.table = t; 261 ft.options = o; 262 ft.breakpoints = []; 263 ft.breakpointNames = ''; 264 ft.columns = {}; 265 ft.plugins = w.footable.plugins.load(ft); 266 267 var opt = ft.options, 268 cls = opt.classes, 269 evt = opt.events, 270 trg = opt.triggers, 271 indexOffset = 0; 272 273 // This object simply houses all the timers used in the FooTable. 274 ft.timers = { 275 resize: new Timer(), 276 register: function (name) { 277 ft.timers[name] = new Timer(); 278 return ft.timers[name]; 279 } 280 }; 281 282 ft.init = function () { 283 var $window = $(w), $table = $(ft.table); 284 285 w.footable.plugins.init(ft); 286 287 if ($table.hasClass(cls.loaded)) { 288 //already loaded FooTable for the table, so don't init again 289 ft.raise(evt.alreadyInitialized); 290 return; 291 } 292 293 //raise the initializing event 294 ft.raise(evt.initializing); 295 296 $table.addClass(cls.loading); 297 298 // Get the column data once for the life time of the plugin 299 $table.find(opt.columnDataSelector).each(function () { 300 var data = ft.getColumnData(this); 301 ft.columns[data.index] = data; 302 }); 303 304 // Create a nice friendly array to work with out of the breakpoints object. 305 for (var name in opt.breakpoints) { 306 ft.breakpoints.push({ 'name': name, 'width': opt.breakpoints[name] }); 307 ft.breakpointNames += (name + ' '); 308 } 309 310 // Sort the breakpoints so the smallest is checked first 311 ft.breakpoints.sort(function (a, b) { 312 return a['width'] - b['width']; 313 }); 314 315 $table 316 .unbind(trg.initialize) 317 //bind to FooTable initialize trigger 318 .bind(trg.initialize, function () { 319 //remove previous "state" (to "force" a resize) 320 $table.removeData('footable_info'); 321 $table.data('breakpoint', ''); 322 323 //trigger the FooTable resize 324 $table.trigger(trg.resize); 325 326 //remove the loading class 327 $table.removeClass(cls.loading); 328 329 //add the FooTable and loaded class 330 $table.addClass(cls.loaded).addClass(cls.main); 331 332 //raise the initialized event 333 ft.raise(evt.initialized); 334 }) 335 .unbind(trg.redraw) 336 //bind to FooTable redraw trigger 337 .bind(trg.redraw, function () { 338 ft.redraw(); 339 }) 340 .unbind(trg.resize) 341 //bind to FooTable resize trigger 342 .bind(trg.resize, function () { 343 ft.resize(); 344 }) 345 .unbind(trg.expandFirstRow) 346 //bind to FooTable expandFirstRow trigger 347 .bind(trg.expandFirstRow, function () { 348 $table.find(opt.toggleSelector).first().not('.' + cls.detailShow).trigger(trg.toggleRow); 349 }) 350 .unbind(trg.expandAll) 351 //bind to FooTable expandFirstRow trigger 352 .bind(trg.expandAll, function () { 353 $table.find(opt.toggleSelector).not('.' + cls.detailShow).trigger(trg.toggleRow); 354 }) 355 .unbind(trg.collapseAll) 356 //bind to FooTable expandFirstRow trigger 357 .bind(trg.collapseAll, function () { 358 $table.find('.' + cls.detailShow).trigger(trg.toggleRow); 359 }); 360 361 //trigger a FooTable initialize 362 $table.trigger(trg.initialize); 363 364 //bind to window resize 365 $window 366 .bind('resize.footable', function () { 367 ft.timers.resize.stop(); 368 ft.timers.resize.start(function () { 369 ft.raise(trg.resize); 370 }, opt.delay); 371 }); 372 }; 373 374 ft.addRowToggle = function () { 375 if (!opt.addRowToggle) return; 376 377 var $table = $(ft.table), 378 hasToggleColumn = false;
379 380 //first remove all toggle spans 381 $table.find('span.' + cls.toggle).remove(); 382 383 for (var c in ft.columns) { 384 var col = ft.columns[c]; 385 if (col.toggle) { 386 hasToggleColumn = true; 387 var selector = '> tbody > tr:not(.' + cls.detail + ',.' + cls.disabled + ') > td:nth-child(' + (parseInt(col.index, 10) + 1) + ')'; 388 $table.find(selector).not('.' + cls.detailCell).prepend($(opt.toggleHTMLElement).addClass(cls.toggle)); 389 return; 390 } 391 } 392 //check if we have an toggle column. If not then add it to the first column just to be safe 393 if (!hasToggleColumn) { 394 $table 395 .find('> tbody > tr:not(.' + cls.detail + ',.' + cls.disabled + ') > td:first-child') 396 .not('.' + cls.detailCell) 397 .prepend($(opt.toggleHTMLElement).addClass(cls.toggle)); 398 } 399 }; 400 401 ft.setColumnClasses = function () { 402 $table = $(ft.table); 403 for (var c in ft.columns) { 404 var col = ft.columns[c]; 405 if (col.className !== null) { 406 var selector = '', first = true; 407 $.each(col.matches, function (m, match) { //support for colspans 408 if (!first) selector += ', '; 409 selector += '> tbody > tr:not(.' + cls.detail + ') > td:nth-child(' + (parseInt(match, 10) + 1) + ')'; 410 first = false; 411 }); 412 //add the className to the cells specified by data-class="blah" 413 $table.find(selector).not('.' + cls.detailCell).addClass(col.className); 414 } 415 } 416 }; 417 418 //moved this out into it's own function so that it can be called from other add-ons 419 ft.bindToggleSelectors = function () { 420 var $table = $(ft.table); 421 422 if (!ft.hasAnyBreakpointColumn()) return; 423 424 $table.find(opt.toggleSelector).unbind(trg.toggleRow).bind(trg.toggleRow, function (e) { 425 var $row = $(this).is('tr') ? $(this) : $(this).parents('tr:first'); 426 ft.toggleDetail($row); 427 }); 428 429 $table.find(opt.toggleSelector).unbind('click.footable').bind('click.footable', function (e) { 430 if ($table.is('.breakpoint') && $(e.target).is('td,.'+ cls.toggle)) { 431 $(this).trigger(trg.toggleRow); 432 } 433 }); 434 }; 435 436 ft.parse = function (cell, column) { 437 var parser = opt.parsers[column.type] || opt.parsers.alpha; 438 return parser(cell); 439 }; 440 441 ft.getColumnData = function (th) { 442 var $th = $(th), hide = $th.data('hide'), index = $th.index(); 443 hide = hide || ''; 444 hide = jQuery.map(hide.split(','), function (a) { 445 return jQuery.trim(a); 446 }); 447 var data = { 448 'index': index, 449 'hide': { }, 450 'type': $th.data('type') || 'alpha', 451 'name': $th.data('name') || $.trim($th.text()), 452 'ignore': $th.data('ignore') || false, 453 'toggle': $th.data('toggle') || false, 454 'className': $th.data('class') || null, 455 'matches': [], 456 'names': { }, 457 'group': $th.data('group') || null, 458 'groupName': null 459 }; 460 461 if (data.group !== null) { 462 var $group = $(ft.table).find('> thead > tr.footable-group-row > th[data-group="' + data.group + '"], > thead > tr.footable-group-row > td[data-group="' + data.group + '"]').first(); 463 data.groupName = ft.parse($group, { 'type': 'alpha' }); 464 } 465 466 var pcolspan = parseInt($th.prev().attr('colspan') || 0, 10); 467 indexOffset += pcolspan > 1 ? pcolspan - 1 : 0; 468 var colspan = parseInt($th.attr('colspan') || 0, 10), curindex = data.index + indexOffset; 469 if (colspan > 1) { 470 var names = $th.data('names'); 471 names = names || ''; 472 names = names.split(','); 473 for (var i = 0; i < colspan; i++) { 474 data.matches.push(i + curindex); 475 if (i < names.length) data.names[i + curindex] = names[i]; 476 } 477 } else { 478 data.matches.push(curindex); 479 } 480 481 data.hide['default'] = ($th.data('hide') === "all") || ($.inArray('default', hide) >= 0); 482 483 var hasBreakpoint = false;
484 for (var name in opt.breakpoints) { 485 data.hide[name] = ($th.data('hide') === "all") || ($.inArray(name, hide) >= 0); 486 hasBreakpoint = hasBreakpoint || data.hide[name]; 487 } 488 data.hasBreakpoint = hasBreakpoint; 489 var e = ft.raise(evt.columnData, { 'column': { 'data': data, 'th': th } }); 490 return e.column.data; 491 }; 492 493 ft.getViewportWidth = function () { 494 return window.innerWidth || (document.body ? document.body.offsetWidth : 0); 495 }; 496 497 ft.calculateWidth = function ($table, info) { 498 if (jQuery.isFunction(opt.calculateWidthOverride)) { 499 return opt.calculateWidthOverride($table, info); 500 } 501 if (info.viewportWidth < info.width) info.width = info.viewportWidth; 502 if (info.parentWidth < info.width) info.width = info.parentWidth; 503 return info; 504 }; 505 506 ft.hasBreakpointColumn = function (breakpoint) { 507 for (var c in ft.columns) { 508 if (ft.columns[c].hide[breakpoint]) { 509 if (ft.columns[c].ignore) { 510 continue; 511 } 512 return true; 513 } 514 } 515 return false; 516 }; 517 518 ft.hasAnyBreakpointColumn = function () { 519 for (var c in ft.columns) { 520 if (ft.columns[c].hasBreakpoint) { 521 return true; 522 } 523 } 524 return false; 525 }; 526 527 ft.resize = function () { 528 var $table = $(ft.table); 529 530 if (!$table.is(':visible')) { 531 return; 532 } //we only care about FooTables that are visible 533 534 if (!ft.hasAnyBreakpointColumn()) { 535 return; 536 } //we only care about FooTables that have breakpoints 537 538 var info = { 539 'width': $table.width(), //the table width 540 'viewportWidth': ft.getViewportWidth(), //the width of the viewport 541 'parentWidth': $table.parent().width() //the width of the parent 542 }; 543 544 info = ft.calculateWidth($table, info); 545 546 var pinfo = $table.data('footable_info'); 547 $table.data('footable_info', info); 548 ft.raise(evt.resizing, { 'old': pinfo, 'info': info }); 549 550 // This (if) statement is here purely to make sure events aren't raised twice as mobile safari seems to do 551 if (!pinfo || (pinfo && pinfo.width && pinfo.width !== info.width)) { 552 553 var current = null, breakpoint; 554 for (var i = 0; i < ft.breakpoints.length; i++) { 555 breakpoint = ft.breakpoints[i]; 556 if (breakpoint && breakpoint.width && info.width <= breakpoint.width) { 557 current = breakpoint; 558 break; 559 } 560 } 561 562 var breakpointName = (current === null ? 'default' : current['name']), 563 hasBreakpointFired = ft.hasBreakpointColumn(breakpointName), 564 previousBreakpoint = $table.data('breakpoint'); 565 566 $table 567 .data('breakpoint', breakpointName) 568 .removeClass('default breakpoint').removeClass(ft.breakpointNames) 569 .addClass(breakpointName + (hasBreakpointFired ? ' breakpoint' : '')); 570 571 //only do something if the breakpoint has changed 572 if (breakpointName !== previousBreakpoint) { 573 //trigger a redraw 574 $table.trigger(trg.redraw); 575 //raise a breakpoint event 576 ft.raise(evt.breakpoint, { 'breakpoint': breakpointName, 'info': info }); 577 } 578 } 579 580 ft.raise(evt.resized, { 'old': pinfo, 'info': info }); 581 }; 582 583 ft.redraw = function () { 584 //add the toggler to each row 585 ft.addRowToggle(); 586 587 //bind the toggle selector click events 588 ft.bindToggleSelectors(); 589 590 //set any cell classes defined for the columns 591 ft.setColumnClasses(); 592 593 var $table = $(ft.table), 594 breakpointName = $table.data('breakpoint'), 595 hasBreakpointFired = ft.hasBreakpointColumn(breakpointName); 596 597 $table 598 .find('> tbody > tr:not(.' + cls.detail + ')').data('detail_created', false).end() 599 .find('> thead > tr:last-child > th') 600 .each(function () { 601 var data = ft.columns[$(this).index()], selector = '', first = true; 602 $.each(data.matches, function (m, match) { 603 if (!first) { 604 selector += ', '; 605 } 606 var count = match + 1; 607 selector += '> tbody > tr:not(.' + cls.detail + ') > td:nth-child(' + count + ')'; 608 selector += ', > tfoot > tr:not(.' + cls.detail + ') > td:nth-child(' + count + ')'; 609 selector += ', > colgroup > col:nth-child(' + count + ')'; 610 first = false;
611 }); 612 613 selector += ', > thead > tr[data-group-row="true"] > th[data-group="' + data.group + '"]'; 614 var $column = $table.find(selector).add(this); 615 if (breakpointName !== '') { 616 if (data.hide[breakpointName] === false) $column.addClass('footable-visible').show(); 617 else $column.removeClass('footable-visible').hide(); 618 } 619 620 if ($table.find('> thead > tr.footable-group-row').length === 1) { 621 var $groupcols = $table.find('> thead > tr:last-child > th[data-group="' + data.group + '"]:visible, > thead > tr:last-child > th[data-group="' + data.group + '"]:visible'), 622 $group = $table.find('> thead > tr.footable-group-row > th[data-group="' + data.group + '"], > thead > tr.footable-group-row > td[data-group="' + data.group + '"]'), 623 groupspan = 0; 624 625 $.each($groupcols, function () { 626 groupspan += parseInt($(this).attr('colspan') || 1, 10); 627 }); 628 629 if (groupspan > 0) $group.attr('colspan', groupspan).show(); 630 else $group.hide(); 631 } 632 }) 633 .end() 634 .find('> tbody > tr.' + cls.detailShow).each(function () { 635 ft.createOrUpdateDetailRow(this); 636 }); 637 638 $table.find('> tbody > tr.' + cls.detailShow + ':visible').each(function () { 639 var $next = $(this).next(); 640 if ($next.hasClass(cls.detail)) { 641 if (!hasBreakpointFired) $next.hide(); 642 else $next.show(); 643 } 644 }); 645 646 // adding .footable-first-column and .footable-last-column to the first and last th and td of each row in order to allow 647 // for styling if the first or last column is hidden (which won't work using :first-child or :last-child) 648 $table.find('> thead > tr > th.footable-last-column, > tbody > tr > td.footable-last-column').removeClass('footable-last-column'); 649 $table.find('> thead > tr > th.footable-first-column, > tbody > tr > td.footable-first-column').removeClass('footable-first-column'); 650 $table.find('> thead > tr, > tbody > tr') 651 .find('> th.footable-visible:last, > td.footable-visible:last') 652 .addClass('footable-last-column') 653 .end() 654 .find('> th.footable-visible:first, > td.footable-visible:first') 655 .addClass('footable-first-column'); 656 657 ft.raise(evt.redrawn); 658 }; 659 660 ft.toggleDetail = function (row) { 661 var $row = (row.jquery) ? row : $(row), 662 $next = $row.next(); 663 664 //check if the row is already expanded 665 if ($row.hasClass(cls.detailShow)) { 666 $row.removeClass(cls.detailShow); 667 668 //only hide the next row if it's a detail row 669 if ($next.hasClass(cls.detail)) $next.hide(); 670 671 ft.raise(evt.rowCollapsed, { 'row': $row[0] }); 672 673 } else { 674 ft.createOrUpdateDetailRow($row[0]); 675 $row.addClass(cls.detailShow) 676 .next().show(); 677 678 ft.raise(evt.rowExpanded, { 'row': $row[0] }); 679 } 680 }; 681 682 ft.removeRow = function (row) { 683 var $row = (row.jquery) ? row : $(row); 684 if ($row.hasClass(cls.detail)) { 685 $row = $row.prev(); 686 } 687 var $next = $row.next(); 688 if ($row.data('detail_created') === true) { 689 //remove the detail row 690 $next.remove(); 691 } 692 $row.remove(); 693 694 //raise event 695 ft.raise(evt.rowRemoved); 696 }; 697 698 ft.appendRow = function (row) { 699 var $row = (row.jquery) ? row : $(row); 700 $(ft.table).find('tbody').append($row); 701 702 //redraw the table 703 ft.redraw(); 704 }; 705 706 ft.getColumnFromTdIndex = function (index) { 707 /// <summary>Returns the correct column data for the supplied index taking into account colspans.
707</summary> 708 /// <param name="index">The index to retrieve the column data for.</param> 709 /// <returns type="json">A JSON object containing the column data for the supplied index.</returns> 710 var result = null; 711 for (var column in ft.columns) { 712 if ($.inArray(index, ft.columns[column].matches) >= 0) { 713 result = ft.columns[column]; 714 break; 715 } 716 } 717 return result; 718 }; 719 720 ft.createOrUpdateDetailRow = function (actualRow) { 721 var $row = $(actualRow), $next = $row.next(), $detail, values = []; 722 if ($row.data('detail_created') === true) return true; 723 724 if ($row.is(':hidden')) return false; //if the row is hidden for some reason (perhaps filtered) then get out of here 725 ft.raise(evt.rowDetailUpdating, { 'row': $row, 'detail': $next }); 726 $row.find('> td:hidden').each(function () { 727 var index = $(this).index(), column = ft.getColumnFromTdIndex(index), name = column.name; 728 if (column.ignore === true) return true; 729 730 if (index in column.names) name = column.names[index]; 731 values.push({ 'name': name, 'value': ft.parse(this, column), 'display': $.trim($(this).html()), 'group': column.group, 'groupName': column.groupName }); 732 return true; 733 }); 734 if (values.length === 0) return false; //return if we don't have any data to show 735 var colspan = $row.find('> td:visible').length; 736 var exists = $next.hasClass(cls.detail); 737 if (!exists) { // Create 738 $next = $('<tr class="' + cls.detail + '"><td class="' + cls.detailCell + '"><div class="' + cls.detailInner + '"></div></td></tr>'); 739 $row.after($next); 740 } 741 $next.find('> td:first').attr('colspan', colspan); 742 $detail = $next.find('.' + cls.detailInner).empty(); 743 opt.createDetail($detail, values, opt.createGroupedDetail, opt.detailSeparator, cls); 744 $row.data('detail_created', true); 745 ft.raise(evt.rowDetailUpdated, { 'row': $row, 'detail': $next }); 746 return !exists; 747 }; 748 749 ft.raise = function (eventName, args) { 750 751 if (ft.options.debug === true && $.isFunction(ft.options.log)) ft.options.log(eventName, 'event'); 752 753 args = args || { }; 754 var def = { 'ft': ft }; 755 $.extend(true, def, args); 756 var e = $.Event(eventName, def); 757 if (!e.ft) { 758 $.extend(true, e, def); 759 } //pre jQuery 1.6 which did not allow data to be passed to event object constructor 760 $(ft.table).trigger(e); 761 return e; 762 }; 763 764 //reset the state of FooTable 765 ft.reset = function() { 766 var $table = $(ft.table); 767 $table.removeData('footable_info') 768 .data('breakpoint', '') 769 .removeClass(cls.loading) 770 .removeClass(cls.loaded); 771 772 $table.find(opt.toggleSelector).unbind(trg.toggleRow).unbind('click.footable'); 773 774 $table.find('> tbody > tr').removeClass(cls.detailShow); 775 776 $table.find('> tbody > tr.' + cls.detail).remove(); 777 778 ft.raise(evt.reset); 779 }; 780 781 ft.init(); 782 return ft; 783 } 784})(jQuery, window);
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.