/**
 * This code handles the dynamic loading of the index tree on the MyKiffets page
 * - it also dynamically updates the content based on the selection in the index tree
 *
 *
 * TODO: The loadIndexThen... functions could probably be unified.
 * TODO: Need to handle the case where you remove an Index from MyKiffets and then press back 
 *       through various history pages
 * TODO - Use a namespace for all functions and variables
 */

if (typeof KIFFETS.channel == 'undefined' || !KIFFETS.channel) {
 KIFFETS.channel = 
    (function($) {
       var currentChannelNode = null;

       var selectedTab = null;
       var selectedTabName = null;

       this.setChannelNode = function(newChannelNode) {
         currentChannelNode = newChannelNode;
       };
       
       this.getChannelNode = function() {
         return currentChannelNode;
       };
       
       this.getChannelName = function() {
         if (currentChannelNode != null) {
           return currentChannelNode.data('name');
         } else {
           return null;
         }
       };

       this.setSelectedTab = function(newTab, newTabName) {
         if (selectedTab != null && selectedTabName != newTabName) {
           // The tab contents get replaced with the plain text name
           selectedTab.text(selectedTabName);
           
           var tabParent = selectedTab.parent();
           tabParent.removeClass('navBarSelectedTab');
           tabParent.addClass('navBarOption');    
         }
         
         selectedTab = newTab;
         selectedTabName = newTabName;
       };

       return this;
     })(jQuery);
}

var indexListDiv;
var indexMap;

var indexControlsDiv;

var selectedViewDOMNode;
var usernameEl;

// Index related state
var currentTopicNode;
var indexTopicNodes;
var currentDateRange;
var currentOffset = -1;

// Any currently visible dialog nodes
var dialogNode;

// The current page loading request - should be aborted
// before any new request is started
var ajaxRequests;

var COLLAPSED_LIST_MAX_HEIGHT = '175px';
var EXPANDED_LIST_MAX_HEIGHT = '300px';

/**
 * Get the index children - this is abstracted out because changes to the DOM change how we get them
 */
function getIndexListParent() {
  return $(indexListDiv.children()[0]);
}

/**
 * Page change callback - used for within-page history
 */
function historyPageChanged(hash) {
  if (!jQuery.browser.mozilla) {
    hash = decodeURIComponent(hash);
  }

  var valDict = {};
  var vals = (hash) ? hash.split('&') : [];
  for (i in vals) {
    var parts = vals[i].split('=');
    if (parts.length > 1) {
      // We double encode the URI components because the browser
      // automatically decodes the fragment before giving it to us
      valDict[parts[0]] = decodeURIComponent(parts[1]);
    } else {
      valDict[parts[0]] = '';
    }
  }

  var offset = (valDict.hasOwnProperty('offset')) ? valDict.offset : -1;
  if (vals.length == 0 || valDict.hasOwnProperty('overview')) {
    document.title = 'My Overview - Kiffets';
    loadOverviewContent(valDict['overview']);
  } else if (valDict.hasOwnProperty('hotNews')) {
    document.title = 'Hot News - Kiffets';
    loadHotNewsContent(valDict.hotNews);
  } else if (valDict.hasOwnProperty('topClusters')) {
    document.title = 'Browsing Clusters - Kiffets';
    loadTopClustersContent(valDict.topClusters);
  } else if (valDict.hasOwnProperty('browseHotChannels')) {
    document.title = 'Browsing Hot Channels - Kiffets';
    loadBrowseHotChannelsContent(valDict.browseHotChannels);
  } else if (valDict.hasOwnProperty('browseStaffChannels')) {
    document.title = 'Browsing Staff Pick Channels - Kiffets';
    loadBrowseStaffChannelsContent();
  } else if (valDict.hasOwnProperty('browseNewChannels')) {
    document.title = 'Browsing New Channels - Kiffets';
    loadBrowseNewChannelsContent(valDict.browseNewChannels);
  } else if (valDict.hasOwnProperty('browseAllChannels')) {
    document.title = 'Browsing All Channels - Kiffets';
    loadBrowseAllChannelsContent(valDict.browseAllChannels);
  } else if (valDict.hasOwnProperty('index')) {
    document.title = 'Channel '+valDict['index']+' - Kiffets';

    // We handle Index dependent pages differently depending on whether the right index is
    // already loaded
    if (valDict.hasOwnProperty('topic')) {
      // We handle topics differently depending on whether the right index is already loaded
      // - this handles the forward and backward navigation correctly
      if (KIFFETS.channel.getChannelName() == valDict.index) {

        if (currentTopicNode && currentTopicNode.data('topicID') == valDict.topic) {                
          // In this case we're just changing the date range on the currently loaded topic
          loadContentForNode(KIFFETS.channel.getChannelNode(), currentTopicNode, false, offset, valDict.dateRange);
          window.setTimeout(function() {
                              loadTopicArticleCounts(null);
                            }, 1000);
        } else {
          loadTopicContent(valDict.topic, offset, valDict.dateRange);
        }
      } else {
        loadIndexThenTopicContent(valDict.index, valDict.topic, offset, valDict.dateRange);
      }
    } else if (valDict.hasOwnProperty('source')) {
      if (KIFFETS.channel.getChannelName() == valDict.index) {
        loadSourceContent(valDict.source, valDict.offset);
      } else {
        loadIndexThenSourceContent(valDict.index, valDict.source, valDict.offset);
      }
    } else if (valDict.hasOwnProperty('cluster')) {
      if (KIFFETS.channel.getChannelName() == valDict.index) {
        loadClusterContent(valDict.cluster);
      } else {
        loadIndexThenClusterContent(valDict.index, valDict.cluster);
      }
    } else if (valDict.hasOwnProperty('query')) {
      if (KIFFETS.channel.getChannelName() == valDict.index) {
        loadIndexSearchContent(valDict.index, valDict.query);
      } else {
        loadIndexThenSearchContent(valDict.index, valDict.query);
      }
    } else if (valDict.hasOwnProperty('hotTopics')) {
      if (KIFFETS.channel.getChannelName() == valDict.index) {
        loadIndexHotTopicsContent();
      } else {
        loadIndexThenHotTopicsContent(valDict.index);
      }
    } else if (valDict.hasOwnProperty('addSources')) {
      if (KIFFETS.channel.getChannelName() == valDict.index) {
        loadAddSourcesPageContent();
      } else {
        loadIndexThenCallback(valDict.index, function() {
                                loadAddSourcesPageContent();
                              });
      }
    } else if (valDict.hasOwnProperty('addTopic')) {
      if (KIFFETS.channel.getChannelName() == valDict.index) {
        loadAddTopicPageContent();
      } else {
        loadIndexThenCallback(valDict.index, function() {
                                loadAddTopicPageContent();
                              });
      }
    } else if (valDict.hasOwnProperty('improve')) {
      if (KIFFETS.channel.getChannelName() == valDict.index) {
        loadImproveIndexContent(valDict.index);
      } else {
        loadIndexThenCallback(valDict.index, function() {
                                loadImproveIndexContent(valDict.index);
                              });
      }
    } else if (valDict.hasOwnProperty('improveStories')) {
      if (KIFFETS.channel.getChannelName() == valDict.index) {
        loadImproveStoriesContent(valDict.index);
      } else {
        loadIndexThenCallback(valDict.index, function() {
                                loadImproveStoriesContent(valDict.index);
                              });
      }
    } else if (valDict.hasOwnProperty('refineTopic')) {
      var topicID = -1;
      if (valDict.refineTopic) {
        topicID = valDict.refineTopic;
      }

      if (KIFFETS.channel.getChannelName() == valDict.index) {
        loadRefineTopicContent(valDict.index, topicID);
      } else {
        loadIndexThenCallback(valDict.index, function() {
				loadRefineTopicContent(valDict.index, topicID);
			      });
      }
    } else {
      var dateRange = (valDict.dateRange) ? valDict.dateRange : '';
      if (KIFFETS.channel.getChannelName() == valDict.index) {
          // In this case we're just changing the date range on the currently loaded index page
          loadContentForNode(KIFFETS.channel.getChannelNode(), null, false, -1, dateRange);
          window.setTimeout(function() {
                              loadTopicArticleCounts(null);
                            }, 1000);
      } else {
        loadIndexThenTopicContent(valDict.index, 0, -1, dateRange);
      }
    } 
  } else if (valDict.hasOwnProperty('search')) {
    if (valDict.search) {
      if (valDict.hasOwnProperty('allIndexes')) {
        loadSearchCommunitiesContent(valDict.search);
      } else {
        loadSearchContent(valDict.search);
      }
    } else {
      setIndexesPopupVisible(true);
    }
  } else if (valDict.hasOwnProperty('reorder')) {
    setReorderPopupVisible(true);
  }
}

/**
 * Initialize the page with the myIndexList
 *
 * Also add the click listener on the overview label
 */
