// Copyright 2009 Google Inc.
// All Rights Reserved.
// Owner: webgroup-emea@google.com

/**
 * @fileoverview Javascript for Things to do.
 * @author amcgrath@google.com (Adam Mcgrath)
 */

/**
 * Namespace ttd (Things to do).
 */
var ttd = window.ttd || {};

/**
 * The utils namespace.
 * @type {object}
 */
ttd.utils = ttd.utils || {};

/**
 * Updates the browsers location bar with correct tip id.
 * @param {string} id The tip id.
 */
ttd.utils.updateLocationBar = function(id) {
  document.location.hash = '#tip' + id;
  if ((navigator.userAgent.toLowerCase().indexOf('safari') != -1) &&
      window.location.href.match(/#(\w.+)/)) {
    window.location.replace(window.location.hash);
  }
};

/**
 * Takes and object and a template and ouputs HTML.
 * @param {object} obj The data object.
 * @param {string} template The template string.
 * @return {string} The HTML string.
 */
ttd.utils.templatize = function(obj, template) {
  return template.replace(/{(.*?)}/g, function(str, p1) {
    return obj[p1] || '';
  })
};

/**
 * Checks if an element has a certain class name.
 * @param {element} el The element to check.
 * @param {string} str The classname to check.
 * @return {boolean} The result of the check.
 */
ttd.utils.hasClass = function(el, str) {
  return el.className.match(new RegExp('(\\s|^)' + str + '(\\s|$)'));
};

/**
 * Adds a classname to an element.
 * @param {element} el The element.
 * @param {string} str The classname to add.
 */
ttd.utils.addClass = function(el, str) {
  if (!ttd.utils.hasClass(el, str)) {
    el.className += ' ' + str;
  }
};

/**
 * Removes a classname from an element.
 * @param {element} el The element.
 * @param {string} str The classname to remove.
 */
ttd.utils.removeClass = function(el, str) {
  if (ttd.utils.hasClass(el, str)) {
    var reg = new RegExp('(\\s|^)' + str + '(\\s|$)');
    el.className = el.className.replace(reg, ' ');
  }
};

/**
 * Gets a cross browser xhr object.
 * @return {XMLHTTPRequest|ActiveXObject} The xhr request.
 */
ttd.utils.getXhr = function() {
  var request = null;
  if (window.XMLHttpRequest) {
    request = new XMLHttpRequest();
  } else if (window.ActiveXObject) {
    request = new ActiveXObject('Microsoft.XMLHTTP');
  };
  return request;
};

/**
 * Partially applies this function to a particular 'this object' and zero or
 * more arguments. The result is a new function with some arguments of the first
 * function pre-filled and the value of |this| 'pre-specified'.
 * @param {Function} fn A function to partially apply.
 * @param {Object} self Specifies the object which |this| should point to
 *     when the function is run. If the value is null or undefined, it will
 *     default to the global object.
 * @param {Object} var_args Additional arguments that are partially
 *     applied to the function.
 * @return {Function} A partially-applied form of the function bind() was
 *     invoked as a method of.
 */
ttd.utils.bind = function(fn, self, var_args) {
  var boundArgs = fn.boundArgs_;
  if (arguments.length > 2) {
    var args = Array.prototype.slice.call(arguments, 2);
    if (boundArgs) {
      args.unshift.apply(args, boundArgs);
    }
    boundArgs = args;
  }
  self = fn.boundSelf_ || self;
  fn = fn.boundFn_ || fn;
  var newfn;
  var context = self || goog.global;
  if (boundArgs) {
    newfn = function() {
      // Combine the static args and the new args into one big array.
      var args = Array.prototype.slice.call(arguments);
      args.unshift.apply(args, boundArgs);
      return fn.apply(context, args);
    }
  } else {
    newfn = function() {
      return fn.apply(context, arguments);
    }
  }
  newfn.boundArgs_ = boundArgs;
  newfn.boundSelf_ = self;
  newfn.boundFn_ = fn;
  return newfn;
};
