text: /**
   * Save the state of a table in a cookie such that the page can be reloaded
   *  @param {object} oSettings dataTables settings object
   *  @memberof DataTable#oApi
   */
  function _fnSaveState(oSettings) {
      if (!oSettings.oFeatures.bStateSave || oSettings.bDestroying) {
          return;
      }
  
      /* Store the interesting variables */
      var i, iLen, bInfinite = oSettings.oScroll.bInfinite;
      var oState = {
          "iCreate": new Date().getTime(),
          "iStart": (bInfinite ? 0 : oSettings._iDisplayStart),
          "iEnd": (bInfinite ? oSettings._iDisplayLength : oSettings._iDisplayEnd),
          "iLength": oSettings._iDisplayLength,
          "aaSorting": $.extend(true, [], oSettings.aaSorting),
          "oSearch": $.extend(true, {}, oSettings.oPreviousSearch),
          "aoSearchCols": $.extend(true, [], oSettings.aoPreSearchCols),
          "abVisCols": []
      };
  
      for (i = 0, iLen = oSettings.aoColumns.length; i < iLen; i++) {
          oState.abVisCols.push(oSettings.aoColumns[i].bVisible);
      }
  
      _fnCallbackFire(oSettings, "aoStateSaveParams", 'stateSaveParams', [oSettings, oState]);
  
      oSettings.fnStateSave.call(oSettings.oInstance, oSettings, oState);
  }
  
  
  /**
   * Attempt to load a saved table state from a cookie
   *  @param {object} oSettings dataTables settings object
   *  @param {object} oInit DataTables init object so we can override settings
   *  @memberof DataTable#oApi
   */
  function _fnLoadState(oSettings, oInit) {
      if (!oSettings.oFeatures.bStateSave) {
          return;
      }
  
      var oData = oSettings.fnStateLoad.call(oSettings.oInstance, oSettings);
      if (!oData) {
          return;
      }
  
      /* Allow custom and plug-in manipulation functions to alter the saved data set and
       * cancelling of loading by returning false
       */
      var abStateLoad = _fnCallbackFire(oSettings, 'aoStateLoadParams', 'stateLoadParams', [oSettings, oData]);
      if ($.inArray(false, abStateLoad) !== -1) {
          return;
      }
  
      /* Store the saved state so it might be accessed at any time */
      oSettings.oLoadedState = $.extend(true, {}, oData);
  
      /* Restore key features */
      oSettings._iDisplayStart = oData.iStart;
      oSettings.iInitDisplayStart = oData.iStart;
      oSettings._iDisplayEnd = oData.iEnd;
      oSettings._iDisplayLength = oData.iLength;
      oSettings.aaSorting = oData.aaSorting.slice();
      oSettings.saved_aaSorting = oData.aaSorting.slice();
  
      /* Search filtering  */
      $.extend(oSettings.oPreviousSearch, oData.oSearch);
      $.extend(true, oSettings.aoPreSearchCols, oData.aoSearchCols);
  
      /* Column visibility state
       * Pass back visibiliy settings to the init handler, but to do not here override
       * the init object that the user might have passed in
       */
      oInit.saved_aoColumns = [];
      for (var i = 0; i < oData.abVisCols.length; i++) {
          oInit.saved_aoColumns[i] = {};
          oInit.saved_aoColumns[i].bVisible = oData.abVisCols[i];
      }
  
      _fnCallbackFire(oSettings, 'aoStateLoaded', 'stateLoaded', [oSettings, oData]);
  }
  
  
  /**
   * Create a new cookie with a value to store the state of a table
   *  @param {string} sName name of the cookie to create
   *  @param {string} sValue the value the cookie should take
   *  @param {int} iSecs duration of the cookie
   *  @param {string} sBaseName sName is made up of the base + file name - this is the base
   *  @param {function} fnCallback User definable function to modify the cookie
   *  @memberof DataTable#oApi
   */
  function _fnCreateCookie(sName, sValue, iSecs, sBaseName, fnCallback) {
      var date = new Date();
      date.setTime(date.getTime() + (iSecs * 1000));
  
      /*
       * Shocking but true - it would appear IE has major issues with having the path not having
       * a trailing slash on it. We need the cookie to be available based on the path, so we
       * have to append the file name to the cookie name. Appalling. Thanks to vex for adding the
       * patch to use at least some of the path
       */
      var aParts = window.location.pathname.split('/');
      var sNameFile = sName + '_' + aParts.pop().replace(/[\/:]/g, "").toLowerCase();
      var sFullCookie, oData;
  
      if (fnCallback !== null) {
          oData = (typeof $.parseJSON === 'function') ?
              $.parseJSON(sValue) : eval('(' + sValue + ')');
          sFullCookie = fnCallback(sNameFile, oData, date.toGMTString(),
              aParts.join('/') + "/");
      }
      else {
          sFullCookie = sNameFile + "=" + encodeURIComponent(sValue) +
              "; expires=" + date.toGMTString() + "; path=" + aParts.join('/') + "/";
      }
  
      /* Are we going to go over the cookie limit of 4KiB? If so, try to delete a cookies
       * belonging to DataTables. This is FAR from bullet proof
       */
      var sOldName = "", iOldTime = 9999999999999;
      var iLength = _fnReadCookie(sNameFile) !== null ? document.cookie.length :
      sFullCookie.length + document.cookie.length;
  
      if (iLength + 10 > 4096) /* Magic 10 for padding */
      {
          var aCookies = document.cookie.split(';');
          for (var i = 0, iLen = aCookies.length; i < iLen; i++) {
              if (aCookies[i].indexOf(sBaseName) != -1) {
                  /* It's a DataTables cookie, so eval it and check the time stamp */
                  var aSplitCookie = aCookies[i].split('=');
                  try {
                      oData = eval('(' + decodeURIComponent(aSplitCookie[1]) + ')');
                  }
                  catch (e) {
                      continue;
                  }
  
                  if (oData.iCreate && oData.iCreate < iOldTime) {
                      sOldName = aSplitCookie[0];
                      iOldTime = oData.iCreate;
                  }
              }
          }
  
          if (sOldName !== "") {
              document.cookie = sOldName + "=; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=" +
                  aParts.join('/') + "/";
          }
      }
  
      document.cookie = sFullCookie;
  }
  
  
  /**
   * Read an old cookie to get a cookie with an old table state
   *  @param {string} sName name of the cookie to read
   *  @returns {string} contents of the cookie - or null if no cookie with that name found
   *  @memberof DataTable#oApi
   */
  function _fnReadCookie(sName) {
      var
          aParts = window.location.pathname.split('/'),
          sNameEQ = sName + '_' + aParts[aParts.length - 1].replace(/[\/:]/g, "").toLowerCase() + '=',
          sCookieContents = document.cookie.split(';');
  
      for (var i = 0; i < sCookieContents.length; i++) {
          var c = sCookieContents[i];
  
          while (c.charAt(0) == ' ') {
              c = c.substring(1, c.length);
          }
  
          if (c.indexOf(sNameEQ) === 0) {
              return decodeURIComponent(c.substring(sNameEQ.length, c.length));
          }
      }
      return null;
  }
  
  