/*
  JavaScript used for drop-down widgets
  
  (C) Copyright 2009, Compete Inc.
  
  Depends on:
    JQuery
  
  Raises a event named dropdown_selected passing dropdown DIV ID and the selected item as event parameters
  ie. Handler signature should be: function handler(event, dropdown_div, selected_item)...
  
  Example Usage:
    1. In your template create a empty an empty DIV with the class of type dropdown.
       The dropdown class style's the element as absolutely positioned. You will need to
       wrap the dropdown DIV in another DIV to allow for correct repositioning.
       
         eg:
           <div id="my-dropdowns">
             <div id="my-dropdown1" class="dropdown"></div>
           </div>
    
    2. In your page's JavaScript, call the add_group or add_item functions to populate
       the dropdown with groups and/or items. You can pass HTML within the content to
       customize the content style.
       
         eg:
           add_group('my-dropdown1', 'Group 1 <span class="custom">(Custom Style)</span>', 'group1');
           add_group('my-dropdown1', 'Group 2 <span class="custom">(Custom Style)</span>', 'group1');
           
           add_list_item('my-dropdown1', 'item_id_1', 'Item 1 Content', 'Item 1 <span class="title">Title</span>', 'custom-class', 'group1');
           add_list_item('my-dropdown1', 'item_id_2', 'Item 2 Content', 'Item 2 <span class="title">Title</span>', '', 'group1');
           add_list_item('my-dropdown1', 'item_id_2', 'Item 2 Group 2 Content', 'Item 2 <span class="title">Title</span>', '', 'group2');
    
    3. Bind to the "item_selected" event. This will be triggered by the dropdown whenever an item
       is selected. The event passes the Dropdown DIV ID and the Item Object selected.
       
         eg.
           $j(document).bind('item_selected', item_selected_handler);
       
    4. Your handler function for the "item_selected" event should contain the signature:
         function item_selected_handler(event, dropdown_div_id, item_object)
       
       Your handler function must also check if the dropdown_div_id is the DIV it needs to
       handle the event for. If not it should return false.
       
       Lastly, your handler should also stop propagation of the event if the event has been
       successfully handled.
       
         eg.
           function item_selected_handler(event, dropdown_div_id, item_object) {
             // Check if this is the DIV we want to handle the event for
             if (dropdown_div_id != 'mydropdown1') { return false; }
             
             // Handle the event
             alert(item_object.id);
             
             // Stop the propagation of the event since we have handled it
             if (event) { event.stopPropagation(); }
           }
*/

/* we (currently, and temporarily) use both prototype and jquery, so we
   need to replace $ in jquery w/ $j
*/ 

var _dropdowns = new Object();


/*
 * Initialize Handler
 */ 
$j(document).ready(function() {
  // Create the shell elements for each dropdown
  _create_dropdown_shell();
  
  // Bind the event handlers for each dropdown element
  $j('.dropdown').live('click', _open_dropdown_handler);
});

/*
 * Public interface functions
 */ 
function add_group(dropdown_div_id, group_title, group_id, divider, options) {
  /*
     Adds a group to the list within the dropdown_div. Groups can be used to give a custom title
     to a group of dropdown items
       dropdown_div_id - The ID of the dropdown DIV
       group_title     - The item to add. Can contain HTML (ie. span elements for custom formatting)
       group_id        - The ID of the group this need not be unique as it's prefixed with the DIV ID above
       divider         - (Optional) Either above/below. Will place a divider above or below the group
       options         - (Optional) Object with the following attributes:
                            z_index: z-index to set for the dropdown frame. otherwise, default of 999 is used
   */
  var options = (options == undefined) ? {} : options;
  
  // Get the dropdown DIV. If it doesn't exist then do nothing
  var _dd_obj = _get_drop_down_object(dropdown_div_id);
  if (!_dd_obj) { return false; }
  
  // append the html to the container
  var _drop_list_div = '#' + dropdown_div_id + '-content';
  var _inner_html = '';
  var _divider = (divider == undefined) ? '' : divider;
  var _group_elem_id = dropdown_div_id + '-' + group_id + '-li';
  
  if (_divider == 'above') {
    _inner_html += '<hr id="' + dropdown_div_id + '-' + group_id + '-hr" />';
  }
  _inner_html += '<li id="' + dropdown_div_id + '-' + group_id + '-li">';
  _inner_html += '  <ul id="' + dropdown_div_id + '-' + group_id + '-content">';
  _inner_html += '    <li class="group-title">';
  _inner_html += group_title;
  _inner_html += '    </li>';
  _inner_html += '  </ul>';
  _inner_html += '</li>';
  if (_divider == 'below') {
    _inner_html += '<hr id="' + dropdown_div_id + '-' + group_id + '-hr" />';
  }
  
  $j(_drop_list_div).append(_inner_html);
  
  // set any styles or other options
  if (options.z_index) { $j("#"+dropdown_div_id).css("zIndex", options.z_index); }
  
  if ($j('#' + dropdown_div_id).hasClass('dropdown-disabled')) { enable_dropdown(dropdown_div_id); }
  
  // Create a JS object for the group
  _dd_group = new Object();
  _dd_group.id = _group_elem_id;
  _dd_group.group_id = group_id;
  _dd_group.dropdown_id = dropdown_div_id;
  _dd_group.title = group_title;
  _dd_group.divider = _divider;
  
  // Add it to the dropdown's item list
  if (_dd_obj.groups == undefined) { _dd_obj.groups = new Array(); }
  _dd_obj.groups.length++;
  _dd_obj.groups[_conv_to_string(_group_elem_id)] = _dd_group;
  
  return true;
}

