// jquery.tableManager.js
//
// the table manager allows support for sorting, highlighting and reloading a table
// needs to be placed to a <table> Object with <tbody> and <thead>
// exact one row in thead and tbody has to be set, since the plugin needs the info to
// set up the tableManager


/**
 * $Id: jquery.tableManager.js 2501 2009-09-24 11:23:22Z Bjoern.Moenikes $
 **/

(function($){

  $.fn.extend({
    tableManager : function(options)
    {
      if (!$.event._tmCache) $.event._tmCache = [];

      // initialise the date picker controller with the relevant settings...
      options = $.extend(
      {
        id : '',
        renderDirective : '',                  // needed for autorenderer
        emptyRow : ' ',
        updateCallback : undefined, // function is beeing called after update
        selectCallback : undefined, // function is beeing called after change of selection
        highlightCheck : true,
        showLoadOnUpdate : true,
        limit : undefined,

        sortList : undefined,
        sortForce : undefined, //,[3,0],],
        sortMultiSortKey : null,
        headers: {},


        loadStringID : undefined,
        stringVar1 : function(){
          return(' ');
        },
        stringVar2 : function(){
          return(' ');
        },

        setDefaultSelection : function() {},
        JSONaction : undefined,
        onErrorLinkAction : function() {},
        transformCallback : function($response){
          return $response;
        }

      }
      , options
      );

      return this.each(
        function()
        {
          if(!this.tHead || !this.tBodies) return; // check, if we are really working on a table

          var $this = $(this);
          var alreadyExists = true;

          if (!this._tmId) {
            this._tmId = $.event.guid++;
            $.event._tmCache[this._tmId] = new TableManager(this);
            alreadyExists = false;
          }

          var controller = $.event._tmCache[this._tmId];

          controller.init(options);

          // add a tablesorter to table
          // and clear Table
          var conf =
          {
            // renderDirective : options.renderDirective,
            sortList : options.sortList,
            sortForce : options.sortForce,
            sortMultiSortKey : options.sortMultiSortKey,
            headers : options.headers
          };

          $this.tablesorter( conf ); 

        }
        )
    },
    // clear table
    clearTable : function()
    {
      return _w.call(this, 'clearTable');
    },
    // update the table
    // using the default action and the given data
    update : function(data, onSuccess, hidden, sort)
    {
      return _w.call(this, 'update', data, onSuccess, hidden, sort);
    },

    // function that is beeing executed after a field has been clicked
    privClick : function(obj)
    {
      return _w.call(this, 'privClick', obj);
    },
    // callback function to fill the desired table
    fillTable : function(response, object)
    {
      return _w.call(this, 'fillTable', response, object);
    },
    //
    // check, if a row is selected
    //
    tmGetHighlightCheck : function(table)
    {
      var c = _getController(this[0]);
      if (c) {
        return c.getHighlightCheck();
      }
      return null;
    },

    tmSetHighlightCheck : function(hc)
    {
      return _w.call(this, 'setHighlightCheck', hc);
    },
    tmGetShowLoadOnUpdate : function()
    {
      var c = _getController(this[0]);
      if (c) {
        return c.getShowLoadOnUpdate();
      }
      return null;
    },

    tmSetShowLoadOnUpdate : function(hc)
    {
      return _w.call(this, 'setShowLoadOnUpdate', hc);
    },

    hasSelectedRow : function(table)
    {
      return _w.call(this, 'hasSelectedRow', table);
    },

    tmGetSortingKey : function()
    {
      var c = _getController(this[0]);
      if (c) {
        return c.getSortingKey();
      }
      return null;
    },
    tmGetSortingDir : function()
    {
      var c = _getController(this[0]);
      if (c) {
        return c.getSortingDir();
      }
      return null;
    },
    tmSetSorting : function()
    {
      return _w.call(this, 'setSorting');
    },


    populateTable : function(response, $table)
    {
      return _w.call(this, 'populateTable', response, $table);
    },
    tmSetRenderDirective : function(directive)
    {
      return _w.call(this, 'setUpdateCallback', directive);
    },
    tmSetEmptyRow : function(emptyRow)
    {
      return _w.call(this, 'setEmptyRow', emptyRow);
    },
    tmSetUpdateCallback : function(callback)
    {
      return _w.call(this, 'setUpdateCallback', callback);
    },
    tmSetOnErrorLinkAction : function(callback)
    {
      return _w.call(this, 'setOnErrorLinkAction', callback);
    },
    tmGetOnErrorLinkAction : function()
    {
      var c = _getController(this[0]);
      if (c) {
        return c.getOnErrorLinkAction();
      }
      return null;
    },
    tmGetContext : function()
    {
      var c = _getController(this[0]);
      if (c) {
        return c.getContext();
      }
      return null;
    },

    tmSetOnSuccessAction : function(callback)
    {
      return _w.call(this, 'setOnSuccessAction', callback);
    },
    tmGetOnSuccessAction : function()
    {
      var c = _getController(this[0]);
      if (c) {
        return c.getOnSuccessAction();
      }
      return null;
    },
    tmSetTransformCallback : function(callback)
    {
      return _w.call(this, 'setTransformCallback', callback);
    },
    tmScrollIntoView : function($table)
    {
      return _w.call(this, 'scrollIntoView', $table);
    },
    tmSetSelectCallback : function(callback)
    {
      return _w.call(this, 'setSelectCallback', callback);
    },
    tmSetSelection : function($table, $row)
    {
      return _w.call(this, 'setSelection', $table, $row);
    },
    tmSetDefaultSelection : function($table, $row)
    {
      return _w.call(this, 'setDefaultSelection', $table, $row);
    },
    tmSetJSONAction : function(action)
    {
      return _w.call(this, 'setJSONAction', action);
    },
    tmGetRenderDirective : function()
    {
      var c = _getController(this[0]);
      if (c) {
        return c.getRenderDirective();
      }
      return null;
    },
    tmGetTransformCallback : function()
    {
      var c = _getController(this[0]);
      if (c) {
        return c.getTransformCallback();
      }
      return null;
    },

    tmGetEmptyRow : function()
    {
      var c = _getController(this[0]);
      if (c) {
        return c.getEmptyRow();
      }
      return null;
    },
    tmGetClick : function()
    {
      var c = _getController(this[0]);
      if (c) {
        return c.getClick();
      }
      return null;
    },
    tmGetSelectCallback : function()
    {
      var c = _getController(this[0]);
      if (c) {
        return c.getSelectCallback();
      }
      return null;
    },
    tmGetUpdateCallback : function()
    {
      var c = _getController(this[0]);
      if (c) {
        return c.getUpdateCallback();
      }
      return null;
    },

    _tmDestroy : function()
    {
    // TODO - implement this?
    }
  });

  // private internal function to cut down on the amount of code needed where we forward
  // dp* methods on the jQuery object on to the relevant DatePicker controllers...
  var _w = function(f, a1, a2, a3, a4, a5)
  {
    return this.each(
      function()
      {
        var c = _getController(this);
        if (c) {
          c[f](a1, a2, a3, a4, a5);
        }
      }
      );
  };

  function TableManager(ele)
  {
    this.ele = ele;

    // initial values...
    this.id = null;
    this.inline				=	null;
    this.context			=	'#name';
    this.$context = null;
    this.renderDirective = '';                  // needed for autorenderer
    this.emptyRow = ' ';
    this.updateCallback = undefined; // function is beeing called after update
    this.selectCallback = undefined; // function is beeing called after change of selection
    this.JSONaction = undefined;
    this.transformCallback = undefined;
    this.onErrorLinkAction = undefined;
    this.onSuccessAction = undefined;
    this.loadStringID = undefined;
    this.stringVar1 = undefined;
    this.stringVar2 = undefined;
    this.highlightCheck = undefined;
    this.showLoadOnUpdate = undefined;
    this.limit = undefined;
  };
  $.extend(
    TableManager.prototype,
    {
      init : function(s)
      {
        this.id = s.id;
        this.context = this.ele;
        this.$context = $(s.id);
        this.renderDirective = s.renderDirective;
        this.emptyRow = s.emptyRow;
        this.updateCallback = s.updateCallback;
        this.selectCallback = s.selectCallback;
        this.JSONaction = s.JSONaction;
        this.transformCallback = s.transformCallback;
        this.onErrorLinkAction = s.onErrorLinkAction;
        this.onSuccessAction = s.onSuccessAction;
        this.limit = s.limit;

        this.highlightCheck = s.highlightCheck;
        this.showLoadOnUpdate = s.showLoadOnUpdate;
        this.loadStringID = s.loadStringID;
        this.stringVar1 = s.stringVar1;
        this.stringVar2 = s.stringVar2;

        // enable onclick functionality
        this.$context.click( function(obj) {
          // gather some information about our location
          var $obj = $(obj.target);
          var $row = $obj.parents("tr:first");
          if ($row.hasClass("disabled")) // tableManager rows, that have class "disabled" are not longer clickable
            return false;
          var $table = $row.parents("table:first");
          $table.tmSetSelection($table, $row);
        } );
      },

      setShowLoadOnUpdate : function(load)
      {
        this.showLoadOnUpdate = load;
  
      },
      getShowLoadOnUpdate : function()
      {
        return( this.showLoadOnUpdate);
      },

      setHighlightCheck : function(hc)
      {
        var checked = this.$context.find("tr:selectedRow");
        //        C.log("setHighlightCheck "+hc);
        //        C.log()
        this.highlightCheck = hc;
        if (checked.length == 0)
          return;

        if (hc == true)
          this.$context.find("tr:selectedRow").addClass("highlightCheck");
        else
          this.$context.find("tr.highlightCheck").removeClass("highlightCheck");
      },

      getHighlightCheck : function()
      {
        return this.highlightCheck;
      },

      getSortingKey : function()
      {
        var res = undefined;
        $("thead th",this.$context).each(function(index, val){
          if ($(val).hasClass("headerSortUp") || $(val).hasClass("headerSortDown") )
            res = index;
        });
        //      C.log(" get sort key");
        //        C.log(res);
        return res;
      },

      getSortingDir : function()
      {
        var res = undefined;
        this.$context.find("thead th").each(function(index, val){
          if ($(val).hasClass("headerSortUp"))
            res = phpConst.sort.asc;
          else if ($(val.item).hasClass("headerSortDown"))
            res = phpConst.sort.desc;
        });
        //        C.log(" get sort dir");
        //        C.log(res);
        return res;
      },
      setSorting : function(data, colIndex, direction)
      {
        //        C.log(" set sort");
        //        C.log(data);
        return;

        this.$content.find("thead th:nth-child("+colIndex+")").click();
      },
      getContext : function()
      {
        return this.$context;
      },
      setRenderDirective : function(d)
      {
        if (d) {
          this.renderDirective = d;
        }
      },
      setOnErrorLinkAction : function(d)
      {
        if (d) {
          this.onErrorLinkAction = d;
        }
      },
      getOnErrorLinkAction : function(d)
      {
        return(this.onErrorLinkAction);
      },
      setOnSuccessAction : function(d)
      {
        if (d) {
          this.onSuccessAction = d;
        }
      },
      getOnSuccessAction : function(d)
      {
        return(this.onSuccessAction);
      },
      setTransformCallback : function(d)
      {
        if (d) {
          this.transformCallback = d;
        }
      },

      setEmptyRow : function(d)
      {
        if (d) {
          this.emptyRow = d;
        }
      },
      setUpdateCallback : function(a)
      {
        if (a && typeof(a) == 'function') {
          a = [a];
        }
        this.updateCallback = this.updateCallback.concat(a);
      },
      setSelectCallback : function(a)
      {
        if (a && typeof(a) == 'function') {
          a = [a];
        }
        this.selectCallback = this.selectCallback.concat(a);
      },
      getRenderDirective : function(d)
      {
        return(this.renderDirective);
      },
      getTransformCallback : function(d)
      {
        return(this.transformCallback);
      },
      getEmptyRow : function(d)
      {
        return(this.emptyRow);
      },
      getUpdateCallback : function(a)
      {

        return(this.updateCallback);
      },
      getSelectCallback : function(a)
      {
        return(this.selectCallback);
      },

      // clear table
      clearTable : function()
      {
        this.$context.find("tbody tr").remove();             // clear
        this.$context.trigger("update");   // and update
      },

      // update the table
      // using the default action and the given data
      update : function(data, onSuccess, hidden, sort)
      {      
        var table = this.id;
        var sorting = sort;
        var sortWrapperAndOnSuccess = function()
        {
          if (sort != undefined)
            $(table).trigger("sorton",[sorting]);
          if (onSuccess && typeof(onSuccess)=='function')
            onSuccess();
        }

        var $this = $(this);

        if (this.JSONaction == undefined)
        {
          C.log("error in jquery.tableManager.js -> this.update(data), JSONaction undefined, aborting");
          return;
        }


        var myself = this;
        var sr = getShowResponse();

        if (this.loadStringID && this.loadStringID.length > 0 && this.showLoadOnUpdate == true)
          $("#loadMessageBox").lmcShow(this.loadStringID,this.stringVar1,this.stringVar2, function(){
            // comment out next line and remove else for asynchronous json call
            doJSON (myself.JSONaction, data, myself.fillTable, sr, sr, myself.id, sortWrapperAndOnSuccess);
          });
        else
          doJSON (this.JSONaction, data, this.fillTable, sr, sr, this.id, sortWrapperAndOnSuccess);
      },


      getClick : function()
      {
        return this.privClick;
      },

      scrollIntoView : function()
      {
        var $table = this.$context;
        if ($table)
        {
          var $rows = $table.find("tbody tr");
          if ($rows)
          {
            var visibleRows = $table.attr('visiblerows');
            // if no visiblerows attribute found
            if (!visibleRows)
            {
              visibleRows = 0;
            }
            var rowsCnt = $rows.length;
            // if amount of rows in table greater as number of visible rows
            if (visibleRows < rowsCnt)
            {
              var $scrollableObj;
              var $scrollToObj;
              var animationDuration = 1;
              if ($.browser.msie)
              {
                $scrollToObj = $('tbody tr.highlight', $table).prev();
                if ($scrollToObj && $scrollToObj.is('tr'))
                {
                  $scrollableObj = $table.parents("div:first");
                  if ($scrollableObj)
                  {
                    $scrollableObj.css('overflow-y', 'auto');
                    $scrollableObj.scrollTo($scrollToObj, animationDuration);
                  }
                }
              }
              else
              {
                $scrollableObj = $("tbody", $table);
                $scrollableObj.css('height','').css('overflow-y','');
                $scrollToObj = $('tr.highlight', $table);
                if ($scrollableObj && $scrollToObj.is('tr'))
                {
                  $scrollableObj.scrollTo($scrollToObj, animationDuration);
                }
              }
            }
            else
            {
              //var correct = 26;


              $('tbody', $table).css('overflow-y', 'hidden').css(
                'height', (27 * rowsCnt) + 'px');
            }
          }
        }
      },

      setDefaultSelection : function($table)
      {
        //        C.log("setDefaultSelection");
        var id = this.$context.attr("defaultselection");
        var $row = undefined;
        if (id != "")
          $row = $table.find("tbody tr[selectionid='"+id+"']")
        if ( id != "" && $row != undefined && $row.length > 0)
        {
          //          C.log(id);
          //          C.log($row.length);
          //          C.log($row);
          this.setSelection($table,$row);
        }
        else
        {
          C.log("tableManager.js: setDefaultSelection(), using defaults!");
          this.setSelection($table,$table.find("tbody tr:first"));
        }
      },

      setSelection : function($table, $row)
      {
        if ($row.length == 0)
        {
          C.log("selection not found")
          return;
        }

        // deselect all columns in current table:
        // get Parent object. For each child remove highlight class
        var $selected = $("tbody tr:selectedRow", $table);
        
        $selected.removeAttr("highlight").removeClass("highlight").removeClass("highlightCheck");
        if ($selected.hasClass("invalidSelection"))
          $selected.removeClass("invalidSelection");

        // add highlight and selection to current object
        $row.attr("highlight","highlight");
        $row.addClass("highlight");

        if ($table.tmGetHighlightCheck() == true)
          $row.addClass("highlightCheck")
        $table.trigger("update");

        // check if callback method has been set
        // if so, call it
        // if there is a onHighlight callback function, call it submitting the new highlighted item
        if ($.isFunction($table.tmGetSelectCallback()))
          ($table.tmGetSelectCallback())($table);
      },


      populateTable : function(response, $table)
      {
        // clear table and prepare for new data
        $("tbody", $table).html($table.tmGetEmptyRow());
        response["data"].splice(this.limit-1, response["data"].length - this.limit);

        // render data to html variable using the given table and the "dummy" row
        //$('#contentRight').text($.toJSON(response));
        var html =  pure.render($("tbody", $table), response, $table.tmGetRenderDirective());

        $("tbody", $table).replaceWith(html);

        // select default item
        $table.tmSetDefaultSelection( $table);

        // update table
        $table.trigger("update");

        if ($table.tmGetUpdateCallback() != undefined)
          $table.tmGetUpdateCallback()($table);

      },


      // callback function to fill the desired table
      fillTable : function(response, id)
      {
        var $table = $(id);
        var wrapLoading = function()
        {
          // hide loading dialog
          if ($table.tmGetShowLoadOnUpdate() == true)
            $("#loadMessageBox").lmcHide();
        }

        if ($table.tmGetTransformCallback() && typeof($table.tmGetTransformCallback()) == 'function')
          $table.tmGetTransformCallback()(response);

        $table.populateTable(response, $table);

        if ($table.tmGetOnSuccessAction() && typeof($table.tmGetOnSuccessAction()) == 'function')
          $table.tmGetOnSuccessAction()();

        wrapLoading();
      },


      // check, if a row is selected
      hasSelectedRow : function(table)
      {
        this.$context.find(" tbody .highlight")
      }
    }
    );

  function _getController(ele)
  {
    if (ele._tmId) return $.event._tmCache[ele._tmId];
    return false;
  };

  // make it so that no error is thrown if bgIframe plugin isn't included (allows you to use conditional
  // comments to only include bgIframe where it is needed in IE without breaking this plugin).
  if ($.fn.bgIframe == undefined) {
    $.fn.bgIframe = function() {
      return this;
    };
  };


  // clean-up
  $(window)
  .bind('unload', function() {
    var els = $.event._tmCache || [];
    for (var i in els) {
      $(els[i].ele)._tmDestroy();
    }
  });


})(jQuery);
