function $(id) {
  return document.getElementById(id);
}

function htmlToDocumentFragment(htmlString) {
  var tempDiv = document.createElement('div');
  tempDiv.innerHTML = htmlString;
  if (tempDiv.childNodes.length == 1) {
    return tempDiv.firstChild;
  } else {
    var fragment = document.createDocumentFragment();
    while (tempDiv.firstChild) {
      fragment.appendChild(tempDiv.firstChild);
    }
    return fragment;
  }
}

function isLeapYear(year) {
  // Leap year logic; the 4-100-400 rule
  return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}

function getNumberOfDaysInMonth(year, month) {
  switch(month) {
    case 1: // Feb
      return isLeapYear(year) ? 29 : 28;
    case 3: // April
    case 5: // June
    case 8: // Sep
    case 10: // Nov
      return 30;
    default:
      return 31;
  }
}

/**
 * This DateTime library is used to convert to and from XAPI DateTimes
 * to Doozer ICAL_Temporals. This library also handles converting from
 * UTC into the user's apparent local timezone.
 */

/** The global time zone, such as "-07:00", initialized lazily */
var gTimeZone;

/** The time zone offset, as an integer, so "-07:00" becomes -7 */
var gTimeZoneOffset;

/**
 * A map of string timezones to their integer representations, such as:
 * { "-07:00" : -7 }
 * This cache is populated as needed.
 */
var gTimeZoneConversionCache = {};

/** @return {string} the user's timezone, such as "-07:00" or "+05:30" */
function getTimeZone() {
  if (gTimeZone) return gTimeZone;
  var now = new Date();
  var dateLocal = new Date(now.getFullYear(), now.getMonth(), now.getDate());
  var temp = dateLocal.toGMTString();
  var dateGmt = new Date(temp.substring(0, temp.lastIndexOf(' ')-1));
  var offset = (dateLocal - dateGmt) / (1000 * 60 * 60);
  gTimeZoneOffset = offset;
  var h = offset | 0;
  var sign = (h < 0) ? "-" : "+";
  h = Math.abs(h);
  h = (h < 10) ? "0" + h : "" + h;
  var m = (Math.abs(offset % 1) * 60) | 0;
  if (m < 10) m = "0" + m;
  gTimeZone = sign + h + ":" + m;
  gTimeZoneConversionCache[gTimeZone] = gTimeZoneOffset;
  return gTimeZone;
}

/** @return {number} the offset for the user's timezone, such as -7 */
function getTimeZoneOffset() {
  if (gTimeZoneOffset) return gTimeZoneOffset;
  getTimeZone();
  return gTimeZoneOffset;
}

/**
 *
 * @param offset {string}
 * @return {number} of hours that can be added to the datetime with the
 *     specified offset to convert it to the user's timezone
 */
function convertOffset(offset) {
  if (offset in gTimeZoneConversionCache) {
    return gTimeZoneConversionCache[offset];
  } else {
    var m = offset.match(/([+-])(\d{2}):(\d{2})/);
    var n = parseInt(m[2], 10) + (parseInt(m[3], 10) / 60);
    if (m[1] == '-') n = -n;
    gTimeZoneConversionCache[offset] = getTimeZoneOffset() - n;
    return gTimeZoneConversionCache[offset];
  }
}

// Date parsing and conversion code, courtesy of davem
// It's important to get this right to handle daylight saving time correctly
var DATE_TIME_REGEX =
/^(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)\.\d+(\+|-)(\d\d):(\d\d)$/;
var DATE_TIME_REGEX_Z = /^(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)\.\d+Z$/;
var DATE_REGEX = /^(\d\d\d\d)-(\d\d)-(\d\d)$/;

/* Convert the incoming date into a javascript date
 * we accept the following 3 forms:
 * 2006-04-28T09:00:00.000-07:00
 * 2006-04-28T09:00:00.000Z
 * 2006-04-19
 */
function rfc3339StringToDate(rfc3339) {
  var parts = DATE_TIME_REGEX.exec(rfc3339);

  // Try out the Z version
  if (!parts) {
    parts = DATE_TIME_REGEX_Z.exec(rfc3339);
  }

  var d;
  if (parts && parts.length > 0) {
    d = new Date();
    d.setUTCFullYear(parts[1], parseInt(parts[2], 10) - 1, parts[3]);
    d.setUTCHours(parts[4]);
    d.setUTCMinutes(parts[5]);
    d.setUTCSeconds(parts[6]);

    var tzOffsetFeedMin = 0;
    if (parts.length > 7) {
      tzOffsetFeedMin = parseInt(parts[8], 10) * 60 + parseInt(parts[9], 10);
      if (parts[7] != "-") { // This is supposed to be backwards.
        tzOffsetFeedMin = -tzOffsetFeedMin;
      }
    }
    return new Date(d.getTime() + tzOffsetFeedMin * 60 * 1000);
  }

  parts = DATE_REGEX.exec(rfc3339);
  if (parts && parts.length > 0) {
    return new Date(parts[1], parseInt(parts[2], 10) - 1, parts[3]);
  }
  return null;
}

function zeroPad(n) {
  return (n < 10) ? '0' + n : n;
}

// Add a leading '0' if string is only 1 char
function stringPad(str) {
  var newStr = "" + str;
  if (newStr.length == 1) {
    newStr = "0" + newStr;
  }
  return newStr;
}

function getUTCDateString(timeObj) {
  var dateStr = "" + timeObj.getUTCFullYear();
  dateStr += stringPad(timeObj.getUTCMonth()+1);
  dateStr += stringPad(timeObj.getUTCDate());
  dateStr += "T" + stringPad(timeObj.getUTCHours());
  dateStr += stringPad(timeObj.getUTCMinutes()) + "00Z";
  return dateStr;
}

function dateToEventPageDate(date, isAllDay) {
  if (isAllDay) {
    return "" + date.getFullYear() + zeroPad(date.getMonth() + 1) +
           zeroPad(date.getDate());
  } else {
    return getUTCDateString(date);
  }
}

function formatTime(date) {
  var hour = date.getHours();
  var min = date.getMinutes();
  var ampm = hour < 12 ? 'am' : 'pm';
  var h = "" + (hour % 12 || 12);
  var m = (min < 10 ? "0" : "") + min;
  return h + ":" + m + ampm;
}