function myKiffetsInit() {
  var idxList = $('#myIndexList');
  if (idxList.length == 0) {
    return;
  }

  usernameEl = $('#usernameEl');

  /******************************/
  /* Handles any clicks in the myKiffets list */
  indexListDiv = idxList;
  indexListDiv.click(function(evt) {
                       var newNode = $(evt.target);
                       if (newNode.hasClass('myIndexNameParent')) {
                         newNode = $('.myIndexNameDisplay', newNode);
                       }
                       
                       if (newNode.hasClass('myIndexNameDisplay')) {
                         loadIndex(newNode);
                       }
                     });

  /******************************/
  /* Overview navigation buttons */
  var overView = $('#overviewLbl');
  overView.click(function(evt) {
                            loadOverview('');
                          });

  var topClusters = $('#topClustersLbl');
  topClusters.click(function(evt) {
                    loadTopCluster();
                  });


  var hotNewser = $('#hotNewsLbl');
  hotNewser.click(function(evt) {
                    loadHotNews();
                  });

  var browseChannels = $('#browseChannelsLbl');
  browseChannels.click(function(evt) {
                         loadBrowseHotChannels();
                       });

  /******************************/
  /* The "Share" command */
  var inviter = $('#inviteToIndexLbl');
  if (inviter.length > 0) {
    inviter.click(function(evt) {
		    var channelName = KIFFETS.channel.getChannelName();
                    if (channelName) {
                      var wdw = window.open('/invite/?index='+encodeURIComponent(channelName));
                      wdw.focus();
                    } else{
                      var wdw = window.open('/invite/');
                      wdw.focus();                      
                    }
                  });
  }

  /******************************/
  /* Listener to hide any dialogs if something
     other than the dialog is clicked */
  $('body').click(function(evt) {
                    if (dialogNode != null) {
                      var jTarget = $(evt.target);
                      if (jTarget.length == 0 || dialogNode.length == 0 || (jTarget.get(0) != dialogNode.get(0) && jTarget.parents().index(dialogNode.eq(0)) == -1)) {

                        dialogNode.css('visibility', 'hidden');
                        dialogNode.css('display', 'none');
                        dialogNode = null;
                      }
                    }
                  });
  

  /******************************/
  /* The Add Index form listener */

  /* The Add Index Popup visibility controls */
  var addIndexLbl = $('#addIndexLbl');
  addIndexLbl.click(function(evt) {
                      if (!addIndexLbl.hasClass('inactive')) {
                        if (dialogNode != null) {
                          dialogNode.hide();
                          dialogNode.css('display','none');
                        }
                        setIndexesPopupVisible(true);
                        return false;
                      }
                    });
  $('#addIndexClose').click(function(evt) {
                              setIndexesPopupVisible(false);
                            });

  $('#addIndexBrowse').click(function(evt) {
                               loadBrowseHotChannels();
                               setIndexesPopupVisible(false);
                               return false;
                             });

  var reorderLbl = $('#reorderIndexLbl');
  reorderLbl.click(function(evt) {
                      if (!reorderLbl.hasClass('inactive')) {
                        if (dialogNode != null) {
                          dialogNode.hide();
                          dialogNode.css('display','none');
                        }
                        setReorderPopupVisible(true);
                        return false;
                      }
                              });
  $('#reorderIndexClose').click(function(evt) {
                                  setReorderPopupVisible(false);
                                });

  var searchFormOps = {
    url: '/ajaxSearch/',
    beforeSubmit: function (formData, jqForm, options) { 
      abortAjaxRequests();

      removeIndexDOMNodes();
      updateIndexListSize(false, null);

      // NOTE: We check the form value this way since these
      // form options are reused for multiple forms

      var queryTxt = jqForm[0].query.value;

      if (jqForm.attr('id') != 'topLevelSearchForm') {
        $('#searchQueryInput').val(queryTxt);
      }
      
      if (queryTxt && queryTxt.length > 0) {
        setIndexesPopupVisible(false);

        // This passes the search off so it goes through history
        loadSearch(queryTxt);
      }
      return false;
    },
    success: function (responseTxt, statusTxt) {
      // We don't actually submit this form
      // we pass off the ajax request so it goes through history
    },
    error: function(request, textStatus, errorThrown) {
      loadHTML(request.responseText);
    },
    type: 'post'
  };
  $('#addIndexForm').ajaxForm(searchFormOps);
  /* Initialize the top level search form to do the same thing
     as the "addIndex" search */
  $('#topLevelSearchForm').ajaxForm(searchFormOps);

  KIFFETS.core.setupTopicControlsPopup(function() {
                            KIFFETS.core.currentHoverTopicNode = null;
                          });

  /******************************/
  /* Load an save the list of indexes in the MyKiffets list */
  indexMap = {};
  getIndexListParent().children().each(function() {
                                 var indexNode = $('.myIndexNameDisplay',$(this).get());
                                 indexMap[indexNode.text().toLowerCase()] = indexNode;
                                 indexNode.data('name',indexNode.text());
                               });

  /******************************/  
  /* Initialize history support - which also loads the initial right side page */
  jQuery.historyInit(historyPageChanged, null);

  if (!location.hash) {
    if (usernameEl.length > 0) {
      loadOverviewContent('');
    } else {
      loadBrowseHotChannels();
    }
  }
  
  if (usernameEl.length > 0) {
    // Optimization - currently not expecting to show indexes for anonymouse user

    /******************************/
    /* Also async load the article counts for each of the indexes - start on a slight delay */  
    window.setTimeout(function() {
                        loadIndexArticleCounts();
                      }, 500);
  }
}

// Shows and hides the 'Add Index' box
function setIndexesPopupVisible(visible) {
  var idxDlgNode = $('#addIndexesPopup');
  if (visible) {    
    idxDlgNode.parent().append(idxDlgNode);
    idxDlgNode.css('visibility','visible');
    idxDlgNode.css('display','block');
    $('#addIndexText').focus();

    dialogNode = idxDlgNode;
  } else {
    idxDlgNode.css('visibility','hidden');
    idxDlgNode.css('display','none');

    dialogNode = null;
  }
}

// Shows and hides the 'Reorder' box
function setReorderPopupVisible(visible) {
  var reorderDlgNode = $('#reorderIndexesPopup');

  if (visible) {
    abortAjaxRequests();

    reorderDlgNode.parent().append(reorderDlgNode);
    reorderDlgNode.css('visibility','visible');
    reorderDlgNode.css('display','block');
    $('#reorderContent').empty();

    // Form setup for the sortable list
    var reorderFormOps = {
      url: '/reorderMyListSubmit/',
      beforeSubmit: function (formData, jqForm, options) { 
        abortAjaxRequests();

        var names = [];
        var index = 0;
        $('#reorderIndexList').children().each(function() {
                                                  names[index] = $(this).text();
                                                  index++;
                                                });

        // This adds data to the form submission
        formData.push({'name':'reorderList', 'value': JSON.stringify(names)});

        return true;
      },
      success: function (responseTxt, statusTxt) {
        // Hide the popup on success
        setReorderPopupVisible(false);
        location = '/';
        location.hash = '';
        location.reload();
      },
      error: function(request, textStatus, errorThrown) {
        loadHTML(request.responseText);
      },
      type: 'post'
    };

    // Reload the latest indexes list
    // This actually does the ajax call
    var req = jQuery.ajax({
      type: 'GET',
          url: '/reorderMyList/',
          success: function(data) {
            $('#reorderContent').html(data);

	    $().ready(function() {
			// Hookup the list dragging functionality
			$('#reorderIndexList').dragsort({dragSelector: 'li', dragEnd: function() {}, dragBetween: false, placeHolderTemplate: '<li></li>'});	    
			$('#reorderIndexForm').ajaxForm(reorderFormOps);
			
			$('#reorderCancel').click(
			  function() {
			    setReorderPopupVisible(false);
			    return false;
			  });	      
		      });
        },
          dataType: 'html',
          error: function(request, textStatus, errorThrown) {
          loadHTML(request.responseText);
        }
      });
    addAjaxRequest(req);    

    dialogNode = reorderDlgNode;
  } else {
    $('#reorderIndexesPopup').css('visibility','hidden');
    $('#reorderIndexesPopup').css('display','none');

    dialogNode = null;
  }  
}

// Shows and hides the 'Reorder' box
function setRearrangeTopicsPopupVisible(visible) {
  var rearrangeDlgNode = $('#rearrangeTopicsPopup');

  if (visible) {
    abortAjaxRequests();

    if (dialogNode != null) {
      dialogNode.hide();
      dialogNode.css('display','none');
    }

    // Shouldn't happen - just as a precaution
    var channelName = KIFFETS.channel.getChannelName();    
    if (channelName == null) {
      return false;
    }

    rearrangeDlgNode.parent().append(rearrangeDlgNode);
    rearrangeDlgNode.css('visibility','visible');
    rearrangeDlgNode.css('display','block');
    $('#rearrangeContent').empty();

    // Reload the latest indexes list
    // This actually does the ajax call
    var req = jQuery.ajax({
      type: 'GET',
          url: '/i/'+encodeURIComponent(channelName)+'/rearrangeTopicsAjax/',
          success: function(data) {
          $('#rearrangeContent').html(data);
        },
          dataType: 'html',
          error: function(request, textStatus, errorThrown) {
          $('#rearrangeContent').html(request.responseText);
        }
      });
    addAjaxRequest(req);    

    dialogNode = rearrangeDlgNode;
  } else {
    rearrangeDlgNode.css('visibility','hidden');
    rearrangeDlgNode.css('display','none');

    dialogNode = null;
  }  

  return false;
}