function add_list_item(dropdown_div_id, item_id, item, item_title, item_class, group_id) {
  /*
     Adds a item to the list within the dropdown_div
       dropdown_div_id - The ID of the dropdown DIV
       item_id         - The ID of the item element
       item            - The text for item to add. Can contain HTML (ie. span elements for custom formatting, etc)
       item_title      - (optional) The title to display when the item is selected. Will default to item.
                           Can contain HTML (ie. span elements for custom formatting, etc)
       item_class      - (optional) A class/classes for additional custom formatting the item.
       group_id        - (optional) The ID of the group in which to add the item
   */
  // Get the dropdown DIV. If it doesn't exist then do nothing
  var _dd_obj = _get_drop_down_object(dropdown_div_id);
  if (!_dd_obj) { return false; }
  
  var _group_id = (group_id == undefined) ? '' : group_id;
  var _item_title = (item_title == undefined) ? item : item_title;
  var _item_class = (item_class == undefined) ? '' : item_class;
  var _content_block = '#' + dropdown_div_id + ((_group_id != '') ? ('-' + _group_id) : '') + '-content';
  var _item_elem_id = dropdown_div_id + ((_group_id != '') ? ('-' + _group_id) : '') + '-' + item_id;
  var _inner_html = '<li id="' + _item_elem_id + '" class="' + item_class + '">' + item_title + '</li>';
  
  $j(_content_block).append(_inner_html);
  
  // Create a JS object for the item
  _dd_item = new Object();
  _dd_item.id = _item_elem_id;
  _dd_item.item_id = item_id;
  _dd_item.dropdown_id = dropdown_div_id;
  _dd_item.content = item;
  _dd_item.title = _item_title;
  _dd_item.group_id = _group_id;
  _dd_item.item_class = _item_class;
  
  // Add it to the dropdown's item list
  if (_dd_obj.items == undefined) { _dd_obj.items = new Array(); }
  _dd_obj.items.length++;
  _dd_obj.items[_conv_to_string(_item_elem_id)] = _dd_item;
  
  // Bind event handlers to this item
  $j('#' + _item_elem_id).bind('click', { dropdown: dropdown_div_id }, _item_click_handler);
  $j('#' + _item_elem_id).hover(function () {
    // On Element Hover In Event
    if (!$j(this).hasClass('item-disabled')) {
      $j(this).addClass('dropdown-hovered');
    }
  }, function () {
    // On Element Hover Out Event
    if ($j(this).hasClass('dropdown-hovered')) {
      $j(this).removeClass('dropdown-hovered');
    }
  });
  
  if ($j('#' + dropdown_div_id).hasClass('dropdown-disabled')) { enable_dropdown(dropdown_div_id); }
  
  return true;
}

function enable_dropdown(dropdown_div_id) {
  /*
     Enables a disabled dropdown div
       dropdown_div_id - The ID of the dropdown DIV
   */
  $j('#' + dropdown_div_id).removeClass('dropdown-disabled');
}

function disable_dropdown(dropdown_div_id) {
  /*
     Disables a dropdown div, preventing it from opening regardless of it's content
       dropdown_div_id - The ID of the dropdown DIV
   */
  $j('#' + dropdown_div_id).addClass('dropdown-disabled');
}

