// Place your application-specific JavaScript functions and classes here
// This file is automatically included by javascript_include_tag :defaults

// Maintain selected and available scrolling lists with movement arrows.
function moveSelectedOptions(fromSelect, toSelect, moveAll) {
  if ((fromSelect.selectedIndex < 0) && !moveAll) return;

  // Make copies of existing options for sorting.
  var options = new Array(toSelect.options.length);
  for (var i = 0; i < toSelect.options.length; i++) {
    var option = toSelect.options[i];
    options[i] = new Option(option.text, option.value, 
                            option.defaultSelected, option.selected);
  }

  for (i = 0; i < fromSelect.options.length; i++) {
    var option = fromSelect.options[i];
    if (option.selected || moveAll)
      options.push(new Option(option.text, option.value, 
                              option.defaultSelected, option.selected));
  }

  options.sort(function(o1, o2) { 
                 if (o1.text < o2.text) return -1; 
                 else if (o1.text != o2.text) return +1;
                 else return 0;
               });

  for (i = 0; i < options.length; i++) {
    toSelect.options[i] = options[i];
  }

  for (i = fromSelect.options.length -1; i >= 0; i--) {
    if (moveAll || fromSelect.options[i].selected) {
      fromSelect.options[i] = null;
    }
  }
}

// Need to select options in a mover list to get them passed back.
function selectMovedOptions(form) {
  for (var i = 0; i < form.elements.length; i++) {
    if (form.elements[i].attributes.select_on_submit) {
      var options = form.elements[i].options;
      for (var j = 0; j < options.length; j++) {
        options[j].selected = true;
      }
    }
  }
  return true;                  // Continue to submit.
}

IEFixes = {
  version: (/MSIE (\d+\.\d+);/.test(navigator.userAgent) ? parseFloat(RegExp.$1) : null),

  needTransparency: function() {
    return (IEFixes.version != null) && (IEFixes.version < 7);
  },

  // These don't account for all the cases of repeating background,
  // img with width/height and div without, etc.; but they are good enough for now.

  fixImgTransparency: function(parent) {
    var img = Element.select(parent, 'img').first();
    var src = img.src;
    parent.removeChild(img);
    parent.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "', sizingMethod='crop')";
  },

  fixBackgroundTransparency: function(elem) {
    var bgimg = elem.currentStyle.backgroundImage;
    if (/^url\((.+)\)$/.test(bgimg)) {
      elem.style.background = "";
      elem.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src=" + RegExp.$1 + ", sizingMethod='crop')";
    }
  },

  needHasLayout: function() {
    return (IEFixes.version != null) && (IEFixes.version < 8);
  },

  forceHasLayout: function(elem) {
    if (!elem.currentStyle.hasLayout)
      elem.style.zoom = 1.0;
  }
}

// Functions related to countdown clock.
CountdownTimer = {
  // Initialize the timer display, hiding if now countdown active.
  init: function() {
    if (CountdownTimer.target == null) {
      CountdownTimer.cancelTimer();
      $('clock').style.visibility = 'hidden';
    }
    else {
      $('clock').style.visibility = 'visible';
      CountdownTimer.update();
    }
  },

  units: ['DAYS', 'HRS', 'MINS', 'SECS'],

  // 2 places, leading zeros.
  lz2: function(n) {
    if (n < 10)
      return "0" + n;
    else
      return n.toString();
  },

  // 3 places, leading space plus zero.
  lz3: function(n) {
    if (n < 100)
      return " " + CountdownTimer.lz2(n);
    else
      return n.toString();
  },

  // Update time display.
  update: function() {
    var now = Math.floor(new Date().getTime() / 1000);
    var delta = CountdownTimer.target - now;
    if (delta < 0) {
      CountdownTimer.cancelTimer();
      $('clock').style.visibility = 'hidden';
      return;
    }

    var nums = [];
    nums[3] = delta % 60;
    delta = Math.floor(delta / 60);
    nums[2] = delta % 60;
    delta = Math.floor(delta / 60);
    nums[1] = delta % 24;
    delta = Math.floor(delta / 24);
    nums[0] = delta;

    // When less than a day, transition to seconds countdown.
    var in_day = (nums[0] == 0);
    if (CountdownTimer.timer != null) {
      if (in_day != (CountdownTimer.timer.frequency == 1))
        CountdownTimer.cancelTimer();
    }
    if (in_day != ($('clock-units1').innerHTML == 'day')) {
      var offset = in_day ? 1 : 0;
      $('clock-units1').innerHTML = CountdownTimer.units[offset++];
      $('clock-units2').innerHTML = CountdownTimer.units[offset++];
      $('clock-units3').innerHTML = CountdownTimer.units[offset++];
    }

    var offset = in_day ? 1 : 0;
    $('clock-digits1').innerHTML = CountdownTimer.lz3(nums[offset++]);
    $('clock-digits2').innerHTML = CountdownTimer.lz2(nums[offset++]);
    $('clock-digits3').innerHTML = CountdownTimer.lz2(nums[offset++]);

    if (CountdownTimer.timer == null) {
      CountdownTimer.timer = new PeriodicalExecuter(CountdownTimer.update, 
                                                    in_day ? 1 : 60);
    }
  },

  // Cancel (and forget) any pending timer.
  cancelTimer: function() {
    if (CountdownTimer.timer != null) {
      CountdownTimer.timer.stop();
      CountdownTimer.timer = null;
    }
  }
}