function topicArrangerClosed(saved) {
  setRearrangeTopicsPopupVisible(false);
  if (saved == 'true') {
    location.reload();
  }
}

/**
 * Simple function to load ajax content into the main MyKiffets content box
 */
function loadHTML(data) {
  $('#myKiffetsContent').html(data);
  scrollTo(0,0);
}

/**
 * Update the MyKiffets page selection
 */
function updateViewSelection(newSelection, indexNode, topicNode, dateRange, offset) {
  if (selectedViewDOMNode != null) {
    selectedViewDOMNode.removeClass('myKiffetsSelection');
  }

  if (newSelection != null) {
    newSelection.addClass('myKiffetsSelection');
  }

  // Unselect any selected tab - it will be reselected shortly
  KIFFETS.channel.setSelectedTab(null, null);

  // Update the overview tab if we're showing the overview 
  // - otherwise make sure it is unselected
  var oTab = $('#myOverviewTab');
  var oTabDiv = oTab.parent();
  if (newSelection && newSelection.attr('id') == 'overviewLbl') {
  } else {
    if (!oTabDiv.hasClass('navBarOption')) {
      oTab.text('MY OVERVIEW');
      oTabDiv.removeClass('navBarSelectedTab');
      oTabDiv.addClass('navBarOption');
    }
  }

  // Always unselected the browse tab - the browse functions will select the tab as needed
  var bTab = $('#browseTab');
  var bTabDiv = bTab.parent();
  if (!bTabDiv.hasClass('navBarOption')) {
      bTab.text('BROWSE');
      bTabDiv.removeClass('navBarSelectedTab');
      bTabDiv.addClass('navBarOption');    
  }

  // If the current topic selection is not null, then remove the selected
  // class
  if (currentTopicNode) {
    currentTopicNode.removeClass('selected');
  }

  selectedViewDOMNode = newSelection;

  KIFFETS.channel.setChannelNode(indexNode);
  currentTopicNode = topicNode;
  currentDateRange = dateRange;
  currentOffset = offset;

  // Now select the current topic node
  if (currentTopicNode) {
    currentTopicNode.addClass('selected');
    currentTopicNode.ensureExpanded(true);
  }
}

/**
 * Remove any index related DOM nodes from the DOM
 */
function removeIndexDOMNodes() {
  if (indexControlsDiv != null) {
    indexControlsDiv.remove();
    indexControlsDiv = null;
  }  
}

/**
 * Add an ajax request to be canceled when something new happens
 */
function addAjaxRequest(request) {
  if (ajaxRequests == null) {
    ajaxRequests = [];
  }
  ajaxRequests.push(request);
}

/**
 * Cancel any index requests.  Note that this doesn't get called
 * until there is a new request.  So we waste memory by keeping
 * these around between requests. The code is much simpler to
 * implement though.
 */
function abortAjaxRequests() {
  if (ajaxRequests) {
    for (i in ajaxRequests) {
      ajaxRequests[i].abort();
    }
    ajaxRequests = null;
  }
}

/**
 * Get the root of a given index entry
 */
function getIndexListEntryRoot(idxNode) {
  return idxNode.parent().parent().parent().parent();
}

/**
 * If an index is selected, change the date range for the index
 */
function changeDateRange(dateRange) {
  if (dateRange == '24hr') {
    dateRange = null;
  }

  var channelName = KIFFETS.channel.getChannelName();
  if (channelName) {
    var queryStr = 'index='+encodeFragmentComponent(channelName);
    if (dateRange) {
      queryStr += '&dateRange='+dateRange;      
    }
    if (currentOffset != -1) {
      queryStr += '&offset='+currentOffset;
    }

    if (currentTopicNode) {
      var topicID = currentTopicNode.data('topicID');
      queryStr += '&topic='+topicID;
      jQuery.historyLoad(queryStr);
    } else {
      jQuery.historyLoad(queryStr);      
    }
  } 

  return false;
}

/**
 * Expand or collapse the index list size
 */
function updateIndexListSize(collapse, idxNode) {
  var maxHeight = indexListDiv.css('maxHeight');
  if (collapse) {
    if (maxHeight == EXPANDED_LIST_MAX_HEIGHT) {
      var selectionTop = idxNode.offset().top + indexListDiv.scrollTop() - indexListDiv.offset().top;
      indexListDiv.animate({maxHeight:COLLAPSED_LIST_MAX_HEIGHT, scrollTop: selectionTop}, {duration:500, easing: 'linear'});
    }
  } else {
    if (maxHeight == COLLAPSED_LIST_MAX_HEIGHT) {
      indexListDiv.animate({maxHeight:EXPANDED_LIST_MAX_HEIGHT, scrollTop: 0}, {duration:500, easing: 'linear'});
    }      
  }
}

/**
 * Dynamically add a new community to MyKiffets
 */
function addToMyKiffets(idxName, idxID) {
  if (indexListDiv.css('display') == 'none') {
    $('.myIndexesListEmpty').css('display','none');
    indexListDiv.css('display','block');
  }

  var children = getIndexListParent().children();
  
  var lowerIdxName = idxName.toLowerCase();
  var afterNode = null;
  for (var i=0; i<children.length; ++i) {
    var child = $(children.get(i));
    var nameNode = $('.myIndexNameDisplay',child);
    var lowerChildName = nameNode.data('name').toLowerCase();
    if (lowerIdxName < lowerChildName) {
      afterNode = child;
      break;
    } else if (lowerIdxName == lowerChildName) {
      // The user already has this index
      return;
    }
  }

  var newIdxNode = $('#dummyIndexListEntry').clone();

  var newIdxNameNode = $('.myIndexNameDisplay',newIdxNode);
  newIdxNameNode.text(idxName);

  $('.removeFromMyIndexesBtn',newIdxNode).click(function() {
                                                     removeFromMyKiffets(idxName);
                                                   });

  newIdxNode.appendTo(getIndexListParent());

  // Animate to show the newly added index
  var selectionTop = newIdxNode.offset().top + indexListDiv.scrollTop() - indexListDiv.offset().top;
  indexListDiv.animate({scrollTop: selectionTop}, {duration:100, easing: 'linear'});

  // Put the new index into the map
  newIdxNameNode.data('name',idxName);
  indexMap[idxName.toLowerCase()] = newIdxNameNode;

  loadArticleCount(newIdxNameNode);

  // Also subscribe the user on the server
  // NOTE: Don't need to add this to the list of requests to cancel
  var requestObj = {'methodName': 'subscribeCommunity','community':idxName};
  jQuery.ajax({
    type: 'POST',
        url: '/json/1.0/', 
        success: function (data) {
      }, 
        data: {request:JSON.stringify(requestObj)},
        error: function(request, textStatus, errorThrown) {
      }
    });
}

/**
 * Remove the specified index from my kiffets list
 */
function removeFromMyKiffets(idxName) {
  if (!idxName) {
    return;
  }

  var lowerName =idxName.toLowerCase();
  var idxNode = indexMap[lowerName];
  if (!idxNode) {
    return;
  }

  // If we deleted the current selected node, then load in the overview instead
  var currentDeleted = (KIFFETS.channel.getChannelName() == idxNode.data('name'));

  // Remove the index node from the display
  getIndexListEntryRoot(idxNode).remove();

  // And our datastructure
  delete indexMap[lowerName];

  // Also unsubscribe the user on the server
  // NOTE: Don't need to add this to the list of requests to cancel
  var requestObj = {'methodName': 'unsubscribeCommunity','community':idxName};
  jQuery.ajax({
    type: 'POST',
        url: '/json/1.0/', 
        success: function (data) {
      }, 
        data: {request:JSON.stringify(requestObj)},
        error: function(request, textStatus, errorThrown) {
      }
    });

  // If we deleted the current selected node, then load in the overview instead
  if (currentDeleted) {
    loadOverviewContent('');
  }

}

/**
 * Remove a source from this index
 */
function deleteSource(communityName, sourceTitle, sourceID) {
  if (confirm("Really remove source '"+sourceTitle+"' from "+communityName+"?")) {
    jQuery.ajax({
        'url': '/json/1.0/',
          'type': 'POST',
          'data': {'request': JSON.stringify({
            'methodName': 'updateCollectionURLs',
              'communityName': communityName,
              'deletedCollectionURLs': [{'id':sourceID}]
                })},
          'success': function (data) {
          var channelNode = KIFFETS.channel.getChannelNode();
          loadIndexContent(channelNode, true, false, function() {
                             // Show the sources to start
                             //elementToggle('sourceTogglable','sourceToggler',true);  
                           });
        },
          'error': function (XMLHttpRequest, textStatus, errorThrown) {
          alert('There was an error communicating with the server');
        }
      });
  }
}

/**
 * Load the topics for the specified node
 */
