// JavaScript Document
//TODO load dependencies: browserdetect

//onDOMReady Event Extension
//http://clientside.cnet.com/code-snippets/event-scripting/a-dom-ready-extension-for-prototype/
Object.extend(Event, {
  _domReady : function() {
    if (arguments.callee.done) return;
    arguments.callee.done = true;

    if (this._timer)  clearInterval(this._timer);
    
    this._readyCallbacks.each(function(f) { f() });
    this._readyCallbacks = null;
},
  onDOMReady : function(f) {
    if (!this._readyCallbacks) {
      var domReady = this._domReady.bind(this);
      
      if (document.addEventListener)
        document.addEventListener("DOMContentLoaded", domReady, false);
        
        /*@cc_on @*/
        /*@if (@_win32)
            document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>");
            document.getElementById("__ie_onload").onreadystatechange = function() {
                if (this.readyState == "complete") domReady(); 
            };
        /*@end @*/
        
        if (/WebKit/i.test(navigator.userAgent)) { 
          this._timer = setInterval(function() {
            if (/loaded|complete/.test(document.readyState)) domReady(); 
          }, 10);
        }
        
        Event.observe(window, 'load', domReady);
        Event._readyCallbacks =  [];
    }
    Event._readyCallbacks.push(f);
  }
});


if (typeof(AC) == "undefined") AC = {};

AC.decorateSearchInput = function(field, options) {
  
  var searchField = $(field);
  var standIn = null;

  var results = 0;
  var placeholder = '';
  var autosave = '';

  if(options) {
    
    if(options.results) { results = options.results; }
    if(options.placeholder) { placeholder = options.placeholder; }
    if(options.autosave) { autosave = options.autosave; }
    
  }
  
  if(AC.Detector.isWebKit()) {
    
    searchField.setAttribute('type', 'search');
    if(!searchField.getAttribute('results')) {
      searchField.setAttribute('results', results);
    }
    
    if(null != placeholder) {
      searchField.setAttribute('placeholder', placeholder);
      searchField.setAttribute('autosave', autosave);
    }
    
  } else {
    
    //prevent browser from doing its own autocomplete, threw odd xul 
    //error on reset sometimes, although this feels a little
    //heavy handed
    searchField.setAttribute('autocomplete', 'off');
    
    //replace the field with a standin while we create the wrapper
    //we can't lose the reference to this field as other objects may
    //have already registered listeners on this field
    
    standIn = document.createElement('input');
    searchField.parentNode.replaceChild(standIn, searchField)

    var left = document.createElement('span');
    Element.addClassName(left, 'left');
  
    var right = document.createElement('span');
    Element.addClassName(right, 'right');
    
    var reset = document.createElement('div');
    Element.addClassName(reset, 'reset');
    
    var wrapper = document.createElement('div');
    Element.addClassName(wrapper, 'search-wrapper');
    
    var alreadyHasPlaceholder = field.value == placeholder;
    var isEmpty = field.value.length == 0;
    
    if (alreadyHasPlaceholder || isEmpty) {
      searchField.value = placeholder;
      Element.addClassName(wrapper, 'blurred');
      Element.addClassName(wrapper, 'empty');
    }
  
    wrapper.appendChild(left);
    wrapper.appendChild(searchField);
    wrapper.appendChild(right);
    wrapper.appendChild(reset);
  
    var focus = function() {
      
      var blurred = Element.hasClassName(wrapper, 'blurred');

      //need to check for flag AND placeholder lest somebody need to 
      //search for the placeholder text itself
      if(searchField.value == placeholder && blurred) {
        searchField.value = '';
      }
      
      Element.removeClassName(wrapper, 'blurred');
    }
    Event.observe(searchField, 'focus', focus);
    
    var blur = function() {
      
      if(searchField.value == '') {
        Element.addClassName(wrapper, 'empty');
        searchField.value = placeholder;
      }
      
      Element.addClassName(wrapper, 'blurred');
    }
    Event.observe(searchField, 'blur', blur);
    
    
    var toggleReset = function() {
      
      if(searchField.value.length >= 0) {
        Element.removeClassName(wrapper, 'empty');
      }
    }
    Event.observe(searchField, 'keydown', toggleReset);
  
  
    var resetField = function() {
      return( function(evt) {
        
        var escaped = false;
        
        if(evt.type == 'keydown') {
          if(evt.keyCode != 27) {
            return; //if it's not escape ignore it
          } else {
            escaped = true;
          }
        }
        
        searchField.blur(); //can't change value while in field
        searchField.value = '';
        Element.addClassName(wrapper, 'empty');
        searchField.focus();

      })
    }
    Event.observe(reset, 'mousedown', resetField());
    Event.observe(searchField, 'keydown', resetField());
  
    if (standIn) {
      standIn.parentNode.replaceChild(wrapper, standIn);
    }
    
  }
}