// Functions related to competitions display.
CompetitionsContent = {
  // Called over position bars loaded
  onLoad: function() {
    CountdownTimer.init.defer(); // prototype defers evalScripts, which sets target.
    CompetitionsContent.initRace();
    CompetitionsContent.ie6TransparencyFixes();
  },

  // Begin the race: hide the totals for now, start the timer to step
  // in tenths every 1/10 second.
  initRace: function() {
    if (CompetitionsContent.raceTimer != null) {
      CompetitionsContent.raceTimer.stop();
      CompetitionsContent.raceTimer = null;
    }
    CompetitionsContent.racePosition = 0;
    raceBars = $$('div.team-bar');
    if (raceBars.length > 0) {
      CompetitionsContent.totalWidth = raceBars.first().clientWidth;
    }
    CompetitionsContent.showHideBarLegends(raceBars, 'hide');
    CompetitionsContent.updateRace(raceBars);
    CompetitionsContent.raceTimer = 
      new PeriodicalExecuter(function() { CompetitionsContent.updateRace(raceBars); },
                             0.1);
  },

  // Called to step.
  updateRace: function (raceBars) {
    CompetitionsContent.racePosition++;
    raceBars.map(function (bar) {
                   bar.style.width = CompetitionsContent.totalWidth *
                     parseFloat(Element.readAttribute(bar, 'bar-fraction')) *
                     (CompetitionsContent.racePosition / 10.0) + "px";
                 });
    if (CompetitionsContent.racePosition >= 10) {
      CompetitionsContent.raceTimer.stop();
      CompetitionsContent.raceTimer = null;
      CompetitionsContent.showHideBarLegends(raceBars, 'show');
    }
  },

  // Show or hide the scores.
  showHideBarLegends: function(raceBars, method) {
    raceBars.map(function (bar) {
                   Element.select(bar.parentNode, 'div.team-score').invoke(method);
                 });
  },

  ie6TransparencyFixes: function() {
    if (IEFixes.needTransparency()) {
      var root = $('content');
      Element.select(root, 'div.team-position-indicator').map(IEFixes.fixImgTransparency);
      CompetitionsContent.ie6TeamBadges(root); // In case any expanded initially.
    }
  },

  ie6TeamBadges: function(root) {
    if (IEFixes.needTransparency()) {
      Element.select($(root), 'div.team-member-badge').map(IEFixes.fixBackgroundTransparency);
    }
  }
}