function loadContentForNode(indexNode, topicNode, forceLoad, offset, dateRange) {
  // If the focus hasn't changed - or if we've just
  // switched from the index node to the "Topics" node
  // then do nothing
  var curIndexName = KIFFETS.channel.getChannelName();
  var curTopicID = (currentTopicNode == null) ? null : currentTopicNode.data('topicID');
  var newIndexName = (indexNode == null) ? null : indexNode.data('name');
  var newTopicID = (topicNode == null) ? null : topicNode.data('topicID');

  // This checking can save a little extra work if things haven't changed
  // - but we also have to make sure all state is the same.  Currently not
  // checking enough state, so disabled for now.
  if (false &&
      !forceLoad &&
      curIndexName == newIndexName &&
      curTopicID == newTopicID &&
      (currentDateRange == dateRange || (!currentDateRange && !dateRange)) &&
      currentOffset == offset) {
    return;
  } else {
    updateViewSelection(getIndexListEntryRoot(indexNode), indexNode, topicNode, dateRange, offset);

    // If the root node is selected and a dateRange is specified, then
    // we need the actual id of the root node
    var topicID = -1;
    if (topicNode != null && 
        topicNode.data('topicID') == 0 &&
        offset != -1) {
      topicID = topicNode.data('rootTopicID');
    } else if (topicNode != null &&
               topicNode.data('topicID') != 0) {
      topicID = topicNode.data('topicID');
    }

    // Either the root was selected or the index itself - load the root index page
    if (topicID == -1) {
      var url = '/i/'+encodeURIComponent(newIndexName)+'/ajaxRead/';
      if (dateRange) {
        url += 'd/'+dateRange+'/';
      }

      var req = jQuery.ajax({
        type: 'GET',
            url: url, 
            success: function(data) {
            loadHTML(data);
            pageTracker._trackPageview(url);            
          },
            dataType: 'html',
            error: function(request, textStatus, errorThrown) {
          }
        });
      addAjaxRequest(req);
    } else {
      var url = '/i/'+encodeURIComponent(newIndexName)+'/'+topicID+'/ajaxRead/';
      if (offset != -1) {
        url += 'all/';
      }
      if (dateRange) {
        url += 'd/'+dateRange+'/';
      }

      // Add the offset parameter
      if (offset > 0) {
        url += '?offset='+offset;
      }

      var req = jQuery.ajax({
        type: 'GET',
            url: url, 
            success: function(data) {
            loadHTML(data);
            pageTracker._trackPageview(url);            
          },
            dataType: 'html',
            error: function(request, textStatus, errorThrown) {
            loadHTML(request.responseText);
          }
        });      
      addAjaxRequest(req);
    }
  }
}

/*****************************************************************/
/* Functions that load pieces of pages
/*****************************************************************/

/**
 * Load the article counts for the specified topics
 */
function loadTopicArticleCounts(dateOverride) {
  var dateRange = (dateOverride == null) ? currentDateRange : dateOverride;

  var channelName = KIFFETS.channel.getChannelName();  
  if (channelName != null && indexTopicNodes != null) {

    var dataHandler = function(data) {
      var stripped = stripComments(data);
      var resultObj = JSON.parse(stripped);

      if (resultObj['result'] == 'success') {
        var topicCounts = resultObj['topicCounts'];

        // Store all the article counts in case there are unloaded topics - we'll use them
        // to set the counts on load later
	var channelNode = KIFFETS.channel.getChannelNode();
        channelNode.data('topicArticleCounts', topicCounts);

        for (tID in topicCounts) {
          var topicNode = indexTopicNodes[tID];
          if (topicNode) {
            var topicName = topicNode.data('name');
            topicNode.data('articleCount', topicCounts[tID]);
            topicNode.text(topicCounts[tID] ? topicName + ' (' + topicCounts[tID] + ')' : topicName);
          } 
        }
      }
    };

    var url = '/allTopicArticleCounts/' + encodeURIComponent(channelName) + '/';
    if (dateRange) {
      url += 'd/'+dateRange+'/';
    }     

    // This actually does the ajax call
    var ajaxRequest = jQuery.ajax({
      type: 'GET',
          url: url,
          data: {},
          success: dataHandler, 
          dataType: 'html',
          error: function(request, textStatus, errorThrown) {
          loadHTML(request.responseText);
        }
      });      
  }
}

/**
 * Load All of the user's Index Article Counts - assume 24hours
 */
function loadIndexArticleCounts() {
    var dataHandler = function(data) {
      var stripped = stripComments(data);
      var resultObj = JSON.parse(stripped);

      if (resultObj['result'] == 'success') {
        var commCounts = resultObj['communityCounts'];

        for (iName in commCounts) {
          var lowerName = iName.toLowerCase();
          var indexNode = indexMap[lowerName];
          if (indexNode) {
            indexNode.data('articleCount', commCounts[iName]);
            indexNode.text(commCounts[iName] ? iName + ' (' + commCounts[iName] + ')' : iName);
          }
        }
      }
    };

    var url = '/userCommunityArticleCounts/';

    // This actually does the ajax call
    var ajaxRequest = jQuery.ajax({
      type: 'GET',
          url: url,
          data: {},
          success: dataHandler, 
          dataType: 'html',
          error: function(request, textStatus, errorThrown) {
          loadHTML(request.responseText);
        }
      });      
}

/**
 * Load the article count for the specified index
 */
function loadArticleCount(indexNode) {
  var lIndexName = indexNode.data('name');

  var ajaxRequest = null;

  var dataHandler = function(data) {
    var stripped = stripComments(data);
    var resultObj = JSON.parse(stripped);

    if (resultObj['result'] == 'success') {
      indexNode.data('articleCount', resultObj['articleCount']);
      indexNode.text(resultObj['articleCount'] ? lIndexName + ' (' + resultObj['articleCount'] + ')' : lIndexName);
    }
  };

  var url = '/communityArticleCount/' + encodeURIComponent(lIndexName) + '/';

  // This actually does the ajax call
  ajaxRequest = jQuery.ajax({
    type: 'GET',
        url: url,
        data: {},
        success: dataHandler, 
        dataType: 'html',
        error: function(request, textStatus, errorThrown) {
        loadHTML(request.responseText);
      }
    });

  return ajaxRequest;
}


/** 
 * Utility callback function to load the given tree node data into the local datastructure 
 */
function treeNodeLoader() {
  var jqThis = $(this);
  jqThis.data('name',jqThis.text());
  var fullTopID = jqThis.attr('id');
  var topID = fullTopID.substring(fullTopID.indexOf('_')+1);
  jqThis.data('topicID',topID);
  indexTopicNodes[topID] = jqThis;
}

/**
 * Utility callback to load a new tree level - depends on the 'this' variable being set
 */
function treeLevelLoader() {
  // The "this" variable is set to the parent list
  var topicNodes = this.find('a[id^=topic_]');

  topicNodes.each(treeNodeLoader);

  // Get the topic article counts from the root index node (since this loads internal tree children
  // - safe to assume the channelNode has been set)
  var channelNode = KIFFETS.channel.getChannelNode();
  var topicArtCounts = channelNode.data('topicArticleCounts');

  // In addition to loading the nodes, we also have to set the tree counts
  if (topicArtCounts) {
    topicNodes.each(function() {
                      var jqThis = $(this);
                      var count = topicArtCounts[jqThis.data('topicID')];
                      if (count > 0) {
                        jqThis.text(jqThis.data('name')+' ('+count+')');
                      }
                    });  
  }
}

/**
 * Load the index tree into the left hand index pane
 */
