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