/// <reference path="jQuery.intellisense.js"/>
/// <reference path="xeko.js"/>
/// <reference path="messaging.js"/>

var force_error = false;

$(document).ready(function() {
	
	xeko.widgets.challenges.renderPage();
	
	// Sort By dropdown menu re-renders page
	$("#challengesSortBy").change(function(e) {
		xeko.widgets.challenges.reRenderPage();
	});
	
	// Page must be re-rendered upon login
	xeko.events.notify("xeko.widgets.challenges.reRenderPage", "login");
	
});

xeko.widgets.challenges = {
  "pageType": "",
  "isSingle": false,
  "singleGuid": "",
  "singleHasResponded": false,
  "responseMaxLength": 2000,
  "itemSubmitted": new Array(),
  "challengesXml": "",
  "totalItems": 0,
  "currentPage": 1,
  "numPages": 1,
  "itemsPerPage": 6,
  "itemsLoaded": false,
  "favoritesLoaded": false,
  "favorites": ",",
  "submitted": ",",
  "votedLoaded": false,
  "voted": {},
  "relatedChallenges": {},
  "challengeResponsesLoaded": {},
  "isOwner": {},
  "canRespond": {},
  "creatorVoteDate": {},
  "reRenderPage": function() {
    xeko.widgets.challenges.currentPage = 1,
		xeko.widgets.challenges.votedLoaded = false,
		xeko.widgets.challenges.voted = {};
    xeko.widgets.challenges.challengeResponsesLoaded = {};
    xeko.widgets.challenges.renderPage(true);
  },
  "renderPage": function(showLoading) {
    var singleChallengeUrl = /challenges\/[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}/;
    var singleChallengeVoteUrl = /challengesvote\/[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}/;
    var singleChallengeHofUrl = /challengeshalloffame\/[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}/;

    if (location.search.length > 1) {
      xeko.widgets.challenges.isSingle = true;
      xeko.widgets.challenges.singleGuid = location.search.substr(1);

      // current shorthand is url?[guid]
      xeko.widgets.challenges.loadChallenges("getsinglechallenge", xeko.widgets.challenges.singleGuid, null, showLoading);
    }
    else if (singleChallengeUrl.test(location.href)) {
      xeko.widgets.challenges.isSingle = true;
      xeko.widgets.challenges.singleGuid = location.href.substr(location.href.indexOf("/challenges/") + 12);

      // current shorthand is challenges/[guid]
      xeko.widgets.challenges.loadChallenges("getsinglechallenge", xeko.widgets.challenges.singleGuid, null, showLoading);
    }
    else if (singleChallengeVoteUrl.test(location.href)) {
      xeko.widgets.challenges.isSingle = true;
      xeko.widgets.challenges.singleGuid = location.href.substr(location.href.indexOf("/challengesvote/") + 16);

      // current shorthand is challengesvote/[guid]
      xeko.widgets.challenges.loadChallenges("getsinglechallenge", xeko.widgets.challenges.singleGuid, null, showLoading);
    }
    else if (singleChallengeHofUrl.test(location.href)) {
      xeko.widgets.challenges.isSingle = true;
      xeko.widgets.challenges.singleGuid = location.href.substr(location.href.indexOf("/challengeshalloffame/") + 22);

      // current shorthand is challengesvote/[guid]
      xeko.widgets.challenges.loadChallenges("getsinglechallenge", xeko.widgets.challenges.singleGuid, null, showLoading);
    }
    else {
      var reqType;

      // pageType variable (set in the HTML of the page) defines how to filter challenges
      if (xeko.widgets.challenges.pageType == "challenges") reqType = "getopenchallenges";
      else if (xeko.widgets.challenges.pageType == "challengesvote") reqType = "getresponseclosedchallenges";
      else if (xeko.widgets.challenges.pageType == "challengeshalloffame") reqType = "getvotingclosedchallenges";

      if ($("#challengesSortBy").length > 0) {
        xeko.widgets.challenges.loadChallenges(reqType, null, $("#challengesSortBy").val(), showLoading);
      }
      else {
        xeko.widgets.challenges.loadChallenges(reqType, null, null, showLoading);
      }
    }

    // Load previously voted on for this user
    if (xeko.agentinfo.agentIsGuest) xeko.widgets.challenges.votedLoaded = true;
    else if (xeko.widgets.challenges.pageType == "challengesvote") {
      // Reset
      xeko.widgets.challenges.votedLoaded = false;
      xeko.widgets.challenges.voted = {};

      $.ajax(
			{
			  method: "get",
			  url: "/widgets/widget.Challenges.handler.php",
			  data: "type=getvoted",
			  success: function(xml) {
			    // success?
			    if ($("error", xml).length > 0) {
			      //##TODO how to handle this error?
			      xeko.debug.trace('Unable to load voted: ' + err);
			    }
			    else if ($("agentchallengeresponsevote", xml).length > 0) {
			      $("agentchallengeresponsevote", xml).each(function() {
			        // Agent's votes are stored as a name-value pair with the challenge id as the index
			        xeko.widgets.challenges.voted[$("challengeid", this).text()] = $("agentchallengeresponseid", this).text();
			      });
			    }

			  },
			  error: function(xmlHttpRequest, status, err) {
			    //##TODO how to handle this error?
			    xeko.debug.trace('Unable to load voted: ' + err);
			  },
			  complete: function() {
			    xeko.widgets.challenges.votedLoaded = true
			  }
			});
    }
  },
  "loadChallenges": function(type, guid, sortby, showLoading) {
    var qsGuid = (guid != null) ? "&challengeguid=" + guid : "";
    var qsSort = (sortby != null) ? "&sortby=" + sortby : "";

    // "myfavorites" sort by is a special case
    if (sortby == "myfavorites") {
      type = "getfavorites";
      sortby = null;
    }

    // Clear error
    xeko.widgets.challenges.hideError(null);

    xeko.debug.trace("type=" + type + qsGuid + qsSort);
    // Load and cache Challenges XML
    $.ajax(
		{
		  method: "get",
		  url: "/widgets/widget.Challenges.handler.php",
		  data: "type=" + type + qsGuid + qsSort,
		  beforeSend: function() {
		    if (showLoading) xeko.lightbox.show(xeko.strings.LOADING, true, "ReRender");
		  },
		  complete: function() {
		    $("#challengesLoading").hide();
		    xeko.lightbox.hide("ReRender");
		  },
		  success: function(xml) {
		    // success?
		    // Cache XML for pagination & sorting
		    xeko.widgets.challenges.challengesXml = xml;
		    xeko.widgets.challenges.totalItems = $("challenge", xml).length;

		    xeko.debug.trace('rendering challenges...');
		    xeko.widgets.challenges.renderChallenges();
		    xeko.debug.trace('challenges rendered.');

		    if ($("challenge", xml).length == 0) {
		      // No challenges loaded
		      if (type == "getfavorites") {
		        xeko.widgets.challenges.throwError(null, xeko.messaging.alert.CHALLENGE_NO_FAVORITES);
		      }
		      else if ($("invalid", xml).length == 1) {
		        xeko.widgets.challenges.throwError(null, "Unable to load Challenge at this time. (" + $("invalid", xml).text() + ").");
		      }
		      else {
		        xeko.widgets.challenges.throwError(null, xeko.messaging.error.CHALLENGES_LOAD_ERROR);
		      }
		    }
		  },
		  error: function(xmlHttpRequest, status, err) {
			if (xmlHttpRequest.status > 0) xeko.widgets.challenges.throwError(null, xeko.messaging.error.CHALLENGES_LOAD_ERROR);
		  }
		});
  },
  "renderChallenges": function() {
    xeko.widgets.challenges.numPages = Math.ceil(this.totalItems / this.itemsPerPage);

    var firstItem = (this.itemsPerPage * (this.currentPage - 1)) + 1;
    var lastItem = (this.itemsPerPage * (this.currentPage - 1)) + this.itemsPerPage;
    if (lastItem > this.totalItems) lastItem = this.totalItems;
    
    var currentClass = "";
    var disabled = "";
    var disabledAttr = "";

    // No nav bar for single Challenges
    if (xeko.widgets.challenges.isSingle) {
      $("#challengesBackToAll").show();
    }
    else if (xeko.widgets.challenges.numPages == 1) {
      // Show sorting, but not pagination
      $("#challengesNavBar").show();
    }
    else if (this.totalItems == 0) {
      $("#challengesNavBarRange").hide();
      $(".challengesNavBarPagination").hide();
    }
    else {
      $("#challengesNavBar").show();
      $("#challengesNavBarRange").show();
      $(".challengesNavBarPagination").show();

      // Current range
      $("#challengesNavBarRange").html("showing " + firstItem + " - " + lastItem + " of <strong>" + this.totalItems + "</strong> Challenges");

      /********************************************************************************
      * Pagination
      * Rules are:
      * 	- Current page button is grouped in a group of four surrounding page buttons
      * 	- First page button and last page buttonare always displayed
      * 	- All other page buttons are represented by "..."
      ********************************************************************************/
      // Clear existing buttons, add spacer (for style hack)
      $(".challengesNavBarPagination").html("<img src=\"/images/clear.gif\" style=\"width: 1px; height: 15px;\" />");

      // Previous
      if (this.currentPage == 1) {
        disabled = "disabled";
        disabledAttr = "disabled=\"disabled\"";
      }
      $(".challengesNavBarPagination").append("<input class=\"prev\" type=\"image\" src=\"/images/btn/btn_challenges_prev" + disabled + ".png\" alt=\"prev\" " + disabledAttr + " />");

      // If page button is within 5 (or first, or last) then display
      // If page button is hidden and less than current then "..." and skip to current-2;
      // If page button is hidden and greater than current then "..." and skip to last
      for (var i = 1; i <= xeko.widgets.challenges.numPages; i++) {
        // If this page button is +/-1 the current page (or very first or very last page) show
        if ((i <= this.currentPage + 2 && i >= this.currentPage - 2) || i == xeko.widgets.challenges.numPages || i == 1) {
          currentClass = (i == this.currentPage) ? " current" : "";
          disabled = (i == this.currentPage) ? true : false;
          $(".challengesNavBarPagination").append("<input class=\"challengesPageTo pageTo" + i + "\" type=\"button\" />")
						.children(".pageTo" + i)	// select added elm
						.attr("value", i)
          //	.attr("onclick", "xeko.widgets.challenges.pageTo(" + i + ")")
						.addClass(currentClass)
						.attr("disabled", disabled)
					;
        }
        else if (i < this.currentPage) {
          $(".challengesNavBarPagination").append("...");
          i = this.currentPage - 3;
        }
        else {
          $(".challengesNavBarPagination").append("...");
          i = xeko.widgets.challenges.numPages - 1;
        }
      }

      // Next
      if (this.currentPage == xeko.widgets.challenges.numPages) {
        disabled = "disabled";
        disabledAttr = "disabled=\"disabled\"";
      }
      else {
        disabled = "";
        disabledAttr = "";
      }
      $(".challengesNavBarPagination").append("<input class=\"next\" type=\"image\" src=\"/images/btn/btn_challenges_next" + disabled + ".png\" alt=\"next\" " + disabledAttr + "/>");
    }

    // Remove all existing items
    //$("#challengesItem ~ div").remove();	// Does not work in jquery1.3.2 ??
    $("#challengesItem").siblings("div.challengesItem").remove();
    this.itemsLoaded = false;

    // Running list of Responded
    xeko.widgets.challenges.submitted = ",";

    // Add Challenges to the DOM
    var currentItem = 1;

    $("challenge", this.challengesXml).each(function() {

      if (currentItem >= firstItem && currentItem <= lastItem) {
        var guid = $(this).attr("id");
        var hotspot = xeko.utilities.removeSpaces($("card>hotspot", this).text());
        var cardNo = $("card>number", this).text();
        var title = $("title", this).text();
        var agentId = $("createdbyuserid", this).text();
        var agentGuid = $("createdby", this).text();
        var questiontext = $("questiontext", this).text();
        var points = $("pointvalue", this).text();
        var winnerUserId = $("winninguserid", this).text();
        var hasResponded = ($("agentresponded", this).text() == "1");
        var hasSaved = ($("agentsaved", this).text() == "1");
        var canRespond = ($("isplayable:last", this).text() == "1");

        xeko.widgets.challenges.singleHasResponded = hasResponded;

        var expirationDate = xeko.utilities.parseDate($("expirationdate", this).text());
        var responseCloseDate = xeko.utilities.parseDate($("responseclosedate", this).text());
        var creatorVoteDate = xeko.utilities.parseDate($("creatorvotedate", this).text());
        var votingCloseDate = xeko.utilities.parseDate($("votingclosedate", this).text());
        var now = new Date();

        // Challenge type dictates how the item is rendered
        //##TODO unnecessary?
        var thisChallengeType;
        if (expirationDate > now) thisChallengeType = "open";
        if (expirationDate < now && votingCloseDate > now) thisChallengeType = "voting";
        if (votingCloseDate < now) thisChallengeType = "closed";

        // track whether the current user is the owner of this challenge
        if (xeko.agentinfo.agentGUID == agentGuid) xeko.widgets.challenges.isOwner[guid] = true;
        else xeko.widgets.challenges.isOwner[guid] = false;
        xeko.widgets.challenges.creatorVoteDate[guid] = creatorVoteDate;

        // track whether the current user is able to respond to the challenge
        if (canRespond) xeko.widgets.challenges.canRespond[guid] = true;
        else xeko.widgets.challenges.canRespond[guid] = false;

        // Comma-delimited list of tags
        var tags = "";
        $("tags>tag", this).each(function() { tags += $("value", this).text() + ","; });
        if (tags.length > 0) tags = tags.substr(0, tags.length - 1);

        /************************************************************
        * Clone the template div and append to the DOM with unique ID
        ************************************************************/
        var itemDiv = $("#challengesItem").clone().appendTo("#challengesContent");
        xeko.widgets.challenges.appendGuidToId(itemDiv, guid);

        // Image
        var src = "/images/cardart/" + hotspot + "/activities_medium/" + cardNo + ".jpg";
        $("img:first", itemDiv).attr("src", src);

        // Main div container
        var innerDiv = $("div.challengesContainer", itemDiv);

        // Content div
        var contentDiv = $("#challengesContent", innerDiv);
        xeko.widgets.challenges.appendGuidToId(contentDiv, guid);

        $("h1", contentDiv).text(title);
        $("div.sub1>strong", contentDiv).append(agentId);

        // Description text
        var questionDiv = $("#challengesQuestionText", contentDiv);
        xeko.widgets.challenges.appendGuidToId(questionDiv, guid);
        questionDiv.prepend(questiontext);

        // More link (Challenge creator can't expand the "Try it" div; no messaging displayed)
        var moreLink = $("#challengesSeeMore", contentDiv);
        if (xeko.widgets.challenges.pageType == "challenges" && xeko.widgets.challenges.isOwner[guid])
          moreLink.hide();
        else
          xeko.widgets.challenges.appendGuidToId(moreLink, guid);

        xeko.widgets.challenges.appendGuidToId($("#challengesMore", contentDiv), guid);

        // Info div
        var infoDiv = $("#challengesInfo", itemDiv);
        xeko.widgets.challenges.appendGuidToId(infoDiv, guid);

        // Related challenges
        xeko.widgets.challenges.appendGuidToId($("#challengesRelatedChallenges", infoDiv), guid);
        if (xeko.widgets.challenges.relatedChallenges[guid] == null) {
          // Lookup has not yet been performed; fetch and cache
          $.ajax(
					{
					  method: "get",
					  url: "/widgets/widget.Challenges.handler.php",
					  data: "type=getrelatedchallenges&tags=" + tags,
					  beforeSend: function() { /*xeko.lightbox.show(xeko.strings.LOADING, true, "LoadChallengesXml")*/ },
					  complete: function() { /*xeko.lightbox.hide("LoadChallengesXml");*/ },
					  success: function(xml) {
					    xeko.widgets.challenges.renderRelatedChallenges(xml, guid, infoDiv);
					  },
					  error: function(xmlHttpRequest, status, err) { xeko.debug.trace(err); }
					});
        }
        else {
          // Load from cache
          $("div.challengesRelatedChallenges>ul", infoDiv).append(xeko.widgets.challenges.relatedChallenges[guid]);
        }

        // Section-specific fields
        if (xeko.widgets.challenges.pageType == "challenges") {
          // Fields specific to OPEN CHALLENGES

          xeko.widgets.challenges.appendGuidToId($("#challengesSavedForLater", contentDiv), guid);
          xeko.widgets.challenges.appendGuidToId($("#challengesSaveChallenge", contentDiv), guid);
          xeko.widgets.challenges.appendGuidToId($("#challengesAlreadySubmitted", contentDiv), guid);
          xeko.widgets.challenges.appendGuidToId($("#challengesLocked", contentDiv), guid);
          xeko.widgets.challenges.appendGuidToId($("#challengesCreated", infoDiv), guid);

          // Days left is # of days between closedate & now
          var daysLeft = xeko.utilities.daysBetween(responseCloseDate, now);
          var s = (daysLeft == 1) ? "" : "s";

          $("div.openChallengeInfo span.points", infoDiv).text(points);
          $("div.openChallengeInfo span.days", infoDiv).text(daysLeft);
          $("div.openChallengeInfo span.daysText", infoDiv).text("day" + s);
          $("div.openChallengeInfo", infoDiv).show();

          // Try div
          var tryDiv = $("#challengesTry", itemDiv);
          xeko.widgets.challenges.appendGuidToId(tryDiv, guid);

          $("#challengesTryChallenge", tryDiv).attr("value", guid);

          xeko.widgets.challenges.appendGuidToId($("#challengesTryChallenge", tryDiv), guid);
          xeko.widgets.challenges.appendGuidToId($("#challengesTryForm", tryDiv), guid);

          //$("#challengesEntryTextLabel", tryDiv).attr("for", "challengesEntryTextLabel" + guid);

          xeko.widgets.challenges.appendGuidToId($("#challengesEntryTextLabel", tryDiv), guid);
          xeko.widgets.challenges.appendGuidToId($("#challengesEntryText", tryDiv), guid);
          xeko.widgets.challenges.appendGuidToId($("#challengesCodeOfConduct", tryDiv), guid);

          $("#challengesCodeOfConductLabel", tryDiv).attr("for", "challengesCodeOfConduct" + guid);

          $("#challengesSubmitEntry", tryDiv).attr("value", guid);

          xeko.widgets.challenges.appendGuidToId($("#challengesSubmitEntry", tryDiv), guid);

          // Close button
          xeko.widgets.challenges.appendGuidToId($("#challengesClose", tryDiv), guid);

          // Is the user eligible to respond to this challenge?
          if (!canRespond) {
            xeko.widgets.challenges.showLocked(guid);
          }

          // Has the user already responded to this Challenge?
          if (hasResponded) {
            xeko.widgets.challenges.submitted += guid + ","
            xeko.widgets.challenges.showSubmitted(guid);
          }

          // Has the user already saved this Challenge?
          if (hasSaved) {
            xeko.widgets.challenges.favorites += guid + ","
            xeko.widgets.challenges.showSaved(guid);
          }
        }
        else if (xeko.widgets.challenges.pageType == "challengesvote") {
          // Fields specific to CHALLENGES IN VOTING

          if (xeko.widgets.challenges.voted[guid]) {
            // user or owner has already voted
            $("#challengesInfo" + guid + ">div.votingChallengeInfo a").remove();
            $("#challengesInfo" + guid + ">div.votingChallengeInfo>div.div2").append("<span class=\"vote\">voted</span>");

            // Days left is # of days between votingCloseDate & now
            var daysLeft = xeko.utilities.daysBetween(votingCloseDate, now);
            var s = (daysLeft == 1) ? "" : "s";

            $("div.votingChallengeInfo span.days", infoDiv).text(daysLeft + " day" + s);
            $("div.votingChallengeInfo span.text", infoDiv).text("until close");
            $("div.votingChallengeInfo", infoDiv).show();
          }
          else if (xeko.widgets.challenges.isOwner[guid] && creatorVoteDate > now) {
            // Challenge owners cannot vote until creatorVoteDate has passed
            var daysLeft = xeko.utilities.daysBetween(creatorVoteDate, now);
            var s = (daysLeft == 1) ? "" : "s";

            $("#challengesInfo" + guid + ">div.votingChallengeInfo>div.div2>a").text("see votes");

            $("div.votingChallengeInfo a", infoDiv).click(function(e) {
              this.blur();
              e.preventDefault();
              xeko.widgets.challenges.showMore(guid);
            });

            $("div.votingChallengeInfo span.days", infoDiv).text(daysLeft + " day" + s);
            $("div.votingChallengeInfo div.text", infoDiv).text("until voting");
            $("div.votingChallengeInfo", infoDiv).show();
          }
          else {
            // user or owner can vote
            $("div.votingChallengeInfo a", infoDiv).click(function(e) {
              this.blur();
              e.preventDefault();
              xeko.widgets.challenges.showMore(guid);
            });

            // Days left is # of days between votingCloseDate & now
            var daysLeft = xeko.utilities.daysBetween(votingCloseDate, now);
            var s = (daysLeft == 1) ? "" : "s";

            $("div.votingChallengeInfo span.days", infoDiv).text(daysLeft + " day" + s);
            $("div.votingChallengeInfo span.text", infoDiv).text("left to vote");
            $("div.votingChallengeInfo", infoDiv).show();
          }

          // Vote div
          var voteDiv = $("#challengesVoteEntries", itemDiv);
          xeko.widgets.challenges.appendGuidToId(voteDiv, guid);

          // Close button
          xeko.widgets.challenges.appendGuidToId($("#challengesClose", voteDiv), guid);
        }
        else if (xeko.widgets.challenges.pageType == "challengeshalloffame") {
          // Fields specific to CHALLENGES IN HALL OF FAME

          $("div.hallOfFameChallengeInfo", infoDiv).show();

          if (winnerUserId.length > 0) {
            $("div.hallOfFameChallengeInfo>div.div2>div.chosen", infoDiv).text(winnerUserId);
            $("div.hallOfFameChallengeInfo>div.div2", infoDiv).show();
          }

          // Hall of Fame div
          var hallOfFameDiv = $("#challengesHallOfFameEntries", itemDiv);
          xeko.widgets.challenges.appendGuidToId(hallOfFameDiv, guid);

          // Close button
          xeko.widgets.challenges.appendGuidToId($("#challengesClose", hallOfFameDiv), guid);
        }
      }
      currentItem++;
    });

    this.itemsLoaded = true;
    this.wireUpEvents();

    // Auto-expand these single challenges:
    //		- active challenges where the current user is not the creator
    //		- challenges in the voting stage
    //		- challenge is playable for the current user
    if (
		xeko.widgets.challenges.isSingle &&
		xeko.widgets.challenges.pageType == "challenges" &&
		!xeko.widgets.challenges.isOwner[xeko.widgets.challenges.singleGuid] &&
		xeko.widgets.challenges.canRespond[xeko.widgets.challenges.singleGuid]
		) {
      $(".challengesClose").hide();
      this.showMore(xeko.widgets.challenges.singleGuid);

      // Auto-expand form unless user has already responded or is not logged in
      if (!xeko.widgets.challenges.singleHasResponded && !xeko.agentinfo.agentIsGuest) $(".challengesTryChallenge").trigger("click");
    } else if (
		xeko.widgets.challenges.isSingle &&
		(xeko.widgets.challenges.pageType == "challengesvote" || xeko.widgets.challenges.pageType == "challengeshalloffame")
		) {
      $(".challengesClose").hide();
      this.showMore(xeko.widgets.challenges.singleGuid);
    }
  },
  "renderRelatedChallenges": function(xml, guid, infoDiv) {
    // Setting to empty string prevents future lookups
    xeko.widgets.challenges.relatedChallenges[guid] = "";

    if ($("challenge", xml).length > 0) {
      // Hide while rendering
      $("div.challengesRelatedChallenges>h2, div.challengesRelatedChallenges>ul", infoDiv).hide();

      var count = 0;
      $("challenge", xml).each(function() {
        // Do not include the current Challenge
        // Limit to 3 maximum
        if ($(this).attr("id") != guid && count < 3) {
          xeko.widgets.challenges.relatedChallenges[guid] +=
						"<li>" +
						"<a href=\"/challenges/" + $(this).attr("id") + "\">" +
						$("title", this).text() +
						"</a>" +
						"</li>";
          count++;
        }
      });

      if (count > 0) {
        // Write to DOM
        $("div.challengesRelatedChallenges>ul", infoDiv).append(xeko.widgets.challenges.relatedChallenges[guid]);

        // Now it's safe to show container
        $("div.challengesRelatedChallenges>h2, div.challengesRelatedChallenges>ul", infoDiv).show("slow");
      }
    }
    else {
      // No related challenges loaded
      $("div.challengesRelatedChallenges>h2, div.challengesRelatedChallenges>ul", infoDiv).hide();
    }
  },
  "renderResponses": function(xml, challengeId) {
    // It is assumed that there will be at least one record in the XML, otherwise this item wouldn't be included on this page at all
    var itemHasWinner = false, itemHasVotes = false;
    $("agentchallengeresponse", xml).each(function() {
      var responseId = $(this).attr("id");
      var agentUserId = $("agent>userid", this).text();
      var responseText = $("challengeanswer", this).text();
      var totalVotes = $("totalvotes", this).text();
      var s = (parseInt(totalVotes) == 1) ? "" : "s";
      var isWinner = ($("iswinner", this).text() == "1");
      var avatarHotspot = $("agent>avatar>card>hotspot", this).text();
      var avatarCardNo = $("agent>avatar>card>number", this).text();
      var src = "/images/cardart/" + xeko.utilities.removeSpaces(avatarHotspot) + "/altereco/" + avatarCardNo + ".png";

      var itemLi;

      // Fetch or clone the template li and append to the DOM with unique ID
      if (xeko.widgets.challenges.pageType == "challengeshalloffame") {
        // Winning response is displayed first on the Hall of Fame page, not in the main list of responses
        if (isWinner) {
          itemHasWinner = true;
          itemLi = $("#challengesHallOfFameEntries" + challengeId + " .challengesWinner");
        }
        else {
          itemHasVotes = true;
          itemLi = $(".challengesResponse:first").clone().appendTo("#challengesHallOfFameEntries" + challengeId + " ul:last");
        }
      }
      else {
        itemLi = $(".challengesResponse:first").clone().appendTo("#challengesVoteEntries" + challengeId + ">ul");
      }

      itemLi.attr("id", "challengesResponse" + responseId);
      itemLi.show();

      $("img.challengesResponseAlterEco", itemLi).attr("src", src);
      $("div>strong", itemLi).append(agentUserId);
      $("div:first", itemLi).append(responseText);
      $(".votes", itemLi).prepend(totalVotes);
      $(".votes", itemLi).append(s);
      $(".challengesVoteFor", itemLi).attr("id", "challengesVoteFor" + responseId);
      $(".challengesVoteForItem", itemLi).attr("id", "challengesVoteForItem" + responseId);
      $(".challengesVoteForItem", itemLi).val(challengeId);

      // Is the user the owner of this Challenge?
      if (xeko.widgets.challenges.isOwner[challengeId]) {
        // Owners can only vote after creatorVoteDate has passed
        var now = new Date();
        if (now > xeko.widgets.challenges.creatorVoteDate[challengeId]) {
          $(".challengesVoteFor", itemLi).addClass("creator");
          $(".challengesVoteFor", itemLi).val(xeko.strings.CHALLENGE_VOTE_OWNER_CLICK);
        }
        else $(".challengesVoteFor", itemLi).hide();

        $(".votes", itemLi).show();
      }
    });

    // Whether to display winners and/or votes for hall of fame
    if (xeko.widgets.challenges.pageType == "challengeshalloffame") {
      if (!itemHasWinner) $("#challengesHallOfFameEntries" + challengeId + ">.challengesHallOfFameWinner").hide();
      if (!itemHasVotes) $("#challengesHallOfFameEntries" + challengeId + ">.challengesHallOfFameVotes").hide();
    }

    // Set voted status for any responses the user may have already voted on
    if (xeko.widgets.challenges.votedLoaded) xeko.widgets.challenges.setVoted(challengeId);

    // Vote button handler
    $(".challengesVoteFor").click(function(e) {
      e.preventDefault();

      if (xeko.forceLogin(xeko.messaging.alert.VOTE_LOGIN_REQUIRED)) {
        var entryId = $(this).attr("id").substr("challengesVoteFor".length);
        var challengeId = $("#challengesVoteForItem" + entryId).val();

        $.ajax(
				{
				  method: "get",
				  url: "/widgets/widget.Challenges.handler.php",
				  data: "type=submitvote&challengeguid=" + challengeId + "&responseid=" + entryId,
				  beforeSend: function() { xeko.lightbox.show(xeko.strings.CHALLENGE_VOTE_WAIT, true, "SubmitVote") },
				  complete: function() { xeko.lightbox.hide("SubmitVote"); },
				  success: function(xml) {
				    // success?
				    if ($("error", xml).length > 0) {
				      var errNum = $("error", xml).attr("number");
				      xeko.widgets.challenges.hideError(challengeId);
				      xeko.widgets.challenges.throwError(challengeId, xeko.messaging.error.CHALLENGES_VOTE_ERROR + " (" + errNum + ")");
				    }
				    else {
				      xeko.widgets.challenges.hideError(challengeId);
				      xeko.widgets.challenges.showVoted(challengeId, entryId);
				    }
				  },
				  error: function(xmlHttpRequest, status, err) {
					if (xmlHttpRequest.status > 0) xeko.widgets.challenges.throwError(challengeId, xeko.messaging.error.CHALLENGES_VOTE_ERROR);
				  }
				});
      }
    });
  },
  "setVoted": function(challengeId) {
    // If an item with this Challenge ID as the index exists, user has already voted on this challenge
    if (xeko.widgets.challenges.voted[challengeId])
      xeko.widgets.challenges.showVoted(challengeId, xeko.widgets.challenges.voted[challengeId]);
  },
  "showSaved": function(itemId) {
    // Abort if Challenge response has already been submitted
    if (xeko.widgets.challenges.submitted.indexOf(itemId) == -1) {
      if ($("#challengeSingle").get(0)) {
        $("#challengesSaveChallenge" + itemId).hide("normal", function() {
          $("#challengesSavedForLater" + itemId).show("slow");
          $("#challengesTryChallenge" + itemId).show("slow");
        });
      }
      else {
        $("#challengesSaveChallenge" + itemId).hide("normal");
        $("#challengesTryForm" + itemId).hide("normal", function() {
          $("#challengesSavedForLater" + itemId).show("slow");
          $("#challengesTryChallenge" + itemId).show("slow");
        });
      }
    }
  },
  "showSubmitted": function(itemId) {
    this.itemSubmitted[itemId + ""] = true;
    $("#challengesSaveChallenge" + itemId).hide("normal");
    $("#challengesTryChallenge" + itemId).hide("normal");
    $("#challengesSavedForLater" + itemId).hide("normal");
    $("#challengesSeeMore" + itemId).hide("normal");
    $("#challengesTryForm" + itemId).hide("normal", function() {
      $("#challengesAlreadySubmitted" + itemId).show("slow");
    });
  },
  "showLocked": function(itemId) {
    $("#challengesSaveChallenge" + itemId).hide("normal");
    $("#challengesTryChallenge" + itemId).hide("normal");
    $("#challengesSavedForLater" + itemId).hide("normal");
    $("#challengesSeeMore" + itemId).hide("normal");
    $("#challengesTryForm" + itemId).hide("normal", function() {
      $("#challengesLocked" + itemId).show("slow");
    });
  },
  "showVoted": function(challengeId, entryId) {
    // Disable click for all Vote buttons in this group, update image for selected button
    $("#challengesVoteEntries" + challengeId + ">ul>li>input.challengesVoteFor").each(function() {
      $(this).unbind("click");
      $(this).attr("disabled", "true");
      $(this).addClass("notclickable");
      // Change the class of the button
      if ($(this).attr("id").indexOf(entryId) == -1) {
        $(this).addClass("challengesVoteForOff");
        $(this).val("");
      }
    });

    // Update "vote now" link
    if ($("#challengesInfo" + challengeId + ">div.votingChallengeInfo>div.div2").length > 0) {
      $("#challengesInfo" + challengeId + ">div.votingChallengeInfo>div.div2").html("<span class=\"vote\">voted</span>");
    }

    //##TODO text/image is different for the creator of this challenge
    $("#challengesVoteFor" + entryId).val(xeko.strings.CHALLENGE_VOTE_VOTED);
  },
  "saveChallenge": function(itemId) {
    $.ajax(
		{
		  method: "get",
		  url: "/widgets/widget.Challenges.handler.php",
		  data: "type=savefavorite&challengeguid=" + itemId,
		  beforeSend: function() { xeko.lightbox.show(xeko.strings.CHALLENGE_FAV_WAIT, true, "SaveChallengeFav") },
		  complete: function() { xeko.lightbox.hide("SaveChallengeFav"); },
		  success: function(xml) {
		    // success?
		    if ($("agentfavorite", xml).length > 0) {
		      // Save success
		      xeko.widgets.challenges.hideError(itemId);
		      xeko.widgets.challenges.showSaved(itemId);

		      // Keep list up to date
		      xeko.widgets.challenges.favorites += itemId + ",";

		      // My Saved Activities widget in the nav will need to be re-rendered
		      //##TODO better to raise an event
		      xeko.widgets.navActivities.savedLoaded = false;
		    }
		    else {
		      xeko.widgets.challenges.hideError(itemId);
		      xeko.widgets.challenges.throwError(itemId, xeko.messaging.error.CHALLENGES_SAVE_ERROR);
		    }
		  },
		  error: function(xmlHttpRequest, status, err) {
			if (xmlHttpRequest.status > 0) xeko.widgets.challenges.throwError(itemId, xeko.messaging.error.CHALLENGES_SAVE_ERROR);
		  }
		});
  },
  "submitResponse": function(itemId) {
    if (xeko.widgets.challenges.validate(itemId)) {
      $.ajax(
			{
			  method: "get",
			  url: "/widgets/widget.Challenges.handler.php",
			  data: "type=submitresponse&challengeguid=" + itemId + "&challengedata=" + $("#challengesEntryText" + itemId).val(),
			  beforeSend: function() { xeko.lightbox.show(xeko.strings.CHALLENGE_RESPONSE_WAIT, true, "SubmitResponse") },
			  complete: function() { xeko.lightbox.hide("SubmitResponse"); },
			  success: function(xml) {
			    // success?
			    if ($("agentchallengeresponse", xml).length > 0) {
			      // Save success
			      xeko.widgets.challenges.hideError(itemId);
			      xeko.widgets.challenges.showSubmitted(itemId);

			      // Keep list up to date
			      xeko.widgets.challenges.submitted += itemId + ",";
			    }
			    else {
			      //##TODO look for known error codes
			      xeko.widgets.challenges.hideError(itemId);
			      xeko.widgets.challenges.throwError(itemId, xeko.messaging.error.CHALLENGES_SUBMIT_ERROR);
			    }
			  },
			  error: function(xmlHttpRequest, status, err) {
				if (xmlHttpRequest.status > 0) xeko.widgets.challenges.throwError(itemId, xeko.messaging.error.CHALLENGES_SUBMIT_ERROR);
			  }
			});
    } else xeko.debug.trace("invalid");
  },
  "validate": function(itemId) {
    var isValid = true;

    // Challenge response must be entered
    if ($("#challengesEntryText" + itemId).val().length == 0 || $("#challengesEntryText" + itemId).val() == xeko.strings.CHALLENGE_RESPONSE_PROMPT) {
      isValid = false;
      xeko.validation.showMessage("challengesEntryTextLabel" + itemId, xeko.messaging.alert.CHALLENGE_RESPONSE_MISSING);
    }
    else xeko.validation.clearMessage("challengesEntryTextLabel" + itemId);

    // User must agree w/ ToS
    if (!$("#challengesCodeOfConduct" + itemId).get(0).checked) {
      isValid = false;
      xeko.validation.showMessage("challengesCodeOfConduct" + itemId, xeko.messaging.alert.CHALLENGE_RESPONSE_AGREE);
    }
    else xeko.validation.clearMessage("challengesCodeOfConduct" + itemId);

    return isValid;
  },
  "openItem": function(itemId)	// shows expanded divs, and collapses any open div
  {
    // close all other items
    $(".challengesItem").each(function() {
      var thisItemId = $(this).attr("id").substr("challengesItem".length);
      if (thisItemId.length > 0 && thisItemId != itemId) xeko.widgets.challenges.closeItem(thisItemId);
    });

    if ($("#challengesVote").get(0)) {
      $("#challengesRelatedChallenges" + itemId).show("slow");
      $("#challengesVoteEntries" + itemId).show("slow");
    }
    else if ($("#challengesHallOfFame").get(0)) {
      $("#challengesRelatedChallenges" + itemId).show("slow");
      $("#challengesHallOfFameEntries" + itemId).show("slow");
    }
    else {
      $("#challengesRelatedChallenges" + itemId).show("slow");
      $("#challengesTry" + itemId).show("slow");
      $("#challengesMore" + itemId).show("slow");
    }

    $("#challengesSeeMore" + itemId).hide();
  },
  "closeItem": function(itemId)	// hides expanded divs
  {
    if ($("#challengesVote").get(0)) {
      $("#challengesRelatedChallenges" + itemId).hide("normal");
      $("#challengesVoteEntries" + itemId).hide("normal", function() {
        $("#challengesSeeMore" + itemId).show();
      });
    }
    else if ($("#challengesHallOfFame").get(0)) {
      $("#challengesRelatedChallenges" + itemId).hide("normal");
      $("#challengesHallOfFameEntries" + itemId).hide("normal", function() {
        $("#challengesSeeMore" + itemId).show();
      });
    }
    else {
      $("#challengesMore" + itemId).hide("normal");
      $("#challengesTry" + itemId).hide("normal");
      $("#challengesTryForm" + itemId).hide("normal");
      $("#challengesRelatedChallenges" + itemId).hide("normal", function() {
        // Only show "See More" if user can respond to challenge
        if (xeko.widgets.challenges.canRespond[itemId + ""]) {
			$("#challengesSeeMore" + itemId).show();
			// Only show Try button if user has not already submitted the challenge
			if (!xeko.widgets.challenges.itemSubmitted[itemId + ""]) $("#challengesTryChallenge" + itemId).show();
        }
      });
    }
  },
  "showMore": function(challengeId) {
    if (xeko.widgets.challenges.pageType == "challenges") {
      // No secondary AJAX call necessary
      xeko.widgets.challenges.openItem(challengeId);
    }
    else {
      var serviceType;
      if (xeko.widgets.challenges.pageType == "challengesvote") serviceType = "getresponses";
      else if (xeko.widgets.challenges.pageType == "challengeshalloffame") serviceType = "gethalloffame";

      // If Challenge is in voting or hall of fame stage, load the responses (unless they have already been written to the DOM)
      if (xeko.widgets.challenges.challengeResponsesLoaded[challengeId] == null) {
        // Lookup has not yet been performed; fetch and cache
        $.ajax(
				{
				  method: "get",
				  url: "/widgets/widget.Challenges.handler.php",
				  data: "type=" + serviceType + "&challengeid=" + challengeId + "&isowner=" + xeko.widgets.challenges.isOwner[challengeId],
				  beforeSend: function() { xeko.lightbox.show(xeko.strings.LOADING, true, "LoadResponsesXml") },
				  complete: function() { xeko.lightbox.hide("LoadResponsesXml"); },
				  success: function(xml) {
				    // Render responses to the page
				    xeko.widgets.challenges.renderResponses(xml, challengeId);

				    // Note loaded state
				    xeko.widgets.challenges.challengeResponsesLoaded[challengeId] = true;

				    xeko.widgets.challenges.openItem(challengeId);
				  },
				  error: function(xmlHttpRequest, status, err) { xeko.debug.trace(err); }
				});
      }
      else {
        // Already loaded
        xeko.widgets.challenges.openItem(challengeId);
      }
    }
  },
  "pageTo": function(n) {
    if (parseInt(n) > 0 && parseInt(n) <= xeko.widgets.challenges.numPages) {
      xeko.widgets.challenges.currentPage = parseInt(n);
      xeko.widgets.challenges.renderChallenges();
      $("html").scrollTop(0);
    }
  },
  "pageBy": function(n) {
    xeko.widgets.challenges.pageTo(xeko.widgets.challenges.currentPage + parseInt(n));
  },
  "wireUpEvents": function() {
    // See More click
    $(".challengesSeeMore").click(function(e) {
      e.preventDefault();
      var challengeId = $(this).attr("id").substr("challengesSeeMore".length);
      xeko.widgets.challenges.showMore(challengeId);
    });

    // Close click
    $(".challengesClose").click(function(e) {
      e.preventDefault();
      var itemId = $(this).attr("id").substr("challengesClose".length);
      xeko.widgets.challenges.closeItem(itemId);
    });

    // Try Challenge click shows form
    $(".challengesTryChallenge").click(function(e) {
      e.preventDefault();

      if (xeko.forceLogin(xeko.messaging.alert.RESPONSE_LOGIN_REQUIRED)) {
        var itemId = $(this).attr("id").substr("challengesTryChallenge".length);
        $("#challengesTryForm" + itemId).show("slow", function() {
          $("#challengesTryChallenge" + itemId).hide("fast");
        });
      }
    });

    // Save Challenge click
    $(".challengesSaveChallenge").click(function(e) {
      e.preventDefault();
      var itemId = $(this).attr("id").substr("challengesSaveChallenge".length);
      xeko.widgets.challenges.saveChallenge(itemId);
    });

    // Save For Later click saves w/o validation
    $(".challengesSaveForLater").click(function(e) {
      e.preventDefault();
      var itemId = $(this).attr("id").substr("challengesSaveForLater".length);
      xeko.widgets.challenges.saveChallenge(itemId);
    });

    // Validate and submit challenge
    $(".challengesSubmitEntry").click(function(e) {
      e.preventDefault();
      var itemId = $(this).attr("id").substr("challengesSubmitEntry".length);
      xeko.widgets.challenges.submitResponse(itemId);
    });

    // add inline entry text prompt
    $(".challengesEntryText").val(xeko.strings.CHALLENGE_RESPONSE_PROMPT);
    $(".challengesEntryText").addClass("instructional");

    // remove prompt text on field focus
    $(".challengesEntryText").focus(function(e) {
      if (this.value == xeko.strings.CHALLENGE_RESPONSE_PROMPT) {
        this.value = "";
        $(this).removeClass("instructional");
      }
    });

    // re-add prompt text as necessary
    $(".challengesEntryText").blur(function(e) {
      if (this.value == "") {
        this.value = xeko.strings.CHALLENGE_RESPONSE_PROMPT;
        $(this).addClass("instructional");
      }
    });

    // prevent more than max characters
    $(".challengesEntryText").bind("keydown keyup click change mousemove mouseup blur focus", function(e) {
      if (this.value.length > xeko.widgets.challenges.responseMaxLength)
        this.value = this.value.substr(0, xeko.widgets.challenges.responseMaxLength);
    });

    // Pagination -- previous
    $(".prev").bind("click", function(e) {
      xeko.widgets.challenges.pageBy(-1);
    });

    // Pagination -- next
    $(".next").bind("click", function(e) {
      xeko.widgets.challenges.pageBy(1);
    });

    // Pagination -- direct nav
    $(".challengesPageTo").bind("click", function(e) {
      xeko.widgets.challenges.pageTo($(this).val());
    });
  },
  "appendGuidToId": function($elm, guid) {
    var id = $elm.attr("id");
    $elm.attr("id", id + guid);
  },
  "throwError": function(itemId, msg) {
    if (itemId) $("div#challengesContent" + itemId + " p.error").text(msg).show();
    else {
      $("div#challengesContent>p.error").text(msg).show();
    }
  },
  "hideError": function(itemId) {
    if (itemId) $("div#challengesContent" + itemId + " p.error").hide();
    else $("div#challengesContent>p.error").hide();
  }
};