function loadIndexControls(idxNode, callback) {
  var url = '/i/'+encodeURIComponent(idxNode.data('name'))+'/indexControlsAjax/';

  removeIndexDOMNodes();

  var outerReq = jQuery.ajax({
    type: 'GET',
        url: url, 
        success: function(data) {

        indexControlsDiv = $(data);
        $('.treeview',indexControlsDiv).css('display','none');
        $('.indexLeftBorderBox').append(indexControlsDiv);

        $('#tree').treeview({
          collapsed: true,
              animated: "medium",
              prerendered: true,
              persist: "location",
              url: '/i/'+encodeURIComponent(idxNode.data('name'))+'/topicChildrenAjax/',
              
              // Our toggle function will get called after the topic's children are loaded
              toggle: function() {

	      // Setup the topic controls on these nodes if the user has permission
	      if ($('#isSuperuser').length > 0 ||
		  $('#isIndexOwner').length > 0) {
		KIFFETS.core.setupTopicControls(this,function(node) {
		    KIFFETS.core.currentHoverTopicNode = node;
		  });
	      }

	      treeLevelLoader.call(this);
	    }
	  });
	
        // Initialize the data for the topic
        indexTopicNodes = {};
        $('a[id^=topic_]',indexControlsDiv).each(treeNodeLoader);


        // Don't show the user editing controls if they don't have permission
        // (also enforced on the backend, of course)
        if ($('#isSuperuser').length > 0 ||
            $('#isIndexOwner').length > 0) {
          KIFFETS.core.setupTopicControls(indexControlsDiv, function(node) {
                               KIFFETS.core.currentHoverTopicNode = node;
                             });

          // Also add a listener to the rearrange button - this is added
          // here so it fires first
          $('#rearrangeTopicsButton').click(function(evt) {
                                              setRearrangeTopicsPopupVisible(true);
					      var topPx = $('#rearrangeTopicsPopup').css('top');
					      topPx = (topPx.indexOf('px') == -1) ? topPx : topPx.substring(0,topPx.indexOf('px'));
					      $('html, body').animate({'scrollTop':parseInt(topPx)});
					      
                                              return false;
                                            });
        }

        // We do a special case initialization for the root topic - which has the value
        // 0 in the topic tree
        indexTopicNodes[$('#indexRootTopicID').text()] = indexTopicNodes['0'];
        indexTopicNodes['0'].data('rootTopicID',$('#indexRootTopicID').text());

        // Unselect the root node - it'll get selected in a minute
        indexTopicNodes['0'].removeClass('selected');

        // Add listener to the index search box
        var searchFormOps = {
          url: '/ajaxSearch/',
          beforeSubmit: function (formData, jqForm, options) {
            var queryTxt = $('#searchIndexForm')[0].query.value;
            if (queryTxt && queryTxt.length > 0) {
              // Send the query through the history mechanism
              jQuery.historyLoad('index='+encodeFragmentComponent(idxNode.data('name'))+'&query='+encodeFragmentComponent(queryTxt));
              return false;
            }
            return false;
          },
          success: function (responseTxt, statusTxt) {
            // This should never get called - the beforeSubmit always returns false
            // and submits through the history mechanism
            //loadHTML(responseTxt);
          },
          error: function(request, textStatus, errorThrown) {
            loadHTML(request.responseText);
          },
          type: 'post'
        };
        $('#searchIndexForm').ajaxForm(searchFormOps);
        
        
        // Notify the callback function that the controls have been loaded
        if (callback) {
          callback();
        }

        // Hide the sources to start
        // elementToggle('sourceTogglable','sourceToggler', true);

        // The community may not be showing the latest date range - if it doesn't have any
        // articles.  So get the min date range, and update the topic labels based on that
        // date range.
        //
        // NOTE: Don't add this request to the cancel list - once the topics are loaded then
        // continue to load the article counts regardless.  This prevents canceling the
        // counts if the user quickly clicks on a topic after they load.

        window.setTimeout(function() {
                            loadTopicArticleCounts('latest');
                          }, 1000);              
        
        $('.treeview', indexControlsDiv).css('display','block');                
      }, 
        dataType: 'html',
        error: function(request, textStatus, errorThrown) {
        loadHTML(request.responseText);
      }
    });
  addAjaxRequest(outerReq);
}

/*****************************************************************/
/* Page loading functions
/*****************************************************************/

/**
 * Force a topic to be loaded - via history
 */
function loadIndexThenTopic(lIndexName, topicID, offset, setDateRange) {
  var lowerName = lIndexName.toLowerCase();

  if (indexMap != null && 
      indexMap.hasOwnProperty(lowerName)) {

    var queryStr = 'index='+encodeFragmentComponent(lIndexName)+'&topic='+topicID;
    if (setDateRange) {
      queryStr += '&dateRange='+setDateRange;
    }
    if (offset > 0) {
      queryStr += '&offset='+offset;
    }
    jQuery.historyLoad(queryStr);
  } else {    
    var fullURL = '/i/'+encodeURIComponent(lIndexName)+'/';
    if (topicID > 0) {
      fullURL += topicID + '/';
    }
    if (setDateRange) {
      fullURL += 'd/'+setDateRange+'/';
    }
    if (offset > 0) {
      fullURL += '?offset='+offset;
    }
    location = fullURL;
  }
  return false;
}

/**
 * Force a topic to be loaded - which means first loading the index if it's not loaded 
 */
function loadIndexThenTopicContent(lIndexName, topicID, offset, setDateRange) {
  var lowerName = lIndexName.toLowerCase();

  if (indexMap != null && 
      indexMap.hasOwnProperty(lowerName)) {
    var lowerName = lIndexName.toLowerCase();

    var indexNode = indexMap[lowerName];
    loadIndexContent(indexNode, false, false, function() {
                       loadTopicContent(topicID, offset, setDateRange);
                     });
  } else {
    var fullURL = '/i/'+encodeURIComponent(lIndexName)+'/';
    if (topicID > 0) {
      fullURL += topicID + '/';
    }
    if (setDateRange) {
      fullURL += 'd/'+setDateRange+'/';
    }
    if (offset > 0) {
      fullURL += '?offset='+offset;
    }
    location = fullURL;
  }
}


/**
 * Basic function to load an index then call the callback
 */
function loadIndexThenCallback(lIndexName, callback) {
  var lowerName = lIndexName.toLowerCase();
  if (indexMap != null && 
      indexMap.hasOwnProperty(lowerName)) {
    var indexNode = indexMap[lowerName];
    loadIndexContent(indexNode, false, false, callback);
  }
}


/**
 * Loads the specified index, then searches inside - via history
 */
function loadIndexThenSearch(lIndexName,  searchString) {
  jQuery.historyLoad('index='+encodeFragmentComponent(lIndexName)+'&query='+encodeFragmentComponent(searchString));
  return false;
}

/**
 * Actually loads the index and search content 
 */
function loadIndexThenSearchContent(lIndexName, searchString) {
  var lowerName = lIndexName.toLowerCase();
  if (indexMap != null && 
      indexMap.hasOwnProperty(lowerName)) {
    var indexNode = indexMap[lowerName];
    loadIndexContent(indexNode, false, false, function() {
                       loadIndexSearchContent(lIndexName, searchString);
              });
  } 
}

/**
 * Load just the within index search content 
 */
function loadIndexSearchContent(lIndexName, searchString) {
  // This isn't really necessary, but clears
  // the index request from memory
  abortAjaxRequests();

  // This actually does the ajax call
  var req = jQuery.ajax({
			  type: 'POST',
			  url: '/ajaxSearch/',
			  data: {'commName':lIndexName,'query':searchString, 'sType': 'Search Index'},
			  success: function(data) {
			    var channelNode = KIFFETS.channel.getChannelNode();
			    updateViewSelection(getIndexListEntryRoot(channelNode), channelNode, null, null, -1);
			    loadHTML(data);
			    pageTracker._trackPageview('/ajaxSearch/withinIndex/');            
			  },
			  dataType: 'html',
			  error: function(request, textStatus, errorThrown) {
			    loadHTML(request.responseText);
			  }
    });
  addAjaxRequest(req);
}


/**
 * Load one of the other top story tabs on the overview - 
 * **NOTE that these tab changes don't modify history
 */
function loadMyKiffetTab(tabName) {
  loadOverviewContent(tabName);
}


/**
 * Load the hot topics page for the selected index - via history
 */
function loadIndexHotTopics() {
  var channelName = KIFFETS.channel.getChannelName();
  if (channelName != null) {
    jQuery.historyLoad('index='+encodeFragmentComponent(channelName)+'&hotTopics');
  }
  return false;
}

/**
 * Loads the specified index first and then the hot topic content
 */
function loadIndexThenHotTopicsContent(lIndexName) {
  var lowerName = lIndexName.toLowerCase();
  if (indexMap != null && 
      indexMap.hasOwnProperty(lowerName)) {
    var indexNode = indexMap[lowerName];
    loadIndexContent(indexNode, false, false, function() {
                       loadIndexHotTopicsContent();
              });
  }   
}


/**
 * Loads the hot topic content for the current index
 */
function loadIndexHotTopicsContent() {
  var channelName = KIFFETS.channel.getChannelName();
  if (channelName) {
    abortAjaxRequests();

    var url = '/i/'+encodeURIComponent(channelName)+'/hotAjax/';
    var req = jQuery.ajax({
      type: 'GET',
          url: url,
          success: function(data) {
            loadHTML(data);
            pageTracker._trackPageview(url);            
          },
          dataType: 'html',
          error: function(request, textStatus, errorThrown) {
          loadHTML(request.responseText);
        }
      });  
    addAjaxRequest(req);
  }
}

/*****************************************************************/
/* Load index functions
/*****************************************************************/

/**
 * Load an index into the MyKiffets page - through history
 */
function loadIndex(idxNode) {
  if (idxNode) {
    var idxName = idxNode.data('name');
    jQuery.historyLoad('index='+encodeFragmentComponent(idxName));
  }
  return false;
}

/**
 * Load an index pages content
 */
function loadIndexContent(idxNode, forceLoad, loadContent, callback) {
  // Cancel any other page loading requests
  abortAjaxRequests();

  // When an index is selected, reduce the size of the myKiffets list
  // And scroll our index into view
  updateIndexListSize(true, idxNode);

  // Reset the index state whenever a new node is selected
  indexTopicNodes = null;
  currentDateRange = null;

  // We change the display when a label is clicked
  if (loadContent) {
    loadContentForNode(idxNode, null, forceLoad, -1, null);

    // Load the index tree with a slight delay
    window.setTimeout(function() {
                        loadIndexControls(idxNode, callback);
                      }, 100);
  } else {
    loadIndexControls(idxNode, callback);
    updateViewSelection(getIndexListEntryRoot(idxNode), idxNode, null, null, -1);
  }
}

/*****************************************************************/
/* Load topic functions
/*****************************************************************/


/**
 * Load the specified topic in the current index - via history
 */ 
