/*
 *   Depends:
 *	ui.core.js
 */

(function($) {
  window.KtTableHandler = function(element, options) {
    this.element = element;
    this.options = options;
    this.category = undefined;
    this.tab = undefined;
    this.page = undefined;
    this.table_id = undefined;
    this.root_url = undefined;
    this.data = undefined;
    this.sort_dict = {}; // true means ascending; false means descending.
    this.type_dict = {}; // int, float, or string
    this.prefix_dict = {};
    this.suffix_dict = {};
    this.sparkline_dict = {};
    this.DATA_LENGTH_LIMIT = 3000;
  };

  KtTableHandler.prototype = {
    _trigger_load_table_data: function() {
      var is_a_member_of_a_subtab_chart = this._is_a_member_of_a_subtab_chart();
      if (is_a_member_of_a_subtab_chart) {
        var table_id = '#t' + this.table_id.split("_")[0];
        var table_widget_ids = $(table_id).ktSubTabTableRefactored("option", "table_widget_ids");
        var target_table_ids = [];
        for (var i = 0; i < table_widget_ids.length; i++) {
          $(document).trigger('load_all_table_data', [table_widget_ids[i]['index']]);
        }
      } else {
        $(document).trigger('load_all_table_data', [this.table_id]);
      }
    },

    _delete_row_handler: function(dom) {
      var arg = this._delete_row_handler_get_ajax_args(dom);
      var _this_obj = this;
      ktAjaxWrapper({
        type: "GET",
        url: "/dashboard/filter/ajax_create_filter/",
        data: arg,
        success: function(data) {
          if (data.exists) {
            var str = "A filter for this row already exists. Enable it?";
            Boxy.confirm(str, function() {
              ktAjaxWrapper({
                type: "GET",
                url: "/dashboard/filter/ajax_toggle_filter_without_id/",
                data: arg,
                success: function(msg) {
                  // If this chart is embedded in a subtype table, we will need to
                  // figure out the other tab charts and update those as well.
                  // Otherwise, update this chart only.
                  _this_obj._trigger_load_table_data();
                }
              });
            });
          } else {
            // If this chart is embedded in a subtype table, we will need to
            // figure out the other tab charts and update those as well.
            // Otherwise, update this chart only.
            _this_obj._trigger_load_table_data();
          }
        }
      }); // $.ajax
    },

    _delete_row_handler_get_ajax_args: function(dom) {
      var this_row = $(dom).parent().parent();
      var channel_type = $(dom).attr('channel_type');
      var arg = {
        channel_type: channel_type
      };
      arg['st1'] = $("td[coln_id=subtype1]", this_row).text(); // category
      arg['st2'] = $("td[coln_id=subtype2]", this_row).text(); // campaign
      arg['st3'] = $("td[coln_id=subtype3]", this_row).text(); // content
      return arg;
    },

    _bind_table_events: function() {
      var $remove_btns = $("#" + this.table_id + "_table a.remove_btn");
      var this_obj = this;
      if ($remove_btns.length > 0) {
        $remove_btns.click(function(event) {
          this_obj._delete_row_handler(event.target);
        });
      }
    },

    _is_a_member_of_a_subtab_chart: function() {
      var tmp_lst = this.table_id.split("_");
      var size = tmp_lst.length;
      if (tmp_lst[size - 1] == 'tab') {
        return true;
      }
      return false;
    },

    _get_table_j: function() {
      var my_id = $(this.element).attr('id');
      return $("#" + my_id + "_table");
    },

    _get_table_header: function(show_frame) {
      if (show_frame == true) {
        var template_str = "<div id='[=table_id]_secondary_data' class='k-box-secondary'></div>\
          <div class='k-box'> \
          <div class='k-box-head'> \
          <div class='k-box-head-info'>\
          <h1>[=title]</h1> \
          <a href='#' index='[=index]' class='help' title='[=bubble_text]'>help</a> \
          <a href='#' title='Export to CSV' class='k-box-icon export_link' id='[=data_export_id]'>&nbsp;</a>\
        </div> \
        </div>\
          <div class='k-box-body'> \
          <table class='k-table viral' id='[=table_id]_table' \
        cellpadding=\"0\" cellspacing=\"0\" border=\"0\"  id=\"data\" style=\"width: 100%\"> \
          <tr class=\"head-tr left-border\"> \
          <td decimal='[=first_coln.coln_decimal]' class='lit first-coln [=first_coln.coln_type] [=first_coln.coln_class]' coln_id='[=first_coln.coln_id]'><small>[=first_coln.coln_name]</small></td>\
        [[for coln in coln_list]] \
          <td decimal='[=coln.coln_decimal]' class='table-right lit [=coln.coln_type] [=coln.coln_class]' coln_id='[=coln.coln_id]'><small>[=coln.coln_name]</small></td>\
        [[endfor]]\
        </tr>\
        </table>\
        </div>\
        </div>";
      } else {
        var template_str = "<div id='[=table_id]_secondary_data' class='tab_secondary_info' style='display:none;'></div>\
          <div> \
          <table class='k-table' id='[=table_id]_table' \
        cellpadding=\"0\" cellspacing=\"0\" border=\"0\"  id=\"data\" style=\"width: 100%\"> \
          <tr class=\"head-tr left-border\"> \
          <td decimal='[=first_coln.coln_decimal]' class='lit first-coln [=first_coln.coln_type] [=first_coln.coln_class]' coln_id='[=first_coln.coln_id]'><small>[=first_coln.coln_name]</small></td>\
        [[for coln in coln_list]] \
          <td decimal='[=coln.coln_decimal]' class='table-right lit [=coln.coln_type] [=coln.coln_class]' coln_id='[=coln.coln_id]'><small>[=coln.coln_name]</small></td>\
        [[endfor]]\
        </tr>\
        </table>\
        </div>";
      }
      return template_str;
    },

    _construct_coln_header: function(show_frame) {
      var my_id = $(this.element).attr('id');
      var template_str = null;
      var data_export_id = "data_export_" + this.table_id;

      var coln_count = this.options['coln'].length;
      var first_coln = undefined;
      var remainder_coln_array = [];
      var colns_from_option = this.options['coln'];
      var idx = 0;
      for (var i = 0; i < coln_count; i++) {
        if (i == 0) first_coln = colns_from_option[i];
        else {
          remainder_coln_array[idx] = colns_from_option[i];
          idx++;
        }
      }

      var params = {
        title: this.options['title'],
        my_id: my_id,
        first_coln: first_coln,
        coln_list: remainder_coln_array,
        table_id: this.table_id,
        bubble_text: this.options['bubble_text'],
        index: this.options['index'],
        data_export_id: data_export_id
      };

      if (this.options['subtab'] != undefined) {
        params['subtab'] = this.options['subtab'];
      }


      template_str = this._get_table_header(show_frame);

      if (show_frame) params['data_export_id'] = data_export_id;

      $(this.element).ktTemplate({
        template: template_str
      });

      $(this.element).ktTemplate('render', params);
      $(this.element).find(".k-box-head-info").data("esc-title", escape(this.options['title']));
      
      this._build_load_table_notification();
      //setup sort_dict
      var coln_list = this.options['coln'];
      var len = coln_list.length;
      for (var i = 0; i < len; i++) {
        var coln = coln_list[i];
        this.sort_dict[coln.coln_id] = true;
        this.type_dict[coln.coln_id] = coln.coln_type;
        this.prefix_dict[coln.coln_id] = coln.coln_prefix;
        this.suffix_dict[coln.coln_id] = coln.coln_suffix;
        this.sparkline_dict[coln.coln_id] = coln.coln_sparkline;
      }
    },

    _setup_rows_template: function(rows) {
      $("#" + this.table_id + "_table").ktTemplate({
        template: '[[for item in list]]' + rows + '[[endfor]]'
      });
    },

    _setup_sorting: function() {
      // make title headers clickable
      var _this_obj = this;
      $(this._gen_table_id() + " td.lit").click(

        function() {
          var id = $(this).attr('coln_id');
          _this_obj._sort(id, _this_obj.data);
          _this_obj._bind_table_events();
        });
    },

    _gen_table_id: function() {
      return "#" + this.table_id + "_table";
    },

    _clear_content: function() {
      var rows = $(this._gen_table_id()).children().children();
      var len = rows.length;

      // purposedly skip the title row
      for (var i = 1; i < len; i++) {
        $(rows[i]).remove();
      }

    },

    _dollarize: function(value) {
      if (value != "N/A" && value != "--") {
        return "$" + value;
      }
      return value;
    },

    _seconds_to_hms: function(secs) {
      if (secs != "N/A" && secs != "--") {
        var remaining_secs = Math.floor(secs);
        if (remaining_secs == 0) return "00:00:00";
        var hr = Math.floor(remaining_secs / 3600);
        remaining_secs -= hr * 3600;
        hr += "";
        if (hr.length == 1) hr = "0" + hr;

        var min = Math.floor(remaining_secs / 60);
        remaining_secs -= min * 60;
        min += "";
        if (min.length == 1) min = "0" + min;

        var sec = remaining_secs;
        sec += "";
        if (sec.length == 1) sec = "0" + sec;

        var r = hr + ":" + min + ":" + sec;
        return r;
      } else {
        return secs;
      }
    },

    _sort_comp_ascending_float: function(a, b, coln_id) {
      var a_v = parseFloat(a[coln_id]);
      var b_v = parseFloat(b[coln_id]);
      if (isNaN(a_v) && isNaN(b_v)) return 0;
      if (isNaN(a_v)) return -1 - b_v;
      if (isNaN(b_v)) return a_v - -1;
      return a_v - b_v;
    },

    _sort_comp_descending_float: function(a, b, coln_id) {
      var a_v = parseFloat(a[coln_id]);
      var b_v = parseFloat(b[coln_id]);
      if (isNaN(b_v) && isNaN(a_v)) return 0;
      if (isNaN(b_v)) return -1 - a_v;
      if (isNaN(a_v)) return b_v - (-1);
      return b_v - a_v;
    },

    _format_data: function(data) {
      var self = this;
      var row_script_handler = {};
      return $.map($.extend(true, [],data), function(row, i) {
        $.each(row, function(key, val) {
          if(!row_script_handler[key]) {
            var type = self.type_dict[key];
            if(!type) {
              row_script_handler[key] = "void(0)";
              return;
            }
            var prefix = self.prefix_dict[key];
            var suffix = self.suffix_dict[key];
            var script_str = [];
            if (typeof(row[key]) === "number") {
              script_str.push("val = parseFloat(val);");
              type == "cent" && script_str.push("val = val/100.0;");
              type == "percentage" && script_str.push("val = val*100.0;");
              // // format the decimal places first
              var decimal_places_str = $(self._gen_table_id() + " td[coln_id='" + key + "']").attr('decimal');
              decimal_places_str && script_str.push("val = val.toFixed(" + parseInt(decimal_places_str) + ");");
              // don't add commas to seconds; otherwise, _seconds_to_hms will break
              type != 'time' && script_str.push("val = self.num_formatter.comma_format(val);");
            } else {
              script_str.push("val;");
            }
            
            switch(type) {
            case "percentage":
              script_str.push("val+='%';");
              break;
            case "dollar":
            case "cent":
              script_str.push("val = self._dollarize(val);");
              break;
            case "time":
              script_str.push("val = self._seconds_to_hms(val);");
              break;
            }
            
            prefix && script_str.push("val='" + prefix + "' + val;");
            suffix && script_str.push("val+='" + suffix + "';");
            row_script_handler[key] = script_str.join("");
          }
          row_script_handler[key] != "void(0)" && (row[key] = eval(row_script_handler[key]));
        });
        return $.isArray(row) ? [row] : row;
      });
    },

    _sort_impl: function(coln_id, data, rollup_key) {
      var this_td = $(this._gen_table_id() + " td[coln_id=" + coln_id + "]");
      var this_obj = this;
      var ascending = this.sort_dict[coln_id];
      var type = this.type_dict[coln_id];
      var og_height = this_td.height();
      if (ascending == true) {
        this_td.children("small").removeClass('sort_ascending').addClass('sort_ascending');
        // clear all the other columns
        this_td.siblings().children("small").removeClass('sort_descending').removeClass('sort_ascending');
      } else {
        this_td.children("small").removeClass('sort_ascending').addClass('sort_descending');
        // clear all the other columns
        this_td.siblings().children("small").removeClass('sort_descending').removeClass('sort_ascending');
      }
      this_td.height(og_height);

      if ($.isFunction(data.sort)) {
        if (type == "int") {
          if (ascending == true) {
            data.sort(function(a, b) {
              var a_v = parseInt(a[coln_id]);
              var b_v = parseInt(b[coln_id]);
              if (isNaN(a_v)) return -1;
              if (isNaN(b_v)) return a_v;
              return a_v - b_v;
            });
          } else {
            data.sort(function(a, b) {
              var a_v = parseInt(a[coln_id]);
              var b_v = parseInt(b[coln_id]);
              if (isNaN(b_v)) return -1;
              if (isNaN(a_v)) return b_v;
              return b_v - a_v;
            });
          }
        } else if (type == "float" || type == "percentage" || type == "dollar" || type == "cent") {
          if (ascending == true) {

            data.sort(function(a, b) {

              return this_obj._sort_comp_ascending_float(a, b, coln_id);
            });
          } else {

            data.sort(function(a, b) {

              return this_obj._sort_comp_descending_float(a, b, coln_id);
            });
          }
        } else if (type == "time") {
          if (ascending == true) {
            data.sort(function(a, b) {
              return this_obj._sort_comp_ascending_float(a, b, coln_id);
            });
          } else {
            data.sort(function(a, b) {
              return this_obj._sort_comp_descending_float(a, b, coln_id);
            });
          }
        } else if (type == "string" || type == "rollup") {
          if (rollup_key) {
            coln_id = rollup_key;
          }

          if (ascending == true) {
            data.sort(function(a, b) {
              if (a[coln_id] > b[coln_id]) return 1;
              else return -1;
            });
          } else {
            data.sort(function(a, b) {
              if (b[coln_id] > a[coln_id]) return 1;
              else return -1;
            });
          }
        }
      }
    },

    _sort: function(coln_id, data, repopulate) {
      //do some bookkeeping first
      this.sort_dict[coln_id] = !this.sort_dict[coln_id];
      this._sort_impl(coln_id, data);
      if (data.length == 0) {
        // If there is no data don't need to clear data
        return;
      }

      // repopulate data
      if (repopulate == null || repopulate) {
        this._clear_content();
        var formated_data = this._format_data(data);

        $(this._gen_table_id()).ktTemplate('render', {
          list: formated_data
        });
      }
    },

    _mark_sorted_cells: function() {

      var all_cells = $(this._gen_table_id() + " td");

      //Clear all other cells
      all_cells.removeClass("sorted");

      //Find sorted column
      var sorted_header = $(this._gen_table_id() + " .sort_descending, .sort_ascending");

      if (sorted_header.length > 0) {
        var sorted_cell = sorted_header.parent();
        var idx = sorted_cell.index();

        //Mark each cell in the column as sorted
        all_cells.filter(":nth-child(" + (idx + 1) + ")").addClass("sorted");
      }
    },
    // INTERNET EXPLORER (IE) DOES NOT LIKE IT WHEN COLSPAN IS SET TO ZERO
    // IF CONDITION TO CHECK IF ZERO IS REQURED
    // WRONG --> cell.attr("colspan", 0) <-- WRONG
    _build_empty_table_notification: function() {
      this._build_table_notification($('<td>There is no data to display</td>'), '');
    },

    _build_load_table_notification: function() {
      var img = $("<img src=\"" + kt_media_url + "/images/indicator.gif\"/>");
      img.css("padding", "5px");
      var cell = $("<td>Loading Data </td>").append(img);

      this._build_table_notification(cell, 'loading-row');      
    },

    _build_large_data_notification: function() {
      this._build_table_notification($('<td>We have detected a large volume of data and are unable to render it at this time. Please <a href="http://support.kontagent.com/requests/new/">Contact Support</a> for assistance with this issue.</td>'), '');
    },
    
    _build_table_notification: function(cell, row_class) {
      cell.css("text-align", "center").css("padding", "8px");

      var c_length = this.options['coln'].length;

      if (c_length == 0) {
       cell.attr("colspan", "1");
      } else {
       cell.attr("colspan", c_length);
      }

      var row = $('<tr></tr>')
        .addClass(row_class)
        .append(cell);

      $("#" + this.table_id + "_table").find("tbody").append(row);
     },

    /*
     * This function is resposible for rendering the st1 level data.
     */
    _ajax_load_all_table_data_handler: function(data) {
      this._clear_content();

      if (data.length == 0) {
        this._build_empty_table_notification();
        return;
      }

      if (data.length > this.DATA_LENGTH_LIMIT) {
        this._build_large_data_notification();
        return;
      }

      this.data = data;

      var to_be_rendered_data = [];
      for (var i in this.data) {
        var d = this.data[i];
        if (d['deleted'] == undefined || d['deleted'] == false) {
          d['row_id'] = i;
          to_be_rendered_data.push(d);
        }
      }
      
      this._perform_initial_sorting(this);
      
      $("#" + this.table_id + "_table").ktTemplate('render', {
        //list: this._format_data(data)
        list: this._format_data(to_be_rendered_data)
      });

      this._bind_table_events();
    },

    _export_handler: function() {
      var data_export_url = np_get_table_export_url(this, this.category, this.tab, this.page, this.table_id);
      document.location = data_export_url;
      return false;
    },

    _load_all_table_data_handler: function() {
      var _this_obj = this;
      _this_obj._clear_content(); //clear out the old content.
      this._build_load_table_notification();
      ktAjaxWrapper({
        type: 'GET',
        traditional: true,
        url: np_get_table_url(_this_obj, _this_obj.category, _this_obj.tab, _this_obj.page, _this_obj.table_id),
        dataType: 'json',
        success: function(data, textStatus) {
          if (data.data != undefined) {
            data = data.data;
          } else {
            if (data == undefined) {
              data = [];
            }
          }
          
          if (data['__primary_data__'] != undefined) {
            _this_obj._ajax_load_all_table_data_handler(data['__primary_data__']);

            if (data['__secondary_data__'] != undefined) {
              $("#" + _this_obj.table_id + "_secondary_data").text(data['__secondary_data__']);
              $("#" + _this_obj.table_id + "_secondary_data").css("display", "block");
            } else {
              $("#" + _this_obj.table_id + "_secondary_data").css("display", "none");
            }
            $(document).trigger("onload_all_table_data", [_this_obj.table_id]);
          } else {
            $("#" + _this_obj.table_id + "_secondary_data").css("display", "none");
            _this_obj._ajax_load_all_table_data_handler(data);
            np_run_post_table_load(_this_obj, _this_obj.table_id);
          }

        },
        //sucess
        error: function(xhr) {
          var response;
          try {
            response = JSON.parse(xhr.responseText);
          } catch(e) {
            console && console.error("An error occurred while parsing error response.", e);
            response = {};
          }
          $('#' + _this_obj.table_id + "_table .loading-row td").html(response.errorMessage ? response.errorMessage : "No data available");                  
        }
      });
    },

    _perform_initial_sorting: function(table) {

      //Perform an initial sorting once the data has been loaded
      var initial_sorting = table.initial_sorting;
      if (initial_sorting && table.data) {
        table.sort_dict[initial_sorting] = false;
        table._sort(initial_sorting, table.data, false);
      }

    },

    add_sparklines: function(selector) {
      var max = 1;
      var pos = 0;
      var len = $(selector + " .sparkline_cell").length;

      function iteration() {
        var j = Math.min(len - pos, max);
        for (var i = 0; i < j; i++) {
          $($(selector + " .sparkline_cell")[pos]).show();
          if ($($(selector + " .sparkline_cell")[pos]).children("canvas").length == 0) {
            var js_str = "[" + $($(selector + " .sparkline_cell")[pos]).attr("data") + "]";
            var data_lst = eval(js_str);

            $($(selector + " .sparkline_cell")[pos]).sparkline(data_lst, {
              lineColor: '#057D9F',
              width: '50px',
              lineWidth: '2',
              spotColor: false,
              spotRadius: '0',
              fillColor: false
            });
          }
          pos++;
        }

        if (pos < len) {
          setTimeout(iteration, 60);
        } else {
        }
      }
      iteration();

    },

    init_impl: function() {
      this.category = this.options['category'];
      this.tab = this.options['tab'];
      this.page = this.options['page'];
      this.table_id = this.options['index'];
      this.root_url = this.options['root_url'];
      this.num_formatter = new NumFormatter();

      if (this.options['no_table_frame']) if (this.options['no_table_frame'] == true) this._construct_coln_header(false);
      else this._construct_coln_header(true);
      else this._construct_coln_header(true);

      this._setup_rows_template(this.options['rows']);


      var category = this.category;
      var tab = this.tab;
      var page = this.page;
      var table_id = this.table_id;
      var root_url = this.root_url;

      var _this_obj = this;

      // make title headers clickable
      this._setup_sorting();

      //Things we want to handle have data has been rendered
      $(this.element).bind('render_complete', function(event, params) {
        //Load sparklines
        var row_selector = "";
        if ('subtype2' in params) {
          row_selector = " tr.st3_row";
        } else if ('subtype1' in params) {
          row_selector = " tr.st2_row";
        }

        _this_obj.add_sparklines(_this_obj._gen_table_id() + row_selector);
        _this_obj._mark_sorted_cells();
      });

      $(document).bind('load_all_table_data', function(event, msg) {
        if (msg == undefined || msg == _this_obj.table_id) {
          _this_obj._load_all_table_data_handler();
        } // if(msg == ...
      });

      // bind data export link
      var data_export_id = "data_export_" + this.table_id;
      $("#" + data_export_id).click(

        function() {
          _this_obj._export_handler();
          return false;
        });

      // jQuery(this.element).data('kt_table_handler', this);
    }


  };


  function KtTable() {
    this.handler = null;
  }

  KtTable.prototype = {
    _init: function() {
      this.handler = new KtTableHandler(this.element, this.options);
      this.handler.init_impl();
    },
    get_handler: function() {
      return this.handler;
    }
  };

  ///////////////////// KtNotPermittedTable ////////////////////////

  function KtNotPermittedTableHandler(element, options) {
    this.inheritedFrom = KtTableHandler;
    this.inheritedFrom(element, options);
  };

  KtNotPermittedTableHandler.prototype = new KtTableHandler();

  KtNotPermittedTableHandler.prototype._construct_coln_header = function(show_frame) {
    var my_id = $(this.element).attr('id');
    var template_str = null;

    var coln_count = this.options['coln'].length;
    var first_coln = undefined;
    var remainder_coln_array = [];
    var colns_from_option = this.options['coln'];
    var idx = 0;
    for (var i = 0; i < coln_count; i++) {
      if (i == 0) first_coln = colns_from_option[i];
      else {
        remainder_coln_array[idx] = colns_from_option[i];
        idx++;
      }
    }

    var params = {
      title: this.options['title'],
      my_id: my_id,
      first_coln: first_coln,
      coln_list: remainder_coln_array,
      table_id: this.table_id,
      index: this.options['index']
    };


    if (this.options['subtab'] != undefined) {
      params['subtab'] = this.options['subtab'];
    }

    template_str = this._get_table_header(show_frame);
    $(this.element).ktTemplate({
      template: template_str
    });

    $(this.element).ktTemplate('render', params);

  };

  KtNotPermittedTableHandler.prototype._export_handler = function() {
    return false;
  };

  KtNotPermittedTableHandler.prototype._load_all_table_data_handler = function() {
    $("#" + this.table_id + "_table").append(this.row_str);
  };

  KtNotPermittedTableHandler.prototype.init_impl = function() {
    this.category = this.options['category'];
    this.tab = this.options['tab'];
    this.page = this.options['page'];
    this.table_id = this.options['index'];
    this.row_str = this.options['rows'];

    if (this.options['no_table_frame']) if (this.options['no_table_frame'] == true) this._construct_coln_header(false);
    else this._construct_coln_header(true);
    else this._construct_coln_header(true);

    var category = this.category;
    var tab = this.tab;
    var page = this.page;
    var table_id = this.table_id;
    var root_url = this.root_url;

    var _this_obj = this;

    $(document).bind('load_all_table_data', function(event, msg) {
      if (msg == undefined || msg == _this_obj.table_id) {
        _this_obj._load_all_table_data_handler();
      }
    });
    $(this.element).attr("not_permitted", true);
  };


  function KtNotPermittedTable() {
    this.inheritedFrom = KtTable;
  }

  KtNotPermittedTable.prototype = new KtTable();
  KtNotPermittedTable.prototype._init = function() {
    this.handler = new KtNotPermittedTableHandler(this.element, this.options);
    this.handler.init_impl();
  };

  //////////////////////// KtSt1St2Table ///////////////////////////

  function KtSt1St2TableHandler(element, options) {
    this.inheritedFrom = KtTableHandler;
    this.inheritedFrom(element, options);
    this.row_click_state = {}; //true: expanded false: folded
    this.rows_expanded = {
      'st2': 0,
      'st3': 0
    }; //Keep track of how many rows expanded at each level
    this.initial_sorting = 'subtype1';
    this.st2_data = {};
    this.st3_data = {};
    this.total_st2_st3_ajax_reload_calls = 0;
    this.st2_st3_ajax_reload_calls_returned_so_far = 0;
  };

  KtSt1St2TableHandler.prototype = new KtTableHandler();
  KtSt1St2TableHandler.prototype.init_impl = function() {
    // call parent's method
    KtTableHandler.prototype.init_impl.call(this);
    var _this_obj = this;
    $(document).bind('toggle_st2st3', function(event, table_id, st1, st2, st3) {
      _this_obj._toggle_or_removefilter_st2st3_handler(event, table_id, st1, st2, st3, false);
    });
    $(document).bind('delete_st2st3', function(event, table_id, st1, st2, st3) {
      _this_obj._toggle_or_removefilter_st2st3_handler(event, table_id, st1, st2, st3, true);
    });

    $(document).bind("repopulate_st2_st3_entries", function(event, table_id, render_data) {
      if (table_id == _this_obj.table_id) {
        KtTableHandler.prototype._ajax_load_all_table_data_handler.call(_this_obj, render_data);
        _this_obj._repopulate_after_bind_table_events(render_data);
      }
    });
  };

  // if force_delete is false, toggles.
  // if force_delete is true, remove it for good.
  KtSt1St2TableHandler.prototype._toggle_or_removefilter_st2st3_handler = function(event, table_id, st1, st2, st3, force_delete) {
    if (this.table_id == table_id) {
      if (st2 != "*" && st3 == "*" && this.st2_data[st1] != undefined) {
        var st2_data_lst = this.st2_data[st1];
        var len = st2_data_lst.length;
        for (var i = 0; i < len; i++) {
          if (st2_data_lst[i]['subtype2'] == st2) {
            if (force_delete) st2_data_lst[i]['deleted'] = false;
            else st2_data_lst[i]['deleted'] = !st2_data_lst[i]['deleted'];
            this._populate_cached_st2_row(st1);
            break;
          }
        } // for
      } else if (st2 != "*" && st3 != "*" && this.st3_data[st1] != undefined && this.st3_data[st1][st2] != undefined) {
        // if st3 data under st2 have been duplicated.
        var st3_data_lst = this.st3_data[st1][st2];
        var len = st3_data_lst.length;
        for (var i = 0; i < len; i++) {
          if (st3_data_lst[i]['subtype3'] == st3) {
            if (force_delete) st3_data_lst[i]['deleted'] = false;
            else st3_data_lst[i]['deleted'] = !st3_data_lst[i]['deleted'];
            this._populate_cached_st3_row(st1, st2);
          }
          break;
        } // for
      }
    }
  };

  KtSt1St2TableHandler.prototype._sort = function(coln_id, data, repopulate) {
    // sort st1 level data
    KtTableHandler.prototype._sort.call(this, coln_id, data, repopulate);

    // sort st2 level data
    var st1_key = null;
    for (st1_key in this.st2_data) {
      this._sort_impl(coln_id, this.st2_data[st1_key], "subtype2");
    }
    // sort st3 level data
    for (st1_key in this.st3_data) {
      for (var st2_key in this.st3_data[st1_key]) {
        this._sort_impl(coln_id, this.st3_data[st1_key][st2_key], "subtype3");
      }
    }
    //this._bind_table_events();
    this._repopulate_after_bind_table_events(data);
  };

  KtSt1St2TableHandler.prototype._repopulate_after_bind_table_events = function(data) {
    this._populate_all_cached_st2_rows();
    this._populate_all_cached_st3_rows();
    this._show_or_hide_all();
    this._get_rid_of_st1_arrows(data);
    for (st1_key in this.st2_data) {
      this._get_rid_of_st2_arrows(this.st2_data[st1_key], st1_key);
    }
  };

  KtSt1St2TableHandler.prototype._get_rid_of_arrows_helper = function(img_lst, data) {
    var len = data.length;
    // purge deleted data
    var d = [];
    for (var i = 0; i < len; i++) {
      if (data[i]['deleted'] == undefined || data[i]['deleted'] == false) {
        d.push(data[i]);
      }
    } // for
    len = d.length;
    for (i = 0; i < len; i++) {
      if (d[i]['expandable'] == false) {
        $(img_lst[i]).css('visibility', 'hidden');
        $(img_lst[i]).parent().parent().css('cursor', 'default');
      }
    }
  };

  KtSt1St2TableHandler.prototype._get_rid_of_st1_arrows = function(data) {
    var img_lst = $(this._gen_table_id() + " tr.st1_row img.plus");
    this._get_rid_of_arrows_helper(img_lst, data);
  };

  KtSt1St2TableHandler.prototype._get_rid_of_st2_arrows = function(data, st1_key) {
    var img_lst = $(this._gen_table_id() + " tr.st2_row[subtype1=" + st1_key + "] img.plus");
    this._get_rid_of_arrows_helper(img_lst, data);
  };

  KtSt1St2TableHandler.prototype._ajax_load_all_table_data_handler = function(data) {
    var this_obj = this;

    var num_of_ajax_calls = 0;
    $.each(this.st2_data, function(k, v) {
      num_of_ajax_calls++;
    });
    $.each(this.st3_data, function(subtype1, item) {
      $.each(item, function(subtype2, v3) {
        num_of_ajax_calls++;
      });
    });

    this_obj.total_st2_st3_ajax_reload_calls = num_of_ajax_calls;
    this_obj.st2_st3_ajax_reload_calls_returned_so_far = 0;
    if (num_of_ajax_calls == 0) {
      KtTableHandler.prototype._ajax_load_all_table_data_handler.call(this, data);
    } else {
      $.each(this.st2_data, function(k, v) {
        this_obj._ajax_fetch_st2_data(k, null, false, data);
      });

      $.each(this.st3_data, function(subtype1, item) {
        $.each(item, function(subtype2, v3) {
          this_obj._ajax_fetch_st3_data(subtype1, subtype2, null, false, data);
        });
      });
    }
  };

  KtSt1St2TableHandler.prototype._save_st3_data = function(subtype1, subtype2, data) {
    if (this.st3_data[subtype1] == undefined) {
      this.st3_data[subtype1] = {};
    }
    this.st3_data[subtype1][subtype2] = data;
  };

  KtSt1St2TableHandler.prototype._get_undeleted_st2_data = function(st1) {
    var r = [];
    var st2_data = this.st2_data[st1];
    var len = st2_data.length;
    for (var i = 0; i < st2_data.length; i++) {
      if (st2_data[i]['deleted'] == undefined || st2_data[i]['deleted'] == false) {
        r.push(st2_data[i]);
      }
    }
    return r;
  };
  KtSt1St2TableHandler.prototype._get_undeleted_st3_data = function(st1, st2) {
    var r = [];
    var st3_data = this.st3_data[st1][st2];
    var len = st3_data.length;
    for (var i = 0; i < st3_data.length; i++) {
      if (st3_data[i]['deleted'] == undefined || st3_data[i]['deleted'] == false) {
        r.push(st3_data[i]);
      }
    }
    return r;
  };

  KtSt1St2TableHandler.prototype._populate_cached_st2_row = function(subtype1) {
    var st2_data = this._get_undeleted_st2_data(subtype1);

    var $row = $('tr[table_id=' + this.table_id + '][subtype1=' + subtype1 + ']');

    if ($row.length == 1) {


      $row.after("<div></div>"); // add a temporary div
      $($row.next()).ktTemplate({
        template: '[[ for item in list ]]' + this.options['st2_rows'] + '[[ endfor ]]'
      });
      var formatted_st2_data = this._format_data(st2_data);
      $($row.next()).ktTemplate('render', {
        'list': formatted_st2_data,
        'subtype1': subtype1
      });

      var subtype2_rows = $row.next().children();
      $row.next().remove();
      $row.after(subtype2_rows);

      //iterate thru all the st2 rows and bind a click event handler
      var $curr_dom = $row.next();
      while ($curr_dom.length != 0 && $($curr_dom).attr('class') != 'st1_row') {
        var this_obj = this;
        $curr_dom.click(function(event) {
          var $og_target = $(event.target);
          if ($og_target.hasClass('remove_btn')) {
            this_obj._delete_row_handler(event.target);
          } else {
            var $row = $(this);
            if ($row.attr('expandable') == 'true') {
              var subtype2 = $($row.children()[1]).text();

              var need_to_fetch_data = false;
              if (this_obj.row_click_state[subtype1][1][subtype2] == undefined) {
                this_obj.row_click_state[subtype1][1][subtype2] = true;
                need_to_fetch_data = true;
              } else {
                //toggle;
                this_obj.row_click_state[subtype1][1][subtype2] = !this_obj.row_click_state[subtype1][1][subtype2];
              }

              if (need_to_fetch_data) {
                $row.find('img.plus').hide();
                $row.find('img.loading').show();
                this_obj._ajax_fetch_st3_data(subtype1, subtype2, $row, true);
              } else {
                this_obj._show_or_hide_st3_row(subtype1, subtype2);
              }
            }
          }
        });
        $curr_dom = $curr_dom.next();
      }
    }
  };
  KtSt1St2TableHandler.prototype._populate_cached_st3_row = function(subtype1, subtype2) {
    if (this.row_click_state[subtype1] == undefined || this.row_click_state[subtype1][1][subtype2] == undefined || this.row_click_state[subtype1][1][subtype2] == false) {
      return;
    }

    var st3_data = this._get_undeleted_st3_data(subtype1, subtype2);
    var $row = $('tr[table_id=' + this.table_id + '][subtype1=' + subtype1 + '][subtype2=' + subtype2 + '][class="st2_row"]');

    if ($row.length == 1) {
      $row.after("<div></div>"); // add a temporary div
      $($row.next()).ktTemplate({
        template: '[[ for item in list ]]' + this.options['st3_rows'] + '[[ endfor ]]'
      });
      $($row.next()).ktTemplate('render', {
        'list': this._format_data(st3_data),
        'subtype1': subtype1,
        'subtype2': subtype2
      });
      var subtype3_rows = $row.next().children();

      $row.next().remove();
      $row.after(subtype3_rows);

      var $curr_dom = $row.next();
      var this_obj = this;
      while ($curr_dom.length != 0 && $($curr_dom).attr('class') != 'st1_row' && $($curr_dom).attr('class') != 'st2_row') {
        $curr_dom.click(function(event) {
          var $og_target = $(event.target);
          if ($og_target.hasClass('remove_btn')) {
            this_obj._delete_row_handler(event.target);
          }
        });
        $curr_dom = $curr_dom.next();
      }
    }
  };
  KtSt1St2TableHandler.prototype._show_or_hide_st2_row = function(subtype1) {
    // show or hide the rows accordingly
    var $row = $('tr[table_id=' + this.table_id + '][subtype1=' + subtype1 + '][class="st1_row"]');
    if ($row.length == 0) return;

    var this_obj = this;
    var $curr_dom_lst = $('tr[table_id=' + this.table_id + '][subtype1=' + subtype1 + '][class="st2_row"]');
    if (this.row_click_state[subtype1] != undefined && this.row_click_state[subtype1][0] == true)
      //Show
    {
      $row.contents().find("img.plus").hide();
      $row.contents().find("img.minus").show();
      $.each($curr_dom_lst, function(i, dom) {
        $(dom).show();
        var subtype2 = '';
        if ($(dom).attr('subtype2') != undefined) subtype2 = $(dom).attr('subtype2');
        this_obj._show_or_hide_st3_row(subtype1, subtype2);
      });

      this.rows_expanded['st2']++;
    } else
      //Hide
    {
      $row.contents().find("img.plus").show();
      $row.contents().find("img.minus").hide();
      $.each($curr_dom_lst, function(i, dom) {
        $(dom).hide();
        var subtype2 = '';
        if ($(dom).attr('subtype2') != undefined) subtype2 = $(dom).attr('subtype2');
        this_obj._show_or_hide_st3_row(subtype1, subtype2, true /*force_hide*/ );
      });
      this.rows_expanded['st2']--;
    }

    this._show_or_hide_header('st2');
    $.sparkline_display_visible();
  };

  KtSt1St2TableHandler.prototype._show_or_hide_header = function(subtype) {
    var ele = $(this._gen_table_id() + " ." + subtype + "_header");
    if (this.rows_expanded[subtype] > 0) {
      ele.show();
    } else {
      ele.hide();
    }
  };

  KtSt1St2TableHandler.prototype._show_or_hide_st3_row = function(subtype1, subtype2, force_hide) {
    var $row = $('tr[table_id=' + this.table_id + '][subtype1=' + subtype1 + '][subtype2=' + subtype2 + '][class="st2_row"]');
    if ($row.length == 0) return;

    var $curr_dom = $row.next();
    if (this.row_click_state[subtype1] != undefined && this.row_click_state[subtype1][1][subtype2] != undefined && this.row_click_state[subtype1][1][subtype2] == true && force_hide == undefined)
      //Show
    {
      $row.contents().find("img.plus").hide();
      $row.contents().find("img.minus").show();
      while ($curr_dom.attr('class') == 'st3_row') {
        //Only count showing if currently hidden or unloaded
        if (!$curr_dom.is(':visible') || $row.contents().find("img.loading").is(":visible")) {
          this.rows_expanded['st3']++;
        }

        $curr_dom.show();
        $curr_dom = $curr_dom.next();
      }
    } else
      //Hide
    {
      $row.contents().find("img.plus").show();
      $row.contents().find("img.minus").hide();

      while ($curr_dom.attr('class') == 'st3_row') {
        //Only count hidding if currently showing
        if ($curr_dom.is(':visible')) {
          this.rows_expanded['st3']--;
        }

        $curr_dom.hide();
        $curr_dom = $curr_dom.next();
      }
    }

    this._show_or_hide_header('st3');
    $.sparkline_display_visible();

  };
  KtSt1St2TableHandler.prototype._show_or_hide_all = function() {
    for (var st1_key in this.st2_data) {
      this._show_or_hide_st2_row(st1_key);
    }
  };
  KtSt1St2TableHandler.prototype._populate_all_cached_st2_rows = function() {
    for (var st1_key in this.st2_data) {
      this._populate_cached_st2_row(st1_key);

    }
  };
  KtSt1St2TableHandler.prototype._populate_all_cached_st3_rows = function() {
    for (var st1_key in this.st3_data) {
      var st3_data = this.st3_data[st1_key];
      for (var st2_key in st3_data) {
        this._populate_cached_st3_row(st1_key, st2_key);
      }
    }
  };


  KtSt1St2TableHandler.prototype._delete_st2_from_cache = function(st1, st2) {
    if (this.st2_data[st1] != undefined) {
      var st2_data_lst = this.st2_data[st1];
      var len = st2_data_lst.length;
      if (st2 != undefined && st2 != null) {
        for (var i = 0; i < len; i++) {
          if (st2_data_lst[i]['subtype2'] == st2) {
            st2_data_lst[i]['deleted'] = true;
            break;
          }
        } // for
      } else {
        for (var i = 0; i < len; i++) {
          st2_data_lst[i]['deleted'] = true;
        }
      }
    }
    this._delete_st3_from_cache(st1, st2);
  };

  KtSt1St2TableHandler.prototype._delete_st3_from_cache = function(st1, st2, st3) {
    if (this.st3_data[st1] != undefined && this.st3_data[st1][st2] != undefined) {
      var st3_data_lst = this.st3_data[st1][st2];
      var len = st3_data_lst.length;
      if (st3 != undefined && st3 != null) {
        for (var i = 0; i < len; i++) {
          if (st3_data_lst[i]['subtype3'] == st3) {
            st3_data_lst[i]['deleted'] = true;
          } // if(st3_data_lst[i]['subtype3'] == st3) ...
          break;
        } // for(var i ...
      } else {
        //st3 is not specified, so delete all st3 associated with st1 and st2
        for (var i = 0; i < len; i++) {
          st3_data_lst[i]['deleted'] = true;
        } // for
      } // else
    }
  };

  KtSt1St2TableHandler.prototype._delete_row_handler_get_ajax_args = function(dom) {
    var this_row = $(dom).parent().parent();
    var channel_type = $(dom).attr('channel_type');
    var row_type = $(this_row).attr('class');

    var arg = {
      channel_type: channel_type
    };
    // clean up the row_click_state while at it
    // also, clean up the st2 and st3 cache while at it.
    if (row_type == 'st1_row') {
      var st1 = $(this_row).attr('subtype1');
      arg['st1'] = st1;
      if (this.row_click_state[st1] != undefined) {
        delete this.row_click_state[st1];
      }
      this._delete_st2_from_cache(st1);
    } else if (row_type == 'st2_row') {
      var st1 = $(this_row).attr('subtype1');
      var st2 = $(this_row).attr('subtype2');
      arg['st1'] = st1;
      arg['st2'] = st2;
      if (this.row_click_state[st1][1][st2] != undefined) {
        delete this.row_click_state[st1][1][st2];
      }
      this._delete_st2_from_cache(st1, st2);
    } else if (row_type == 'st3_row') {
      var st1 = $(this_row).attr('subtype1');
      var st2 = $(this_row).attr('subtype2');
      var st3 = $(this_row).attr('subtype3');
      arg['st1'] = st1;
      arg['st2'] = st2;
      arg['st3'] = st3;
      this._delete_st3_from_cache(st1, st2, st3);
    }
    return arg;
  };

  KtSt1St2TableHandler.prototype._ajax_fetch_st2_data = function(subtype1, $row, wait_for_everyone_else, render_data) {
    var this_obj = this;
    var st_prefix = this.options['st123_prefix']; // st_pref =  fcsst in this case
    var url = np_get_table_url(this_obj, this_obj.options['category'], this_obj.options['tab'], this_obj.options['page'], this_obj.table_id) + '&' + st_prefix + '1=' + encodeURIComponent(subtype1) + '&groupby=' + encodeURIComponent(subtype1);
    ktAjaxWrapper({
      type: 'GET',
      url: url,
      async: true,
      traditional: true,
      dataType: 'json',
      success: function(data, textStatus) {
        if (data['__primary_data__'] != undefined) {
          data.data = data['__primary_data__'];
        }

        // cache the subtype2 data
        this_obj.st2_data[subtype1] = data.data;
        if ($row != null) {
          this_obj._populate_cached_st2_row(subtype1);
          this_obj._show_or_hide_st2_row(subtype1);
          $row.find('img.loading').hide();
          this_obj._get_rid_of_st2_arrows(data.data, subtype1);
          $(document).trigger("load_st2_table_data", [this_obj.table_id, render_data]);
        }
        if (wait_for_everyone_else == false) {
          this_obj.st2_st3_ajax_reload_calls_returned_so_far = this_obj.st2_st3_ajax_reload_calls_returned_so_far + 1;
          if (this_obj.st2_st3_ajax_reload_calls_returned_so_far == this_obj.total_st2_st3_ajax_reload_calls) {
            this_obj.total_st2_st3_ajax_reload_calls = 0;
            this_obj.st2_st3_ajax_reload_calls_returned_so_far = 0;
            $(document).trigger("repopulate_st2_st3_entries", [this_obj.table_id, render_data]);
          }
        }
      } // success
    });
  };

  KtSt1St2TableHandler.prototype._ajax_fetch_st3_data = function(subtype1, subtype2, $row, wait_for_everyone_else, render_data) {
    var this_obj = this;
    var st_prefix = this.options['st123_prefix']; // st_pref =  fcsst in this case
    var url = np_get_table_url(this_obj, this_obj.options['category'], this_obj.options['tab'], this_obj.options['page'], this_obj.table_id) + '&' + st_prefix + '1=' + encodeURIComponent(subtype1) + '&' + st_prefix + '2=' + encodeURIComponent(subtype2) + '&groupby=' + encodeURIComponent(subtype1);
    ktAjaxWrapper({
      type: 'GET',
      traditional: true,
      url: url,
      async: true,
      dataType: 'json',
      success: function(data, textStatus) {

        if (data['__primary_data__'] != undefined) {
          data.data = data['__primary_data__'];
        }

        this_obj._save_st3_data(subtype1, subtype2, data.data);
        if ($row != null) {
          this_obj._populate_cached_st3_row(subtype1, subtype2);
          this_obj._show_or_hide_st3_row(subtype1, subtype2);
          $row.find('img.loading').hide();
          $(document).trigger("load_st3_table_data", [this_obj.table_id, render_data]);
        }
        if (wait_for_everyone_else == false) {
          this_obj.st2_st3_ajax_reload_calls_returned_so_far = this_obj.st2_st3_ajax_reload_calls_returned_so_far + 1;
          if (this_obj.st2_st3_ajax_reload_calls_returned_so_far == this_obj.total_st2_st3_ajax_reload_calls) {
            this_obj.total_st2_st3_ajax_reload_calls = 0;
            this_obj.st2_st3_ajax_reload_calls_returned_so_far = 0;
            $(document).trigger("repopulate_st2_st3_entries", [this_obj.table_id, render_data]);
          }
        }

      } //success
    }); //ktAjaxWrapper
  };

  KtSt1St2TableHandler.prototype._bind_table_events = function() {
    var this_obj = this;

    this_obj._get_rid_of_st1_arrows(this_obj.data);
    $("#" + this_obj.table_id + "_table tr.st1_row").click(

      function(event) {
        var $og_target = $(event.target);
        if ($og_target.hasClass('remove_btn')) {
          // remove event
          this_obj._delete_row_handler(event.target);
        } else {
          // normal toggle event
          var $row = $(this);
          if ($row.attr('expandable') == 'true') {
            var subtype1 = ($($($(this).children()[1]).children()[0]).text());

            // update the click state first
            var need_to_fetch_data = false;
            if (this_obj.row_click_state[subtype1] == undefined) {
              this_obj.row_click_state[subtype1] = [true,
                                                    {}];
              need_to_fetch_data = true;
            } else {
              this_obj.row_click_state[subtype1][0] = !this_obj.row_click_state[subtype1][0];
            }

            if (need_to_fetch_data) {
              $row.find('img.plus').hide();
              $row.find('img.loading').show();
              this_obj._ajax_fetch_st2_data(subtype1, $row, true);
            } else {
              this_obj._show_or_hide_st2_row(subtype1);
            }
          }
        }
        return false;
      });
  };

  function KtSt1St2Table() {
    this.inheritedFrom = KtTable;
  };

  KtSt1St2Table.prototype = new KtTable();
  KtSt1St2Table.prototype._init = function() {
    this.handler = new KtSt1St2TableHandler(this.element, this.options);
    this.handler.init_impl();
  };

  //////////////////////// KtPaginatedTable ///////////////////////////


  function KtPaginatedTableHandler(element, options) {
    this.inheritedFrom = KtTableHandler;
    this.inheritedFrom(element, options);
    this.pagination = null;
  }

  KtPaginatedTableHandler.prototype = new KtTableHandler();
  KtPaginatedTableHandler.prototype.init_impl = function() {
    // call parent's method
    KtTableHandler.prototype.init_impl.call(this);
  };

  KtPaginatedTableHandler.prototype._ajax_load_all_table_data_handler = function(data) {
    $("#" + this.table_id + "_table").find(".loading-row").remove();
    $("#t" + this.table_id + " a[id='prev']").unbind('click');
    $("#t" + this.table_id + " a[id='next']").unbind('click');

    this.data = data;
    // TODO: make the number of paginated rows customizable.
    this.pagination = new Pagination(this.data, this.table_id, this.options['num_rows']);

    $("#" + this.table_id + "_table").ktTemplate('render', {
      list: this._format_data(this.pagination.getData(0))
    });
    this._bind_table_events();
  };

  function KtPaginatedHeader(show_frame) {
    if (show_frame == true) {
      var template_str = "<div class='k-box'> \
        <div class='k-box-head'> \
        <div class='k-box-head-info'>\
        <h1>[=title]</h1> \
        <a href='#' index='[=index]' class='help' title='[=bubble_text]'>help</a> \
        <a href='#' title='Export to CSV' class='k-box-icon export_link' id='[=data_export_id]'>&nbsp;</a>\
      </div> \
      </div>\
        <div class='k-box-body'> \
        <table class='k-table' id='[=table_id]_table' cellpadding=\"0\" cellspacing=\"0\" border=\"0\"  id=\"data\" style=\"width: 100%\"> \
        <tr class=\"head-tr left-border\"> \
        <td class='lit first-coln [=first_coln.coln_type] [=first_coln.coln_class]' decimal='[=first_coln.coln_decimal]' coln_id='[=first_coln.coln_id]'><small>[=first_coln.coln_name]</small></td>\
      [[for coln in coln_list]] \
        <td class='lit [=coln.coln_type] [=coln.coln_class]' decimal='[=coln.coln_decimal]' coln_id='[=coln.coln_id]'><small>[=coln.coln_name]</small></td>\
      [[endfor]]\
      </tr>\
      </table>\
        <ul>\
        <li class=\"first\"><a id='prev' class=\"blank\" href='#' title=\"Previous\"><img class=\"prev_pagin_page\" src=\"/static/images/arrowleft3.png\" style=\"display:none;\"></img></a></li>\
        <li class=\"first\"><img class=\"prev_pagin_page_inactive\" src=\"/static/images/arrowleft3-disabled.png\"></img></li>\
        <li><span class='table_pagination'>Page&nbsp;</span><span class='table_pagination' id='curr_page'></span><span class='table_pagination'>&nbsp;of&nbsp;</span><span id='max_page'' class='table_pagination'></span></li>\
        <li><a id='next' class=\"blank\" href='#' title=\"Next\"><img class=\"next_pagin_page\" src=\"/static/images/arrowright3.png\"></img></a></li>\
        <li><img class=\"next_pagin_page_inactive\" src=\"/static/images/arrowright3-disabled.png\" style=\"display:none;\"></img></li>\
      </ul>\
      </div>\
      </div>";
    } else {
      var template_str = "<div class='k-box'> \
        <div class='k-box-body'> \
        <table class='k-table' id='[=table_id]_table' cellpadding=\"0\" cellspacing=\"0\" border=\"0\"  id=\"data\" style=\"width: 100%\"> \
        <tr class=\"head-tr left-border\"> \
        <td decimal='[=first_coln.coln_decimal]' class='lit first-coln [=first_coln.coln_type] [=first_coln.coln_class]' coln_id='[=first_coln.coln_id]'><small>[=first_coln.coln_name]</small></td>\
      [[for coln in coln_list]] \
        <td decimal='[=coln.coln_decimal]' class='lit [=coln.coln_type] [=coln.coln_class]' coln_id='[=coln.coln_id]'><small>[=coln.coln_name]</small></td>\
      [[endfor]]\
      </tr>\
      </table>\
        <ul>\
        <li class=\"first\"><a id='prev' class=\"blank\" href='#' title=\"Previous\"><img class=\"prev_pagin_page\" src=\"/static/images/arrowleft3.png\" style=\"display:none;\"></img></a></li>\
        <li class=\"first\"><img class=\"prev_pagin_page_inactive\" src=\"/static/images/arrowleft3-disabled.png\"></img></li>\
        <li><span class='table_pagination'>Page&nbsp;</span><span class='table_pagination' id='curr_page'></span><span class='table_pagination'>&nbsp;of&nbsp;</span><span id='max_page'' class='table_pagination'></span></li>\
        <li><a id='next' class=\"blank\" href='#' title=\"Next\"><img class=\"next_pagin_page\" src=\"/static/images/arrowright3.png\"></img></a></li>\
        <li><img class=\"next_pagin_page_inactive\" src=\"/static/images/arrowright3-disabled.png\" style=\"display:none;\"></img></li>\
      </ul>\
      </div>\
      </div>";
    }
    return template_str;
  };

  KtPaginatedTableHandler.prototype._get_table_header = function(show_frame) {
    return KtPaginatedHeader(show_frame);
  };

  KtPaginatedTableHandler.prototype._bind_table_events = function() {
    this.show_hide_prev_next_btn();
    this.bind_prev_click();
    this.bind_post_click();
    $("#t" + this.table_id + " span[id='curr_page']").text(this.pagination.page + 1);
    $("#t" + this.table_id + " span[id='max_page']").text(this.pagination.max_page);
  };

  KtPaginatedTableHandler.prototype.show_hide_prev_next_btn = function() {
    var element_id = this.element[0].id;
    if (this.pagination.page == 0) {
      //deactivate prev
      $('#' + element_id + " .prev_pagin_page_inactive").show();
      $("#" + element_id + " .prev_pagin_page").hide();
    } else {
      $("#" + element_id + " .prev_pagin_page_inactive").hide();
      $("#" + element_id + " .prev_pagin_page").show();
    }

    if (this.pagination.page == this.pagination.max_page - 1) {
      $("#" + element_id + " .next_pagin_page_inactive").show();
      $("#" + element_id + " .next_pagin_page").hide();
    } else {
      $("#" + element_id + " .next_pagin_page_inactive").hide();
      $("#" + element_id + " .next_pagin_page").show();
    }
  };

  KtPaginatedTableHandler.prototype.bind_prev_click = function() {
    var this_obj = this;
    var $header_td_obj = $($(this._gen_table_id() + " td")[0]);
    var og_height = $header_td_obj.height();
    $("#t" + this.table_id + " a[id='prev']").unbind('click');
    $("#t" + this.table_id + " a[id='prev']").click(

      function() {
        this_obj._clear_content();
        var data = this_obj.pagination.getPrevData();
        $(this_obj._gen_table_id()).ktTemplate('render', {
          list: this_obj._format_data(data)
        });
        this_obj.show_hide_prev_next_btn();
        $("#t" + this_obj.table_id + " span[id='curr_page']").text(this_obj.pagination.page + 1);

        return false;
      });
    $header_td_obj.height(og_height); //hack to work around the column shrinkage problem
  };

  KtPaginatedTableHandler.prototype.bind_post_click = function() {
    var this_obj = this;
    var $header_td_obj = $($(this._gen_table_id() + " td")[0]);
    var og_height = $header_td_obj.height();
    $("#t" + this_obj.table_id + " a[id='next']").unbind('click');
    $("#t" + this_obj.table_id + " a[id='next']").click(

      function() {
        this_obj._clear_content();
        var data = this_obj.pagination.getNextData();
        $(this_obj._gen_table_id()).ktTemplate('render', {
          list: this_obj._format_data(data)
        });
        this_obj.show_hide_prev_next_btn();
        $("#t" + this_obj.table_id + " span[id='curr_page']").text(this_obj.pagination.page + 1);
        return false;
      });
    $header_td_obj.height(og_height); //hack to work around the column shrinkage problem
  };

  KtPaginatedTableHandler.prototype._sort = function(coln_id, data) {
    //do some bookkeeping first
    this.sort_dict[coln_id] = !this.sort_dict[coln_id];

    this._sort_impl(coln_id, data);
    var curr_page = this.pagination.page;
    this.pagination = new Pagination(this.data, this.table_id, this.options['num_rows']); //TODO: customize how many is showing per paginated page.
    this.pagination.page = curr_page;
    // repopulate data
    this._clear_content();
    $(this._gen_table_id()).ktTemplate('render', {
      list: this._format_data(this.pagination.getData(curr_page))
    });
  };

  function KtPaginatedTable() {
    this.inheritedFrom = KtTable;
  }

  KtPaginatedTable.prototype = new KtTable();
  KtPaginatedTable.prototype._init = function() {
    this.handler = new KtPaginatedTableHandler(this.element, this.options);
    this.handler.init_impl();
  };

  ////////////////// KtPaginatedTableNotPermitted //////////////////////

  function KtPaginatedTableNotPermittedHandler(element, options) {
    this.inheritedFrom = KtNotPermittedTableHandler;
    this.inheritedFrom(element, options);
    this.pagination = null;
  }

  KtPaginatedTableNotPermittedHandler.prototype = new KtNotPermittedTableHandler();


  KtPaginatedTableNotPermittedHandler.prototype._get_table_header = function(show_frame) {
    return KtPaginatedHeader(show_frame);
  };

  function KtPaginatedTableNotPermitted() {
    this.inheritedFrom = KtNotPermittedTable;
  }

  KtPaginatedTableNotPermitted.prototype = new KtNotPermittedTable();
  KtPaginatedTableNotPermitted.prototype._init = function() {
    this.handler = new KtPaginatedTableNotPermittedHandler(this.element, this.options);
    this.handler.init_impl();
    $(this.element).attr("not_permitted", true);
  };

  //////////////////////// KtSubTabTable ///////////////////////////


  function KtSubTabTableHandler(element, options) {
    this.inheritedFrom = KtTableHandler;
    this.inheritedFrom(element, options);
    this.pagination = null;
  }

  KtSubTabTableHandler.prototype = new KtTableHandler();

  KtSubTabTableHandler.prototype._highlight_selected_tab = function(tab_str) {
    if (tab_str == undefined) {
      //pick the first one
      $($("#t" + this.table_id + " div.k-box-body a")[0]).addClass('active');
    } else {
      $("#t" + this.table_id + " a[subTab='" + tab_str + "']").addClass('active');
    }
  };

  KtSubTabTableHandler.prototype._bind_sub_tab_events = function() {
    var _this_obj = this;
    $("#t" + this.table_id + " div.k-box-body a").click(
      function() {
        var tab_str = $($(this).children()[0]).text();
        var url = np_get_table_url(_this_obj, _this_obj.category, _this_obj.tab, _this_obj.page, _this_obj.table_id) + '&table_sub_tab=' + escape(tab_str);
        var element = this;
        ktAjaxWrapper({
          type: 'GET',
          url: url,
          traditional: true,
          dataType: 'json',
          success: function(data, textStatus) {
            _this_obj.options['coln'] = data['__colns__'];
            $(_this_obj.element).ktTemplate('clear');
            _this_obj._construct_coln_header(true);
            _this_obj._bind_sub_tab_events(); // this is not a recursive call.
            _this_obj._setup_rows_template(data['__rows__']); //setup the table content template
            _this_obj._highlight_selected_tab(tab_str);
            _this_obj._setup_sorting();
            $(document).trigger('load_all_table_data', [_this_obj.table_id]);
          }
        });
        return false;
      });
  };

  KtSubTabTableHandler.prototype.init_impl = function() {
    // call parent's method
    KtTableHandler.prototype.init_impl.call(this);
    this._bind_sub_tab_events();
    this._highlight_selected_tab();

    var this_obj = this;
  };


  KtSubTabTableHandler.prototype._get_table_header = function(show_frame) {
    if (show_frame == true) {
      var template_str = "<div class='k-box'> \
	<div class='k-box-head'> \
	<div class='k-box-head-info'>\
	<h1>[=title]</h1> \
        <a href='#' index='[=index]' class='help' title='[=bubble_text]'>help</a> \
	<a href='#' title='Export to CSV' class='k-box-icon export_link' id='[=data_export_id]'>&nbsp;</a>\
      </div> \
	       </div>\
	       <div class='k-box-body'> \
                 <ul>\
		   [[for tab in subtab]]\
       	           <li><a href='#' subTab='[=tab]'><span>[=tab]</span></a></li>\
	           [[endfor]]\
	 	 </ul>\
 		 <table class='k-table' id='[=table_id]_table' \
			cellpadding=\"0\" cellspacing=\"0\" border=\"0\"  id=\"data\" style=\"width: 100%\"> \
		   <tr class=\"head-tr left-border\"> \
		     <td decimal='[=first_coln.coln_decimal]' class='lit first-coln [=first_coln.coln_type] [=first_coln.coln_class]' coln_id='[=first_coln.coln_id]'><small>[=first_coln.coln_name]</small></td>\
                     [[for coln in coln_list]] \
                     <td decimal='[=coln.coln_decimal]' class='lit [=coln.coln_type] [=coln.coln_class]' coln_id='[=coln.coln_id]'><small>[=coln.coln_name]</small></td>\
	             [[endfor]]\
		   </tr>\
		 </table>\
	       </div>\
	   </div>";
        } else {

        }
        return template_str;
    };

    function KtSubTabTable() {
        this.inheritedFrom = KtTable;
    };

    KtSubTabTable.prototype = new KtTable();
    KtSubTabTable.prototype._init = function() {
        this.handler = new KtSubTabTableHandler(this.element, this.options);
        this.handler.init_impl();
    };

    //////////////////////// KtSubTabTableRefactored ///////////////////////////


    function KtSubTabTableRefactoredHandler(element, options) {
        this.inheritedFrom = KtTableHandler;
        this.inheritedFrom(element, options);
    }
    KtSubTabTableRefactoredHandler.prototype = new KtTableHandler();

    KtSubTabTableRefactoredHandler.prototype._get_table_header = function(show_frame) {
        var template_str = "<div id='[=table_id]_secondary_data' class='k-box-secondary'></div>\
        <div class='k-box'> \
	       <div class='k-box-head'> \
	          <div class='k-box-head-info'>\
		      <h1>[=title]</h1> \
                      <a href='#' index='[=index]' class='help' title='[=bubble_text]'>help</a> \
		      <a href='#' title='Export to CSV' class='k-box-icon export_link' id='[=data_export_id]'>&nbsp;</a>\
		  </div> \
	       </div>\
	       <div class='k-box-body'> \
                 <ul>\
		   [[for tab in subtab]]\
       	           <li><a href='#' subTab='[=tab.tab_id]' class='subtab_btn'>[=tab.tab_str]</a><span>|</span></li>\
	           [[endfor]]\
	 	 </ul>\
               </div>\
       </div>";
        return template_str;
    };

    KtSubTabTableRefactoredHandler.prototype._load_all_table_data_handler = function() {
        //do nothing.
    };

    KtSubTabTableRefactoredHandler.prototype._export_handler = function() {
        var subtab_id = $("#t" + this.table_id + " a.active").attr('subtab');
        var data_export_url = np_get_table_export_url(this, this.category, this.tab, this.page, subtab_id);
        document.location = data_export_url;
        return false;
    };

    KtSubTabTableRefactoredHandler.prototype._push_secondary_data_to_parent = function(tab_id_str) {
        var $secondary_data_dom = $("#" + tab_id_str + "_secondary_data");
        if ($secondary_data_dom.length > 0) {
            $("#" + this.table_id + "_secondary_data").text($secondary_data_dom.text());
        }
    };

    KtSubTabTableRefactoredHandler.prototype._highlight_selected_tab = function(tab_id_str) {
        if (tab_id_str == undefined) {
            //pick the first one
            $($("#t" + this.table_id + " div.k-box-body a")[0]).addClass('active');
            tab_id_str = $($("#t" + this.table_id + " div.k-box-body a")[0]).attr('subtab');
        } else {
            $("#t" + this.table_id + " a[subTab='" + tab_id_str + "']").addClass('active');
        }

        var _this_obj = this;
        $(document).bind('onload_all_table_data', function(event, msg) {
            if (tab_id_str == msg) {
                _this_obj._push_secondary_data_to_parent(tab_id_str);
            }
        });
    };

    KtSubTabTableRefactoredHandler.prototype._unhighlight_selected_tab = function(tab_id_str) {
        $("#t" + this.table_id + " a[subTab='" + tab_id_str + "']").removeClass('active');
    };

    KtSubTabTableRefactoredHandler.prototype._bind_sub_tab_events = function() {
        var _this_obj = this;
        $("#t" + this.table_id + " div.k-box-body a").click(

        function() {
            var tab_table_index_list = _this_obj.options['table_widget_ids'];
            var tab_table_index_list_len = tab_table_index_list.length;
            for (var i = 0; i < tab_table_index_list_len; i++) {
                var sub_tab_index = tab_table_index_list[i]['index'];
                if (sub_tab_index == $(this).attr('subtab')) {
                    $("#t" + sub_tab_index).show();
                    _this_obj._highlight_selected_tab(sub_tab_index);
                    _this_obj._push_secondary_data_to_parent(sub_tab_index + "_tab");
                } else {
                    $("#t" + sub_tab_index).hide();
                    _this_obj._unhighlight_selected_tab(sub_tab_index);
                }
            }

            $.sparkline_display_visible();
            return false;
        });
    };

    KtSubTabTableRefactoredHandler.prototype.init_impl = function() {
        KtTableHandler.prototype.init_impl.call(this);

        var tab_table_index_list = this.options['table_widget_ids'];
        var tab_table_index_list_len = tab_table_index_list.length;
        for (var i = 0; i < tab_table_index_list_len; i++) {
            $("#t" + this.table_id + " div[class='k-box-body']").append($("#t" + tab_table_index_list[i]['index']));
        }
        this._bind_sub_tab_events();
        this._highlight_selected_tab();
        // unbind load_all_table_data
        // remove last divider
        $(".subtab_btn:last").next().remove();        
    };

    function KtSubTabTableRefactored() {
        //     this.handler = new KtSubTabTableHandler(this.element, this.options);
        this.inheritedFrom = KtTable;
    }


    KtSubTabTableRefactored.prototype._init = function() {
        this.handler = new KtSubTabTableRefactoredHandler(this.element, this.options);
        this.handler.init_impl();
    };


    //////////////////////// KtFiltersForDynTable ///////////////////////////


    function KtFilterForDynTableHandler(element, options) {
        this.inheritedFrom = KtTableHandler;
        this.inheritedFrom(element, options);
    };

    KtFilterForDynTableHandler.prototype = new KtTableHandler();

    KtFilterForDynTableHandler.prototype._delete_row_handler = function(dom) {
        var filter_id = $(dom).attr('filter_id');
        var _this_obj = this;
        ktAjaxWrapper({
            type: "GET",
            url: "/dashboard/filter/ajax_delete_filter/",
            data: {
                id: filter_id
            },
            success: function(msg) {
                _this_obj._trigger_load_table_data();
            }
        });
        return false;
    };

  KtFilterForDynTableHandler.prototype._bind_table_events = function() {
    var $remove_btns = $("#" + this.table_id + "_table a.remove_btn");
    var this_obj = this;
    if ($remove_btns.length > 0) {

      var idx = 0;

      $remove_btns.each(function() {
        $(this).click(function(event) {
          this_obj._delete_row_handler(event.target);
        });
        
        $(this).attr('filter_id', this_obj.data[idx]['filter_id']);
        idx++;
      });
    }
  };

    function KtFilterForDynTable() {
        this.inheritedFrom = KtTable;
    };
    KtFilterForDynTable.prototype = new KtTable();
    KtFilterForDynTable.prototype._init = function() {
        this.handler = new KtFilterForDynTableHandler(this.element, this.options);
        this.handler.init_impl();
    };


    ////////////////////////  ktFilterForSt1St2Table ///////////////////////////


    function KtFilterForSt1St2TableHandler(element, options) {
        this.inheritedFrom = KtFilterForDynTableHandler;
        this.inheritedFrom(element, options);
        this.initial_sorting = 'st1';
    }

    KtFilterForSt1St2TableHandler.prototype = new KtFilterForDynTableHandler();

    KtFilterForSt1St2TableHandler.prototype._delete_row_handler = function(dom) {
        var filter_id = $(dom).attr('filter_id');
        var _this_obj = this;
        ktAjaxWrapper({
            type: "GET",
            url: "/dashboard/filter/ajax_delete_filter/",
            data: {
                id: filter_id
            },
            success: function(msg) {
                var $st1_dom = $(dom).parent().next().next();
                var $st2_dom = $st1_dom.next();
                var $st3_dom = $st2_dom.next();
                var st1_text = $st1_dom.text();
                var st2_text = $st2_dom.text();
                var st3_text = $st3_dom.text();

                var table_id = '#t' + _this_obj.table_id.split("_")[0];
                var table_widget_ids = $(table_id).ktSubTabTableRefactored("option", "table_widget_ids");
                for (var i = 0; i < table_widget_ids.length; i++) {
                    $(document).trigger('delete_st2st3', [table_widget_ids[i]['index'], st1_text, st2_text, st3_text]);
                }
                _this_obj._trigger_load_table_data();
            } // success
        });
        return false;
    };

    function KtFilterForSt1St2Table() {
        this.inheritedFrom = KtFilterForDynTable;
    }
    KtFilterForSt1St2Table.prototype = new KtFilterForDynTable();
    KtFilterForSt1St2Table.prototype._init = function() {
        this.handler = new KtFilterForSt1St2TableHandler(this.element, this.options);
        this.handler.init_impl();
    };

    var kt_table = new KtTable();
    var kt_not_permitted_table = new KtNotPermittedTable();
    var kt_st1_st2_table = new KtSt1St2Table();
    var kt_paginated_table = new KtPaginatedTable();
    var kt_paginated_table_not_permitted = new KtPaginatedTableNotPermitted();
    var kt_sub_tab_table = new KtSubTabTable();
    var kt_sub_tab_table_refactored = new KtSubTabTableRefactored();
    var kt_filter_for_dyn_table = new KtFilterForDynTable();
    var kt_filter_for_st1_st2_table = new KtFilterForSt1St2Table();

    $.widget("kt.ktTable", kt_table);
    $.widget("kt.ktNotPermittedTable", kt_not_permitted_table);
    $.widget("kt.ktSt1St2Table", kt_st1_st2_table);
    $.widget("kt.ktPaginatedTable", kt_paginated_table);
    $.widget("kt.ktPaginatedTableNotPermitted", kt_paginated_table_not_permitted);
    $.widget("kt.ktSubTabTable", kt_sub_tab_table);
    $.widget("kt.ktSubTabTableRefactored", kt_sub_tab_table_refactored);
    $.widget("kt.ktFilterForDynTable", kt_filter_for_dyn_table);
    $.widget("kt.ktFilterForSt1St2Table", kt_filter_for_st1_st2_table);

    $.kt.ktTable.getter = ["get_handler"];
    $.kt.ktSt1St2Table.getter = ["get_handler"];
    $.kt.ktPaginatedTable.getter = ["get_handler"];
    $.kt.ktSubTabTable.getter = ["get_handler"];
    $.kt.ktSubTabTableRefactored.getter = ["get_handler"];
})(jQuery);