function select_item(dropdown_div_id, item_id, group_id) {
  /*
     Adds a item to the list within the dropdown_div
       dropdown_div_id - The ID of the dropdown DIV
       item_id         - The ID of the item element
       group_id        - The ID of the group in which the item exists
                         (Required if the item is part of a group)
   */
  // Get the dropdown DIV. If it doesn't exist then do nothing
  var _dropdiv = _get_drop_down_object(dropdown_div_id);
  if (!_dropdiv) { return false; }
  
  var _group_id = (group_id == undefined) ? '' : group_id;
  var _item_elem_id = dropdown_div_id + ((_group_id != '') ? ('-' + _group_id) : '') + '-' + item_id;
  var _item = _dropdiv.items[_conv_to_string(_item_elem_id)];
  
  if (!_item) { return false; }
  
  _update_dropdown(dropdown_div_id, _item);
  
  // Trigger a event to indicate that the dropdown changed
  $j(document).trigger('item_selected', [ dropdown_div_id, _item ]);
  
  return true;
}

function disable_item(dropdown_div_id, item_id, group_id) {
  /*
     Disables a item to the list within the dropdown_div
       dropdown_div_id - The ID of the dropdown DIV
       item_id         - The ID of the item element
       group_id        - The ID of the group in which the item exists
                         (Required if the item is part of a group)
   */
  // Get the dropdown DIV. If it doesn't exist then do nothing
  var _dropdiv = _get_drop_down_object(dropdown_div_id);
  if (!_dropdiv) { return false; }
  
  var _group_id = (group_id == undefined) ? '' : group_id;
  var _item_elem_id = dropdown_div_id + ((_group_id != '') ? ('-' + _group_id) : '') + '-' + item_id;
  
  if (!_dropdiv) { return; }
  if (_dropdiv.items == undefined) { return; }
  
  var _item = get_item_by_id(dropdown_div_id, _item_elem_id);
  
  if (!$j('#' + _item.id).hasClass('item-disabled')) { $j('#' + _item.id).addClass('item-disabled'); }
}

function get_item_by_id(dropdown_div_id, id) {
  /*
     Returns the internal item given the item's DOM Element ID
       dropdown_div_id - The ID of the dropdown DIV
       item_id         - The ID of the item element
   */
  var _dropdown_div = _get_drop_down_object(dropdown_div_id)
  
  if (_dropdown_div) {
    return _dropdown_div.items[_conv_to_string(id)];
  } else {
    return null;
  }
}

function get_item_by_item_id(dropdown_div_id, item_id) {
  /*
     Returns the internal item given the item's id as passed when the item was created
       dropdown_div_id - The ID of the dropdown DIV
       item_id         - The ID of the item element
   */
  var _dropdown_div = _get_drop_down_object(dropdown_div_id)
  var _idx = null;
  
  for (_idx in _dropdown_div.items) {
    var _item = _dropdown_div.items[_idx];
    
    if (typeof(_item) == 'object') {
      if (_item.item_id == item_id) { break; }
    } else {
      _idx = null;
    }
  }
  
  return (_idx != null) ? _dropdown_div.items[_idx] : null;
}

function enable_all_items() {
  /*
     Enables all items that have been disabled. This affects ALL dropdowns
   */
  $j('.item-disabled').removeClass('item-disabled');
}

function get_group_count(dropdown_div_id) {
  /*
     Returns the number of groups in a dropdown
       dropdown_div_id - The ID of the dropdown DIV
   */
  var _dd_obj = _get_drop_down_object(dropdown_div_id);
  if ((!_dd_obj) || (!_dd_obj.groups)) { return 0; }
  
  return _dd_obj.groups.length;
}

function group_exists(dropdown_div_id, group_id) {
  /*
     Returns true if the group exists in the DOM, false otherwise
       dropdown_div_id - The ID of the dropdown DIV
       group_id        - The ID of the group to check
   */
  var _group_id = dropdown_div_id + '-' + group_id + '-li';
  return ($j('#' + _group_id).length > 0);
}

function hide_group(dropdown_div_id, group_id) {
  /*
     Hides a group within the dropdown_div
       dropdown_div_id - The ID of the dropdown DIV
       group_id        - The ID of the group in which the item exists
   */
  var _group_id = dropdown_div_id + '-' + group_id + '-li';
  if (!$j('#' + _group_id).hasClass('hidden')) { $j('#' + _group_id).addClass('hidden'); }
}

function unhide_group(dropdown_div_id, group_id) {
  /*
     Unhides a group within the dropdown_div
       dropdown_div_id - The ID of the dropdown DIV
       group_id        - The ID of the group in which the item exists
   */
  var _group_id = dropdown_div_id + '-' + group_id + '-li';
  if ($j('#' + _group_id).hasClass('hidden')) { $j('#' + _group_id).removeClass('hidden'); }
}