function loadTopic(topicID, offset, setDateRange) {
  var channelName = KIFFETS.channel.getChannelName();  
  if (channelName) {
    var queryStr = 'index='+encodeFragmentComponent(channelName)+'&topic='+topicID;
    if (setDateRange) {
      queryStr += '&dateRange='+setDateRange;
    }
    if (offset > 0) {
      queryStr += '&offset='+offset;
    }

    jQuery.historyLoad(queryStr);
  }
  return false;
}


/**
 * Load the full topic tree - assuming the bare bones topic tree is loaded
 */
function loadFullTopicTree(callback) {
  var channelName = KIFFETS.channel.getChannelName();
  jQuery.ajax(
              {
              type: 'GET',
                  url: '/i/'+encodeURIComponent(channelName)+'/fullTopicTreeAjax/',
                  success: function(data) {
                  // Load this data into the topic tree
                  $('.topicTreeContent .treeview').html(data);
                  $('.topicTreeContent #tree').treeview(
                                                        {collapsed: true,
                                                            animated: "medium",
                                                            prerendered: true,
                                                            persist: "location"
                                                            });


                  var leftBarControls = $('.indexLeftBar');

                  // Initialize the data for the topic
                  indexTopicNodes = {};
                  $('a[id^=topic_]',leftBarControls).each(treeNodeLoader);


                  // Don't show the user editing controls if they don't have permission
                  // (also enforced on the backend, of course)
                  if ($('#isSuperuser').length > 0 ||
                      $('#isIndexOwner').length > 0) {
                    KIFFETS.core.setupTopicControls(leftBarControls, function(node) {
                                                      KIFFETS.core.currentHoverTopicNode = node;
                                                    });

                    // Also add a listener to the rearrange button - this is added
                    // here so it fires first
                    $('#rearrangeTopicsButton').click(function(evt) {
                                                        setRearrangeTopicsPopupVisible(true);
                                                        var topPx = $('#rearrangeTopicsPopup').css('top');
                                                        topPx = (topPx.indexOf('px') == -1) ? topPx : topPx.substring(0,topPx.indexOf('px'));
                                                        $('html, body').animate({'scrollTop':parseInt(topPx)});
							
                                                        return false;
                                                      });
                  }

                  // We do a special case initialization for the root topic - which has the value
                  // 0 in the topic tree
                  indexTopicNodes[$('#indexRootTopicID').text()] = indexTopicNodes['0'];
                  indexTopicNodes['0'].data('rootTopicID',$('#indexRootTopicID').text());

                  // Unselect the root node - it'll get selected in a minute
                  indexTopicNodes['0'].removeClass('selected');

                  if (callback) {
                    callback();
                  }
                }
              });
}

/**
 * Load the specified topic in the given index
 */
function loadTopicContent(topicID, offset, setDateRange) {
  if (indexTopicNodes == null) {
    return;
  }

  var topicNode = indexTopicNodes[topicID.toString()];
  if (topicNode == null) {
    // If the topic node isn't found, then just go ahead and load the whole tree
    // this happens if the user clicks on a nested topic name on the right side of the page

    loadFullTopicTree(function() {
                        // Re-call the topic loading function
                        loadTopicContent(topicID, offset, setDateRange);
                      });

    return;
  }

  // Abort any other content loading
  abortAjaxRequests();

  loadContentForNode(KIFFETS.channel.getChannelNode(), topicNode, false, offset, setDateRange);
}

/*****************************************************************/
/* Load source functions
/*****************************************************************/

/**
 * Load the specified source in the current index - via history
 */ 
function loadSource(sourceID, offset) {
  var channelName = KIFFETS.channel.getChannelName();
  if (channelName) {
    jQuery.historyLoad('index='+encodeFragmentComponent(channelName)+'&source='+sourceID+'&offset='+offset);
  }
  return false;
}

/**
 * Load the specified index then the content for the specified source
 */
function loadIndexThenSourceContent(lIndexName, sourceID, offset) {
  var lowerName = lIndexName.toLowerCase();
  if (indexMap != null && 
      indexMap.hasOwnProperty(lowerName)) {
    var indexNode = indexMap[lowerName];
    loadIndexContent(indexNode, false, false, function() {
                       loadSourceContent(sourceID, offset);
                     });
  }
  return false;
}


/**
 * Load content for the specified source in the given index
 */
function loadSourceContent(sourceID, offset) {
  var channelName = KIFFETS.channel.getChannelName();
  if (channelName == null) {
    return;
  }

  url = '/i/'+encodeURIComponent(channelName)+'/s/'+sourceID+'/ajaxRead/';
  if (offset > 0) {
    url += '?offset='+offset;
  }

  // Abort any other content loading
  abortAjaxRequests();

  var channelNode = KIFFETS.channel.getChannelNode();  
  var req = jQuery.ajax({
    type: 'GET',
        url: url,
        success: function (data) {
        updateViewSelection(getIndexListEntryRoot(channelNode), channelNode, null, null, -1);
        loadHTML(data);
        pageTracker._trackPageview(url);
          },
        dataType: 'html',
        error: function(request, textStatus, errorThrown) {
        loadHTML(request.responseText);
      }
    });    
  addAjaxRequest(req);
}

/*****************************************************************/
/* Add source functions
/*****************************************************************/

/**
 * Loads the add sources page via history
 */
function loadAddSourcesPage() {
  var channelName = KIFFETS.channel.getChannelName();
  if (channelName) {
    jQuery.historyLoad('index='+encodeFragmentComponent(channelName)+'&addSources');
  }
  return false;
}

/**
 * Loads the add sources page content
 */
function loadAddSourcesPageContent() {
  var channelName = KIFFETS.channel.getChannelName();
  if (channelName == null) {
    return;
  }

  // Abort any other content loading
  abortAjaxRequests();  

  var channelNode = KIFFETS.channel.getChannelNode();
  var req = jQuery.ajax({
    type: 'GET',
        url: '/i/'+encodeURIComponent(channelName)+'/addSourceAjax/',
        success: function(data) {
        updateViewSelection(getIndexListEntryRoot(channelNode), channelNode, null, null, -1);
        loadHTML(data);
        initializeFindSourcePage(function() {
                                   // Reload the index after a source is added - this
                                   // reloads the topic tree and source list
                                   //loadIndexContent(channelNode, true, false, function() {
                                   //                   loadAddSourcesPageContent();
                                   //});
                                 });
        pageTracker._trackPageview("/addSourceAjax/");
      },
        dataType: 'html',
        error: function(request, textStatus, errorThrown) {
        loadHTML(request.responseText);
      }
    });      

  addAjaxRequest(req);
}

/*****************************************************************/
/* Add topic functions                                           */
/*****************************************************************/

/**
 * Loads the add topic page via history
 */
function loadAddTopicPage() {
  var channelName = KIFFETS.channel.getChannelName();
  if (channelName) {
    jQuery.historyLoad('index='+encodeFragmentComponent(channelName)+'&addTopic');
  }
  return false;
}

/**
 * Loads the actual add sources page content
 */
function loadAddTopicPageContent() {
  var channelName = KIFFETS.channel.getChannelName();
  if (channelName == null) {
    return;
  }

  // Abort any other content loading
  abortAjaxRequests();  

  var channelNode = KIFFETS.channel.getChannelNode();
  var req = jQuery.ajax({
    type: 'GET',
        url: '/i/'+encodeURIComponent(channelName)+'/addTopicAjax/',
        success: function(data) {

	  
          updateViewSelection(getIndexListEntryRoot(channelNode), channelNode, null, null, -1);
          loadHTML(data);
          initializeAddTopicPage(channelName, function(topicID) {
                                   // For the index to reload so the topics reload
                                   loadIndexContent(channelNode, true, false, function() {
                                                      // This updates the hash and loads the refine topic page
                                                      var topicNode = indexTopicNodes[topicID.toString()];
                                                      loadRefineTopicPage(topicNode);
						    });
				 });
          pageTracker._trackPageview("/addTopicAjax/");
      },
        dataType: 'html',
        error: function(request, textStatus, errorThrown) {
        loadHTML(request.responseText);
      }
    });      

  addAjaxRequest(req);
}

/*****************************************************************/
/* Search functions                                              */
/*****************************************************************/

/**
 * Passes a top level search into the history mechanism
 */
function loadSearch(searchQuery) {
  jQuery.historyLoad('search='+encodeFragmentComponent(searchQuery));
  return false;;
}

/**
 * Loads the search results for the specified query
 */
function loadSearchContent(searchQuery) {
  // Abort any other content loading
  abortAjaxRequests();  

  $('#searchQueryInput').val(searchQuery);  

  var req = jQuery.ajax({
    type: 'GET',
        url: '/ajaxSearch/', 
        data: {'query': searchQuery},
        success: function(data) {

        // Reset the page state
        removeIndexDOMNodes();
        updateIndexListSize(false, null);
        updateViewSelection(null, null, null, null, -1);

        loadHTML(data);
        pageTracker._trackPageview('/ajaxSearch/topLevel/');
      }, 
        dataType: 'html',
        error: function(request, textStatus, errorThrown) {
        loadHTML(request.responseText);
      }
    });

  addAjaxRequest(req);
}

function loadSearchCommunities(searchQuery) {
  jQuery.historyLoad('search='+encodeFragmentComponent(searchQuery)+'&allIndexes');  
  return false;
}

