text: /*! FixedHeader 3.1.2
   * ©2009-2016 SpryMedia Ltd - datatables.net/license
   */
  
  /**
   * @summary     FixedHeader
   * @description Fix a table's header or footer, so it is always visible while
   *              scrolling
   * @version     3.1.2
   * @file        dataTables.fixedHeader.js
   * @author      SpryMedia Ltd (www.sprymedia.co.uk)
   * @contact     www.sprymedia.co.uk/contact
   * @copyright   Copyright 2009-2016 SpryMedia Ltd.
   *
   * This source file is free software, available under the following license:
   *   MIT license - http://datatables.net/license/mit
   *
   * This source file is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
   * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
   *
   * For details please refer to: http://www.datatables.net
   */
  
  (function (factory) {
      if (typeof define === 'function' && define.amd) {
          // AMD
          define(['jquery', 'datatables.net'], function ($) {
              return factory($, window, document);
          });
      }
      else if (typeof exports === 'object') {
          // CommonJS
          module.exports = function (root, $) {
              if (!root) {
                  root = window;
              }
  
              if (!$ || !$.fn.dataTable) {
                  $ = require('datatables.net')(root, $).$;
              }
  
              return factory($, root, root.document);
          };
      }
      else {
          // Browser
          factory(jQuery, window, document);
      }
  }(function ($, window, document, undefined) {
      'use strict';
      var DataTable = $.fn.dataTable;
  
  
      var _instCounter = 0;
  
      var FixedHeader = function (dt, config) {
          // Sanity check - you just know it will happen
          if (!(this instanceof FixedHeader)) {
              throw "FixedHeader must be initialised with the 'new' keyword.";
          }
  
          // Allow a boolean true for defaults
          if (config === true) {
              config = {};
          }
  
          dt = new DataTable.Api(dt);
  
          this.c = $.extend(true, {}, FixedHeader.defaults, config);
  
          this.s = {
              dt: dt,
              position: {
                  theadTop: 0,
                  tbodyTop: 0,
                  tfootTop: 0,
                  tfootBottom: 0,
                  width: 0,
                  left: 0,
                  tfootHeight: 0,
                  theadHeight: 0,
                  windowHeight: $(window).height(),
                  visible: true
              },
              headerMode: null,
              footerMode: null,
              autoWidth: dt.settings()[0].oFeatures.bAutoWidth,
              namespace: '.dtfc' + (_instCounter++),
              scrollLeft: {
                  header: -1,
                  footer: -1
              },
              enable: true
          };
  
          this.dom = {
              floatingHeader: null,
              thead: $(dt.table().header()),
              tbody: $(dt.table().body()),
              tfoot: $(dt.table().footer()),
              header: {
                  host: null,
                  floating: null,
                  placeholder: null
              },
              footer: {
                  host: null,
                  floating: null,
                  placeholder: null
              }
          };
  
          this.dom.header.host = this.dom.thead.parent();
          this.dom.footer.host = this.dom.tfoot.parent();
  
          var dtSettings = dt.settings()[0];
          if (dtSettings._fixedHeader) {
              throw "FixedHeader already initialised on table " + dtSettings.nTable.id;
          }
  
          dtSettings._fixedHeader = this;
  
          this._constructor();
      };
  
  
      /*
       * Variable: FixedHeader
       * Purpose:  Prototype for FixedHeader
       * Scope:    global
       */
      $.extend(FixedHeader.prototype, {
          /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
           * API methods
           */
  
          /**
           * Enable / disable the fixed elements
           *
           * @param  {boolean} enable `true` to enable, `false` to disable
           */
          enable: function (enable) {
              this.s.enable = enable;
  
              if (this.c.header) {
                  this._modeChange('in-place', 'header', true);
              }
  
              if (this.c.footer && this.dom.tfoot.length) {
                  this._modeChange('in-place', 'footer', true);
              }
  
              this.update();
          },
  
          /**
           * Set header offset
           *
           * @param  {int} new value for headerOffset
           */
          headerOffset: function (offset) {
              if (offset !== undefined) {
                  this.c.headerOffset = offset;
                  this.update();
              }
  
              return this.c.headerOffset;
          },
  
          /**
           * Set footer offset
           *
           * @param  {int} new value for footerOffset
           */
          footerOffset: function (offset) {
              if (offset !== undefined) {
                  this.c.footerOffset = offset;
                  this.update();
              }
  
              return this.c.footerOffset;
          },
  
  
          /**
           * Recalculate the position of the fixed elements and force them into place
           */
          update: function () {
              this._positions();
              this._scroll(true);
          },
  
  
          /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
           * Constructor
           */
  
          /**
           * FixedHeader constructor - adding the required event listeners and
           * simple initialisation
           *
           * @private
           */
          _constructor: function () {
              var that = this;
              var dt = this.s.dt;
  
              $(window)
                  .on('scroll' + this.s.namespace, function () {
                      that._scroll();
                  })
                  .on('resize' + this.s.namespace, function () {
                      that.s.position.windowHeight = $(window).height();
                      that.update();
                  });
  
              var autoHeader = $('.fh-fixedHeader');
              if (!this.c.headerOffset && autoHeader.length) {
                  this.c.headerOffset = autoHeader.outerHeight();
              }
  
              var autoFooter = $('.fh-fixedFooter');
              if (!this.c.footerOffset && autoFooter.length) {
                  this.c.footerOffset = autoFooter.outerHeight();
              }
  
              dt.on('column-reorder.dt.dtfc column-visibility.dt.dtfc draw.dt.dtfc column-sizing.dt.dtfc', function () {
                  that.update();
              });
  
              dt.on('destroy.dtfc', function () {
                  dt.off('.dtfc');
                  $(window).off(that.s.namespace);
              });
  
              this._positions();
              this._scroll();
          },
  
  
          /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
           * Private methods
           */
  
          /**
           * Clone a fixed item to act as a place holder for the original element
           * which is moved into a clone of the table element, and moved around the
           * document to give the fixed effect.
           *
           * @param  {string}  item  'header' or 'footer'
           * @param  {boolean} force Force the clone to happen, or allow automatic
           *   decision (reuse existing if available)
           * @private
           */
          _clone: function (item, force) {
              var dt = this.s.dt;
              var itemDom = this.dom[item];
              var itemElement = item === 'header' ?
                  this.dom.thead :
                  this.dom.tfoot;
  
              if (!force && itemDom.floating) {
                  // existing floating element - reuse it
                  itemDom.floating.removeClass('fixedHeader-floating fixedHeader-locked');
              }
              else {
                  if (itemDom.floating) {
                      itemDom.placeholder.remove();
                      this._unsize(item);
                      itemDom.floating.children().detach();
                      itemDom.floating.remove();
                  }
  
                  itemDom.floating = $(dt.table().node().cloneNode(false))
                      .css('table-layout', 'fixed')
                      .removeAttr('id')
                      .append(itemElement)
                      .appendTo('body');
  
                  // Insert a fake thead/tfoot into the DataTable to stop it jumping around
                  itemDom.placeholder = itemElement.clone(false);
                  itemDom.host.prepend(itemDom.placeholder);
  
                  // Clone widths
                  this._matchWidths(itemDom.placeholder, itemDom.floating);
              }
          },
  
          /**
           * Copy widths from the cells in one element to another. This is required
           * for the footer as the footer in the main table takes its sizes from the
           * header columns. That isn't present in the footer so to have it still
           * align correctly, the sizes need to be copied over. It is also required
           * for the header when auto width is not enabled
           *
           * @param  {jQuery} from Copy widths from
           * @param  {jQuery} to   Copy widths to
           * @private
           */
          _matchWidths: function (from, to) {
              var get = function (name) {
                  return $(name, from)
                      .map(function () {
                          return $(this).width();
                      }).toArray();
              };
  
              var set = function (name, toWidths) {
                  $(name, to).each(function (i) {
                      $(this).css({
                          width: toWidths[i],
                          minWidth: toWidths[i]
                      });
                  });
              };
  
              var thWidths = get('th');
              var tdWidths = get('td');
  
              set('th', thWidths);
              set('td', tdWidths);
          },
  
          /**
           * Remove assigned widths from the cells in an element. This is required
           * when inserting the footer back into the main table so the size is defined
           * by the header columns and also when auto width is disabled in the
           * DataTable.
           *
           * @param  {string} item The `header` or `footer`
           * @private
           */
          _unsize: function (item) {
              var el = this.dom[item].floating;
  
              if (el && (item === 'footer' || (item === 'header' && !this.s.autoWidth))) {
                  $('th, td', el).css({
                      width: '',
                      minWidth: ''
                  });
              }
              else if (el && item === 'header') {
                  $('th, td', el).css('min-width', '');
              }
          },
  
          /**
           * Reposition the floating elements to take account of horizontal page
           * scroll
           *
           * @param  {string} item       The `header` or `footer`
           * @param  {int}    scrollLeft Document scrollLeft
           * @private
           */
          _horizontal: function (item, scrollLeft) {
              var itemDom = this.dom[item];
              var position = this.s.position;
              var lastScrollLeft = this.s.scrollLeft;
  
              if (itemDom.floating && lastScrollLeft[item] !== scrollLeft) {
                  itemDom.floating.css('left', position.left - scrollLeft);
  
                  lastScrollLeft[item] = scrollLeft;
              }
          },
  
          /**
           * Change from one display mode to another. Each fixed item can be in one
           * of:
           *
           * * `in-place` - In the main DataTable
           * * `in` - Floating over the DataTable
           * * `below` - (Header only) Fixed to the bottom of the table body
           * * `above` - (Footer only) Fixed to the top of the table body
           *
           * @param  {string}  mode        Mode that the item should be shown in
           * @param  {string}  item        'header' or 'footer'
           * @param  {boolean} forceChange Force a redraw of the mode, even if already
           *     in that mode.
           * @private
           */
          _modeChange: function (mode, item, forceChange) {
              var dt = this.s.dt;
              var itemDom = this.dom[item];
              var position = this.s.position;
  
              // Record focus. Browser's will cause input elements to loose focus if
              // they are inserted else where in the doc
              var tablePart = this.dom[item === 'footer' ? 'tfoot' : 'thead'];
              var focus = $.contains(tablePart[0], document.activeElement) ?
                  document.activeElement :
                  null;
  
              if (mode === 'in-place') {
                  // Insert the header back into the table's real header
                  if (itemDom.placeholder) {
                      itemDom.placeholder.remove();
                      itemDom.placeholder = null;
                  }
  
                  this._unsize(item);
  
                  if (item === 'header') {
                      itemDom.host.prepend(this.dom.thead);
                  }
                  else {
                      itemDom.host.append(this.dom.tfoot);
                  }
  
                  if (itemDom.floating) {
                      itemDom.floating.remove();
                      itemDom.floating = null;
                  }
              }
              else if (mode === 'in') {
                  // Remove the header from the read header and insert into a fixed
                  // positioned floating table clone
                  this._clone(item, forceChange);
  
                  itemDom.floating
                      .addClass('fixedHeader-floating')
                      .css(item === 'header' ? 'top' : 'bottom', this.c[item + 'Offset'])
                      .css('left', position.left + 'px')
                      .css('width', position.width + 'px');
  
                  if (item === 'footer') {
                      itemDom.floating.css('top', '');
                  }
              }
              else if (mode === 'below') { // only used for the header
                  // Fix the position of the floating header at base of the table body
                  this._clone(item, forceChange);
  
                  itemDom.floating
                      .addClass('fixedHeader-locked')
                      .css('top', position.tfootTop - position.theadHeight)
                      .css('left', position.left + 'px')
                      .css('width', position.width + 'px');
              }
              else if (mode === 'above') { // only used for the footer
                  // Fix the position of the floating footer at top of the table body
                  this._clone(item, forceChange);
  
                  itemDom.floating
                      .addClass('fixedHeader-locked')
                      .css('top', position.tbodyTop)
                      .css('left', position.left + 'px')
                      .css('width', position.width + 'px');
              }
  
              // Restore focus if it was lost
              if (focus && focus !== document.activeElement) {
                  focus.focus();
              }
  
              this.s.scrollLeft.header = -1;
              this.s.scrollLeft.footer = -1;
              this.s[item + 'Mode'] = mode;
          },
  
          /**
           * Cache the positional information that is required for the mode
           * calculations that FixedHeader performs.
           *
           * @private
           */
          _positions: function () {
              var dt = this.s.dt;
              var table = dt.table();
              var position = this.s.position;
              var dom = this.dom;
              var tableNode = $(table.node());
  
              // Need to use the header and footer that are in the main table,
              // regardless of if they are clones, since they hold the positions we
              // want to measure from
              var thead = tableNode.children('thead');
              var tfoot = tableNode.children('tfoot');
              var tbody = dom.tbody;
  
              position.visible = tableNode.is(':visible');
              position.width = tableNode.outerWidth();
              position.left = tableNode.offset().left;
              position.theadTop = thead.offset().top;
              position.tbodyTop = tbody.offset().top;
              position.theadHeight = position.tbodyTop - position.theadTop;
  
              if (tfoot.length) {
                  position.tfootTop = tfoot.offset().top;
                  position.tfootBottom = position.tfootTop + tfoot.outerHeight();
                  position.tfootHeight = position.tfootBottom - position.tfootTop;
              }
              else {
                  position.tfootTop = position.tbodyTop + tbody.outerHeight();
                  position.tfootBottom = position.tfootTop;
                  position.tfootHeight = position.tfootTop;
              }
          },
  
  
          /**
           * Mode calculation - determine what mode the fixed items should be placed
           * into.
           *
           * @param  {boolean} forceChange Force a redraw of the mode, even if already
           *     in that mode.
           * @private
           */
          _scroll: function (forceChange) {
              var windowTop = $(document).scrollTop();
              var windowLeft = $(document).scrollLeft();
              var position = this.s.position;
              var headerMode, footerMode;
  
              if (!this.s.enable) {
                  return;
              }
  
              if (this.c.header) {
                  if (!position.visible || windowTop <= position.theadTop - this.c.headerOffset) {
                      headerMode = 'in-place';
                  }
                  else if (windowTop <= position.tfootTop - position.theadHeight - this.c.headerOffset) {
                      headerMode = 'in';
                  }
                  else {
                      headerMode = 'below';
                  }
  
                  if (forceChange || headerMode !== this.s.headerMode) {
                      this._modeChange(headerMode, 'header', forceChange);
                  }
  
                  this._horizontal('header', windowLeft);
              }
  
              if (this.c.footer && this.dom.tfoot.length) {
                  if (!position.visible || windowTop + position.windowHeight >= position.tfootBottom + this.c.footerOffset) {
                      footerMode = 'in-place';
                  }
                  else if (position.windowHeight + windowTop > position.tbodyTop + position.tfootHeight + this.c.footerOffset) {
                      footerMode = 'in';
                  }
                  else {
                      footerMode = 'above';
                  }
  
                  if (forceChange || footerMode !== this.s.footerMode) {
                      this._modeChange(footerMode, 'footer', forceChange);
                  }
  
                  this._horizontal('footer', windowLeft);
              }
          }
      });
  
  
      /**
       * Version
       * @type {String}
       * @static
       */
      FixedHeader.version = "3.1.2";
  
      /**
       * Defaults
       * @type {Object}
       * @static
       */
      FixedHeader.defaults = {
          header: true,
          footer: false,
          headerOffset: 0,
          footerOffset: 0
      };
  
  
      /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
       * DataTables interfaces
       */
  
  // Attach for constructor access
      $.fn.dataTable.FixedHeader = FixedHeader;
      $.fn.DataTable.FixedHeader = FixedHeader;
  
  
  // DataTables creation - check if the FixedHeader option has been defined on the
  // table and if so, initialise
      $(document).on('init.dt.dtfh', function (e, settings, json) {
          if (e.namespace !== 'dt') {
              return;
          }
  
          var init = settings.oInit.fixedHeader;
          var defaults = DataTable.defaults.fixedHeader;
  
          if ((init || defaults) && !settings._fixedHeader) {
              var opts = $.extend({}, defaults, init);
  
              if (init !== false) {
                  new FixedHeader(settings, opts);
              }
          }
      });
  
  // DataTables API methods
      DataTable.Api.register('fixedHeader()', function () {
      });
  
      DataTable.Api.register('fixedHeader.adjust()', function () {
          return this.iterator('table', function (ctx) {
              var fh = ctx._fixedHeader;
  
              if (fh) {
                  fh.update();
              }
          });
      });
  
      DataTable.Api.register('fixedHeader.enable()', function (flag) {
          return this.iterator('table', function (ctx) {
              var fh = ctx._fixedHeader;
  
              if (fh) {
                  fh.enable(flag !== undefined ? flag : true);
              }
          });
      });
  
      DataTable.Api.register('fixedHeader.disable()', function () {
          return this.iterator('table', function (ctx) {
              var fh = ctx._fixedHeader;
  
              if (fh) {
                  fh.enable(false);
              }
          });
      });
  
      $.each(['header', 'footer'], function (i, el) {
          DataTable.Api.register('fixedHeader.' + el + 'Offset()', function (offset) {
              var ctx = this.context;
  
              if (offset === undefined) {
                  return ctx.length && ctx[0]._fixedHeader ?
                      ctx[0]._fixedHeader[el + 'Offset']() :
                      undefined;
              }
  
              return this.iterator('table', function (ctx) {
                  var fh = ctx._fixedHeader;
  
                  if (fh) {
                      fh[el + 'Offset'](offset);
                  }
              });
          });
      });
  
  
      return FixedHeader;
  }));
  