function set_default_title(dropdown_div_id, title) {
  if (_get_drop_down_object(dropdown_div_id)) {
    _get_drop_down_object(dropdown_div_id).title = title;
    clear_selected(dropdown_div_id);
  }
}

function clear_selected(dropdown_div_id) {
  /*
     Clears a selection within a dropdown and displays he default message    
       dropdown_div_id - The ID of the dropdown DIV
   */
  if (_get_drop_down_object(dropdown_div_id)) {
    $j('#' + dropdown_div_id + '-title').html(_get_drop_down_object(dropdown_div_id).title)
  }
}

function get_selected_item(dropdown_div_id) {
  /*
     Returns the selected item within a dropdown.
       dropdown_div_id - The ID of the dropdown DIV
   */
  if (!_get_drop_down_object(dropdown_div_id)) { return null; }
  
  var _title = $j.trim($j('#' + dropdown_div_id + '-title').html());
  var _items = _get_drop_down_object(dropdown_div_id).items;
  var _item = null;
  
  for (_item in _items) {
    if (typeof(_items[_item]) == 'object') {
      if (_title == _items[_item].title) { break; }
    }
    
    _item = null;
  }
  
  if (_item) {
    return _items[_item];
  } else {
    return null;
  }
}

/*
 * Internal Event handlers
 */ 
function _get_dropdown(target_element) {
  // Walk the DOM to find the parent dropdown
  while (target_element) {
    if ($j.inArray("dropdown", target_element.className.split(' ')) != -1) { break; }
    target_element = target_element.parentNode;
  }
  
  return target_element;
}

function _open_dropdown_handler(event) {
  var _target = _get_dropdown(this);
  var _drop_list_div = '#' + _target.id + ' > .dropdown-list';
  
  // If there are no items or the dropdown is disabled, do not open
  if ((_get_drop_down_object(this.id).items == undefined) || ($j('#' + this.id).hasClass('dropdown-disabled'))) { return true; }
  
  if ($j(_drop_list_div).hasClass('hidden')) {
    // Hide all open dropdown lists
    $j('.dropdown-list').addClass('hidden');
    
    // Unhide the clicked dropdown
    $j(_drop_list_div).removeClass('hidden');
    $j('#' + _target.id).bind('mouseleave', _close_dropdown_handler);
  } else {
    // Hide the clicked dropdown
    $j(_drop_list_div).addClass('hidden');
  }
}

function _close_dropdown_handler(event) {
  var _target = this;
  var _timer = null;
  
  function _cancel_close(event) {
    if (_timer == null) { return; }
    clearInterval(_timer);
  }
  function _enable_close(event) {
    if (_timer != null) { clearInterval(_timer); _timer = null; }
    _timer = setInterval(_close_dropdown, 500);
  }
  function _close_dropdown(event) {
    if (_timer == null) { return; } else { clearInterval(_timer); _timer = null; }
    
    $j('.dropdown').unbind('mouseleave');
    $j('.dropdown').unbind('mouseenter');
    $j('.dropdown-list').addClass('hidden');
  }
  
  $j('.dropdown').unbind('mouseenter');
  $j('.dropdown').bind('mouseenter', _cancel_close);
  $j('.dropdown').bind('mouseleave', _enable_close);
  
  if (_timer != null) { clearInterval(_timer); _timer = null; }
  _timer = setInterval(_close_dropdown, 500);
}

function _item_click_handler(event) {
  /*
     Triggers a event when a item in the dropdown is selected. The client's event 
     handler should have the following signature:
       function handler(event, dropdown_div_id, item_object)
  */
  var _target = this;
  var _dropdown_div = event.data.dropdown;
  
  // If the item is disabled do nothing
  if ($j(_target).hasClass('item-disabled')) { return false; }
  
  _update_dropdown(_dropdown_div, _target);
  $j('.dropdown-list').addClass('hidden');
  
  // Trigger a event to indicate that the dropdown changed
  $j(document).trigger('item_selected', [ _dropdown_div, _target ]);
  
  if (event) { event.stopPropagation(); }
}


/*
 * Private functions
 */
function _conv_to_string(value) {
  return String(value.replace(/-/g, '_'))
}

function _get_drop_down_object(dropdown_id) {
  return _dropdowns[_conv_to_string(dropdown_id)];
}