// this is called Element2 because
// adding methods to Element BLOWS UP IE7 
// for a reason I still haven't got to the bottom
// of.  It appears to be fine though, as long
// as you don't try to add additional methods
// to Element itself.
var Element2 = {};
Element2.Methods = {
  
  getInnerDimensions: function(element) {
      
    element = $(element);
    var d = Element.getDimensions(element);
    
    var innerHeight = d.height;
    var styleOf = Element.getStyle;
    innerHeight -= styleOf(element, 'border-top-width') && styleOf(element, 'border-top-width') != 'medium' ? parseInt(styleOf(element, 'border-top-width'), 10) : 0;
    innerHeight -= styleOf(element, 'border-bottom-width') && styleOf(element, 'border-bottom-width') != 'medium' ? parseInt(styleOf(element, 'border-bottom-width'), 10) : 0;
    innerHeight -= styleOf(element, 'padding-top') ? parseInt(styleOf(element, 'padding-top'), 10) : 0;
    innerHeight -= styleOf(element, 'padding-bottom') ? parseInt(styleOf(element, 'padding-bottom'), 10) : 0;

    var innerWidth = d.width;
    innerWidth -= styleOf(element, 'border-left-width') && styleOf(element, 'border-left-width') != 'medium' ? parseInt(styleOf(element, 'border-left-width'), 10) : 0;
    innerWidth -= styleOf(element, 'border-right-width') && styleOf(element, 'border-right-width') != 'medium' ? parseInt(styleOf(element, 'border-right-width'), 10) : 0;
    innerWidth -= styleOf(element, 'padding-left') ? parseInt(styleOf(element, 'padding-left'), 10) : 0;
    innerWidth -= styleOf(element, 'padding-right') ? parseInt(styleOf(element, 'padding-right'), 10) : 0;

      return {width: innerWidth, height: innerHeight};
  },
  
  /*
    Yes, we understand this is a hack. Safari is calculating margins for unpositioned elements
    as the total remaining viewport width
  */
  getOuterDimensions: function(element) {
    element = $(element);
    var clone = element.cloneNode(true);
    
    document.body.appendChild(clone);
    Element.setStyle(clone, { position: "absolute", visibility: "hidden" });
    var d = Element.getDimensions(clone);
    
    var outerHeight = d.height;
    var styleOf = Element.getStyle;
    outerHeight += styleOf(clone, 'margin-top') ? parseInt(styleOf(clone, 'margin-top'), 10) : 0;
    outerHeight += styleOf(clone, 'margin-bottom') ? parseInt(styleOf(clone, 'margin-bottom'), 10) : 0;

    var outerWidth = d.width;
    outerWidth += styleOf(clone, 'margin-left') ? parseInt(styleOf(clone, 'margin-left'), 10) : 0;
    outerWidth += styleOf(clone, 'margin-right') ? parseInt(styleOf(clone, 'margin-right'), 10) : 0;

    Element.remove(clone);
    
    return {width: outerWidth, height: outerHeight};
  },
  
  removeAllChildNodes: function(element) {
    element = $(element);
    if(! element) { return; }
    
    while (element.hasChildNodes()) {
        element.removeChild(element.lastChild);
    }
  }
  
};

Object.extend(Element, Element2.Methods);




/**
* Omniture Tracking library
*/
if (typeof(AC.Tracking) == "undefined") {AC.Tracking = {};}

AC.Tracking.getLinkClicked = function(target) {
  
  if (!target) {
    return null;
  }
  
  while (target.nodeName != 'A' && target.nodeName != 'BODY') {
    target = target.parentNode;
  }
  
  if (!target.href) {
    target = null;
  }
  
  return target;
};

AC.Tracking.trackLinksWithin = function(container, test, title, properties, options) {
  
  $(container).observe('mousedown', function(evt) {
    
    var target = AC.Tracking.getLinkClicked(Event.element(evt));
    
    if (target && test(target)) {
      
      if (options && options.beforeTrack) {
        // provides a way to alter the properties or the title in some way for the mousedown
        // most felixble way to capture what link was actually clicked or whatever else
        // you want at the time of the event
        var altered = options.beforeTrack(target, title, properties);
        if (altered) {
          title = altered.title;
          properties = altered.properties;
        }
      }
      
      AC.Tracking.trackClick(properties, this, 'o', title);
    }
    
  });
  
}