UserPopUp = {
  post: function(target) {
    var url = UserPopUp.url + '/' + Element.readAttribute(target, 'user_id');
    var context = Element.readAttribute(target, 'context');
    if (context != null) url = url + "?" + context;
    new Ajax.Updater('user-pop-up', url,
                     { asynchronous: true, evalScripts: true, method: 'get', 
                       onComplete: function(request) { UserPopUp.complete(target); } });
  },

  // Relocate (and display) the user pop-up so that its icon exactly
  // lines up with the given member's icon.
  complete: function(target) {
    var content = $('content');
    var popUp = $('user-pop-up');
    var icon = Element.select(popUp, 'div.pop-up-icon').first()
    popUp.show();
    var iconLeft = (popUp.clientWidth - icon.clientWidth - icon.clientLeft) / 2;
    icon.style.left = iconLeft + "px";
    var popUpOffsets = UserPopUp.totalOffsets(Element.select(icon, 'img').first(),
                                              popUp);
    var targetOffsets = UserPopUp.totalOffsets(Element.select(target, 'img').first(),
                                               content);
    popUp.style.top = (targetOffsets.y - popUpOffsets.y) + "px";
    var popUpLeft = targetOffsets.x - popUpOffsets.x;
    if (popUpLeft < 0) {
      var left = icon.clientWidth / 2;
      var delta = iconLeft - left;
      icon.style.left = left + "px";
      popUpLeft += delta;
    }
    if (popUpLeft + popUp.clientWidth > content.clientWidth) {
      var right = popUp.clientWidth - icon.clientWidth * 3/2;
      var delta = right - iconLeft;
      icon.style.left = right + "px";
      popUpLeft -= delta;
    }
    popUp.style.left = popUpLeft + "px";
  //popUp.show();
  },

  // Compute total offsets for absolute positioning of the given
  // element to the given ancestor element.
 
  // Does CSSOM offsetLeft/Top measure to the offsetParent's padding edge or border edge?
  // That is, do clientLeft/Top need to be added in while summing up the tree?
  ie8mode: (/MSIE (\d+\.\d+);/.test(navigator.userAgent) ? (document.documentMode == 8) : /Opera\//.test(navigator.userAgent)),

  totalOffsets: function(elem, ancestor) {
    var totalX = 0, totalY = 0;
    while (elem != ancestor) {
      totalX += elem.offsetLeft;
      totalY += elem.offsetTop;
      if (!UserPopUp.ie8mode) {
        totalX += elem.clientLeft;
        totalY += elem.clientTop;
      }
      elem = elem.offsetParent;
    }
    return { x: totalX, y: totalY };
  },

  hide: function() {
    $('user-pop-up').hide();
  },

  onClick: function(event) {
    var target = Event.element(event);
    switch (target.tagName) {
    case "INPUT":
    case "SELECT":
      return;
    }

    UserPopUp.hide();
    if (target.className != "user-pop-up")
      return;

    // Clicked on pop-up itself, see whether another user showing through.
    // (IE does much of this automatically, but not in the case where there is a badge.)
    var clickPosition = Event.pointer(event);
    var users = $$('div.team-member');
    for (var i = 0; i < users.length; i++) {
      var user = users[i];
      var position = Element.cumulativeOffset(user);
      if ((clickPosition.x >= position.left) &&
          (clickPosition.y >= position.top) &&
          (clickPosition.x < position.left + user.offsetWidth) &&
          (clickPosition.y < position.top + user.offsetHeight)) {
        UserPopUp.post(user);
        return;
      }
    }
  }
}

// Functions related to the message board.
MessageBoard = {
  cornerStyles: [ 'message-corner-tl', 'message-corner-tr', 
                  'message-corner-bl', 'message-corner-br' ],

  // Add round corners to the post boxes. Reposition the icons to overlap a little.
  redecorate: function() {
    var posts = $('posts');
    /*
    Element.select(posts, 'div.message-user-icon').map(function(icon) {
                                                    var top = icon.offsetTop;
                                                    icon.style.position = 'absolute';
                                                    icon.style.top = (top - 24) + "px";
                                                  })
    */
    Element.select(posts, 'div.message-box').map(function(box) {
               var parent = box.parentNode;
               // First get rid of any existing corners.
               var child = parent.firstChild;
               while (child != null) {
                 var next = child.nextSibling;
                 if (/^message-corner-/.test(child.className))
                   parent.removeChild(child);
                 child = next;
               }
               for (var i = 0; i < 4; i++) {
                 var div = document.createElement('div');
                 div.className = MessageBoard.cornerStyles[i];
                 var left = box.offsetLeft;
                 var top = box.offsetTop;
                 if ((left <= 0) || (top <= 0))
                   continue;    // Not a reasonable position: maybe not up-to-date.
                 if ((i & 1) != 0)
                   left += box.offsetWidth - 8;
                 div.style.left = left + "px";
                 if ((i & 2) != 0)
                   top += box.offsetHeight - 8;
                 div.style.top = top + "px";
                 parent.appendChild(div);
               }
               if (IEFixes.needHasLayout())
                 IEFixes.forceHasLayout(parent);
                                                 });
  },

  // Toggle between showing the new message composition area and a menu opening it.
  showHideNew: function(show) {
    var posts = $('posts');
    var scrollable = Element.select(posts, 'div.messages').first();
    var oldWidth = scrollable.clientWidth;
    Element.select(posts, 'div.message-menu').invoke(show ? 'hide' : 'show');
    var mnew = Element.select(posts, 'div.message-new');
    mnew.invoke(show ? 'show' : 'hide');
    // Yes, this really does something useful!
    // IE6 & 7 return the wrong value the first time. Some kind of caching problem?
    scrollable.clientWidth;
    if (oldWidth != scrollable.clientWidth)
      MessageBoard.redecorate(); // Need new corners for changed width.
    if (show) {
      var form = mnew.first();
      var text = Element.select(form, 'textarea').first();
      text.value = "";
      form.scrollIntoView(false);
      text.focus();
    }
  },

  // Scroll so that first recent post is at the top 
  // or else so new button is at the bottom.
  scrollToRecent: function() {
    var posts = $('posts');
    var recent = Element.select(posts, 'div.message.recent').first();
    if (recent != null)
      recent.scrollIntoView(true);
    else {
      var menu = Element.select(posts, 'div.message-menu').first();
      if (menu != null)
        menu.scrollIntoView(false);
    }
  },

  // Called after posts are loaded.
  postsLoaded: function() {
    MessageBoard.redecorate();
    MessageBoard.scrollToRecent();
  },

  lightboxPostsLoaded: function() {
    MessageBoard.postsLoaded.delay(0.5);
  },

  // Called after new post has succeeded from the lightbox; close it,
  // unless something new has appeared in the interim.
  newLightboxPost: function() {
    var posts = $('posts');
    var recent = Element.select(posts, 'div.message.recent');
    if (recent.length == 0)
      RedBox.close();
    else
      MessageBoard.postsLoaded();
  }
}

// Functions related to teams content.
TeamsContent = {
  // Called after content loaded.
  onLoad: function() {
  }
}

UserForm = {
  // Perform the action indicated by the selected option to the left of the Go button.
  // Essentially, change the target of the form to be its value.
  action: function(form) {
    var action = form.action_menu.value;
    
    var method = "get";
    var option = form.action_menu.options[form.action_menu.selectedIndex];
    if (option.attributes.submit_method != null)
      method = option.attributes.submit_method.value;

    if (method == "get") {
      // Need to turn URL parameters into hidden fields, since the method is GET.

      // Get rid of any old ones from last time (and the unnecessary authenticity token).
      Element.select(form, 'input').map(function(input) {
                                          if (input.type == 'hidden') {
                                            Element.remove(input);
                                          }
                                        });

      var pieces = action.split('?');
      action = pieces[0];

      if (pieces.length > 1) {
        var params = pieces[1].split('&');
        for (var i = 0; i < params.length; i++) {
          var pval = params[i].split('=');
          var input = document.createElement('input');
          input.type = 'hidden';
          var name = pval[0];
          name = name.replace('%5B', '[');
          name = name.replace('%5D', ']');
          input.name = name;
          if (pval.length > 1)
            input.value = pval[1];
          form.appendChild(input);
        }
      }
    }

    form.method = method;
    form.action = action;
  }
}

Reports = {
  intervalOnchange: function(select) {
    var vals = select.value.split(',');
    var form = select.form;
    for (var i = 0; i < 6; i++) {
      var elem = form["parameters_" + ((i < 3) ? "Start" : "End") + "Date_" +
                      ["year","month","day"][i % 3]];
      if (vals.length < 6)
        elem.disabled = false;
      else {
        elem.value = vals[i];
        elem.disabled = true;
      }
    }
  }
}

ProfileEditor = {
  currentItem: null,

  onLoad: function(item) {
    if (item == null) {
      ProfileEditor.currentItem = null;
    }
    else {
      var elem = $("profile_" + item);
      if (Element.select(elem, 'div.profile-item-edit').some(Element.visible)) {
        ProfileEditor.currentItem = elem;
      }
      else {
        ProfileEditor.currentItem = null;
      }
    }
  },

  onMouseOver: function(item) {
    if (ProfileEditor.currentItem != null) return;
    var menu = Element.select(item, 'div.profile-item-edit-menu');
    if (menu.length > 0) {
      Element.addClassName(item, "editable");
      menu.map(function(menu) {
                 menu.style.visibility = 'visible';
               });
    }
  },

  onMouseOut: function(item, event) {
    if (ProfileEditor.currentItem != null) return;
    var relatedTarget = event.relatedTarget || event.toElement;
    while (true) {
      if (relatedTarget == item) return; // Moving in to something else in tree.
      if ((relatedTarget == null) || (relatedTarget.nodeName == 'BODY')) break;
      relatedTarget = relatedTarget.parentNode;
    }
    var menu = Element.select(item, 'div.profile-item-edit-menu');
    if (menu.length > 0) {
      menu.map(function(menu) {
                 menu.style.visibility = 'hidden';
               });
      Element.removeClassName(item, "editable");
    }
  },

  resizeIframe: function(iframe) {
    var body = (iframe.contentDocument || document.frames(iframe.id).document).body;
    var height = body.scrollHeight + (body.offsetHeight - body.clientHeight) + 10;
    var width = body.scrollWidth + (body.offsetWidth - body.clientWidth);
    // It would be better to shrink when loading shorter content, but then there's
    // no way to expand with the content within the iframe if it changes via Javascript.
    if (iframe.offsetHeight < height)
      iframe.style.height = height + "px";
    iframe.style.width = width + "px";
  },

  showEditor: function(item, options) {
    var elem = $("profile_" + item);
    Element.removeClassName(elem, "editable");
    if (options['iframe']) {
      elem.innerHTML = 
        "<iframe id='" + item + "_iframe'" +
        "        src='" + options['iframe'] + "'" +
        "        onload='ProfileEditor.resizeIframe(this)'" +
        "        frameborder='no' scrolling='no'></iframe>";
      return;
    }
    Element.select(elem, 'div.profile-item-display').map(Element.hide);
    Element.select(elem, 'div.profile-item-edit').map(Element.show);
    ProfileEditor.currentItem = elem;
    // If there is exactly one text field in the main section, select it for convenience.
    var form = Element.select(elem, 'form').first();
    var main = Element.select(form, 'div.profile-item-details').first();
    var oneInput = null, more = false;
    Element.select(main, 'input').map(function(input) {
                                        switch (input.type) {
                                        case 'hidden':
                                        case 'submit':
                                          return;
                                        case 'text':
                                          if (oneInput == null)
                                            oneInput = input;
                                          else
                                            more = true;
                                          break;
                                        default:
                                          more = true;
                                        }
                                      });
    Element.select(main, 'textarea').map(function(input) {
                                          if (oneInput == null)
                                            oneInput = input;
                                          else
                                            more = true;
                                      });
    if (oneInput != null && !more)
      oneInput.select();
  },

  spinnerLocation: '/images/spinner.gif',

  spinner: function(item) {
    var elem = $("profile_" + item);
    var height = elem.offsetHeight;
    elem.innerHTML = "<div style='height: " + (height+32)/2 + "px; padding-top: " + 
      (height-32)/2 + "px; text-align:center;'>" +
    "<img width='32' height='32' alt='spinner' src='" + ProfileEditor.spinnerLocation + 
      "'></img></div>";
  }
}

Friends = {
  onMouseOver: function(item) {
    var menu = Element.select(item, 'div.friend-info-more-menu');
    if (menu.length > 0) {
      Element.addClassName(item, "expandable");
      menu.map(function(menu) {
                 menu.style.visibility = 'visible';
               });
    }
  },

  onMouseOut: function(item, event) {
    var relatedTarget = event.relatedTarget || event.toElement;
    while (true) {
      if (relatedTarget == item) return; // Moving in to something else in tree.
      if ((relatedTarget == null) || (relatedTarget.nodeName == 'BODY')) break;
      relatedTarget = relatedTarget.parentNode;
    }
    var menu = Element.select(item, 'div.friend-info-more-menu');
    if (menu.length > 0) {
      menu.map(function(menu) {
                 menu.style.visibility = 'hidden';
               });
      Element.removeClassName(item, "expandable");
    }
  },

  toggleExpanded: function(item, options) {
    var elem = $("friend_" + item);
    var more = Element.select(elem, 'div.friend-info-more').first();
    var menu = Element.select(elem, 'div.friend-info-more-menu').first();
    var link = Element.select(menu, 'a').first();
    if (Element.visible(more)) {
      Element.hide(more);
      link.innerHTML = "View Full Profile";
    }
    else {
      Element.show(more);
      link.innerHTML = "Hide Full Profile";
    }
  },

  clearNotes: function(item) {
    var elem = $("friend_" + item);
    Element.select(elem, 'div.friend-desc-notes').map(function(notes) {
                                                        Element.removeClassName(notes, "with-note");
                                                      })
  },

  onFocusSearch: function(input, focus) {
    input = $(input);
    if (focus) {
      if (Element.hasClassName(input, 'watermark')) {
        input.value = '';
        Element.removeClassName(input, 'watermark');
      }
    }
    else {
      if (input.value == '') {
        input.value = 'Find friends';
        Element.addClassName(input, 'watermark');
      }
    }
  }
}