function _create_dropdown_shell() {
  function _add_dropdown_object(dropdown_id) {
    var _dropdown = new Object();
    
    _dropdown.id = _conv_to_string(dropdown_id);
    _dropdown.title = _conv_to_string('<span class="default">&nbsp</span>');
    _dropdowns[_dropdown.id] = _dropdown;
  }

  $j('.dropdown').each(function () {
    var _inner_html = '';
    
    _inner_html += '<div id="' + this.id + '-title" class="dropdown-title"><span class="default">Select Item...</span></div>';
    _inner_html += '<div class="dropdown-list hidden">';
    _inner_html += '  <ul id="' + this.id + '-content">';
    _inner_html += '  </ul>';
    _inner_html += '</div>';
    
    this.innerHTML = _inner_html;
    _add_dropdown_object(this.id);
    
    $j(this).addClass('dropdown-disabled');
  });
}

function _update_dropdown(dropdown_div_id, selected_item) {
  _item = _get_drop_down_object(dropdown_div_id).items[_conv_to_string(selected_item.id)];
  $j('#' + dropdown_div_id + '-title').html(_item.title);
}



/*
 * Category Drop-downs
 *
 */
if (!cat_dropdown) { var cat_dropdown = {}; }

/*
 * Internal variables
 *
 */

// _dropdowns is a collection of dropdown objects containing only properties
// that are shared between the various methods inside cat_dropdown. This needs
// to be done to handle state for each dropdown created on a page.
// See the cat_dropdown.initialize code for details.
cat_dropdown._dropdowns = new Object();


/*
 * Internal Functions
 *
 */
cat_dropdown._getul = function (dropdown) {
  /*
   * This function creates the base UL DOM object that the widget uses as the
   * dropdown.
   *
   */
  var ddul = $j('ul#' + dropdown.ulid);
  if (ddul.length == 0) {
    ddul = $j('<ul id="' + dropdown.ulid + '"></ul>').addClass('mcdropdown_menu');
    $j('body').append(ddul);
  }
  
  return ddul;
}

cat_dropdown._add_to_menu = function (data, title, root_menu) {
  /*
   * This function creates the UL LI DOM object heirachy for the dropdown menu
   * used by the widget.
   *
   */
  var sub_menu = $j('<li></li>').html(title);
  var node = $j('<ul></ul>');
  
  // Build each sub-menu by recursively iterating through the json data
  $j.each(data, function(key, value) {
    if (typeof(value) == "object") {
      // We have a new sub-node. Create the root and recursively add items
      cat_dropdown._add_to_menu(value, key, node);
    } else {
      node.append($j('<li></li>').html(key).attr('rel', value).addClass('mc_child'));
    }
  });
  
  sub_menu.addClass('mc_parent');
  sub_menu.append(node);
  sub_menu = (root_menu == undefined) ? sub_menu : root_menu.append(sub_menu);
  
  return sub_menu;
}

cat_dropdown._json_handler = function (data) {
  /*
   * Handler for rendering the result JSON data
   *
   */
  var drop_elem_id = data['dropdown'];
  var dropdown = cat_dropdown._dropdowns[drop_elem_id];
  var groups = data['groups'];
  
  var ddul = cat_dropdown._getul(dropdown);
  ddul.empty();
  
  // Build the dropdown content based on the returned JSON heirachy
  if ((groups['Custom Categories'] != undefined) && (dropdown.show_custom)) {
    ddul.append(cat_dropdown._add_to_menu(groups['Custom Categories'], 'Custom Categories'));
  }
  if ((groups['Industry Categories'] != undefined) && (dropdown.level == 3)) {
    ddul.append(cat_dropdown._add_to_menu(groups['Industry Categories'], 'Industry Categories'));
  } else if ((groups['Industry Categories'] != undefined) && (dropdown.level == 2)) {
    $j.each(groups['Industry Categories'], function(key, value) {
      ddul.append(cat_dropdown._add_to_menu(value, key));
    });
  }
  if (groups['Behavioral Categories'] != undefined) {
    ddul.append(cat_dropdown._add_to_menu(groups['Behavioral Categories'], 'Behavioral Categories'));
  }
  
  // If a default value was set then select it in the dropdown
  if ((dropdown.currently_set_id != undefined) && dropdown.currently_set_id) {
    dropdown.mcdropdown.setValue(dropdown.currently_set_id, false);
  }
}

cat_dropdown._set_popup_value = function(drop_elem_id, value) {
  /*
   * Refreshes the dropdown category list based on show_custom and filter
   * settings specified when the widget was initialized/filtered.
   *
   * This function must be called after setting filter to apply it.
   *
   * This function will return self so that chaining can be performed.
   *
   * Parameters:
   *   drop_elem_id: The DOM ID of the element to refresh the category list for
   *
   *   value: The value to set in the popup
   *
   */
  $j('#' + drop_elem_id).parent().siblings('.popup').html(value);
  
  return true;
}