/**
 * Effectively tags all links within a container conforming to the supplied 
 * test function reference with the specified key and value.
 * 
 * The test argument should be a function reference expecting the link as 
 * its first and only parameter. It should simply return true or false 
 * indicating whether the link should be tagged or not.
 */
AC.Tracking.tagLinksWithin = function(container, key, value, test) {
  
  $(container).observe('mousedown', function(evt) {
    
    var link = Event.element(evt);

    if (!link) {
      return;
    }

    while (link.nodeName != 'A' && link.nodeName != 'BODY') {
      link = link.parentNode;
    }
    
    if (link.href && test(link)) {
      AC.Tracking.tagLink(link, key, value);
    }
    
    link = null;
  })
  
};

/**
 * Appends the specified key and value to the querystring of the supplied 
 * anchor's href attribute.
 */
AC.Tracking.tagLink = function(link, key, value) {
  
  var href = link.getAttribute('href');
  
  if (href.match(/\?/)) {
    var params = href.toQueryParams();
    params[key] = value;
    href = href.split(/\?/)[0] + '?' + $H(params).toQueryString();
  } else {
    href += '?' + key + '=' + value;
  }
  
  link.setAttribute('href', href);
};

/**
 * Makes a tracking request
 * 
 * Note: Typically you won't need to call this directly, instead you should
 * track events using either TrackClick or TrackPage whihc provide more
 * friendly interfaces to this method 
 * 
 * @trackingMethod the method of Omniture tracking to use
 * @properties associative array of property names and their associated values to track
 * @options associate array of options to use in this tracking context, 
 *  some of these are required depending upon the trackingMethod you have chose
 */
AC.Tracking.track = function(trackingMethod, properties, options) {

  if (typeof(s_gi) == 'undefined' || !s_gi) {
    return;
  }

  options = options || {};

  //use existing tracking account if available, or use one from the options
  if (typeof(s_account) != 'undefined') {
    s = s_gi(s_account)
  } else if (options.s_account){
    s = s_gi(options.s_account);
  } else {
    return;
  }

  if (trackingMethod == s.tl) {
    
    var linkTrackVars = ''
    
    for (var key in properties) {
      linkTrackVars += key + ',';
    }
    linkTrackVars = linkTrackVars.replace(/,$/, '');
    
    s.linkTrackVars = linkTrackVars;
  } else {
    s.linkTrackVars = '';
  }

  //clear properties set by default within a page
  s.prop4 = "";
  s.g_prop4 = "";
  s.prop6 = "";
  s.g_prop6 = "";
  s.pageName = "";
  s.g_pageName = "";
  s.pageURL = "";
  s.g_pageURL = "";
  s.g_channel = "";
  
  var sanitize = function(value) {
      if (typeof(value) == "string") {
          return value.replace(/[\'\"\“\”\‘\’]/g, '');
      } else {
          return value;
      }
  }

  for (var key in properties) {

    s[key] = sanitize(properties[key]);

    if (key == 'events') {
      s.linkTrackEvents = sanitize(properties[key]);
    }
  }

  if (trackingMethod == s.t) {
    void(s.t());
  } else {
    s.tl(options.obj, options.linkType, sanitize(options.title));
  }
  
  for (var key in properties) {
    s[key] = '';
    
    if (key == 'events') {
      s.linkTrackEvents = 'None';
    }
  }

};

/**
 * Uses the Omniture s.tl Tracking method to track a "click"
 * 
 * @properties associative array of params and associated values
 * @obj object for context, usually "this"
 * @linkType type of link for Omniture usually 'o'
 * @title human readable title for this link that shows up in reports
 * @options associative array of options to apply to this tracking context (currently no valid options are available)
 */
AC.Tracking.trackClick = function(properties, obj, linkType, title, options) {
  
  var options = {
    obj: obj,
    linkType: linkType,
    title: title
  };
  
  AC.Tracking.track(s.tl, properties, options);
}

/**
 * Uses the Omniture s.t Tracking method to track a "page load"
 * 
 * @properties associative array of params and associated values
 * @options associative array of options to apply to this tracking context
 */
AC.Tracking.trackPage = function(properties, options) {
  AC.Tracking.track(s.t, properties, options);
}