function loadSearchCommunitiesContent(searchQuery) {
  // Abort any other content loading
  abortAjaxRequests();  

  $('#searchQueryInput').val(searchQuery);  

  var req = jQuery.ajax({
    type: 'GET',
        url: '/ajaxSearch/communities/', 
        data: {'query': searchQuery},
        success: function(data) {

        // Reset the page state
        removeIndexDOMNodes();
        updateIndexListSize(false, null);
        updateViewSelection(null, null, null, null, -1);

        loadHTML(data);
        pageTracker._trackPageview('/ajaxSearch/communities/topLevel/');
      }, 
        dataType: 'html',
        error: function(request, textStatus, errorThrown) {
        loadHTML(request.responseText);
      }
    });

  addAjaxRequest(req);  
}

/*****************************************************************/
/* Improve Index Page functions                                  */
/*****************************************************************/

function loadImproveIndex() {
  var channelName = KIFFETS.channel.getChannelName();
  if (channelName) {
    jQuery.historyLoad('index='+encodeFragmentComponent(channelName)+'&improve');
  }
  return false;
}

function loadImproveIndexContent(lIndexName) {
  // Abort any other content loading
  abortAjaxRequests();  

  var channelNode = KIFFETS.channel.getChannelNode();
  var loadURL = '/i/'+encodeURIComponent(lIndexName)+'/improveAjax/';
  var req = jQuery.ajax({
    type: 'GET',
        url: loadURL, 
        data: {},
        success: function(data) {
          updateViewSelection(getIndexListEntryRoot(channelNode), channelNode, null, null, -1);
          loadHTML(data);
          pageTracker._trackPageview(loadURL);
	}, 
        dataType: 'html',
        error: function(request, textStatus, errorThrown) {
        loadHTML(request.responseText);
      }
    });
  
  addAjaxRequest(req);    
}

function loadImproveStoriesPage() {
  var channelName = KIFFETS.channel.getChannelName();
  if (channelName) {
    jQuery.historyLoad('index='+encodeFragmentComponent(channelName)+'&improveStories');
  }
  return false;
}

function loadImproveStoriesContent(lIndexName) {
  // Abort any other content loading
  abortAjaxRequests();  

  var channelNode = KIFFETS.channel.getChannelNode();
  var loadURL = '/i/'+encodeURIComponent(lIndexName)+'/improveStoriesAjax/';
  var req = jQuery.ajax({
    type: 'GET',
        url: loadURL, 
        data: {},
        success: function(data) {
          updateViewSelection(getIndexListEntryRoot(channelNode), channelNode, null, null, -1);
          loadHTML(data);
          pageTracker._trackPageview(loadURL);
      }, 
        dataType: 'html',
        error: function(request, textStatus, errorThrown) {
        loadHTML(request.responseText);
      }
    });
  
  addAjaxRequest(req);    
}

/**
 * Passes on the request for the refine topic page with the currently selected topic
 */
function loadRefineCurrentTopicPage() {
  loadRefineTopicPage(currentTopicNode);
}

function loadRefineTopicPage(topicNode) {
  var channelName = KIFFETS.channel.getChannelName();
  if (channelName) {
    var newHash = 'index='+encodeFragmentComponent(channelName)+'&refineTopic';
    if (topicNode) {
      newHash = newHash + '='+topicNode.data('topicID');
    }
    jQuery.historyLoad(newHash);
  }
  return false;
}

function loadRefineTopicContent(lIndexName, topicID) {
  // Abort any other content loading
  abortAjaxRequests();  

  var loadURL = '/i/'+encodeURIComponent(lIndexName)+'/refineTopicAjax/';
  if (topicID != -1) {
    loadURL = loadURL + topicID + '/';
  }

  var channelNode = KIFFETS.channel.getChannelNode();
  var req = jQuery.ajax({
			  type: 'GET',
			  url: loadURL, 
			  data: {},
			  success: function(data) {
			    updateViewSelection(getIndexListEntryRoot(channelNode), channelNode, null, null, -1);        
			    loadHTML(data);
			    pageTracker._trackPageview(loadURL);
			  }, 
			  dataType: 'html',
			  error: function(request, textStatus, errorThrown) {
			    loadHTML(request.responseText);
			  }
    });
  
  addAjaxRequest(req);    
}

/*****************************************************************/
/* Topic control callbacks - acts on the currently hovered node
/*****************************************************************/

/**
 * Rename the currently hovered topic.  
 */
function renameHoveredTopic() {
  var renameTopic = KIFFETS.core.currentHoverTopicNode;

  var channelName = KIFFETS.channel.getChannelName();
  if (renameTopic && channelName != null) {
    var topicID = renameTopic.data('topicID');

    function renameCallback(newName) {
      if (topicID == '0') {
        location = '/i/'+encodeURIComponent(newName)+'/';
      } else {
        var artCount = renameTopic.data('articleCount');
                
        renameTopic.data('name',newName);
        renameTopic.text(artCount ? newName + ' (' + artCount + ')' : newName);

        /**
         * Currently not reloading the right side of the page - 
           this will cause it to sometimes hold invalid data, but it will be temporary
           and will allow for faster editing 

        // Reload the right side of the page
        var hash = (location.hash) ? location.hash.substring(1) : '';
        historyPageChanged(hash);
        */
      }
    }

    renameTopicBase(channelName, renameTopic, renameCallback);
  }
}

function refineHoveredTopic() {
  if (KIFFETS.core.currentHoverTopicNode) {
    loadRefineTopicPage(KIFFETS.core.currentHoverTopicNode);
  }
  return false;
}

function addChildToHoveredTopic() {
  var parentTopic = KIFFETS.core.currentHoverTopicNode;  

  var channelName = KIFFETS.channel.getChannelName();
  var channelNode = KIFFETS.channel.getChannelNode();
  if (parentTopic && channelName != null) {

    function addChildRefine(topicID) {
      loadIndexThenCallback(channelName, function() {
                              function loadRefineTopicCallback() {
                                var topicNode = indexTopicNodes[topicID];
                                if (topicNode != null) {
                                  loadRefineTopicPage(topicNode);
                                  updateViewSelection(getIndexListEntryRoot(channelNode), channelNode, topicNode, '', '');                                  
                                }
                              }

                              // If loading the index doesn't load the topic,
                              // then we have to manually load the full tree
                              // and retry
                              if (!indexTopicNodes.hasOwnProperty(topicID)) {
                                loadFullTopicTree(loadRefineTopicCallback);
                              } else {
                                loadRefineTopicCallback();
                              }
                            });
    }
    
    addChildBase(channelName, parentTopic, addChildRefine);
  }
  return false;
}

function deleteHoveredTopic() {
  var delTopic = KIFFETS.core.currentHoverTopicNode;
  var channelName = KIFFETS.channel.getChannelName();
  if (delTopic && channelName != null) {
    var topicID = delTopic.data('topicID');

    function delCallback() {
      if (topicID == '0') {
        // Reload this page with no hash
        location.hash = '';
        location.reload();
      } else {
        // This forces the control reload
        loadIndexContent(KIFFETS.channel.getChannelNode(), true, false, function() {
                           // This updates the hash to the index page, but doesn't force
                           // a reload on the controls
                           loadIndex(KIFFETS.channel.getChannelNode());
                         });
      }
    }


    deleteTopicBase(channelName, delTopic, delCallback);
  }  
  return false;
}

/*****************************************************************/
/* Overview functions                                            */
/*****************************************************************/

function selectOverviewTab() {
  var oTab = $('#myOverviewTab');
  var oTabDiv = oTab.parent();
  if (!oTabDiv.hasClass('navBarSelectedTab')) {
    oTab.html('<IMG SRC="/media/img/new/homeTab.gif" ALT="MY OVERVIEW">');
    oTabDiv.removeClass('navBarOption');
    oTabDiv.addClass('navBarSelectedTab');
  }

  KIFFETS.channel.setSelectedTab(oTab, 'HOME');
}

function selectNewsTab() {
  var nTab = $('#newsTab');
  var nTabDiv = nTab.parent();
  if (!nTabDiv.hasClass('navBarSelectedTab')) {
    nTab.html('<IMG SRC="/media/img/new/newsTab.gif" ALT="NEWS">');
    nTabDiv.removeClass('navBarOption');
    nTabDiv.addClass('navBarSelectedTab');
  }

  KIFFETS.channel.setSelectedTab(nTab, 'NEWS');
}


function selectBrowseTab() {
  var bTab = $('#browseTab');
  var bTabDiv = bTab.parent();
  if (!bTabDiv.hasClass('navBarSelectedTab')) {
    bTab.html('<IMG SRC="/media/img/new/browseTab.gif" ALT="BROWSE">');
    bTabDiv.removeClass('navBarOption');
    bTabDiv.addClass('navBarSelectedTab');
  }

  KIFFETS.channel.setSelectedTab(bTab, 'BROWSE');
}

/**
 * Called externally and passed to the history mechanism
 */
function loadOverview(tabName) {
  if (usernameEl.length == 0) {
    doLogin();
  } else {
    jQuery.historyLoad('overview='+encodeFragmentComponent(tabName));
    return false;
  }
}