cat_dropdown._select_handler = function (drop_elem_id, id, value) {
  /*
   * Handler called by the widget when an item is selected from the dropdown.
   * This function will trigger a DOM event to indicate that the dropdown
   * changed.
   *
   * Parameters:
   *   drop_elem_id: The DOM ID of the element to refresh the category list for
   *
   *   id: The ID value of the item selected
   *
   *   value: The display value of the item selected
   *
   */
  var dropdown = cat_dropdown._dropdowns[drop_elem_id];
  if (!dropdown) { return false; }
  
  if (id != undefined) {
    if (value) { cat_dropdown._set_popup_value(drop_elem_id, value); }
    
    // If we have a valid ID then re-raise the event
    if (id > -1) {
      $j('#' + drop_elem_id).trigger('category_selected', [ id, drop_elem_id ]);
    }
  }
}

cat_dropdown._bind_hevents = function (domobject) {
  /*
   * Binds hoverin, hoverout and hide events to a dom object to handle how
   * the object hides itself after the user has hovered off. This is used to
   * hide the popup tooltip object when the user moves off the popup tooltip.
   *
   * Parameters:
   *   domobject: The DOM element to bind the events to. This can be retrieved
   *              by the using the construct "$j(selector).get(0)"
   *
   */
  if (domobject._hidepopup == undefined) {
    domobject._hidepopup = function () {
      $j(domobject).hide();
      $j.removeData(domobject, "ptimer");
    }
  }
  if (domobject._hoverin == undefined) {
    domobject._hoverin = function () {
      var tmr = $j.data(domobject, "ptimer");
      if (tmr) {
        clearTimeout(tmr);
        $j.removeData(domobject, "ptimer");
      }
    }
  }
  if (domobject._hoverout == undefined) {
    domobject._hoverout = function () {
      $j.data(domobject, "ptimer", setTimeout(domobject._hidepopup, 500));
    }
  }
  
  $j(domobject).hover(domobject._hoverin, domobject._hoverout);
}

cat_dropdown._bind_drop_event = function (domobject, mcdropdown) {
  /*
   * Binds a click event to the domobject to open the respective dropdown widget
   *
   * Parameters:
   *   domobject: The DOM element to bind the events to. This can be retrieved
   *              by the using the construct "$j(selector).get(0)"
   *
   */
  if (domobject._ddclick == undefined) {
    domobject._ddclick = function () {
      if ($j(domobject).attr('disabled') != 'disabled') {
        mcdropdown.openMenu();
      }
    }
  }
  
  $j(domobject).click(domobject._ddclick);
}

cat_dropdown._hover_in = function () {
  /*
   * Handles the hover-in event for the popup tooltip
   *
   */
  var popup = $j(this).parent().siblings('.popup');
  
  function show_popup() {
    popup.show().bgiframe();
    
    $j.removeData(popup.get(0), "htimer");
    
    cat_dropdown._bind_hevents(popup.get(0));
  }
  
  if (popup.html().replace(/\ /g, '') != '') {
    $j.data(popup.get(0), "htimer", setTimeout(show_popup, 500));
  }
}

cat_dropdown._hover_out = function () {
  /*
   * Handles the hover-out event for the popup tooltip
   *
   */
  var popup = $j(this).parent().siblings('.popup');
  var tmr = $j.data(popup.get(0), "htimer");
  
  if (tmr) {
    clearTimeout(tmr);
    $j.removeData(popup.get(0), "htimer");
  }
}


/*
 * Public Functions
 *
 */
cat_dropdown.refresh = function (drop_elem_id) {
  /*
   * Refreshes the dropdown category list based on show_custom and filter
   * settings specified when the widget was initialized/filtered.
   *
   * This function must be called after setting filter to apply it.
   *
   * This function will return self so that chaining can be performed.
   *
   * Parameters:
   *   drop_elem_id: The DOM ID of the element to refresh the category list for
   *
   */
  var dropdown = cat_dropdown._dropdowns[drop_elem_id];
  if (!dropdown) { return false; }
  
  var params = {
    'filter': ((dropdown.filter) ? dropdown.filter.filter_name : ''),
    'params': ((dropdown.filter) ? dropdown.filter.filter_params : '{}'),
    'dropdown': drop_elem_id
  };
  
  // Create an insance of the base UL DOM object. This will be used when
  // binding the UL to the widget as the dropdown part.
  cat_dropdown._getul(dropdown);
  
  $j.getJSON('/s/categories/dropdown/', params, cat_dropdown._json_handler);
  
  return this;
}

