  /*
   * Get a parameter from the windows location - http://www.mabaloo.com/Web-Development/Accessing-GET-parameters-with-Javascript.html
   */
  function getParameterString(name) {
    name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
    var regexS = "[\\?&]"+name+"=([^&#]*)";
    var regex = new RegExp( regexS );
    var results = regex.exec( window.location.href );
    if( results == null ) return "";
    else return results[1];
  }

  // Object that holds the current search filter sent to the Ajax call
  function ViewState() {
    this.categoryId = '';
    this.subcategoryId = '';
    this.tagId = '';
    this.subtagId = '';
    this.expandedCategories = '';

    this.getState = jcaState_getState;
    this.setUrl = jcaState_setUrl;
  }

  /*
   *  Appends a comma-separated name=value string
   */
  function appendParam(str, name, value)
  {
    if (value != '')
    {
      if (str != '')
        str += '&';
      str += name + '=' + value;
    }
    return str;
  }

  /*
   * Gets a string representing the current page state
   */
  function jcaState_getState()
  {
    var state = '';
    state = appendParam(state, 'categoryId', this.categoryId);
    state = appendParam(state, 'subcategoryId', this.subcategoryId);
    state = appendParam(state, 'tagId', this.tagId);
    state = appendParam(state, 'subtagId', this.subtagId);
    state = appendParam(state, 'expandedCategories', this.expandedCategories);
    if (state.length > 0)
      state = '#' + state;
    return state;
  }

  /*
   * Writes the current page state to the location bar
   */
  function jcaState_setUrl()
  {
    var state = this.getState();
    if (state.length > 0)
      window.location.hash = this.getState();
  }

  /*
   * Gets a comma-separated list of expanded category and subcategory IDs
   */
  function getExpandedCategories()
  {
    var catlist = '';
    $('div#course_list_results ul ul:visible, div#course_list_results ul ul ul:visible').each( function(i) {
      if (catlist != '')
        catlist += ',';
      catlist += this.id;
    });
    return catlist;
  }

  /*
   * Read the # querystring and populate the state object
   */
  function jcaState_load(state)
  {
    if (state != undefined)
    {
      state = state.replace(/#/, '');
      var parts = state.split('&');
      for (var i = 0; i < parts.length; i++)
      {
        var param = parts[i].split('=');
        State[param[0]] = param[1];
      }
    }
  }

  /*
   * Reads a page state string from the # querystring and restores the page state
   */
  function jcaState_restore()
  {
    // Load state
    jcaState_load(window.location.hash);

    highlightCategoryLink(State.categoryId);

    // Restore the topic selections
    $('#tag_filter').val(State.tagId);
    populateSubtagFilter();
    $('#sub_tag_filter').val(State.subtagId);

    // Execute search
    $('#course_list_results').load(JCA_AJAX_URL, State, function() {
        // Expand categories
        if (State.expandedCategories != '')
        {
          // TODO: check whether it's actually necessary to hide the subcategories
          $('.course_list li ul, .course_list li ul li ul').each( function(i) {
            $(this).hide();
          });

          var showCategories = '#' + State.expandedCategories.replace(/,/g, ',#');
          $(showCategories).each( function(i) {
            $(this).show();
          });
        }

    });

    $('#course_list').show();
  }

  /**
  * JCA_ApplyCategoryFilter is called when we want to apply a single category
  * filter to the course list. This function will apply the filter and then
  * refresh the list.
  *
  * @param string category the id of the category we are now filtering by
  *
  * @returns void
  * @see JCA_CourseSearch()
  */
  function JCA_ApplyCategoryFilter(categoryId, showCourses)
  {
    highlightCategoryLink(categoryId);
    State.categoryId = categoryId;
    JCA_CourseSearch(showCourses);
  }

  /**
  * JCA_ApplyCategoryOnlyFilter is called when "See all <Top Level Cat Name> courses"
  * is clicked. It clears all selected topics and then calls JCA_ApplyCategoryFilter()
  *
  * @param string category the id of the category we are now filtering by
  *
  * @returns void
  * @see JCA_ApplyCategoryFilter()
  * @see JCA_CourseSearch()
  */
  function JCA_ApplyCategoryOnlyFilter(categoryId)
  {
    $('#tag_filter')[0].selectedIndex = 0;
    $('#sub_tag_filter')[0].selectedIndex = 0;

    State.tagId = '';
    State.subtagId = '';

    JCA_ApplyCategoryFilter(categoryId, true);
  }

  /**
  * JCA_ApplyTagFilter is called when we want to apply a single top level tag
  * filter to the course list. This function will apply the filter and then
  * refresh the list.
  *
  * @returns void
  * @see JCA_CourseSearch()
  */
  function JCA_ApplyTagFilter() {
    State.tagId = $('#tag_filter').val();
    State.subtagId = '';
    populateSubtagFilter();
    $('#sub_tag_filter').selectbox();
  }

  /*
   * Populates the subtopic list based on the currently selected topic
   */
  function populateSubtagFilter()
  {
    var sub_tag_filter_dropdown = $('#sub_tag_filter')[0];
    sub_tag_filter_dropdown.options.length = 1;
    var tag_filter = $('#tag_filter').val();
    if (tag_filter !== '') {
      for (var i = 0; i < JCA_TAGS.length; i++) {
        if (JCA_TAGS[i].id == tag_filter) {
          for (var j = 0; j < JCA_TAGS[i].children.length; j++) {
            var opt = new Option(JCA_TAGS[i].children[j].label, JCA_TAGS[i].children[j].id);
            sub_tag_filter_dropdown.options[sub_tag_filter_dropdown.options.length] = opt;
          }
        }
      }
    }
  }

  /**
  * JCA_ApplySubTagFilter is called when we want to apply a single sub level tag
  * filter to the course list. This function will apply the filter and then
  * refresh the list.
  *
  * @returns void
  * @see JCA_CourseSearch()
  */
  function JCA_ApplySubTagFilter() {

    var sub_tag_filter_dropdown = $('#sub_tag_filter')[0];
    var sub_tag_filter = sub_tag_filter_dropdown.options[sub_tag_filter_dropdown.selectedIndex].value;

    if (sub_tag_filter == '') {
      JCA_ApplyTagFilter();
    }
    else {
      State.subtagId = sub_tag_filter;
      //JCA_CourseSearch(true);
    }

  }

  /**
  * JCA_CourseSearch is called to perform the search and refresh the results list
  *
  * @returns void
  */
  function JCA_CourseSearch(showCourses) {
    if (showCourses == true) {
      $('#course_list_results').load(JCA_AJAX_URL, State, function() {
        $(".course_list ul li ul").show();
        State.expandedCategories = getExpandedCategories();
        State.setUrl();
      });
    } else {
      $('#course_list_results').load(JCA_AJAX_URL, State, function() {
        State.expandedCategories = getExpandedCategories();
        State.setUrl();
      });
    }

    $('#course_list').show();
  }

  /**
  * On load create the tag filter drop down
  */
  $(document).ready(function(){

    //fixTitleAnchorBug();

    // Populate topic filter list
    var tag_filter_dropdown = $('#tag_filter')[0];
    for (var i in JCA_TAGS) {
      var opt = new Option(JCA_TAGS[i].label, JCA_TAGS[i].id);
      tag_filter_dropdown.options[tag_filter_dropdown.options.length] = opt;
    }

    // Catch Enter key in text search
    $("#free_search_submit").click( function(e) {
      e.preventDefault();
      doFreeSearch();
    } );
    $("#q").keyup(function(e) {
      e.preventDefault();
    	if(e.keyCode == 13) {
        doFreeSearch();
    	}
    });

    // Load a category by default when passed as GET parameter
    var catId = getParameterString('categoryId');
    if ( catId != '' ) {
      JCA_ApplyCategoryFilter(catId);
    } else if (requestHash != '' && window.location.hash == '') {
      // for friendly URLs with state querystrings, requestHash must be set on the page.
      jcaState_load(requestHash.replace(/&amp;/g, "&"));
      State.setUrl();
      jcaState_restore();
    } else {
      // Display categories and courses
      jcaState_restore();
    }

    $('#tag_filter').selectbox();
    $('#tag_filter_container').attr('onclick', 'javascript:JCA_ApplyTagFilter()');
    $('#sub_tag_filter').selectbox();


  });

  /*
   * Sets the style on the selected top-level category
   */
  function highlightCategoryLink(categoryId)
  {
    // make the category list item appear highlighted
    $('#categories_filter li').removeClass('onIt');
    $('#category_' + ((categoryId === '') ? 'all' : categoryId)).addClass('onIt');
  }

  /*
   * Attaches the page state to the URL before viewing a course
   */
  function viewCourse(link)
  {
    link.href += State.getState();
  }

  /*
   * Hide/Show a list of courses for a particular subcategory
   */
  function toggleCategory(cat) {
    $("#" + cat).toggle();
    State.expandedCategories = getExpandedCategories();
    State.setUrl();
  }

  /*
   * Pass a text search term to the advanced search page
   */
  function doFreeSearch(obj) {
    window.location = "search.page#q=" + encodeURIComponent($("#q").val());
  }

  /*
   * IE7 appends the page state # querystring to the browser title when it loads so let's change it back to what it should be
   */
  function fixTitleAnchorBug()
  {
    // Fix IE7 anchor-in-the-title bug
    //alert(document.title);
    //alert(document.title.split('#')[0]);
    //document.title = document.title.split('#')[0];
    // Actually, IE changes the title after this so we'll have to do something with setInterval to check when it has changed
  }