/**
 * Actually loads the overview content
 */
function loadOverviewContent(tabName) {
  removeIndexDOMNodes();

  // Abort any other content loading
  abortAjaxRequests();

  var url = '/my/ajaxHome/';
  if (tabName) {
    url += tabName+'/';
  }

  var req = jQuery.ajax({
    type: 'GET',
        url: url, 
        success: function(data) {
        updateViewSelection($('#overviewLbl'), null, null, null, -1);
        updateIndexListSize(false, null);

        selectOverviewTab();
        loadHTML(data);
        pageTracker._trackPageview(url);
      }, 
        dataType: 'html',
        error: function(request, textStatus, errorThrown) {
        loadHTML(request.responseText);
      }
    });

  addAjaxRequest(req);

  removeIndexDOMNodes();
}

/**
 * Set the hash to browsing
 */
function loadHotNews(offset) {
  if (offset) {
    jQuery.historyLoad('hotNews='+offset);
  } else {
    jQuery.historyLoad('hotNews');
  }
  return false;  
}

/**
 * Load the browse content
 */
function loadHotNewsContent(offset) {
  removeIndexDOMNodes();

  // Abort any other content loading
  abortAjaxRequests();

  var url = '/browseHotNewsAjax/';
  if (offset) {
    url += '?offset='+offset;
  }

  var req = jQuery.ajax({
    type: 'GET',
        url: url, 
        success: function(data) {
        updateViewSelection($('#hotNewsLbl'), null, null, null, -1);
        updateIndexListSize(false, null);

        selectNewsTab();
        loadHTML(data);
        pageTracker._trackPageview(url);

      }, 
        dataType: 'html',
        error: function(request, textStatus, errorThrown) {
        loadHTML(request.responseText);
      }
    });

  addAjaxRequest(req);

  removeIndexDOMNodes();
}

/**
 * Set the hash to browsing
 */
function loadTopCluster(id) {
  if (id) {
    jQuery.historyLoad('topClusters='+id);
  } else {
    jQuery.historyLoad('topClusters');
  }
  return false;  
}

/**
 * Load the browse content
 */
function loadTopClustersContent(id) {
  removeIndexDOMNodes();

  // Abort any other content loading
  abortAjaxRequests();

  var url = '/browseTopClustersAjax/';
  if (id) {
    url += '?id='+id;
  }

  var req = jQuery.ajax({
    type: 'GET',
        url: url, 
        success: function(data) {
        updateViewSelection($('#topClustersLbl'), null, null, null, -1);
        updateIndexListSize(false, null);

        selectNewsTab();
        loadHTML(data);
        pageTracker._trackPageview(url);

      }, 
        dataType: 'html',
        error: function(request, textStatus, errorThrown) {
        loadHTML(request.responseText);
      }
    });

  addAjaxRequest(req);

  removeIndexDOMNodes();
}


/**
 * Set the hash to browsing
 */
function loadBrowseHotChannels(offset) {
  if (offset) {
    jQuery.historyCallback('browseHotChannels='+offset);
  } else {
    jQuery.historyCallback('browseHotChannels');
  }
  return false;  
}

/**
 * Load the browse content
 */
function loadBrowseHotChannelsContent(offset) {
  // Abort any other content loading
  abortAjaxRequests();

  var url = '/browseHotChannelsAjax/';
  if (offset) {
    url += '?offset='+offset;
  }

  var req = jQuery.ajax({
    type: 'GET',
        url: url, 
        success: function(data) {
        updateViewSelection($('#browseChannelsLbl'), null, null, null, -1);
        updateIndexListSize(false, null);

        selectBrowseTab();
        loadHTML(data);
        pageTracker._trackPageview(url);
      }, 
        dataType: 'html',
        error: function(request, textStatus, errorThrown) {
        loadHTML(request.responseText);
      }
    });

  addAjaxRequest(req);

  removeIndexDOMNodes();
}

/**
 * Set the hash to browsing
 */
function loadBrowseStaffChannels() {
  jQuery.historyLoad('browseStaffChannels');
  return false;  
}

/**
 * Load the browse content
 */
function loadBrowseStaffChannelsContent() {
  // Abort any other content loading
  abortAjaxRequests();

  var url = '/browseStaffChannelsAjax/';
  var req = jQuery.ajax({
    type: 'GET',
        url: url, 
        success: function(data) {
        updateViewSelection($('#browseChannelsLbl'), null, null, null, -1);
        updateIndexListSize(false, null);

        selectBrowseTab();
        loadHTML(data);
        pageTracker._trackPageview(url);
      }, 
        dataType: 'html',
        error: function(request, textStatus, errorThrown) {
        loadHTML(request.responseText);
      }
    });

  addAjaxRequest(req);

  removeIndexDOMNodes();
}

/**
 * Set the hash to browsing
 */
function loadBrowseAllChannels(offset) {
  if (offset) {
    jQuery.historyLoad('browseAllChannels='+offset);
  } else {
    jQuery.historyLoad('browseAllChannels');
  }
  return false;  
}

/**
 * Load the browse content
 */
function loadBrowseAllChannelsContent(offset) {
  // Abort any other content loading
  abortAjaxRequests();

  var url = '/browseAllChannelsAjax/';
  if (offset) {
    url += '?offset='+offset;
  }

  var req = jQuery.ajax({
    type: 'GET',
        url: url, 
        success: function(data) {
        updateViewSelection($('#browseChannelsLbl'), null, null, null, -1);
        updateIndexListSize(false, null);

        selectBrowseTab();
        loadHTML(data);
        pageTracker._trackPageview(url);
      }, 
        dataType: 'html',
        error: function(request, textStatus, errorThrown) {
        loadHTML(request.responseText);
      }
    });

  addAjaxRequest(req);

  removeIndexDOMNodes();
}


/**
 * Set the hash to browsing
 */
function loadBrowseNewChannels(offset) {
  if (offset) {
    jQuery.historyLoad('browseNewChannels='+offset);
  } else {
    jQuery.historyLoad('browseNewChannels');
  }
  return false;  
}

/**
 * Load the browse content
 */
function loadBrowseNewChannelsContent(offset) {
  // Abort any other content loading
  abortAjaxRequests();

  var url = '/browseNewChannelsAjax/';
  if (offset) {
    url += '?offset='+offset;
  }

  var req = jQuery.ajax({
    type: 'GET',
        url: url, 
        success: function(data) {
        updateViewSelection($('#browseChannelsLbl'), null, null, null, -1);
        updateIndexListSize(false, null);

        selectBrowseTab();
        loadHTML(data);
        pageTracker._trackPageview(url);
      }, 
        dataType: 'html',
        error: function(request, textStatus, errorThrown) {
        loadHTML(request.responseText);
      }
    });

  addAjaxRequest(req);

  removeIndexDOMNodes();
}


/*****************************************************************/
/* Load Cluster functions
/*****************************************************************/


/**
 * Load the specified cluster in the current index - via history
 */ 
function loadCluster(clusterID) {
  var channelName = KIFFETS.channel.getChannelName();
  if (channelName) {
    jQuery.historyLoad('index='+encodeFragmentComponent(channelName)+'&cluster='+clusterID);
  }
  return false;
}

/**
 * Load the specified index then the content for the specified source
 */
function loadIndexThenClusterContent(lIndexName, clusterID) {
  var lowerName = lIndexName.toLowerCase();
  if (indexMap != null && 
      indexMap.hasOwnProperty(lowerName)) {
    var indexNode = indexMap[lowerName];
    loadIndexContent(indexNode, false, false, function() {
                       loadClusterContent(clusterID);
                     });
  }
  return false;
}


/**
 * Load content for the specified source in the given index
 */
function loadClusterContent(clusterID) {
  var channelNode = KIFFETS.channel.getChannelNode();
  if (channelNode == null) {
    return;
  }

  var url = '/i/'+encodeURIComponent(channelNode.data('name'))+'/c/'+clusterID+'/ajaxRead/';

  // Abort any other content loading
  abortAjaxRequests();
  
  var req = jQuery.ajax({
			  type: 'GET',
			  url: url,
			  success: function (data) {
			    updateViewSelection(getIndexListEntryRoot(channelNode), channelNode, null, null, -1);
			    loadHTML(data);
			    pageTracker._trackPageview(url);
			  },
			  dataType: 'html',
			  error: function(request, textStatus, errorThrown) {
			    loadHTML(request.responseText);
			  }
			});    
  addAjaxRequest(req);
}


/*****************************************************************/
/* Page redirect functions
/*****************************************************************/

/**
 * Do a smart redirect in the index wizard
 */
function gotoIndexWizard() {
  var channelName = KIFFETS.channel.getChannelName();
  if (channelName) {
    /* Check to see if the user is an owner or superuser -
       this is also checked later on the server, of course */
    var superNodes = $('#isSuperuser');
    var ownerNodes = $('#isIndexOwner');
    if (superNodes.length > 0 ||
        ownerNodes.length > 0) {
      // location = '/indexWizard/#'+encodeURIComponent(channelName);
      location = '/channelStart/';
    } else {
      location = '/channelStart/';
    }
  } else {
    location = '/channelStart/';
  }
  return false;
}