cat_dropdown.disable = function(drop_elem_id) {
  /*
   * Disables an enabled dropdown category widget
   *
   * Parameters:
   *   drop_elem_id: The DOM ID of the element to refresh the category list for
   *
   */
  var dropdown = cat_dropdown._dropdowns[drop_elem_id];
  if (!dropdown) { return false; }
  
  dropdown.mcdropdown.disable(true);
  
  return this;
}

cat_dropdown.enable = function(drop_elem_id) {
  /*
   * Enables a disabled dropdown category widget
   *
   * Parameters:
   *   drop_elem_id: The DOM ID of the element to refresh the category list for
   *
   */
  var dropdown = cat_dropdown._dropdowns[drop_elem_id];
  if (!dropdown) { return false; }
  
  dropdown.mcdropdown.disable(false);
  
  return this;
}

cat_dropdown.set_text = function(drop_elem_id, text) {
  /*
   * Sets the text within a dropdown category widget. This is used for custom
   * messaging. This function will not set the value of the widget.
   *
   * Parameters:
   *   drop_elem_id: The DOM ID of the element to refresh the category list for
   *
   */
  var dropdown = cat_dropdown._dropdowns[drop_elem_id];
  if (!dropdown) { return false; }
  
  dropdown.mcdropdown.setRawValue(text);
  
  return this;
}

cat_dropdown.filter = function(drop_elem_id, show_custom, displevel, filter) {
  /*
   * Sets a filter for a given dropdown. The filter is a string of comma
   * separated category IDs that are to be included in the dropdown. If
   * show_custom is true, custom categories will be included, however, they too
   * are filtered by the filtered list.
   *
   * This function will return self so that chaining can be performed.
   *
   * Parameters:
   *   drop_elem_id: The DOM ID of the element to bind the widget to
   *
   *   show_custom: (optional) true/false value indicating if Custom Categories
   *                should be displayed [default: false]
   *
   *   displevel: (optional) Either 2 or 3, indicating the number of levels from
   *              the bottom up to display in the dropdown. Any other number will
   *              default to 3 [default 3]
   *
   *   filter: (optional) A filter object created using the create_filter
   *           function. The filter can point to any custom function on the
   *           backend that returns a list of category ids [default: null]
   *
   */
  var dropdown = cat_dropdown._dropdowns[drop_elem_id];
  if (!dropdown) { return false; }
  
  dropdown.show_custom = (show_custom == undefined) ? cat_dropdown.show_custom : show_custom;
  dropdown.filter = ((filter == undefined) ? null : filter);
  dropdown.level = ((displevel == undefined) ? 3 : ((displevel != 2) ? 3 : 2));
  
  cat_dropdown._dropdowns[drop_elem_id] = dropdown;
  
  return this;
}

cat_dropdown.get_category_id = function(drop_elem_id) {
  /*
   * Returns the selected category ID or null if none is selected
   *
   * Parameters:
   *   drop_elem_id: The DOM ID of the element to bind the widget to
   *
   */
  var dropdown = cat_dropdown._dropdowns[drop_elem_id];
  if (!dropdown) { return false; }
  
  var selitem = dropdown.mcdropdown.getValue();
  
  return ((selitem[0]) ? selitem[0] : null);
}

cat_dropdown.get_category_name = function(drop_elem_id) {
  /*
   * Returns the selected category name or null if none is selected
   *
   * Parameters:
   *   drop_elem_id: The DOM ID of the element to bind the widget to
   *
   */
  var dropdown = cat_dropdown._dropdowns[drop_elem_id];
  if (!dropdown) { return false; }
  
  var selitem = dropdown.mcdropdown.getValue();
  
  return ((selitem[0]) ? selitem[1] : null);
}

cat_dropdown.set_category_id = function(drop_elem_id, category_id, allow_event) {
  /*
   * Sets the selected category ID. If an invalid ID is passed the dropdown
   * will get cleared
   *
   * This function will return self so that chaining can be performed.
   *
   * Parameters:
   *   drop_elem_id: The DOM ID of the element to bind the widget to
   *
   *   category_id: The category id to be selected
   *
   *   allow_event: (optional) true/false indicating if the dropdown should
   *                raise an event when changed
   *
   */
  var dropdown = cat_dropdown._dropdowns[drop_elem_id];
  if (!dropdown) { return false; }
  
  dropdown.last_selected_id = dropdown.mcdropdown.getValue()[0];
  dropdown.currently_set_id = category_id;
  
  dropdown.mcdropdown.setValue(category_id, (allow_event != undefined) ? true : false);
  
  return this;
}

cat_dropdown.clear = function(drop_elem_id, allow_event) {
  /*
   * Clears the category dropdown
   *
   * This function will return self so that chaining can be performed.
   *
   * Parameters:
   *   drop_elem_id: The DOM ID of the element to bind the widget to
   *
   *   allow_event: (optional) true/false indicating if the dropdown should
   *                raise an event when changed
   *
   */
  var dropdown = cat_dropdown._dropdowns[drop_elem_id];
  if (!dropdown) { return false; }
  
  // Clear the widget
  dropdown.mcdropdown.setValue(-1, (allow_event != undefined) ? true : false);
  
  // Clear the popup tooltip
  cat_dropdown._set_popup_value(drop_elem_id, '');
  
  return this;
}

cat_dropdown.create_filter = function(filter_parms, filter_name) {
  /*
   * Creates a filter object that can be passed into initialize or filter
   *
   * Parameters:
   *   filter_parms: A JSON string representing a dictionary of parameter-value
   *                 combinations that will be passed into the filter function
   *
   *   filter_name: (optional) The name of the filter function to call on the
   *                backend. If the filter doesn't exist in core_filters, a
   *                fully canonical path to the function should be provided
   *
   */
  var filter = {};
  
  filter.filter_name = filter_name || 'delimited_filter';
  filter.filter_params = filter_parms;
  
  return filter;
}

cat_dropdown.initialize = function(drop_elem_id, show_custom, displevel, title, filter) {
  /*
   * Initializes the dropdown against a given element_id.
   *
   * The dropdown will raise an event against the drop_elem_id DOM object when an
   * item has been selected. To handle this event, subscribe to the event named
   * "category_selected" against the drop_elem_id DOM object. The handler's
   * signature should look like: "handler(event, category_id, dropdown)"
   *
   * eg.
   *      // Subscript to the category_selected event
   *      $j('#' + drop_elem_id).bind('category_selected', category_selected_handler);
   *
   *      // Category selected handler
   *      function category_selected_handler(event, category_id, dropdown) {
   *        alert('category_id: ' + category_id + ' for: ' + dropdown);
   *      }
   *
   * Parameters:
   *   drop_elem_id: The DOM ID of the element to bind the widget to
   *
   *   show_custom: (optional) true/false value indicating if Custom Categories
   *                should be displayed [default: false]
   *
   *   displevel: (optional) Either 2 or 3, indicating the number of levels from
   *              the bottom up to display in the dropdown. Any other number will
   *              default to 3 [default 3]
   *
   *   title: (optional) The title to be displayed in the category dropdown
   *                     initially [default: 'choose a category']
   *
   *   filter: (optional) A filter object created using the create_filter
   *           function. The filter can point to any custom function on the
   *           backend that returns a list of category ids [default: null]
   * 
   */
  var dropdown = new Object();
  
  dropdown.id = drop_elem_id;
  dropdown.ulid = 'ct_' + drop_elem_id;
  dropdown.show_custom = ((show_custom == undefined) ? false : show_custom);
  dropdown.filter = ((filter == undefined) ? null : filter);
  dropdown.level = ((displevel == undefined) ? 3 : ((displevel != 2) ? 3 : 2));

  title = title || 'choose a category';
  
  // Add to the list of initialized dropdowns
  cat_dropdown._dropdowns[drop_elem_id] = dropdown;
  
  var defaults = {
      select: cat_dropdown._select_handler
    , tooltip: true
  };
  
  cat_dropdown.refresh(drop_elem_id);
  
  // Bind the widget
  $j('#' + drop_elem_id).addClass('category-dropdown').attr('defaultValue', title).mcDropdown('#' + dropdown.ulid, defaults);
  
  // Store a reference to the widget and set a default value
  dropdown.mcdropdown = $j('#' + drop_elem_id).mcDropdown();
  
  /*
   * TBD: Fix bug with on-hover popup in IE. For now this is being removed and
   * reverted to use regular tooltips.
   *
  // Setup the on-hover popup
  var popup = $j('<div></div>').addClass('popup').hide();
  $j('#' + drop_elem_id).parent().after(popup);
   */
  
  // Bind click handers to open the widget when clicked
  var ddobj = $j('#' + drop_elem_id).siblings('.category-dropdown');
  cat_dropdown._bind_drop_event(ddobj.get(0), dropdown.mcdropdown);
  // cat_dropdown._bind_drop_event(popup.get(0), dropdown.mcdropdown);
  
  // Hook the HOVER event for name popups
  //ddobj.hover(cat_dropdown._hover_in, cat_dropdown._hover_out);
  
  return this;
}

