var brandConst = {
	BRAND_ID: "ON",
	BRAND_NAME: "Old Navy",
	BRAND_URL: "oldnavy.com",
	BRAND_CODE:3,

	HAS_BUTTON_MOUSE_OVERS: false,

	POPUP_DEFAULT_WIDTH: 360,
	POPUP_DEFAULT_HEIGHT: 260,
	POPUP_GIFTCARD_WIDTH: 376,
	POPUP_GIFTCARD_HEIGHT: 260,

	BIZRATE_INT_RANGE: 100000,
	BIZRATE_COOKIE_EXPIRATION_TIME: 60 * 1000 * 60 * 24 * 7 * 26,
	BIZRATE_HOME_PERCENT: 1,
	BIZRATE_DIVISION_PERCENT: 1,
	BIZRATE_CATEGORY_PERCENT: 1,
	BIZRATE_PRODUCT_PERCENT: 1,
	BIZRATE_OUTFIT_PERCENT: 1,

	LAYERED_POPUP_CONTENT_COMBINED_BORDER_WIDTH: 0, //historically objB.LP.w1 from brandFunctions.js
	LAYERED_POPUP_CONTENT_COMBINED_BORDER_HEIGHT: 0, //historically objB.LP.h1
	LAYERED_POPUP_COMBINED_BORDER_WIDTH: 10, //historically objB.LP.w
	LAYERED_POPUP_COMBINED_BORDER_HEIGHT: 55, //historically objB.LP.h
	LAYERED_POPUP_SHADOW_WIDTH: 0, //historically objB.LP.w2
	LAYERED_POPUP_MAIN_WIDTH_OFFSET: 37,
	LAYERED_POPUP_SIDE_IMAGE_WIDTH: 22, // This is the width of offset for any side image

	LAYERED_POPUP_BRAND_NAME: "Oldnavy.com",
	LAYERED_POPUP_CLOSE_COPY: "Close Window",
    SURVEY_POPUP_DISTRIBUTION: { home: [99.2,0.8,0], division: [99.2,0.8,0], product: [99.2,0.8,0], category: [99.2,0.8,0], outfit: [99.2,0.8,0], confirm: [75,25,0] },
    SURVEY_BRAND: [ '','RockbridgeSurvey', 'BizrateSurvey' ],
    PAGE_TYPES: { home: 1, division: 2, product: 3, category: 4, outfit: 5, confirm: 6 },

    PRODUCT_THUMB_WIDTH : 38,
    PRODUCT_THUMB_HEIGHT : 51,
    PRODUCT_IMAGE_THUMB_WIDTH : 75,
    PRODUCT_IMAGE_THUMB_HEIGHT : 100,
    ALT_VIEWS : ['Main', 'Large'],

    MAX_CROSSSELL: 4,

    imagePath: 'strImagePath',
    imageThumbPath : 'strThumbImagePath',
    
    inlineBag: {
		CONTENT_DATA_ELEMENT_ID:"inlineShoppingBagDataLayer",
		MARKETING_DATA_ELEMENT_ID:"inlineBagMarketingCenter",
		CONTENT_X_OFFSET:10,
		CONTENT_Y_OFFSET:0,
		MARKETING_CONTENT_X_OFFSET:4,
		MARKETING_CONTENT_Y_OFFSET:0
    },
    
    footer: {
    	IMAGES: {
			objStoreLocatorOff:{src:"/assets/common/navigation/en/footer_storelocator_off.gif"},
			objStoreLocatorOver:{src:"/assets/common/navigation/en/footer_storelocator_over.gif"},
			objOnCreditCardOff:{src:"/assets/common/navigation/en/footer_oncreditcard_off.gif"},
			objOnCreditCardOver:{src:"/assets/common/navigation/en/footer_oncreditcard_over.gif"},
			objSundaysAdOff:{src:"/assets/common/navigation/en/footer_sundaysad_off.gif"},
			objSundaysAdOver:{src:"/assets/common/navigation/en/footer_sundaysad_over.gif"},
			objLatestBuzzOff:{src:"/assets/common/navigation/en/footer_latestbuzz_off.gif"},
			objLatestBuzzOver:{src:"/assets/common/navigation/en/footer_latestbuzz_over.gif"}
    	}
    },
    
    isCombineColorImages: true

};

var brandProperties = {
    INT1SIZEMAXWIDTH : 156,
    INT2SIZEMAXWIDTH : 156,
    INTCOMMONSWATCHWIDTHDELTA : 20,
    MINIMUMSIZESWATCHWIDTH : 20,
    INTSIZESWATCHHEIGHT : '20px',
    SOLDOUTMSGDX : 10,
    SOLDOUTMSGDY : 100,
	ONORDERMSGDX : -154,
	ONORDERMSGDY : -31,
    OVERVIEWTABID : 1,
    INTCOLORTEXTWIDTH : 150,
    SIZECHARTWINDOWWIDTH : 675,
    SIZECHARTWINDOWHEIGHT : 504,
    QUICKLOOKIMGWIDTH : 202,
    QUICKLOOKIMGHEIGHT : 270,
    PRODUCTIMGWIDTH: 196,
    PRODUCTIMGHEIGHT: 263,
    QUICKLOOKTARGETXPOS : 60,
    QUICKLOOKTARGETYPOS : 200,
    QUICKLOOKOFFXOFFSET : -20,
    QUICKLOOKANIMATIONSTEPS : 8,
    TABCONTENTOFFSET : -13,
    MOREVIEWSPOPUPWIDTH : 700,
    DEFAULTSIZECHARTID : 17328,
    VENDORIDSYMBOL:'',
    PRODUCTIMAGE_SPRITE_SWATCH_WIDTH: 18,
    PRODUCTIMAGE_SPRITE_SWATCH_HEIGHT: 18,
    PRODUCTIMAGE_SPRITE_MAIN_IMAGE_POSITION: '-2px 0',
    PRODUCTIMAGE_SPRITE_MAIN_IMAGE_HEIGHT: '270px',
    inLineBag: {}
};

var resourceBundleValues = window["resourceBundleValues"];
if (resourceBundleValues) {
	for (var key in resourceBundleValues) {
		brandProperties[key] = resourceBundleValues[key];
	}
}


// Modernizr v1.7  www.modernizr.com
window.Modernizr=function(a,b,c){function G(){e.input=function(a){for(var b=0,c=a.length;b<c;b++)t[a[b]]=!!(a[b]in l);return t}("autocomplete autofocus list placeholder max min multiple pattern required step".split(" ")),e.inputtypes=function(a){for(var d=0,e,f,h,i=a.length;d<i;d++)l.setAttribute("type",f=a[d]),e=l.type!=="text",e&&(l.value=m,l.style.cssText="position:absolute;visibility:hidden;",/^range$/.test(f)&&l.style.WebkitAppearance!==c?(g.appendChild(l),h=b.defaultView,e=h.getComputedStyle&&h.getComputedStyle(l,null).WebkitAppearance!=="textfield"&&l.offsetHeight!==0,g.removeChild(l)):/^(search|tel)$/.test(f)||(/^(url|email)$/.test(f)?e=l.checkValidity&&l.checkValidity()===!1:/^color$/.test(f)?(g.appendChild(l),g.offsetWidth,e=l.value!=m,g.removeChild(l)):e=l.value!=m)),s[a[d]]=!!e;return s}("search tel url email datetime date month week time datetime-local number range color".split(" "))}function F(a,b){var c=a.charAt(0).toUpperCase()+a.substr(1),d=(a+" "+p.join(c+" ")+c).split(" ");return!!E(d,b)}function E(a,b){for(var d in a)if(k[a[d]]!==c&&(!b||b(a[d],j)))return!0}function D(a,b){return(""+a).indexOf(b)!==-1}function C(a,b){return typeof a===b}function B(a,b){return A(o.join(a+";")+(b||""))}function A(a){k.cssText=a}var d="1.7",e={},f=!0,g=b.documentElement,h=b.head||b.getElementsByTagName("head")[0],i="modernizr",j=b.createElement(i),k=j.style,l=b.createElement("input"),m=":)",n=Object.prototype.toString,o=" -webkit- -moz- -o- -ms- -khtml- ".split(" "),p="Webkit Moz O ms Khtml".split(" "),q={svg:"http://www.w3.org/2000/svg"},r={},s={},t={},u=[],v,w=function(a){var c=b.createElement("style"),d=b.createElement("div"),e;c.textContent=a+"{#modernizr{height:3px}}",h.appendChild(c),d.id="modernizr",g.appendChild(d),e=d.offsetHeight===3,c.parentNode.removeChild(c),d.parentNode.removeChild(d);return!!e},x=function(){function d(d,e){e=e||b.createElement(a[d]||"div");var f=(d="on"+d)in e;f||(e.setAttribute||(e=b.createElement("div")),e.setAttribute&&e.removeAttribute&&(e.setAttribute(d,""),f=C(e[d],"function"),C(e[d],c)||(e[d]=c),e.removeAttribute(d))),e=null;return f}var a={select:"input",change:"input",submit:"form",reset:"form",error:"img",load:"img",abort:"img"};return d}(),y=({}).hasOwnProperty,z;C(y,c)||C(y.call,c)?z=function(a,b){return b in a&&C(a.constructor.prototype[b],c)}:z=function(a,b){return y.call(a,b)},r.flexbox=function(){function c(a,b,c,d){a.style.cssText=o.join(b+":"+c+";")+(d||"")}function a(a,b,c,d){b+=":",a.style.cssText=(b+o.join(c+";"+b)).slice(0,-b.length)+(d||"")}var d=b.createElement("div"),e=b.createElement("div");a(d,"display","box","width:42px;padding:0;"),c(e,"box-flex","1","width:10px;"),d.appendChild(e),g.appendChild(d);var f=e.offsetWidth===42;d.removeChild(e),g.removeChild(d);return f},r.canvas=function(){var a=b.createElement("canvas");return a.getContext&&a.getContext("2d")},r.canvastext=function(){return e.canvas&&C(b.createElement("canvas").getContext("2d").fillText,"function")},r.webgl=function(){return!!a.WebGLRenderingContext},r.touch=function(){return"ontouchstart"in a||w("@media ("+o.join("touch-enabled),(")+"modernizr)")},r.geolocation=function(){return!!navigator.geolocation},r.postmessage=function(){return!!a.postMessage},r.websqldatabase=function(){var b=!!a.openDatabase;return b},r.indexedDB=function(){for(var b=-1,c=p.length;++b<c;){var d=p[b].toLowerCase();if(a[d+"_indexedDB"]||a[d+"IndexedDB"])return!0}return!1},r.hashchange=function(){return x("hashchange",a)&&(b.documentMode===c||b.documentMode>7)},r.history=function(){return !!(a.history&&history.pushState)},r.draganddrop=function(){return x("dragstart")&&x("drop")},r.websockets=function(){return"WebSocket"in a},r.rgba=function(){A("background-color:rgba(150,255,150,.5)");return D(k.backgroundColor,"rgba")},r.hsla=function(){A("background-color:hsla(120,40%,100%,.5)");return D(k.backgroundColor,"rgba")||D(k.backgroundColor,"hsla")},r.multiplebgs=function(){A("background:url(//:),url(//:),red url(//:)");return(new RegExp("(url\\s*\\(.*?){3}")).test(k.background)},r.backgroundsize=function(){return F("backgroundSize")},r.borderimage=function(){return F("borderImage")},r.borderradius=function(){return F("borderRadius","",function(a){return D(a,"orderRadius")})},r.boxshadow=function(){return F("boxShadow")},r.textshadow=function(){return b.createElement("div").style.textShadow===""},r.opacity=function(){B("opacity:.55");return/^0.55$/.test(k.opacity)},r.cssanimations=function(){return F("animationName")},r.csscolumns=function(){return F("columnCount")},r.cssgradients=function(){var a="background-image:",b="gradient(linear,left top,right bottom,from(#9f9),to(white));",c="linear-gradient(left top,#9f9, white);";A((a+o.join(b+a)+o.join(c+a)).slice(0,-a.length));return D(k.backgroundImage,"gradient")},r.cssreflections=function(){return F("boxReflect")},r.csstransforms=function(){return!!E(["transformProperty","WebkitTransform","MozTransform","OTransform","msTransform"])},r.csstransforms3d=function(){var a=!!E(["perspectiveProperty","WebkitPerspective","MozPerspective","OPerspective","msPerspective"]);a&&"webkitPerspective"in g.style&&(a=w("@media ("+o.join("transform-3d),(")+"modernizr)"));return a},r.csstransitions=function(){return F("transitionProperty")},r.fontface=function(){var a,c,d=h||g,e=b.createElement("style"),f=b.implementation||{hasFeature:function(){return!1}};e.type="text/css",d.insertBefore(e,d.firstChild),a=e.sheet||e.styleSheet;var i=f.hasFeature("CSS2","")?function(b){if(!a||!b)return!1;var c=!1;try{a.insertRule(b,0),c=/src/i.test(a.cssRules[0].cssText),a.deleteRule(a.cssRules.length-1)}catch(d){}return c}:function(b){if(!a||!b)return!1;a.cssText=b;return a.cssText.length!==0&&/src/i.test(a.cssText)&&a.cssText.replace(/\r+|\n+/g,"").indexOf(b.split(" ")[0])===0};c=i('@font-face { font-family: "font"; src: url(data:,); }'),d.removeChild(e);return c},r.video=function(){var a=b.createElement("video"),c=!!a.canPlayType;if(c){c=new Boolean(c),c.ogg=a.canPlayType('video/ogg; codecs="theora"');var d='video/mp4; codecs="avc1.42E01E';c.h264=a.canPlayType(d+'"')||a.canPlayType(d+', mp4a.40.2"'),c.webm=a.canPlayType('video/webm; codecs="vp8, vorbis"')}return c},r.audio=function(){var a=b.createElement("audio"),c=!!a.canPlayType;c&&(c=new Boolean(c),c.ogg=a.canPlayType('audio/ogg; codecs="vorbis"'),c.mp3=a.canPlayType("audio/mpeg;"),c.wav=a.canPlayType('audio/wav; codecs="1"'),c.m4a=a.canPlayType("audio/x-m4a;")||a.canPlayType("audio/aac;"));return c},r.localstorage=function(){try{return!!localStorage.getItem}catch(a){return!1}},r.sessionstorage=function(){try{return!!sessionStorage.getItem}catch(a){return!1}},r.webWorkers=function(){return!!a.Worker},r.applicationcache=function(){return!!a.applicationCache},r.svg=function(){return!!b.createElementNS&&!!b.createElementNS(q.svg,"svg").createSVGRect},r.inlinesvg=function(){var a=b.createElement("div");a.innerHTML="<svg/>";return(a.firstChild&&a.firstChild.namespaceURI)==q.svg},r.smil=function(){return!!b.createElementNS&&/SVG/.test(n.call(b.createElementNS(q.svg,"animate")))},r.svgclippaths=function(){return!!b.createElementNS&&/SVG/.test(n.call(b.createElementNS(q.svg,"clipPath")))};for(var H in r)z(r,H)&&(v=H.toLowerCase(),e[v]=r[H](),u.push((e[v]?"":"no-")+v));e.input||G(),e.crosswindowmessaging=e.postmessage,e.historymanagement=e.history,e.addTest=function(a,b){a=a.toLowerCase();if(!e[a]){b=!!b(),g.className+=" "+(b?"":"no-")+a,e[a]=b;return e}},A(""),j=l=null,f&&a.attachEvent&&function(){var a=b.createElement("div");a.innerHTML="<elem></elem>";return a.childNodes.length!==1}()&&function(a,b){function p(a,b){var c=-1,d=a.length,e,f=[];while(++c<d)e=a[c],(b=e.media||b)!="screen"&&f.push(p(e.imports,b),e.cssText);return f.join("")}function o(a){var b=-1;while(++b<e)a.createElement(d[b])}var c="abbr|article|aside|audio|canvas|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",d=c.split("|"),e=d.length,f=new RegExp("(^|\\s)("+c+")","gi"),g=new RegExp("<(/*)("+c+")","gi"),h=new RegExp("(^|[^\\n]*?\\s)("+c+")([^\\n]*)({[\\n\\w\\W]*?})","gi"),i=b.createDocumentFragment(),j=b.documentElement,k=j.firstChild,l=b.createElement("body"),m=b.createElement("style"),n;o(b),o(i),k.insertBefore(m,k.firstChild),m.media="print",a.attachEvent("onbeforeprint",function(){var a=-1,c=p(b.styleSheets,"all"),k=[],o;n=n||b.body;while((o=h.exec(c))!=null)k.push((o[1]+o[2]+o[3]).replace(f,"$1.iepp_$2")+o[4]);m.styleSheet.cssText=k.join("\n");while(++a<e){var q=b.getElementsByTagName(d[a]),r=q.length,s=-1;while(++s<r)q[s].className.indexOf("iepp_")<0&&(q[s].className+=" iepp_"+d[a])}i.appendChild(n),j.appendChild(l),l.className=n.className,l.innerHTML=n.innerHTML.replace(g,"<$1font")}),a.attachEvent("onafterprint",function(){l.innerHTML="",j.removeChild(l),j.appendChild(n),m.styleSheet.cssText=""})}(a,b),e._enableHTML5=f,e._version=d,g.className=g.className.replace(/\bno-js\b/,"")+" js "+u.join(" ");return e}(this,this.document)
/*  Prototype JavaScript framework, version 1.7
 *  (c) 2005-2010 Sam Stephenson
 *
 *  Prototype is freely distributable under the terms of an MIT-style license.
 *  For details, see the Prototype web site: http://www.prototypejs.org/
 *
 *--------------------------------------------------------------------------*/

var Prototype = {

  Version: '1.7',

  Browser: (function(){
    var ua = navigator.userAgent;
    var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]';
    return {
      IE:             !!window.attachEvent && !isOpera,
      Opera:          isOpera,
      WebKit:         ua.indexOf('AppleWebKit/') > -1,
      Gecko:          ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1,
      MobileSafari:   /Apple.*Mobile/.test(ua)
    }
  })(),

  BrowserFeatures: {
    XPath: !!document.evaluate,

    SelectorsAPI: !!document.querySelector,

    ElementExtensions: (function() {
      var constructor = window.Element || window.HTMLElement;
      return !!(constructor && constructor.prototype);
    })(),
    SpecificElementExtensions: (function() {
      if (typeof window.HTMLDivElement !== 'undefined')
        return true;

      var div = document.createElement('div'),
          form = document.createElement('form'),
          isSupported = false;

      if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) {
        isSupported = true;
      }

      div = form = null;

      return isSupported;
    })()
  },

  ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
  JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,

  emptyFunction: function() { },

  K: function(x) { return x }
};

if (Prototype.Browser.MobileSafari)
  Prototype.BrowserFeatures.SpecificElementExtensions = false;


var Abstract = { };


var Try = {
  these: function() {
    var returnValue;

    for (var i = 0, length = arguments.length; i < length; i++) {
      var lambda = arguments[i];
      try {
        returnValue = lambda();
        break;
      } catch (e) { }
    }

    return returnValue;
  }
};

/* Based on Alex Arnell's inheritance implementation. */

var Class = (function() {

  var IS_DONTENUM_BUGGY = (function(){
    for (var p in { toString: 1 }) {
      if (p === 'toString') return false;
    }
    return true;
  })();

  function subclass() {};
  function create() {
    var parent = null, properties = $A(arguments);
    if (Object.isFunction(properties[0]))
      parent = properties.shift();

    function klass() {
      this.initialize.apply(this, arguments);
    }

    Object.extend(klass, Class.Methods);
    klass.superclass = parent;
    klass.subclasses = [];

    if (parent) {
      subclass.prototype = parent.prototype;
      klass.prototype = new subclass;
      parent.subclasses.push(klass);
    }

    for (var i = 0, length = properties.length; i < length; i++)
      klass.addMethods(properties[i]);

    if (!klass.prototype.initialize)
      klass.prototype.initialize = Prototype.emptyFunction;

    klass.prototype.constructor = klass;
    return klass;
  }

  function addMethods(source) {
    var ancestor   = this.superclass && this.superclass.prototype,
        properties = Object.keys(source);

    if (IS_DONTENUM_BUGGY) {
      if (source.toString != Object.prototype.toString)
        properties.push("toString");
      if (source.valueOf != Object.prototype.valueOf)
        properties.push("valueOf");
    }

    for (var i = 0, length = properties.length; i < length; i++) {
      var property = properties[i], value = source[property];
      if (ancestor && Object.isFunction(value) &&
          value.argumentNames()[0] == "$super") {
        var method = value;
        value = (function(m) {
          return function() { return ancestor[m].apply(this, arguments); };
        })(property).wrap(method);

        value.valueOf = method.valueOf.bind(method);
        value.toString = method.toString.bind(method);
      }
      this.prototype[property] = value;
    }

    return this;
  }

  return {
    create: create,
    Methods: {
      addMethods: addMethods
    }
  };
})();
(function() {

  var _toString = Object.prototype.toString,
      NULL_TYPE = 'Null',
      UNDEFINED_TYPE = 'Undefined',
      BOOLEAN_TYPE = 'Boolean',
      NUMBER_TYPE = 'Number',
      STRING_TYPE = 'String',
      OBJECT_TYPE = 'Object',
      FUNCTION_CLASS = '[object Function]',
      BOOLEAN_CLASS = '[object Boolean]',
      NUMBER_CLASS = '[object Number]',
      STRING_CLASS = '[object String]',
      ARRAY_CLASS = '[object Array]',
      DATE_CLASS = '[object Date]',
      NATIVE_JSON_STRINGIFY_SUPPORT = false;
//  NATIVE_JSON_STRINGIFY_SUPPORT = window.JSON &&
//  typeof JSON.stringify === 'function' &&
//  JSON.stringify(0) === '0' &&
//  typeof JSON.stringify(Prototype.K) === 'undefined';

  function Type(o) {
    switch(o) {
      case null: return NULL_TYPE;
      case (void 0): return UNDEFINED_TYPE;
    }
    var type = typeof o;
    switch(type) {
      case 'boolean': return BOOLEAN_TYPE;
      case 'number':  return NUMBER_TYPE;
      case 'string':  return STRING_TYPE;
    }
    return OBJECT_TYPE;
  }

  function extend(destination, source) {
    for (var property in source)
      destination[property] = source[property];
    return destination;
  }

  function inspect(object) {
    try {
      if (isUndefined(object)) return 'undefined';
      if (object === null) return 'null';
      return object.inspect ? object.inspect() : String(object);
    } catch (e) {
      if (e instanceof RangeError) return '...';
      throw e;
    }
  }

  function toJSON(value) {
    return Str('', { '': value }, []);
  }

  function Str(key, holder, stack) {
    var value = holder[key],
        type = typeof value;

    if (Type(value) === OBJECT_TYPE && typeof value.toJSON === 'function') {
      value = value.toJSON(key);
    }

    var _class = _toString.call(value);

    switch (_class) {
      case NUMBER_CLASS:
      case BOOLEAN_CLASS:
      case STRING_CLASS:
        value = value.valueOf();
    }

    switch (value) {
      case null: return 'null';
      case true: return 'true';
      case false: return 'false';
    }

    type = typeof value;
    switch (type) {
      case 'string':
        return value.inspect(true);
      case 'number':
        return isFinite(value) ? String(value) : 'null';
      case 'object':

        for (var i = 0, length = stack.length; i < length; i++) {
          if (stack[i] === value) { throw new TypeError(); }
        }
        stack.push(value);

        var partial = [];
        if (_class === ARRAY_CLASS) {
          for (var i = 0, length = value.length; i < length; i++) {
            var str = Str(i, value, stack);
            partial.push(typeof str === 'undefined' ? 'null' : str);
          }
          partial = '[' + partial.join(',') + ']';
        } else {
          var keys = Object.keys(value);
          for (var i = 0, length = keys.length; i < length; i++) {
            var key = keys[i], str = Str(key, value, stack);
            if (typeof str !== "undefined") {
               partial.push(key.inspect(true)+ ':' + str);
             }
          }
          partial = '{' + partial.join(',') + '}';
        }
        stack.pop();
        return partial;
    }
  }

  function stringify(object) {
    return JSON.stringify(object);
  }

  function toQueryString(object) {
    return $H(object).toQueryString();
  }

  function toHTML(object) {
    return object && object.toHTML ? object.toHTML() : String.interpret(object);
  }

  function keys(object) {
    if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); }
    var results = [];
    for (var property in object) {
      if (object.hasOwnProperty(property)) {
        results.push(property);
      }
    }
    return results;
  }

  function values(object) {
    var results = [];
    for (var property in object)
      results.push(object[property]);
    return results;
  }

  function clone(object) {
    return extend({ }, object);
  }

  function isElement(object) {
    return !!(object && object.nodeType == 1);
  }

  function isArray(object) {
    return _toString.call(object) === ARRAY_CLASS;
  }

  var hasNativeIsArray = (typeof Array.isArray == 'function')
    && Array.isArray([]) && !Array.isArray({});

  if (hasNativeIsArray) {
    isArray = Array.isArray;
  }

  function isHash(object) {
    return object instanceof Hash;
  }

  function isFunction(object) {
    return _toString.call(object) === FUNCTION_CLASS;
  }

  function isString(object) {
    return _toString.call(object) === STRING_CLASS;
  }

  function isNumber(object) {
    return _toString.call(object) === NUMBER_CLASS;
  }

  function isDate(object) {
    return _toString.call(object) === DATE_CLASS;
  }

  function isUndefined(object) {
    return typeof object === "undefined";
  }

  extend(Object, {
    extend:        extend,
    inspect:       inspect,
    toJSON:        NATIVE_JSON_STRINGIFY_SUPPORT ? stringify : toJSON,
    toQueryString: toQueryString,
    toHTML:        toHTML,
    keys:          Object.keys || keys,
    values:        values,
    clone:         clone,
    isElement:     isElement,
    isArray:       isArray,
    isHash:        isHash,
    isFunction:    isFunction,
    isString:      isString,
    isNumber:      isNumber,
    isDate:        isDate,
    isUndefined:   isUndefined
  });
})();
Object.extend(Function.prototype, (function() {
  var slice = Array.prototype.slice;

  function update(array, args) {
    var arrayLength = array.length, length = args.length;
    while (length--) array[arrayLength + length] = args[length];
    return array;
  }

  function merge(array, args) {
    array = slice.call(array, 0);
    return update(array, args);
  }

  function argumentNames() {
    var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
      .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
      .replace(/\s+/g, '').split(',');
    return names.length == 1 && !names[0] ? [] : names;
  }

  function bind(context) {
    if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
    var __method = this, args = slice.call(arguments, 1);
    return function() {
      var a = merge(args, arguments);
      return __method.apply(context, a);
    }
  }

  function bindAsEventListener(context) {
    var __method = this, args = slice.call(arguments, 1);
    return function(event) {
      var a = update([event || window.event], args);
      return __method.apply(context, a);
    }
  }

  function curry() {
    if (!arguments.length) return this;
    var __method = this, args = slice.call(arguments, 0);
    return function() {
      var a = merge(args, arguments);
      return __method.apply(this, a);
    }
  }

  function delay(timeout) {
    var __method = this, args = slice.call(arguments, 1);
    timeout = timeout * 1000;
    return window.setTimeout(function() {
      return __method.apply(__method, args);
    }, timeout);
  }

  function defer() {
    var args = update([0.01], arguments);
    return this.delay.apply(this, args);
  }

  function wrap(wrapper) {
    var __method = this;
    return function() {
      var a = update([__method.bind(this)], arguments);
      return wrapper.apply(this, a);
    }
  }

  function methodize() {
    if (this._methodized) return this._methodized;
    var __method = this;
    return this._methodized = function() {
      var a = update([this], arguments);
      return __method.apply(null, a);
    };
  }

  return {
    argumentNames:       argumentNames,
    bind:                bind,
    bindAsEventListener: bindAsEventListener,
    curry:               curry,
    delay:               delay,
    defer:               defer,
    wrap:                wrap,
    methodize:           methodize
  }
})());



(function(proto) {


  function toISOString() {
    return this.getUTCFullYear() + '-' +
      (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
      this.getUTCDate().toPaddedString(2) + 'T' +
      this.getUTCHours().toPaddedString(2) + ':' +
      this.getUTCMinutes().toPaddedString(2) + ':' +
      this.getUTCSeconds().toPaddedString(2) + 'Z';
  }


  function toJSON() {
    return this.toISOString();
  }

  if (!proto.toISOString) proto.toISOString = toISOString;
  if (!proto.toJSON) proto.toJSON = toJSON;

})(Date.prototype);


RegExp.prototype.match = RegExp.prototype.test;

RegExp.escape = function(str) {
  return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
};
var PeriodicalExecuter = Class.create({
  initialize: function(callback, frequency) {
    this.callback = callback;
    this.frequency = frequency;
    this.currentlyExecuting = false;

    this.registerCallback();
  },

  registerCallback: function() {
    this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
  },

  execute: function() {
    this.callback(this);
  },

  stop: function() {
    if (!this.timer) return;
    clearInterval(this.timer);
    this.timer = null;
  },

  onTimerEvent: function() {
    if (!this.currentlyExecuting) {
      try {
        this.currentlyExecuting = true;
        this.execute();
        this.currentlyExecuting = false;
      } catch(e) {
        this.currentlyExecuting = false;
        throw e;
      }
    }
  }
});
Object.extend(String, {
  interpret: function(value) {
    return value == null ? '' : String(value);
  },
  specialChar: {
    '\b': '\\b',
    '\t': '\\t',
    '\n': '\\n',
    '\f': '\\f',
    '\r': '\\r',
    '\\': '\\\\'
  }
});

Object.extend(String.prototype, (function() {
  var NATIVE_JSON_PARSE_SUPPORT = false;

//  var NATIVE_JSON_PARSE_SUPPORT = window.JSON &&
//  typeof JSON.parse === 'function' &&
//  JSON.parse('{"test": true}').test;
  
  
  function prepareReplacement(replacement) {
    if (Object.isFunction(replacement)) return replacement;
    var template = new Template(replacement);
    return function(match) { return template.evaluate(match) };
  }

  function gsub(pattern, replacement) {
    var result = '', source = this, match;
    replacement = prepareReplacement(replacement);

    if (Object.isString(pattern))
      pattern = RegExp.escape(pattern);

    if (!(pattern.length || pattern.source)) {
      replacement = replacement('');
      return replacement + source.split('').join(replacement) + replacement;
    }

    while (source.length > 0) {
      if (match = source.match(pattern)) {
        result += source.slice(0, match.index);
        result += String.interpret(replacement(match));
        source  = source.slice(match.index + match[0].length);
      } else {
        result += source, source = '';
      }
    }
    return result;
  }

  function sub(pattern, replacement, count) {
    replacement = prepareReplacement(replacement);
    count = Object.isUndefined(count) ? 1 : count;

    return this.gsub(pattern, function(match) {
      if (--count < 0) return match[0];
      return replacement(match);
    });
  }

  function scan(pattern, iterator) {
    this.gsub(pattern, iterator);
    return String(this);
  }

  function truncate(length, truncation) {
    length = length || 30;
    truncation = Object.isUndefined(truncation) ? '...' : truncation;
    return this.length > length ?
      this.slice(0, length - truncation.length) + truncation : String(this);
  }

  function strip() {
    return this.replace(/^\s+/, '').replace(/\s+$/, '');
  }

  function stripTags() {
    return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi, '');
  }

  function stripScripts() {
    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
  }

  function extractScripts() {
    var matchAll = new RegExp(Prototype.ScriptFragment, 'img'),
        matchOne = new RegExp(Prototype.ScriptFragment, 'im');
    return (this.match(matchAll) || []).map(function(scriptTag) {
      return (scriptTag.match(matchOne) || ['', ''])[1];
    });
  }

  function evalScripts() {
    return this.extractScripts().map(function(script) { return eval(script) });
  }

  function escapeHTML() {
    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
  }

  function unescapeHTML() {
    return this.stripTags().replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&amp;/g,'&');
  }


  function toQueryParams(separator) {
    var match = this.strip().match(/([^?#]*)(#.*)?$/);
    if (!match) return { };

    return match[1].split(separator || '&').inject({ }, function(hash, pair) {
      if ((pair = pair.split('='))[0]) {
        var key = decodeURIComponent(pair.shift()),
            value = pair.length > 1 ? pair.join('=') : pair[0];

        if (value != undefined) value = decodeURIComponent(value);

        if (key in hash) {
          if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
          hash[key].push(value);
        }
        else hash[key] = value;
      }
      return hash;
    });
  }

  function toArray() {
    return this.split('');
  }

  function succ() {
    return this.slice(0, this.length - 1) +
      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
  }

  function times(count) {
    return count < 1 ? '' : new Array(count + 1).join(this);
  }

  function camelize() {
    return this.replace(/-+(.)?/g, function(match, chr) {
      return chr ? chr.toUpperCase() : '';
    });
  }

  function capitalize() {
    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
  }

  function underscore() {
    return this.replace(/::/g, '/')
               .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
               .replace(/([a-z\d])([A-Z])/g, '$1_$2')
               .replace(/-/g, '_')
               .toLowerCase();
  }

  function dasherize() {
    return this.replace(/_/g, '-');
  }

  function inspect(useDoubleQuotes) {
    var escapedString = this.replace(/[\x00-\x1f\\]/g, function(character) {
      if (character in String.specialChar) {
        return String.specialChar[character];
      }
      return '\\u00' + character.charCodeAt().toPaddedString(2, 16);
    });
    if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
    return "'" + escapedString.replace(/'/g, '\\\'') + "'";
  }

  function unfilterJSON(filter) {
    return this.replace(filter || Prototype.JSONFilter, '$1');
  }

  function isJSON() {
    var str = this;
    if (str.blank()) return false;
    str = str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@');
    str = str.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
    str = str.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
    return (/^[\],:{}\s]*$/).test(str);
  }

  function evalJSON(sanitize) {
    var json = this.unfilterJSON(),
        cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
    if (cx.test(json)) {
      json = json.replace(cx, function (a) {
        return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
      });
    }
    try {
      if (!sanitize || json.isJSON()) return eval('(' + json + ')');
    } catch (e) { }
    throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
  }

  function parseJSON() {
    var json = this.unfilterJSON();
    return JSON.parse(json);
  }

  function include(pattern) {
    return this.indexOf(pattern) > -1;
  }

  function startsWith(pattern) {
    return this.lastIndexOf(pattern, 0) === 0;
  }

  function endsWith(pattern) {
    var d = this.length - pattern.length;
    return d >= 0 && this.indexOf(pattern, d) === d;
  }

  function empty() {
    return this == '';
  }

  function blank() {
    return /^\s*$/.test(this);
  }

  function interpolate(object, pattern) {
    return new Template(this, pattern).evaluate(object);
  }

  return {
    gsub:           gsub,
    sub:            sub,
    scan:           scan,
    truncate:       truncate,
    strip:          String.prototype.trim || strip,
    stripTags:      stripTags,
    stripScripts:   stripScripts,
    extractScripts: extractScripts,
    evalScripts:    evalScripts,
    escapeHTML:     escapeHTML,
    unescapeHTML:   unescapeHTML,
    toQueryParams:  toQueryParams,
    parseQuery:     toQueryParams,
    toArray:        toArray,
    succ:           succ,
    times:          times,
    camelize:       camelize,
    capitalize:     capitalize,
    underscore:     underscore,
    dasherize:      dasherize,
    inspect:        inspect,
    unfilterJSON:   unfilterJSON,
    isJSON:         isJSON,
    evalJSON:       NATIVE_JSON_PARSE_SUPPORT ? parseJSON : evalJSON,
    include:        include,
    startsWith:     startsWith,
    endsWith:       endsWith,
    empty:          empty,
    blank:          blank,
    interpolate:    interpolate
  };
})());

var Template = Class.create({
  initialize: function(template, pattern) {
    this.template = template.toString();
    this.pattern = pattern || Template.Pattern;
  },

  evaluate: function(object) {
    if (object && Object.isFunction(object.toTemplateReplacements))
      object = object.toTemplateReplacements();

    return this.template.gsub(this.pattern, function(match) {
      if (object == null) return (match[1] + '');

      var before = match[1] || '';
      if (before == '\\') return match[2];

      var ctx = object, expr = match[3],
          pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;

      match = pattern.exec(expr);
      if (match == null) return before;

      while (match != null) {
        var comp = match[1].startsWith('[') ? match[2].replace(/\\\\]/g, ']') : match[1];
        ctx = ctx[comp];
        if (null == ctx || '' == match[3]) break;
        expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
        match = pattern.exec(expr);
      }

      return before + String.interpret(ctx);
    });
  }
});
Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;

var $break = { };

var Enumerable = (function() {
  function each(iterator, context) {
    var index = 0;
    try {
      this._each(function(value) {
        iterator.call(context, value, index++);
      });
    } catch (e) {
      if (e != $break) throw e;
    }
    return this;
  }

  function eachSlice(number, iterator, context) {
    var index = -number, slices = [], array = this.toArray();
    if (number < 1) return array;
    while ((index += number) < array.length)
      slices.push(array.slice(index, index+number));
    return slices.collect(iterator, context);
  }

  function all(iterator, context) {
    iterator = iterator || Prototype.K;
    var result = true;
    this.each(function(value, index) {
      result = result && !!iterator.call(context, value, index);
      if (!result) throw $break;
    });
    return result;
  }

  function any(iterator, context) {
    iterator = iterator || Prototype.K;
    var result = false;
    this.each(function(value, index) {
      if (result = !!iterator.call(context, value, index))
        throw $break;
    });
    return result;
  }

  function collect(iterator, context) {
    iterator = iterator || Prototype.K;
    var results = [];
    this.each(function(value, index) {
      results.push(iterator.call(context, value, index));
    });
    return results;
  }

  function detect(iterator, context) {
    var result;
    this.each(function(value, index) {
      if (iterator.call(context, value, index)) {
        result = value;
        throw $break;
      }
    });
    return result;
  }

  function findAll(iterator, context) {
    var results = [];
    this.each(function(value, index) {
      if (iterator.call(context, value, index))
        results.push(value);
    });
    return results;
  }

  function grep(filter, iterator, context) {
    iterator = iterator || Prototype.K;
    var results = [];

    if (Object.isString(filter))
      filter = new RegExp(RegExp.escape(filter));

    this.each(function(value, index) {
      if (filter.match(value))
        results.push(iterator.call(context, value, index));
    });
    return results;
  }

  function include(object) {
    if (Object.isFunction(this.indexOf))
      if (this.indexOf(object) != -1) return true;

    var found = false;
    this.each(function(value) {
      if (value == object) {
        found = true;
        throw $break;
      }
    });
    return found;
  }

  function inGroupsOf(number, fillWith) {
    fillWith = Object.isUndefined(fillWith) ? null : fillWith;
    return this.eachSlice(number, function(slice) {
      while(slice.length < number) slice.push(fillWith);
      return slice;
    });
  }

  function inject(memo, iterator, context) {
    this.each(function(value, index) {
      memo = iterator.call(context, memo, value, index);
    });
    return memo;
  }

  function invoke(method) {
    var args = $A(arguments).slice(1);
    return this.map(function(value) {
      return value[method].apply(value, args);
    });
  }

  function max(iterator, context) {
    iterator = iterator || Prototype.K;
    var result;
    this.each(function(value, index) {
      value = iterator.call(context, value, index);
      if (result == null || value >= result)
        result = value;
    });
    return result;
  }

  function min(iterator, context) {
    iterator = iterator || Prototype.K;
    var result;
    this.each(function(value, index) {
      value = iterator.call(context, value, index);
      if (result == null || value < result)
        result = value;
    });
    return result;
  }

  function partition(iterator, context) {
    iterator = iterator || Prototype.K;
    var trues = [], falses = [];
    this.each(function(value, index) {
      (iterator.call(context, value, index) ?
        trues : falses).push(value);
    });
    return [trues, falses];
  }

  function pluck(property) {
    var results = [];
    this.each(function(value) {
      results.push(value[property]);
    });
    return results;
  }

  function reject(iterator, context) {
    var results = [];
    this.each(function(value, index) {
      if (!iterator.call(context, value, index))
        results.push(value);
    });
    return results;
  }

  function sortBy(iterator, context) {
    return this.map(function(value, index) {
      return {
        value: value,
        criteria: iterator.call(context, value, index)
      };
    }).sort(function(left, right) {
      var a = left.criteria, b = right.criteria;
      return a < b ? -1 : a > b ? 1 : 0;
    }).pluck('value');
  }

  function toArray() {
    return this.map();
  }

  function zip() {
    var iterator = Prototype.K, args = $A(arguments);
    if (Object.isFunction(args.last()))
      iterator = args.pop();

    var collections = [this].concat(args).map($A);
    return this.map(function(value, index) {
      return iterator(collections.pluck(index));
    });
  }

  function size() {
    return this.toArray().length;
  }

  function inspect() {
    return '#<Enumerable:' + this.toArray().inspect() + '>';
  }









  return {
    each:       each,
    eachSlice:  eachSlice,
    all:        all,
    every:      all,
    any:        any,
    some:       any,
    collect:    collect,
    map:        collect,
    detect:     detect,
    findAll:    findAll,
    select:     findAll,
    filter:     findAll,
    grep:       grep,
    include:    include,
    member:     include,
    inGroupsOf: inGroupsOf,
    inject:     inject,
    invoke:     invoke,
    max:        max,
    min:        min,
    partition:  partition,
    pluck:      pluck,
    reject:     reject,
    sortBy:     sortBy,
    toArray:    toArray,
    entries:    toArray,
    zip:        zip,
    size:       size,
    inspect:    inspect,
    find:       detect
  };
})();

function $A(iterable) {
  if (!iterable) return [];
  if ('toArray' in Object(iterable)) return iterable.toArray();
  var length = iterable.length || 0, results = new Array(length);
  while (length--) results[length] = iterable[length];
  return results;
}


function $w(string) {
  if (!Object.isString(string)) return [];
  string = string.strip();
  return string ? string.split(/\s+/) : [];
}

Array.from = $A;


(function() {
  var arrayProto = Array.prototype,
      slice = arrayProto.slice,
      _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available

  function each(iterator, context) {
    for (var i = 0, length = this.length >>> 0; i < length; i++) {
      if (i in this) iterator.call(context, this[i], i, this);
    }
  }
  if (!_each) _each = each;

  function clear() {
    this.length = 0;
    return this;
  }

  function first() {
    return this[0];
  }

  function last() {
    return this[this.length - 1];
  }

  function compact() {
    return this.select(function(value) {
      return value != null;
    });
  }

  function flatten() {
    return this.inject([], function(array, value) {
      if (Object.isArray(value))
        return array.concat(value.flatten());
      array.push(value);
      return array;
    });
  }

  function without() {
    var values = slice.call(arguments, 0);
    return this.select(function(value) {
      return !values.include(value);
    });
  }

  function reverse(inline) {
    return (inline === false ? this.toArray() : this)._reverse();
  }

  function uniq(sorted) {
    return this.inject([], function(array, value, index) {
      if (0 == index || (sorted ? array.last() != value : !array.include(value)))
        array.push(value);
      return array;
    });
  }

  function intersect(array) {
    return this.uniq().findAll(function(item) {
      return array.detect(function(value) { return item === value });
    });
  }


  function clone() {
    return slice.call(this, 0);
  }

  function size() {
    return this.length;
  }

  function inspect() {
    return '[' + this.map(Object.inspect).join(', ') + ']';
  }

  function indexOf(item, i) {
    i || (i = 0);
    var length = this.length;
    if (i < 0) i = length + i;
    for (; i < length; i++)
      if (this[i] === item) return i;
    return -1;
  }

  function lastIndexOf(item, i) {
    i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
    var n = this.slice(0, i).reverse().indexOf(item);
    return (n < 0) ? n : i - n - 1;
  }

  function concat() {
    var array = slice.call(this, 0), item;
    for (var i = 0, length = arguments.length; i < length; i++) {
      item = arguments[i];
      if (Object.isArray(item) && !('callee' in item)) {
        for (var j = 0, arrayLength = item.length; j < arrayLength; j++)
          array.push(item[j]);
      } else {
        array.push(item);
      }
    }
    return array;
  }

  Object.extend(arrayProto, Enumerable);

  if (!arrayProto._reverse)
    arrayProto._reverse = arrayProto.reverse;

  Object.extend(arrayProto, {
    _each:     _each,
    clear:     clear,
    first:     first,
    last:      last,
    compact:   compact,
    flatten:   flatten,
    without:   without,
    reverse:   reverse,
    uniq:      uniq,
    intersect: intersect,
    clone:     clone,
    toArray:   clone,
    size:      size,
    inspect:   inspect
  });

  var CONCAT_ARGUMENTS_BUGGY = (function() {
    return [].concat(arguments)[0][0] !== 1;
  })(1,2)

  if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat;

  if (!arrayProto.indexOf) arrayProto.indexOf = indexOf;
  if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf;
})();
function $H(object) {
  return new Hash(object);
};

var Hash = Class.create(Enumerable, (function() {
  function initialize(object) {
    this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
  }


  function _each(iterator) {
    for (var key in this._object) {
      var value = this._object[key], pair = [key, value];
      pair.key = key;
      pair.value = value;
      iterator(pair);
    }
  }

  function set(key, value) {
    return this._object[key] = value;
  }

  function get(key) {
    if (this._object[key] !== Object.prototype[key])
      return this._object[key];
  }

  function unset(key) {
    var value = this._object[key];
    delete this._object[key];
    return value;
  }

  function toObject() {
    return Object.clone(this._object);
  }



  function keys() {
    return this.pluck('key');
  }

  function values() {
    return this.pluck('value');
  }

  function index(value) {
    var match = this.detect(function(pair) {
      return pair.value === value;
    });
    return match && match.key;
  }

  function merge(object) {
    return this.clone().update(object);
  }

  function update(object) {
    return new Hash(object).inject(this, function(result, pair) {
      result.set(pair.key, pair.value);
      return result;
    });
  }

  function toQueryPair(key, value) {
    if (Object.isUndefined(value)) return key;
    return key + '=' + encodeURIComponent(String.interpret(value));
  }

  function toQueryString() {
    return this.inject([], function(results, pair) {
      var key = encodeURIComponent(pair.key), values = pair.value;

      if (values && typeof values == 'object') {
        if (Object.isArray(values)) {
          var queryValues = [];
          for (var i = 0, len = values.length, value; i < len; i++) {
            value = values[i];
            queryValues.push(toQueryPair(key, value));
          }
          return results.concat(queryValues);
        }
      } else results.push(toQueryPair(key, values));
      return results;
    }).join('&');
  }

  function inspect() {
    return '#<Hash:{' + this.map(function(pair) {
      return pair.map(Object.inspect).join(': ');
    }).join(', ') + '}>';
  }

  function clone() {
    return new Hash(this);
  }

  return {
    initialize:             initialize,
    _each:                  _each,
    set:                    set,
    get:                    get,
    unset:                  unset,
    toObject:               toObject,
    toTemplateReplacements: toObject,
    keys:                   keys,
    values:                 values,
    index:                  index,
    merge:                  merge,
    update:                 update,
    toQueryString:          toQueryString,
    inspect:                inspect,
    toJSON:                 toObject,
    clone:                  clone
  };
})());

Hash.from = $H;
Object.extend(Number.prototype, (function() {
  function toColorPart() {
    return this.toPaddedString(2, 16);
  }

  function succ() {
    return this + 1;
  }

  function times(iterator, context) {
    $R(0, this, true).each(iterator, context);
    return this;
  }

  function toPaddedString(length, radix) {
    var string = this.toString(radix || 10);
    return '0'.times(length - string.length) + string;
  }

  function abs() {
    return Math.abs(this);
  }

  function round() {
    return Math.round(this);
  }

  function ceil() {
    return Math.ceil(this);
  }

  function floor() {
    return Math.floor(this);
  }

  return {
    toColorPart:    toColorPart,
    succ:           succ,
    times:          times,
    toPaddedString: toPaddedString,
    abs:            abs,
    round:          round,
    ceil:           ceil,
    floor:          floor
  };
})());

function $R(start, end, exclusive) {
  return new ObjectRange(start, end, exclusive);
}

var ObjectRange = Class.create(Enumerable, (function() {
  function initialize(start, end, exclusive) {
    this.start = start;
    this.end = end;
    this.exclusive = exclusive;
  }

  function _each(iterator) {
    var value = this.start;
    while (this.include(value)) {
      iterator(value);
      value = value.succ();
    }
  }

  function include(value) {
    if (value < this.start)
      return false;
    if (this.exclusive)
      return value < this.end;
    return value <= this.end;
  }

  return {
    initialize: initialize,
    _each:      _each,
    include:    include
  };
})());



var Ajax = {
  getTransport: function() {
    return Try.these(
      function() {return new XMLHttpRequest()},
      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
    ) || false;
  },

  activeRequestCount: 0
};

Ajax.Responders = {
  responders: [],

  _each: function(iterator) {
    this.responders._each(iterator);
  },

  register: function(responder) {
    if (!this.include(responder))
      this.responders.push(responder);
  },

  unregister: function(responder) {
    this.responders = this.responders.without(responder);
  },

  dispatch: function(callback, request, transport, json) {
    this.each(function(responder) {
      if (Object.isFunction(responder[callback])) {
        try {
          responder[callback].apply(responder, [request, transport, json]);
        } catch (e) { }
      }
    });
  }
};

Object.extend(Ajax.Responders, Enumerable);

Ajax.Responders.register({
  onCreate:   function() { Ajax.activeRequestCount++ },
  onComplete: function() { Ajax.activeRequestCount-- }
});
Ajax.Base = Class.create({
  initialize: function(options) {
    this.options = {
      method:       'post',
      asynchronous: true,
      contentType:  'application/x-www-form-urlencoded',
      encoding:     'UTF-8',
      parameters:   '',
      evalJSON:     true,
      evalJS:       true
    };
    Object.extend(this.options, options || { });

    this.options.method = this.options.method.toLowerCase();

    if (Object.isHash(this.options.parameters))
      this.options.parameters = this.options.parameters.toObject();
  }
});
Ajax.Request = Class.create(Ajax.Base, {
  _complete: false,

  initialize: function($super, url, options) {
    $super(options);
    this.transport = Ajax.getTransport();
    this.request(url);
  },

  request: function(url) {
    this.url = url;
    this.method = this.options.method;
    var params = Object.isString(this.options.parameters) ?
          this.options.parameters :
          Object.toQueryString(this.options.parameters);

    if (!['get', 'post'].include(this.method)) {
      params += (params ? '&' : '') + "_method=" + this.method;
      this.method = 'post';
    }

    if (params && this.method === 'get') {
      this.url += (this.url.include('?') ? '&' : '?') + params;
    }

    this.parameters = params.toQueryParams();

    try {
      var response = new Ajax.Response(this);
      if (this.options.onCreate) this.options.onCreate(response);
      Ajax.Responders.dispatch('onCreate', this, response);

      this.transport.open(this.method.toUpperCase(), this.url,
        this.options.asynchronous);

      if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);

      this.transport.onreadystatechange = this.onStateChange.bind(this);
      this.setRequestHeaders();

      this.body = this.method == 'post' ? (this.options.postBody || params) : null;
      this.transport.send(this.body);

      /* Force Firefox to handle ready state 4 for synchronous requests */
      if (!this.options.asynchronous && this.transport.overrideMimeType)
        this.onStateChange();

    }
    catch (e) {
      this.dispatchException(e);
    }
  },

  onStateChange: function() {
    var readyState = this.transport.readyState;
    if (readyState > 1 && !((readyState == 4) && this._complete))
      this.respondToReadyState(this.transport.readyState);
  },

  setRequestHeaders: function() {
    var headers = {
      'X-Requested-With': 'XMLHttpRequest',
      'X-Prototype-Version': Prototype.Version,
      'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
    };

    if (this.method == 'post') {
      headers['Content-type'] = this.options.contentType +
        (this.options.encoding ? '; charset=' + this.options.encoding : '');

      /* Force "Connection: close" for older Mozilla browsers to work
       * around a bug where XMLHttpRequest sends an incorrect
       * Content-length header. See Mozilla Bugzilla #246651.
       */
      if (this.transport.overrideMimeType &&
          (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
            headers['Connection'] = 'close';
    }

    if (typeof this.options.requestHeaders == 'object') {
      var extras = this.options.requestHeaders;

      if (Object.isFunction(extras.push))
        for (var i = 0, length = extras.length; i < length; i += 2)
          headers[extras[i]] = extras[i+1];
      else
        $H(extras).each(function(pair) { headers[pair.key] = pair.value });
    }

    for (var name in headers)
      this.transport.setRequestHeader(name, headers[name]);
  },

  success: function() {
    var status = this.getStatus();
    return !status || (status >= 200 && status < 300) || status == 304;
  },

  getStatus: function() {
    try {
      if (this.transport.status === 1223) return 204;
      return this.transport.status || 0;
    } catch (e) { return 0 }
  },

  respondToReadyState: function(readyState) {
    var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);

    if (state == 'Complete') {
      try {
        this._complete = true;
        (this.options['on' + response.status]
         || this.options['on' + (this.success() ? 'Success' : 'Failure')]
         || Prototype.emptyFunction)(response, response.headerJSON);
      } catch (e) {
        this.dispatchException(e);
      }

      var contentType = response.getHeader('Content-type');
      if (this.options.evalJS == 'force'
          || (this.options.evalJS && this.isSameOrigin() && contentType
          && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
        this.evalResponse();
    }

    try {
      (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
      Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
    } catch (e) {
      this.dispatchException(e);
    }

    if (state == 'Complete') {
      this.transport.onreadystatechange = Prototype.emptyFunction;
    }
  },

  isSameOrigin: function() {
    var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
    return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
      protocol: location.protocol,
      domain: document.domain,
      port: location.port ? ':' + location.port : ''
    }));
  },

  getHeader: function(name) {
    try {
      return this.transport.getResponseHeader(name) || null;
    } catch (e) { return null; }
  },

  evalResponse: function() {
    try {
      return eval((this.transport.responseText || '').unfilterJSON());
    } catch (e) {
      this.dispatchException(e);
    }
  },

  dispatchException: function(exception) {
    (this.options.onException || Prototype.emptyFunction)(this, exception);
    Ajax.Responders.dispatch('onException', this, exception);
  }
});

Ajax.Request.Events =
  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];








Ajax.Response = Class.create({
  initialize: function(request){
    this.request = request;
    var transport  = this.transport  = request.transport,
        readyState = this.readyState = transport.readyState;

    if ((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
      this.status       = this.getStatus();
      this.statusText   = this.getStatusText();
      this.responseText = String.interpret(transport.responseText);
      this.headerJSON   = this._getHeaderJSON();
    }

    if (readyState == 4) {
      var xml = transport.responseXML;
      this.responseXML  = Object.isUndefined(xml) ? null : xml;
      this.responseJSON = this._getResponseJSON();
    }
  },

  status:      0,

  statusText: '',

  getStatus: Ajax.Request.prototype.getStatus,

  getStatusText: function() {
    try {
      return this.transport.statusText || '';
    } catch (e) { return '' }
  },

  getHeader: Ajax.Request.prototype.getHeader,

  getAllHeaders: function() {
    try {
      return this.getAllResponseHeaders();
    } catch (e) { return null }
  },

  getResponseHeader: function(name) {
    return this.transport.getResponseHeader(name);
  },

  getAllResponseHeaders: function() {
    return this.transport.getAllResponseHeaders();
  },

  _getHeaderJSON: function() {
    var json = this.getHeader('X-JSON');
    if (!json) return null;
    json = decodeURIComponent(escape(json));
    try {
      return json.evalJSON(this.request.options.sanitizeJSON ||
        !this.request.isSameOrigin());
    } catch (e) {
      this.request.dispatchException(e);
    }
  },

  _getResponseJSON: function() {
    var options = this.request.options;
    if (!options.evalJSON || (options.evalJSON != 'force' &&
      !(this.getHeader('Content-type') || '').include('application/json')) ||
        this.responseText.blank())
          return null;
    try {
      return this.responseText.evalJSON(options.sanitizeJSON ||
        !this.request.isSameOrigin());
    } catch (e) {
      this.request.dispatchException(e);
    }
  }
});

Ajax.Updater = Class.create(Ajax.Request, {
  initialize: function($super, container, url, options) {
    this.container = {
      success: (container.success || container),
      failure: (container.failure || (container.success ? null : container))
    };

    options = Object.clone(options);
    var onComplete = options.onComplete;
    options.onComplete = (function(response, json) {
      this.updateContent(response.responseText);
      if (Object.isFunction(onComplete)) onComplete(response, json);
    }).bind(this);

    $super(url, options);
  },

  updateContent: function(responseText) {
    var receiver = this.container[this.success() ? 'success' : 'failure'],
        options = this.options;

    if (!options.evalScripts) responseText = responseText.stripScripts();

    if (receiver = $(receiver)) {
      if (options.insertion) {
        if (Object.isString(options.insertion)) {
          var insertion = { }; insertion[options.insertion] = responseText;
          receiver.insert(insertion);
        }
        else options.insertion(receiver, responseText);
      }
      else receiver.update(responseText);
    }
  }
});

Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
  initialize: function($super, container, url, options) {
    $super(options);
    this.onComplete = this.options.onComplete;

    this.frequency = (this.options.frequency || 2);
    this.decay = (this.options.decay || 1);

    this.updater = { };
    this.container = container;
    this.url = url;

    this.start();
  },

  start: function() {
    this.options.onComplete = this.updateComplete.bind(this);
    this.onTimerEvent();
  },

  stop: function() {
    this.updater.options.onComplete = undefined;
    clearTimeout(this.timer);
    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
  },

  updateComplete: function(response) {
    if (this.options.decay) {
      this.decay = (response.responseText == this.lastText ?
        this.decay * this.options.decay : 1);

      this.lastText = response.responseText;
    }
    this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
  },

  onTimerEvent: function() {
    this.updater = new Ajax.Updater(this.container, this.url, this.options);
  }
});


function $(element) {
  if (arguments.length > 1) {
    for (var i = 0, elements = [], length = arguments.length; i < length; i++)
      elements.push($(arguments[i]));
    return elements;
  }
  if (Object.isString(element))
    element = document.getElementById(element);
  return Element.extend(element);
}

if (Prototype.BrowserFeatures.XPath) {
  document._getElementsByXPath = function(expression, parentElement) {
    var results = [];
    var query = document.evaluate(expression, $(parentElement) || document,
      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    for (var i = 0, length = query.snapshotLength; i < length; i++)
      results.push(Element.extend(query.snapshotItem(i)));
    return results;
  };
}

/*--------------------------------------------------------------------------*/

if (!Node) var Node = { };

if (!Node.ELEMENT_NODE) {
  Object.extend(Node, {
    ELEMENT_NODE: 1,
    ATTRIBUTE_NODE: 2,
    TEXT_NODE: 3,
    CDATA_SECTION_NODE: 4,
    ENTITY_REFERENCE_NODE: 5,
    ENTITY_NODE: 6,
    PROCESSING_INSTRUCTION_NODE: 7,
    COMMENT_NODE: 8,
    DOCUMENT_NODE: 9,
    DOCUMENT_TYPE_NODE: 10,
    DOCUMENT_FRAGMENT_NODE: 11,
    NOTATION_NODE: 12
  });
}



(function(global) {
  function shouldUseCache(tagName, attributes) {
    if (tagName === 'select') return false;
    if ('type' in attributes) return false;
    return true;
  }

  var HAS_EXTENDED_CREATE_ELEMENT_SYNTAX = (function(){
    try {
      var el = document.createElement('<input name="x">');
      return el.tagName.toLowerCase() === 'input' && el.name === 'x';
    }
    catch(err) {
      return false;
    }
  })();

  var element = global.Element;

  global.Element = function(tagName, attributes) {
    attributes = attributes || { };
    tagName = tagName.toLowerCase();
    var cache = Element.cache;

    if (HAS_EXTENDED_CREATE_ELEMENT_SYNTAX && attributes.name) {
      tagName = '<' + tagName + ' name="' + attributes.name + '">';
      delete attributes.name;
      return Element.writeAttribute(document.createElement(tagName), attributes);
    }

    if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));

    var node = shouldUseCache(tagName, attributes) ?
     cache[tagName].cloneNode(false) : document.createElement(tagName);

    return Element.writeAttribute(node, attributes);
  };

  Object.extend(global.Element, element || { });
  if (element) global.Element.prototype = element.prototype;

})(this);

Element.idCounter = 1;
Element.cache = { };

Element._purgeElement = function(element) {
  var uid = element._prototypeUID;
  if (uid) {
    Element.stopObserving(element);
    element._prototypeUID = void 0;
    delete Element.Storage[uid];
  }
}

Element.Methods = {
  visible: function(element) {
    return $(element).style.display != 'none';
  },

  toggle: function(element) {
    element = $(element);
    Element[Element.visible(element) ? 'hide' : 'show'](element);
    return element;
  },

  hide: function(element) {
    element = $(element);
    element.style.display = 'none';
    return element;
  },

  show: function(element) {
    element = $(element);
    element.style.display = '';
    return element;
  },

  remove: function(element) {
    element = $(element);
    element.parentNode.removeChild(element);
    return element;
  },

  update: (function(){

    var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){
      var el = document.createElement("select"),
          isBuggy = true;
      el.innerHTML = "<option value=\"test\">test</option>";
      if (el.options && el.options[0]) {
        isBuggy = el.options[0].nodeName.toUpperCase() !== "OPTION";
      }
      el = null;
      return isBuggy;
    })();

    var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){
      try {
        var el = document.createElement("table");
        if (el && el.tBodies) {
          el.innerHTML = "<tbody><tr><td>test</td></tr></tbody>";
          var isBuggy = typeof el.tBodies[0] == "undefined";
          el = null;
          return isBuggy;
        }
      } catch (e) {
        return true;
      }
    })();

    var LINK_ELEMENT_INNERHTML_BUGGY = (function() {
      try {
        var el = document.createElement('div');
        el.innerHTML = "<link>";
        var isBuggy = (el.childNodes.length === 0);
        el = null;
        return isBuggy;
      } catch(e) {
        return true;
      }
    })();

    var ANY_INNERHTML_BUGGY = SELECT_ELEMENT_INNERHTML_BUGGY ||
     TABLE_ELEMENT_INNERHTML_BUGGY || LINK_ELEMENT_INNERHTML_BUGGY;

    var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () {
      var s = document.createElement("script"),
          isBuggy = false;
      try {
        s.appendChild(document.createTextNode(""));
        isBuggy = !s.firstChild ||
          s.firstChild && s.firstChild.nodeType !== 3;
      } catch (e) {
        isBuggy = true;
      }
      s = null;
      return isBuggy;
    })();


    function update(element, content) {
      element = $(element);
      var purgeElement = Element._purgeElement;

      var descendants = element.getElementsByTagName('*'),
       i = descendants.length;
      while (i--) purgeElement(descendants[i]);

      if (content && content.toElement)
        content = content.toElement();

      if (Object.isElement(content))
        return element.update().insert(content);

      content = Object.toHTML(content);

      var tagName = element.tagName.toUpperCase();

      if (tagName === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) {
        element.text = content;
        return element;
      }

      if (ANY_INNERHTML_BUGGY) {
        if (tagName in Element._insertionTranslations.tags) {
          while (element.firstChild) {
            element.removeChild(element.firstChild);
          }
          Element._getContentFromAnonymousElement(tagName, content.stripScripts())
            .each(function(node) {
              element.appendChild(node)
            });
        } else if (LINK_ELEMENT_INNERHTML_BUGGY && Object.isString(content) && content.indexOf('<link') > -1) {
          while (element.firstChild) {
            element.removeChild(element.firstChild);
          }
          var nodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts(), true);
          nodes.each(function(node) { element.appendChild(node) });
        }
        else {
          element.innerHTML = content.stripScripts();
        }
      }
      else {
        element.innerHTML = content.stripScripts();
      }

      content.evalScripts.bind(content).defer();
      return element;
    }

    return update;
  })(),

  replace: function(element, content) {
    element = $(element);
    if (content && content.toElement) content = content.toElement();
    else if (!Object.isElement(content)) {
      content = Object.toHTML(content);
      var range = element.ownerDocument.createRange();
      range.selectNode(element);
      content.evalScripts.bind(content).defer();
      content = range.createContextualFragment(content.stripScripts());
    }
    element.parentNode.replaceChild(content, element);
    return element;
  },

  insert: function(element, insertions) {
    element = $(element);

    if (Object.isString(insertions) || Object.isNumber(insertions) ||
        Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
          insertions = {bottom:insertions};

    var content, insert, tagName, childNodes;

    for (var position in insertions) {
      content  = insertions[position];
      position = position.toLowerCase();
      insert = Element._insertionTranslations[position];

      if (content && content.toElement) content = content.toElement();
      if (Object.isElement(content)) {
        insert(element, content);
        continue;
      }

      content = Object.toHTML(content);

      tagName = ((position == 'before' || position == 'after')
        ? element.parentNode : element).tagName.toUpperCase();

      childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());

      if (position == 'top' || position == 'after') childNodes.reverse();
      childNodes.each(insert.curry(element));

      content.evalScripts.bind(content).defer();
    }

    return element;
  },

  wrap: function(element, wrapper, attributes) {
    element = $(element);
    if (Object.isElement(wrapper))
      $(wrapper).writeAttribute(attributes || { });
    else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
    else wrapper = new Element('div', wrapper);
    if (element.parentNode)
      element.parentNode.replaceChild(wrapper, element);
    wrapper.appendChild(element);
    return wrapper;
  },

  inspect: function(element) {
    element = $(element);
    var result = '<' + element.tagName.toLowerCase();
    $H({'id': 'id', 'className': 'class'}).each(function(pair) {
      var property = pair.first(),
          attribute = pair.last(),
          value = (element[property] || '').toString();
      if (value) result += ' ' + attribute + '=' + value.inspect(true);
    });
    return result + '>';
  },

  recursivelyCollect: function(element, property, maximumLength) {
    element = $(element);
    maximumLength = maximumLength || -1;
    var elements = [];

    while (element = element[property]) {
      if (element.nodeType == 1)
        elements.push(Element.extend(element));
      if (elements.length == maximumLength)
        break;
    }

    return elements;
  },

  ancestors: function(element) {
    return Element.recursivelyCollect(element, 'parentNode');
  },

  descendants: function(element) {
    return Element.select(element, "*");
  },

  firstDescendant: function(element) {
    element = $(element).firstChild;
    while (element && element.nodeType != 1) element = element.nextSibling;
    return $(element);
  },

  immediateDescendants: function(element) {
    var results = [], child = $(element).firstChild;
    while (child) {
      if (child.nodeType === 1) {
        results.push(Element.extend(child));
      }
      child = child.nextSibling;
    }
    return results;
  },

  previousSiblings: function(element, maximumLength) {
    return Element.recursivelyCollect(element, 'previousSibling');
  },

  nextSiblings: function(element) {
    return Element.recursivelyCollect(element, 'nextSibling');
  },

  siblings: function(element) {
    element = $(element);
    return Element.previousSiblings(element).reverse()
      .concat(Element.nextSiblings(element));
  },

  match: function(element, selector) {
    element = $(element);
    if (Object.isString(selector))
      return Prototype.Selector.match(element, selector);
    return selector.match(element);
  },

  up: function(element, expression, index) {
    element = $(element);
    if (arguments.length == 1) return $(element.parentNode);
    var ancestors = Element.ancestors(element);
    return Object.isNumber(expression) ? ancestors[expression] :
      Prototype.Selector.find(ancestors, expression, index);
  },

  down: function(element, expression, index) {
    element = $(element);
    if (arguments.length == 1) return Element.firstDescendant(element);
    return Object.isNumber(expression) ? Element.descendants(element)[expression] :
      Element.select(element, expression)[index || 0];
  },

  previous: function(element, expression, index) {
    element = $(element);
    if (Object.isNumber(expression)) index = expression, expression = false;
    if (!Object.isNumber(index)) index = 0;

    if (expression) {
      return Prototype.Selector.find(element.previousSiblings(), expression, index);
    } else {
      return element.recursivelyCollect("previousSibling", index + 1)[index];
    }
  },

  next: function(element, expression, index) {
    element = $(element);
    if (Object.isNumber(expression)) index = expression, expression = false;
    if (!Object.isNumber(index)) index = 0;

    if (expression) {
      return Prototype.Selector.find(element.nextSiblings(), expression, index);
    } else {
      var maximumLength = Object.isNumber(index) ? index + 1 : 1;
      return element.recursivelyCollect("nextSibling", index + 1)[index];
    }
  },


  select: function(element) {
    element = $(element);
    var expressions = Array.prototype.slice.call(arguments, 1).join(', ');
    return Prototype.Selector.select(expressions, element);
  },

  adjacent: function(element) {
    element = $(element);
    var expressions = Array.prototype.slice.call(arguments, 1).join(', ');
    return Prototype.Selector.select(expressions, element.parentNode).without(element);
  },

  identify: function(element) {
    element = $(element);
    var id = Element.readAttribute(element, 'id');
    if (id) return id;
    do { id = 'anonymous_element_' + Element.idCounter++ } while ($(id));
    Element.writeAttribute(element, 'id', id);
    return id;
  },

  readAttribute: function(element, name) {
    element = $(element);
    if (Prototype.Browser.IE) {
      var t = Element._attributeTranslations.read;
      if (t.values[name]) return t.values[name](element, name);
      if (t.names[name]) name = t.names[name];
      if (name.include(':')) {
        return (!element.attributes || !element.attributes[name]) ? null :
         element.attributes[name].value;
      }
    }
    return element.getAttribute(name);
  },

  writeAttribute: function(element, name, value) {
    element = $(element);
    var attributes = { }, t = Element._attributeTranslations.write;

    if (typeof name == 'object') attributes = name;
    else attributes[name] = Object.isUndefined(value) ? true : value;

    for (var attr in attributes) {
      name = t.names[attr] || attr;
      value = attributes[attr];
      if (t.values[attr]) name = t.values[attr](element, value);
      if (value === false || value === null)
        element.removeAttribute(name);
      else if (value === true)
        element.setAttribute(name, name);
      else element.setAttribute(name, value);
    }
    return element;
  },

  getHeight: function(element) {
    return Element.getDimensions(element).height;
  },

  getWidth: function(element) {
    return Element.getDimensions(element).width;
  },

  classNames: function(element) {
    return new Element.ClassNames(element);
  },

  hasClassName: function(element, className) {
    if (!(element = $(element))) return;
    var elementClassName = element.className;
    return (elementClassName.length > 0 && (elementClassName == className ||
      new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
  },

  addClassName: function(element, className) {
    if (!(element = $(element))) return;
    if (!Element.hasClassName(element, className))
      element.className += (element.className ? ' ' : '') + className;
    return element;
  },

  removeClassName: function(element, className) {
    if (!(element = $(element))) return;
    element.className = element.className.replace(
      new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
    return element;
  },

  toggleClassName: function(element, className) {
    if (!(element = $(element))) return;
    return Element[Element.hasClassName(element, className) ?
      'removeClassName' : 'addClassName'](element, className);
  },

  cleanWhitespace: function(element) {
    element = $(element);
    var node = element.firstChild;
    while (node) {
      var nextNode = node.nextSibling;
      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
        element.removeChild(node);
      node = nextNode;
    }
    return element;
  },

  empty: function(element) {
    return $(element).innerHTML.blank();
  },

  descendantOf: function(element, ancestor) {
    element = $(element), ancestor = $(ancestor);

    if (element.compareDocumentPosition)
      return (element.compareDocumentPosition(ancestor) & 8) === 8;

    if (ancestor.contains)
      return ancestor.contains(element) && ancestor !== element;

    while (element = element.parentNode)
      if (element == ancestor) return true;

    return false;
  },

  scrollTo: function(element) {
    element = $(element);
    var pos = Element.cumulativeOffset(element);
    window.scrollTo(pos[0], pos[1]);
    return element;
  },

  getStyle: function(element, style) {
    element = $(element);
    style = style == 'float' ? 'cssFloat' : style.camelize();
    var value = element.style[style];
    if (!value || value == 'auto') {
      var css = document.defaultView.getComputedStyle(element, null);
      value = css ? css[style] : null;
    }
    if (style == 'opacity') return value ? parseFloat(value) : 1.0;
    return value == 'auto' ? null : value;
  },

  getOpacity: function(element) {
    return $(element).getStyle('opacity');
  },

  setStyle: function(element, styles) {
    element = $(element);
    var elementStyle = element.style, match;
    if (Object.isString(styles)) {
      element.style.cssText += ';' + styles;
      return styles.include('opacity') ?
        element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
    }
    for (var property in styles)
      if (property == 'opacity') element.setOpacity(styles[property]);
      else
        elementStyle[(property == 'float' || property == 'cssFloat') ?
          (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
            property] = styles[property];

    return element;
  },

  setOpacity: function(element, value) {
    element = $(element);
    element.style.opacity = (value == 1 || value === '') ? '' :
      (value < 0.00001) ? 0 : value;
    return element;
  },

  makePositioned: function(element) {
    element = $(element);
    var pos = Element.getStyle(element, 'position');
    if (pos == 'static' || !pos) {
      element._madePositioned = true;
      element.style.position = 'relative';
      if (Prototype.Browser.Opera) {
        element.style.top = 0;
        element.style.left = 0;
      }
    }
    return element;
  },

  undoPositioned: function(element) {
    element = $(element);
    if (element._madePositioned) {
      element._madePositioned = undefined;
      element.style.position =
        element.style.top =
        element.style.left =
        element.style.bottom =
        element.style.right = '';
    }
    return element;
  },

  makeClipping: function(element) {
    element = $(element);
    if (element._overflow) return element;
    element._overflow = Element.getStyle(element, 'overflow') || 'auto';
    if (element._overflow !== 'hidden')
      element.style.overflow = 'hidden';
    return element;
  },

  undoClipping: function(element) {
    element = $(element);
    if (!element._overflow) return element;
    element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
    element._overflow = null;
    return element;
  },

  clonePosition: function(element, source) {
    var options = Object.extend({
      setLeft:    true,
      setTop:     true,
      setWidth:   true,
      setHeight:  true,
      offsetTop:  0,
      offsetLeft: 0
    }, arguments[2] || { });

    source = $(source);
    var p = Element.viewportOffset(source), delta = [0, 0], parent = null;

    element = $(element);

    if (Element.getStyle(element, 'position') == 'absolute') {
      parent = Element.getOffsetParent(element);
      delta = Element.viewportOffset(parent);
    }

    if (parent == document.body) {
      delta[0] -= document.body.offsetLeft;
      delta[1] -= document.body.offsetTop;
    }

    if (options.setLeft)   element.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
    if (options.setTop)    element.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
    if (options.setWidth)  element.style.width = source.offsetWidth + 'px';
    if (options.setHeight) element.style.height = source.offsetHeight + 'px';
    return element;
  }
};

Object.extend(Element.Methods, {
  getElementsBySelector: Element.Methods.select,

  childElements: Element.Methods.immediateDescendants
});

Element._attributeTranslations = {
  write: {
    names: {
      className: 'class',
      htmlFor:   'for'
    },
    values: { }
  }
};

if (Prototype.Browser.Opera) {
  Element.Methods.getStyle = Element.Methods.getStyle.wrap(
    function(proceed, element, style) {
      switch (style) {
        case 'height': case 'width':
          if (!Element.visible(element)) return null;

          var dim = parseInt(proceed(element, style), 10);

          if (dim !== element['offset' + style.capitalize()])
            return dim + 'px';

          var properties;
          if (style === 'height') {
            properties = ['border-top-width', 'padding-top',
             'padding-bottom', 'border-bottom-width'];
          }
          else {
            properties = ['border-left-width', 'padding-left',
             'padding-right', 'border-right-width'];
          }
          return properties.inject(dim, function(memo, property) {
            var val = proceed(element, property);
            return val === null ? memo : memo - parseInt(val, 10);
          }) + 'px';
        default: return proceed(element, style);
      }
    }
  );

  Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
    function(proceed, element, attribute) {
      if (attribute === 'title') return element.title;
      return proceed(element, attribute);
    }
  );
}

else if (Prototype.Browser.IE) {
  Element.Methods.getStyle = function(element, style) {
    element = $(element);
    style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
    var value = element.style[style];
    if (!value && element.currentStyle) value = element.currentStyle[style];

    if (style == 'opacity') {
      if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
        if (value[1]) return parseFloat(value[1]) / 100;
      return 1.0;
    }

    if (value == 'auto') {
      if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
        return element['offset' + style.capitalize()] + 'px';
      return null;
    }
    return value;
  };

  Element.Methods.setOpacity = function(element, value) {
    function stripAlpha(filter){
      return filter.replace(/alpha\([^\)]*\)/gi,'');
    }
    element = $(element);
    var currentStyle = element.currentStyle;
    if ((currentStyle && !currentStyle.hasLayout) ||
      (!currentStyle && element.style.zoom == 'normal'))
        element.style.zoom = 1;

    var filter = element.getStyle('filter'), style = element.style;
    if (value == 1 || value === '') {
      (filter = stripAlpha(filter)) ?
        style.filter = filter : style.removeAttribute('filter');
      return element;
    } else if (value < 0.00001) value = 0;
    style.filter = stripAlpha(filter) +
      'alpha(opacity=' + (value * 100) + ')';
    return element;
  };

  Element._attributeTranslations = (function(){

    var classProp = 'className',
        forProp = 'for',
        el = document.createElement('div');

    el.setAttribute(classProp, 'x');

    if (el.className !== 'x') {
      el.setAttribute('class', 'x');
      if (el.className === 'x') {
        classProp = 'class';
      }
    }
    el = null;

    el = document.createElement('label');
    el.setAttribute(forProp, 'x');
    if (el.htmlFor !== 'x') {
      el.setAttribute('htmlFor', 'x');
      if (el.htmlFor === 'x') {
        forProp = 'htmlFor';
      }
    }
    el = null;

    return {
      read: {
        names: {
          'class':      classProp,
          'className':  classProp,
          'for':        forProp,
          'htmlFor':    forProp
        },
        values: {
          _getAttr: function(element, attribute) {
            return element.getAttribute(attribute);
          },
          _getAttr2: function(element, attribute) {
            return element.getAttribute(attribute, 2);
          },
          _getAttrNode: function(element, attribute) {
            var node = element.getAttributeNode(attribute);
            return node ? node.value : "";
          },
          _getEv: (function(){

            var el = document.createElement('div'), f;
            el.onclick = Prototype.emptyFunction;
            var value = el.getAttribute('onclick');

            if (String(value).indexOf('{') > -1) {
              f = function(element, attribute) {
                attribute = element.getAttribute(attribute);
                if (!attribute) return null;
                attribute = attribute.toString();
                attribute = attribute.split('{')[1];
                attribute = attribute.split('}')[0];
                return attribute.strip();
              };
            }
            else if (value === '') {
              f = function(element, attribute) {
                attribute = element.getAttribute(attribute);
                if (!attribute) return null;
                return attribute.strip();
              };
            }
            el = null;
            return f;
          })(),
          _flag: function(element, attribute) {
            return $(element).hasAttribute(attribute) ? attribute : null;
          },
          style: function(element) {
            return element.style.cssText.toLowerCase();
          },
          title: function(element) {
            return element.title;
          }
        }
      }
    }
  })();

  Element._attributeTranslations.write = {
    names: Object.extend({
      cellpadding: 'cellPadding',
      cellspacing: 'cellSpacing'
    }, Element._attributeTranslations.read.names),
    values: {
      checked: function(element, value) {
        element.checked = !!value;
      },

      style: function(element, value) {
        element.style.cssText = value ? value : '';
      }
    }
  };

  Element._attributeTranslations.has = {};

  $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
      'encType maxLength readOnly longDesc frameBorder').each(function(attr) {
    Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
    Element._attributeTranslations.has[attr.toLowerCase()] = attr;
  });

  (function(v) {
    Object.extend(v, {
      href:        v._getAttr2,
      src:         v._getAttr2,
      type:        v._getAttr,
      action:      v._getAttrNode,
      disabled:    v._flag,
      checked:     v._flag,
      readonly:    v._flag,
      multiple:    v._flag,
      onload:      v._getEv,
      onunload:    v._getEv,
      onclick:     v._getEv,
      ondblclick:  v._getEv,
      onmousedown: v._getEv,
      onmouseup:   v._getEv,
      onmouseover: v._getEv,
      onmousemove: v._getEv,
      onmouseout:  v._getEv,
      onfocus:     v._getEv,
      onblur:      v._getEv,
      onkeypress:  v._getEv,
      onkeydown:   v._getEv,
      onkeyup:     v._getEv,
      onsubmit:    v._getEv,
      onreset:     v._getEv,
      onselect:    v._getEv,
      onchange:    v._getEv
    });
  })(Element._attributeTranslations.read.values);

  if (Prototype.BrowserFeatures.ElementExtensions) {
    (function() {
      function _descendants(element) {
        var nodes = element.getElementsByTagName('*'), results = [];
        for (var i = 0, node; node = nodes[i]; i++)
          if (node.tagName !== "!") // Filter out comment nodes.
            results.push(node);
        return results;
      }

      Element.Methods.down = function(element, expression, index) {
        element = $(element);
        if (arguments.length == 1) return element.firstDescendant();
        return Object.isNumber(expression) ? _descendants(element)[expression] :
          Element.select(element, expression)[index || 0];
      }
    })();
  }

}

else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
  Element.Methods.setOpacity = function(element, value) {
    element = $(element);
    element.style.opacity = (value == 1) ? 0.999999 :
      (value === '') ? '' : (value < 0.00001) ? 0 : value;
    return element;
  };
}

else if (Prototype.Browser.WebKit) {
  Element.Methods.setOpacity = function(element, value) {
    element = $(element);
    element.style.opacity = (value == 1 || value === '') ? '' :
      (value < 0.00001) ? 0 : value;

    if (value == 1)
      if (element.tagName.toUpperCase() == 'IMG' && element.width) {
        element.width++; element.width--;
      } else try {
        var n = document.createTextNode(' ');
        element.appendChild(n);
        element.removeChild(n);
      } catch (e) { }

    return element;
  };
}

if ('outerHTML' in document.documentElement) {
  Element.Methods.replace = function(element, content) {
    element = $(element);

    if (content && content.toElement) content = content.toElement();
    if (Object.isElement(content)) {
      element.parentNode.replaceChild(content, element);
      return element;
    }

    content = Object.toHTML(content);
    var parent = element.parentNode, tagName = parent.tagName.toUpperCase();

    if (Element._insertionTranslations.tags[tagName]) {
      var nextSibling = element.next(),
          fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
      parent.removeChild(element);
      if (nextSibling)
        fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
      else
        fragments.each(function(node) { parent.appendChild(node) });
    }
    else element.outerHTML = content.stripScripts();

    content.evalScripts.bind(content).defer();
    return element;
  };
}

Element._returnOffset = function(l, t) {
  var result = [l, t];
  result.left = l;
  result.top = t;
  return result;
};

Element._getContentFromAnonymousElement = function(tagName, html, force) {
  var div = new Element('div'),
      t = Element._insertionTranslations.tags[tagName];

  var workaround = false;
  if (t) workaround = true;
  else if (force) {
    workaround = true;
    t = ['', '', 0];
  }

  if (workaround) {
    div.innerHTML = '&nbsp;' + t[0] + html + t[1];
    div.removeChild(div.firstChild);
    for (var i = t[2]; i--; ) {
      div = div.firstChild;
    }
  }
  else {
    div.innerHTML = html;
  }
  return $A(div.childNodes);
};

Element._insertionTranslations = {
  before: function(element, node) {
    element.parentNode.insertBefore(node, element);
  },
  top: function(element, node) {
    element.insertBefore(node, element.firstChild);
  },
  bottom: function(element, node) {
    element.appendChild(node);
  },
  after: function(element, node) {
    element.parentNode.insertBefore(node, element.nextSibling);
  },
  tags: {
    TABLE:  ['<table>',                '</table>',                   1],
    TBODY:  ['<table><tbody>',         '</tbody></table>',           2],
    TR:     ['<table><tbody><tr>',     '</tr></tbody></table>',      3],
    TD:     ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
    SELECT: ['<select>',               '</select>',                  1]
  }
};

(function() {
  var tags = Element._insertionTranslations.tags;
  Object.extend(tags, {
    THEAD: tags.TBODY,
    TFOOT: tags.TBODY,
    TH:    tags.TD
  });
})();

Element.Methods.Simulated = {
  hasAttribute: function(element, attribute) {
    attribute = Element._attributeTranslations.has[attribute] || attribute;
    var node = $(element).getAttributeNode(attribute);
    return !!(node && node.specified);
  }
};

Element.Methods.ByTag = { };

Object.extend(Element, Element.Methods);

(function(div) {

  if (!Prototype.BrowserFeatures.ElementExtensions && div['__proto__']) {
    window.HTMLElement = { };
    window.HTMLElement.prototype = div['__proto__'];
    Prototype.BrowserFeatures.ElementExtensions = true;
  }

  div = null;

})(document.createElement('div'));

Element.extend = (function() {

  function checkDeficiency(tagName) {
    if (typeof window.Element != 'undefined') {
      var proto = window.Element.prototype;
      if (proto) {
        var id = '_' + (Math.random()+'').slice(2),
            el = document.createElement(tagName);
        proto[id] = 'x';
        var isBuggy = (el[id] !== 'x');
        delete proto[id];
        el = null;
        return isBuggy;
      }
    }
    return false;
  }

  function extendElementWith(element, methods) {
    for (var property in methods) {
      var value = methods[property];
      if (Object.isFunction(value) && !(property in element))
        element[property] = value.methodize();
    }
  }

  var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY = checkDeficiency('object');

  if (Prototype.BrowserFeatures.SpecificElementExtensions) {
    if (HTMLOBJECTELEMENT_PROTOTYPE_BUGGY) {
      return function(element) {
        if (element && typeof element._extendedByPrototype == 'undefined') {
          var t = element.tagName;
          if (t && (/^(?:object|applet|embed)$/i.test(t))) {
            extendElementWith(element, Element.Methods);
            extendElementWith(element, Element.Methods.Simulated);
            extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]);
          }
        }
        return element;
      }
    }
    return Prototype.K;
  }

  var Methods = { }, ByTag = Element.Methods.ByTag;

  var extend = Object.extend(function(element) {
    if (!element || typeof element._extendedByPrototype != 'undefined' ||
        element.nodeType != 1 || element == window) return element;

    var methods = Object.clone(Methods),
        tagName = element.tagName.toUpperCase();

    if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);

    extendElementWith(element, methods);

    element._extendedByPrototype = Prototype.emptyFunction;
    return element;

  }, {
    refresh: function() {
      if (!Prototype.BrowserFeatures.ElementExtensions) {
        Object.extend(Methods, Element.Methods);
        Object.extend(Methods, Element.Methods.Simulated);
      }
    }
  });

  extend.refresh();
  return extend;
})();

if (document.documentElement.hasAttribute) {
  Element.hasAttribute = function(element, attribute) {
    return element.hasAttribute(attribute);
  };
}
else {
  Element.hasAttribute = Element.Methods.Simulated.hasAttribute;
}

Element.addMethods = function(methods) {
  var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;

  if (!methods) {
    Object.extend(Form, Form.Methods);
    Object.extend(Form.Element, Form.Element.Methods);
    Object.extend(Element.Methods.ByTag, {
      "FORM":     Object.clone(Form.Methods),
      "INPUT":    Object.clone(Form.Element.Methods),
      "SELECT":   Object.clone(Form.Element.Methods),
      "TEXTAREA": Object.clone(Form.Element.Methods),
      "BUTTON":   Object.clone(Form.Element.Methods)
    });
  }

  if (arguments.length == 2) {
    var tagName = methods;
    methods = arguments[1];
  }

  if (!tagName) Object.extend(Element.Methods, methods || { });
  else {
    if (Object.isArray(tagName)) tagName.each(extend);
    else extend(tagName);
  }

  function extend(tagName) {
    tagName = tagName.toUpperCase();
    if (!Element.Methods.ByTag[tagName])
      Element.Methods.ByTag[tagName] = { };
    Object.extend(Element.Methods.ByTag[tagName], methods);
  }

  function copy(methods, destination, onlyIfAbsent) {
    onlyIfAbsent = onlyIfAbsent || false;
    for (var property in methods) {
      var value = methods[property];
      if (!Object.isFunction(value)) continue;
      if (!onlyIfAbsent || !(property in destination))
        destination[property] = value.methodize();
    }
  }

  function findDOMClass(tagName) {
    var klass;
    var trans = {
      "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
      "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
      "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
      "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
      "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
      "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
      "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
      "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
      "FrameSet", "IFRAME": "IFrame"
    };
    if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
    if (window[klass]) return window[klass];
    klass = 'HTML' + tagName + 'Element';
    if (window[klass]) return window[klass];
    klass = 'HTML' + tagName.capitalize() + 'Element';
    if (window[klass]) return window[klass];

    var element = document.createElement(tagName),
        proto = element['__proto__'] || element.constructor.prototype;

    element = null;
    return proto;
  }

  var elementPrototype = window.HTMLElement ? HTMLElement.prototype :
   Element.prototype;

  if (F.ElementExtensions) {
    copy(Element.Methods, elementPrototype);
    copy(Element.Methods.Simulated, elementPrototype, true);
  }

  if (F.SpecificElementExtensions) {
    for (var tag in Element.Methods.ByTag) {
      var klass = findDOMClass(tag);
      if (Object.isUndefined(klass)) continue;
      copy(T[tag], klass.prototype);
    }
  }

  Object.extend(Element, Element.Methods);
  delete Element.ByTag;

  if (Element.extend.refresh) Element.extend.refresh();
  Element.cache = { };
};


document.viewport = {

  getDimensions: function() {
    return { width: this.getWidth(), height: this.getHeight() };
  },

  getScrollOffsets: function() {
    return Element._returnOffset(
      window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
      window.pageYOffset || document.documentElement.scrollTop  || document.body.scrollTop);
  }
};

(function(viewport) {
  var B = Prototype.Browser, doc = document, element, property = {};

  function getRootElement() {
    if (B.WebKit && !doc.evaluate)
      return document;

    if (B.Opera && window.parseFloat(window.opera.version()) < 9.5)
      return document.body;

    return document.documentElement;
  }

  function define(D) {
    if (!element) element = getRootElement();

    property[D] = 'client' + D;

    viewport['get' + D] = function() { return element[property[D]] };
    return viewport['get' + D]();
  }

  viewport.getWidth  = define.curry('Width');

  viewport.getHeight = define.curry('Height');
})(document.viewport);


Element.Storage = {
  UID: 1
};

Element.addMethods({
  getStorage: function(element) {
    if (!(element = $(element))) return;

    var uid;
    if (element === window) {
      uid = 0;
    } else {
      if (typeof element._prototypeUID === "undefined")
        element._prototypeUID = Element.Storage.UID++;
      uid = element._prototypeUID;
    }

    if (!Element.Storage[uid])
      Element.Storage[uid] = $H();

    return Element.Storage[uid];
  },

  store: function(element, key, value) {
    if (!(element = $(element))) return;

    if (arguments.length === 2) {
      Element.getStorage(element).update(key);
    } else {
      Element.getStorage(element).set(key, value);
    }

    return element;
  },

  retrieve: function(element, key, defaultValue) {
    if (!(element = $(element))) return;
    var hash = Element.getStorage(element), value = hash.get(key);

    if (Object.isUndefined(value)) {
      hash.set(key, defaultValue);
      value = defaultValue;
    }

    return value;
  },

  clone: function(element, deep) {
    if (!(element = $(element))) return;
    var clone = element.cloneNode(deep);
    clone._prototypeUID = void 0;
    if (deep) {
      var descendants = Element.select(clone, '*'),
          i = descendants.length;
      while (i--) {
        descendants[i]._prototypeUID = void 0;
      }
    }
    return Element.extend(clone);
  },

  purge: function(element) {
    if (!(element = $(element))) return;
    var purgeElement = Element._purgeElement;

    purgeElement(element);

    var descendants = element.getElementsByTagName('*'),
     i = descendants.length;

    while (i--) purgeElement(descendants[i]);

    return null;
  }
});

(function() {

  function toDecimal(pctString) {
    var match = pctString.match(/^(\d+)%?$/i);
    if (!match) return null;
    return (Number(match[1]) / 100);
  }

  function getPixelValue(value, property, context) {
    var element = null;
    if (Object.isElement(value)) {
      element = value;
      value = element.getStyle(property);
    }

    if (value === null) {
      return null;
    }

    if ((/^(?:-)?\d+(\.\d+)?(px)?$/i).test(value)) {
      return window.parseFloat(value);
    }

    var isPercentage = value.include('%'), isViewport = (context === document.viewport);

    if (/\d/.test(value) && element && element.runtimeStyle && !(isPercentage && isViewport)) {
      var style = element.style.left, rStyle = element.runtimeStyle.left;
      element.runtimeStyle.left = element.currentStyle.left;
      element.style.left = value || 0;
      value = element.style.pixelLeft;
      element.style.left = style;
      element.runtimeStyle.left = rStyle;

      return value;
    }

    if (element && isPercentage) {
      context = context || element.parentNode;
      var decimal = toDecimal(value);
      var whole = null;
      var position = element.getStyle('position');

      var isHorizontal = property.include('left') || property.include('right') ||
       property.include('width');

      var isVertical =  property.include('top') || property.include('bottom') ||
        property.include('height');

      if (context === document.viewport) {
        if (isHorizontal) {
          whole = document.viewport.getWidth();
        } else if (isVertical) {
          whole = document.viewport.getHeight();
        }
      } else {
        if (isHorizontal) {
          whole = $(context).measure('width');
        } else if (isVertical) {
          whole = $(context).measure('height');
        }
      }

      return (whole === null) ? 0 : whole * decimal;
    }

    return 0;
  }

  function toCSSPixels(number) {
    if (Object.isString(number) && number.endsWith('px')) {
      return number;
    }
    return number + 'px';
  }

  function isDisplayed(element) {
    var originalElement = element;
    while (element && element.parentNode) {
      var display = element.getStyle('display');
      if (display === 'none') {
        return false;
      }
      element = $(element.parentNode);
    }
    return true;
  }

  var hasLayout = Prototype.K;
  if ('currentStyle' in document.documentElement) {
    hasLayout = function(element) {
      if (!element.currentStyle.hasLayout) {
        element.style.zoom = 1;
      }
      return element;
    };
  }

  function cssNameFor(key) {
    if (key.include('border')) key = key + '-width';
    return key.camelize();
  }

  Element.Layout = Class.create(Hash, {
    initialize: function($super, element, preCompute) {
      $super();
      this.element = $(element);

      Element.Layout.PROPERTIES.each( function(property) {
        this._set(property, null);
      }, this);

      if (preCompute) {
        this._preComputing = true;
        this._begin();
        Element.Layout.PROPERTIES.each( this._compute, this );
        this._end();
        this._preComputing = false;
      }
    },

    _set: function(property, value) {
      return Hash.prototype.set.call(this, property, value);
    },

    set: function(property, value) {
      throw "Properties of Element.Layout are read-only.";
    },

    get: function($super, property) {
      var value = $super(property);
      return value === null ? this._compute(property) : value;
    },

    _begin: function() {
      if (this._prepared) return;

      var element = this.element;
      if (isDisplayed(element)) {
        this._prepared = true;
        return;
      }

      var originalStyles = {
        position:   element.style.position   || '',
        width:      element.style.width      || '',
        visibility: element.style.visibility || '',
        display:    element.style.display    || ''
      };

      element.store('prototype_original_styles', originalStyles);

      var position = element.getStyle('position'),
       width = element.getStyle('width');

      if (width === "0px" || width === null) {
        element.style.display = 'block';
        width = element.getStyle('width');
      }

      var context = (position === 'fixed') ? document.viewport :
       element.parentNode;

      element.setStyle({
        position:   'absolute',
        visibility: 'hidden',
        display:    'block'
      });

      var positionedWidth = element.getStyle('width');

      var newWidth;
      if (width && (positionedWidth === width)) {
        newWidth = getPixelValue(element, 'width', context);
      } else if (position === 'absolute' || position === 'fixed') {
        newWidth = getPixelValue(element, 'width', context);
      } else {
        var parent = element.parentNode, pLayout = $(parent).getLayout();

        newWidth = pLayout.get('width') -
         this.get('margin-left') -
         this.get('border-left') -
         this.get('padding-left') -
         this.get('padding-right') -
         this.get('border-right') -
         this.get('margin-right');
      }

      element.setStyle({ width: newWidth + 'px' });

      this._prepared = true;
    },

    _end: function() {
      var element = this.element;
      var originalStyles = element.retrieve('prototype_original_styles');
      element.store('prototype_original_styles', null);
      element.setStyle(originalStyles);
      this._prepared = false;
    },

    _compute: function(property) {
      var COMPUTATIONS = Element.Layout.COMPUTATIONS;
      if (!(property in COMPUTATIONS)) {
        throw "Property not found.";
      }

      return this._set(property, COMPUTATIONS[property].call(this, this.element));
    },

    toObject: function() {
      var args = $A(arguments);
      var keys = (args.length === 0) ? Element.Layout.PROPERTIES :
       args.join(' ').split(' ');
      var obj = {};
      keys.each( function(key) {
        if (!Element.Layout.PROPERTIES.include(key)) return;
        var value = this.get(key);
        if (value != null) obj[key] = value;
      }, this);
      return obj;
    },

    toHash: function() {
      var obj = this.toObject.apply(this, arguments);
      return new Hash(obj);
    },

    toCSS: function() {
      var args = $A(arguments);
      var keys = (args.length === 0) ? Element.Layout.PROPERTIES :
       args.join(' ').split(' ');
      var css = {};

      keys.each( function(key) {
        if (!Element.Layout.PROPERTIES.include(key)) return;
        if (Element.Layout.COMPOSITE_PROPERTIES.include(key)) return;

        var value = this.get(key);
        if (value != null) css[cssNameFor(key)] = value + 'px';
      }, this);
      return css;
    },

    inspect: function() {
      return "#<Element.Layout>";
    }
  });

  Object.extend(Element.Layout, {
    PROPERTIES: $w('height width top left right bottom border-left border-right border-top border-bottom padding-left padding-right padding-top padding-bottom margin-top margin-bottom margin-left margin-right padding-box-width padding-box-height border-box-width border-box-height margin-box-width margin-box-height'),

    COMPOSITE_PROPERTIES: $w('padding-box-width padding-box-height margin-box-width margin-box-height border-box-width border-box-height'),

    COMPUTATIONS: {
      'height': function(element) {
        if (!this._preComputing) this._begin();

        var bHeight = this.get('border-box-height');
        if (bHeight <= 0) {
          if (!this._preComputing) this._end();
          return 0;
        }

        var bTop = this.get('border-top'),
         bBottom = this.get('border-bottom');

        var pTop = this.get('padding-top'),
         pBottom = this.get('padding-bottom');

        if (!this._preComputing) this._end();

        return bHeight - bTop - bBottom - pTop - pBottom;
      },

      'width': function(element) {
        if (!this._preComputing) this._begin();

        var bWidth = this.get('border-box-width');
        if (bWidth <= 0) {
          if (!this._preComputing) this._end();
          return 0;
        }

        var bLeft = this.get('border-left'),
         bRight = this.get('border-right');

        var pLeft = this.get('padding-left'),
         pRight = this.get('padding-right');

        if (!this._preComputing) this._end();

        return bWidth - bLeft - bRight - pLeft - pRight;
      },

      'padding-box-height': function(element) {
        var height = this.get('height'),
         pTop = this.get('padding-top'),
         pBottom = this.get('padding-bottom');

        return height + pTop + pBottom;
      },

      'padding-box-width': function(element) {
        var width = this.get('width'),
         pLeft = this.get('padding-left'),
         pRight = this.get('padding-right');

        return width + pLeft + pRight;
      },

      'border-box-height': function(element) {
        if (!this._preComputing) this._begin();
        var height = element.offsetHeight;
        if (!this._preComputing) this._end();
        return height;
      },

      'border-box-width': function(element) {
        if (!this._preComputing) this._begin();
        var width = element.offsetWidth;
        if (!this._preComputing) this._end();
        return width;
      },

      'margin-box-height': function(element) {
        var bHeight = this.get('border-box-height'),
         mTop = this.get('margin-top'),
         mBottom = this.get('margin-bottom');

        if (bHeight <= 0) return 0;

        return bHeight + mTop + mBottom;
      },

      'margin-box-width': function(element) {
        var bWidth = this.get('border-box-width'),
         mLeft = this.get('margin-left'),
         mRight = this.get('margin-right');

        if (bWidth <= 0) return 0;

        return bWidth + mLeft + mRight;
      },

      'top': function(element) {
        var offset = element.positionedOffset();
        return offset.top;
      },

      'bottom': function(element) {
        var offset = element.positionedOffset(),
         parent = element.getOffsetParent(),
         pHeight = parent.measure('height');

        var mHeight = this.get('border-box-height');

        return pHeight - mHeight - offset.top;
      },

      'left': function(element) {
        var offset = element.positionedOffset();
        return offset.left;
      },

      'right': function(element) {
        var offset = element.positionedOffset(),
         parent = element.getOffsetParent(),
         pWidth = parent.measure('width');

        var mWidth = this.get('border-box-width');

        return pWidth - mWidth - offset.left;
      },

      'padding-top': function(element) {
        return getPixelValue(element, 'paddingTop');
      },

      'padding-bottom': function(element) {
        return getPixelValue(element, 'paddingBottom');
      },

      'padding-left': function(element) {
        return getPixelValue(element, 'paddingLeft');
      },

      'padding-right': function(element) {
        return getPixelValue(element, 'paddingRight');
      },

      'border-top': function(element) {
        return getPixelValue(element, 'borderTopWidth');
      },

      'border-bottom': function(element) {
        return getPixelValue(element, 'borderBottomWidth');
      },

      'border-left': function(element) {
        return getPixelValue(element, 'borderLeftWidth');
      },

      'border-right': function(element) {
        return getPixelValue(element, 'borderRightWidth');
      },

      'margin-top': function(element) {
        return getPixelValue(element, 'marginTop');
      },

      'margin-bottom': function(element) {
        return getPixelValue(element, 'marginBottom');
      },

      'margin-left': function(element) {
        return getPixelValue(element, 'marginLeft');
      },

      'margin-right': function(element) {
        return getPixelValue(element, 'marginRight');
      }
    }
  });

  if ('getBoundingClientRect' in document.documentElement) {
    Object.extend(Element.Layout.COMPUTATIONS, {
      'right': function(element) {
        var parent = hasLayout(element.getOffsetParent());
        var rect = element.getBoundingClientRect(),
         pRect = parent.getBoundingClientRect();

        return (pRect.right - rect.right).round();
      },

      'bottom': function(element) {
        var parent = hasLayout(element.getOffsetParent());
        var rect = element.getBoundingClientRect(),
         pRect = parent.getBoundingClientRect();

        return (pRect.bottom - rect.bottom).round();
      }
    });
  }

  Element.Offset = Class.create({
    initialize: function(left, top) {
      this.left = left.round();
      this.top  = top.round();

      this[0] = this.left;
      this[1] = this.top;
    },

    relativeTo: function(offset) {
      return new Element.Offset(
        this.left - offset.left,
        this.top  - offset.top
      );
    },

    inspect: function() {
      return "#<Element.Offset left: #{left} top: #{top}>".interpolate(this);
    },

    toString: function() {
      return "[#{left}, #{top}]".interpolate(this);
    },

    toArray: function() {
      return [this.left, this.top];
    }
  });

  function getLayout(element, preCompute) {
    return new Element.Layout(element, preCompute);
  }

  function measure(element, property) {
    return $(element).getLayout().get(property);
  }

  function getDimensions(element) {
    element = $(element);
    var display = Element.getStyle(element, 'display');

    if (display && display !== 'none') {
      return { width: element.offsetWidth, height: element.offsetHeight };
    }

    var style = element.style;
    var originalStyles = {
      visibility: style.visibility,
      position:   style.position,
      display:    style.display
    };

    var newStyles = {
      visibility: 'hidden',
      display:    'block'
    };

    if (originalStyles.position !== 'fixed')
      newStyles.position = 'absolute';

    Element.setStyle(element, newStyles);

    var dimensions = {
      width:  element.offsetWidth,
      height: element.offsetHeight
    };

    Element.setStyle(element, originalStyles);

    return dimensions;
  }

  function getOffsetParent(element) {
    element = $(element);

    if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element))
      return $(document.body);

    var isInline = (Element.getStyle(element, 'display') === 'inline');
    if (!isInline && element.offsetParent) return $(element.offsetParent);

    while ((element = element.parentNode) && element !== document.body) {
      if (Element.getStyle(element, 'position') !== 'static') {
        return isHtml(element) ? $(document.body) : $(element);
      }
    }

    return $(document.body);
  }


  function cumulativeOffset(element) {
    element = $(element);
    var valueT = 0, valueL = 0;
    if (element.parentNode) {
      do {
        valueT += element.offsetTop  || 0;
        valueL += element.offsetLeft || 0;
        element = element.offsetParent;
      } while (element);
    }
    return new Element.Offset(valueL, valueT);
  }

  function positionedOffset(element) {
    element = $(element);

    var layout = element.getLayout();

    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
      if (element) {
        if (isBody(element)) break;
        var p = Element.getStyle(element, 'position');
        if (p !== 'static') break;
      }
    } while (element);

    valueL -= layout.get('margin-top');
    valueT -= layout.get('margin-left');

    return new Element.Offset(valueL, valueT);
  }

  function cumulativeScrollOffset(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.scrollTop  || 0;
      valueL += element.scrollLeft || 0;
      element = element.parentNode;
    } while (element);
    return new Element.Offset(valueL, valueT);
  }

  function viewportOffset(forElement) {
    element = $(element);
    var valueT = 0, valueL = 0, docBody = document.body;

    var element = forElement;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      if (element.offsetParent == docBody &&
        Element.getStyle(element, 'position') == 'absolute') break;
    } while (element = element.offsetParent);

    element = forElement;
    do {
      if (element != docBody) {
        valueT -= element.scrollTop  || 0;
        valueL -= element.scrollLeft || 0;
      }
    } while (element = element.parentNode);
    return new Element.Offset(valueL, valueT);
  }

  function absolutize(element) {
    element = $(element);

    if (Element.getStyle(element, 'position') === 'absolute') {
      return element;
    }

    var offsetParent = getOffsetParent(element);
    var eOffset = element.viewportOffset(),
     pOffset = offsetParent.viewportOffset();

    var offset = eOffset.relativeTo(pOffset);
    var layout = element.getLayout();

    element.store('prototype_absolutize_original_styles', {
      left:   element.getStyle('left'),
      top:    element.getStyle('top'),
      width:  element.getStyle('width'),
      height: element.getStyle('height')
    });

    element.setStyle({
      position: 'absolute',
      top:    offset.top + 'px',
      left:   offset.left + 'px',
      width:  layout.get('width') + 'px',
      height: layout.get('height') + 'px'
    });

    return element;
  }

  function relativize(element) {
    element = $(element);
    if (Element.getStyle(element, 'position') === 'relative') {
      return element;
    }

    var originalStyles =
     element.retrieve('prototype_absolutize_original_styles');

    if (originalStyles) element.setStyle(originalStyles);
    return element;
  }

  if (Prototype.Browser.IE) {
    getOffsetParent = getOffsetParent.wrap(
      function(proceed, element) {
        element = $(element);

        if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element))
          return $(document.body);

        var position = element.getStyle('position');
        if (position !== 'static') return proceed(element);

        element.setStyle({ position: 'relative' });
        var value = proceed(element);
        element.setStyle({ position: position });
        return value;
      }
    );

    positionedOffset = positionedOffset.wrap(function(proceed, element) {
      element = $(element);
      if (!element.parentNode) return new Element.Offset(0, 0);
      var position = element.getStyle('position');
      if (position !== 'static') return proceed(element);

      var offsetParent = element.getOffsetParent();
      if (offsetParent && offsetParent.getStyle('position') === 'fixed')
        hasLayout(offsetParent);

      element.setStyle({ position: 'relative' });
      var value = proceed(element);
      element.setStyle({ position: position });
      return value;
    });
  } else if (Prototype.Browser.Webkit) {
    cumulativeOffset = function(element) {
      element = $(element);
      var valueT = 0, valueL = 0;
      do {
        valueT += element.offsetTop  || 0;
        valueL += element.offsetLeft || 0;
        if (element.offsetParent == document.body)
          if (Element.getStyle(element, 'position') == 'absolute') break;

        element = element.offsetParent;
      } while (element);

      return new Element.Offset(valueL, valueT);
    };
  }


  Element.addMethods({
    getLayout:              getLayout,
    measure:                measure,
    getDimensions:          getDimensions,
    getOffsetParent:        getOffsetParent,
    cumulativeOffset:       cumulativeOffset,
    positionedOffset:       positionedOffset,
    cumulativeScrollOffset: cumulativeScrollOffset,
    viewportOffset:         viewportOffset,
    absolutize:             absolutize,
    relativize:             relativize
  });

  function isBody(element) {
    return element.nodeName.toUpperCase() === 'BODY';
  }

  function isHtml(element) {
    return element.nodeName.toUpperCase() === 'HTML';
  }

  function isDocument(element) {
    return element.nodeType === Node.DOCUMENT_NODE;
  }

  function isDetached(element) {
    return element !== document.body &&
     !Element.descendantOf(element, document.body);
  }

  if ('getBoundingClientRect' in document.documentElement) {
    Element.addMethods({
      viewportOffset: function(element) {
        element = $(element);
        if (isDetached(element)) return new Element.Offset(0, 0);

        var rect = element.getBoundingClientRect(),
         docEl = document.documentElement;
        return new Element.Offset(rect.left - docEl.clientLeft,
         rect.top - docEl.clientTop);
      }
    });
  }
})();
window.$$ = function() {
  var expression = $A(arguments).join(', ');
  return Prototype.Selector.select(expression, document);
};

Prototype.Selector = (function() {

  function select() {
    throw new Error('Method "Prototype.Selector.select" must be defined.');
  }

  function match() {
    throw new Error('Method "Prototype.Selector.match" must be defined.');
  }

  function find(elements, expression, index) {
    index = index || 0;
    var match = Prototype.Selector.match, length = elements.length, matchIndex = 0, i;

    for (i = 0; i < length; i++) {
      if (match(elements[i], expression) && index == matchIndex++) {
        return Element.extend(elements[i]);
      }
    }
  }

  function extendElements(elements) {
    for (var i = 0, length = elements.length; i < length; i++) {
      Element.extend(elements[i]);
    }
    return elements;
  }


  var K = Prototype.K;

  return {
    select: select,
    match: match,
    find: find,
    extendElements: (Element.extend === K) ? K : extendElements,
    extendElement: Element.extend
  };
})();
Prototype._original_property = window.Sizzle;
/*!
 * Sizzle CSS Selector Engine - v1.0
 *  Copyright 2009, The Dojo Foundation
 *  Released under the MIT, BSD, and GPL Licenses.
 *  More information: http://sizzlejs.com/
 */
(function(){

var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
	done = 0,
	toString = Object.prototype.toString,
	hasDuplicate = false,
	baseHasDuplicate = true;

[0, 0].sort(function(){
	baseHasDuplicate = false;
	return 0;
});

var Sizzle = function(selector, context, results, seed) {
	results = results || [];
	var origContext = context = context || document;

	if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
		return [];
	}

	if ( !selector || typeof selector !== "string" ) {
		return results;
	}

	var parts = [], m, set, checkSet, check, mode, extra, prune = true, contextXML = isXML(context),
		soFar = selector;

	while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) {
		soFar = m[3];

		parts.push( m[1] );

		if ( m[2] ) {
			extra = m[3];
			break;
		}
	}

	if ( parts.length > 1 && origPOS.exec( selector ) ) {
		if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
			set = posProcess( parts[0] + parts[1], context );
		} else {
			set = Expr.relative[ parts[0] ] ?
				[ context ] :
				Sizzle( parts.shift(), context );

			while ( parts.length ) {
				selector = parts.shift();

				if ( Expr.relative[ selector ] )
					selector += parts.shift();

				set = posProcess( selector, set );
			}
		}
	} else {
		if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
				Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
			var ret = Sizzle.find( parts.shift(), context, contextXML );
			context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
		}

		if ( context ) {
			var ret = seed ?
				{ expr: parts.pop(), set: makeArray(seed) } :
				Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
			set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;

			if ( parts.length > 0 ) {
				checkSet = makeArray(set);
			} else {
				prune = false;
			}

			while ( parts.length ) {
				var cur = parts.pop(), pop = cur;

				if ( !Expr.relative[ cur ] ) {
					cur = "";
				} else {
					pop = parts.pop();
				}

				if ( pop == null ) {
					pop = context;
				}

				Expr.relative[ cur ]( checkSet, pop, contextXML );
			}
		} else {
			checkSet = parts = [];
		}
	}

	if ( !checkSet ) {
		checkSet = set;
	}

	if ( !checkSet ) {
		throw "Syntax error, unrecognized expression: " + (cur || selector);
	}

	if ( toString.call(checkSet) === "[object Array]" ) {
		if ( !prune ) {
			results.push.apply( results, checkSet );
		} else if ( context && context.nodeType === 1 ) {
			for ( var i = 0; checkSet[i] != null; i++ ) {
				if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
					results.push( set[i] );
				}
			}
		} else {
			for ( var i = 0; checkSet[i] != null; i++ ) {
				if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
					results.push( set[i] );
				}
			}
		}
	} else {
		makeArray( checkSet, results );
	}

	if ( extra ) {
		Sizzle( extra, origContext, results, seed );
		Sizzle.uniqueSort( results );
	}

	return results;
};

Sizzle.uniqueSort = function(results){
	if ( sortOrder ) {
		hasDuplicate = baseHasDuplicate;
		results.sort(sortOrder);

		if ( hasDuplicate ) {
			for ( var i = 1; i < results.length; i++ ) {
				if ( results[i] === results[i-1] ) {
					results.splice(i--, 1);
				}
			}
		}
	}

	return results;
};

Sizzle.matches = function(expr, set){
	return Sizzle(expr, null, null, set);
};

Sizzle.find = function(expr, context, isXML){
	var set, match;

	if ( !expr ) {
		return [];
	}

	for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
		var type = Expr.order[i], match;

		if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
			var left = match[1];
			match.splice(1,1);

			if ( left.substr( left.length - 1 ) !== "\\" ) {
				match[1] = (match[1] || "").replace(/\\/g, "");
				set = Expr.find[ type ]( match, context, isXML );
				if ( set != null ) {
					expr = expr.replace( Expr.match[ type ], "" );
					break;
				}
			}
		}
	}

	if ( !set ) {
		set = context.getElementsByTagName("*");
	}

	return {set: set, expr: expr};
};

Sizzle.filter = function(expr, set, inplace, not){
	var old = expr, result = [], curLoop = set, match, anyFound,
		isXMLFilter = set && set[0] && isXML(set[0]);

	while ( expr && set.length ) {
		for ( var type in Expr.filter ) {
			if ( (match = Expr.match[ type ].exec( expr )) != null ) {
				var filter = Expr.filter[ type ], found, item;
				anyFound = false;

				if ( curLoop == result ) {
					result = [];
				}

				if ( Expr.preFilter[ type ] ) {
					match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );

					if ( !match ) {
						anyFound = found = true;
					} else if ( match === true ) {
						continue;
					}
				}

				if ( match ) {
					for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
						if ( item ) {
							found = filter( item, match, i, curLoop );
							var pass = not ^ !!found;

							if ( inplace && found != null ) {
								if ( pass ) {
									anyFound = true;
								} else {
									curLoop[i] = false;
								}
							} else if ( pass ) {
								result.push( item );
								anyFound = true;
							}
						}
					}
				}

				if ( found !== undefined ) {
					if ( !inplace ) {
						curLoop = result;
					}

					expr = expr.replace( Expr.match[ type ], "" );

					if ( !anyFound ) {
						return [];
					}

					break;
				}
			}
		}

		if ( expr == old ) {
			if ( anyFound == null ) {
				throw "Syntax error, unrecognized expression: " + expr;
			} else {
				break;
			}
		}

		old = expr;
	}

	return curLoop;
};

var Expr = Sizzle.selectors = {
	order: [ "ID", "NAME", "TAG" ],
	match: {
		ID: /#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
		CLASS: /\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
		NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,
		ATTR: /\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
		TAG: /^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,
		CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
		POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
		PSEUDO: /:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
	},
	leftMatch: {},
	attrMap: {
		"class": "className",
		"for": "htmlFor"
	},
	attrHandle: {
		href: function(elem){
			return elem.getAttribute("href");
		}
	},
	relative: {
		"+": function(checkSet, part, isXML){
			var isPartStr = typeof part === "string",
				isTag = isPartStr && !/\W/.test(part),
				isPartStrNotTag = isPartStr && !isTag;

			if ( isTag && !isXML ) {
				part = part.toUpperCase();
			}

			for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
				if ( (elem = checkSet[i]) ) {
					while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}

					checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?
						elem || false :
						elem === part;
				}
			}

			if ( isPartStrNotTag ) {
				Sizzle.filter( part, checkSet, true );
			}
		},
		">": function(checkSet, part, isXML){
			var isPartStr = typeof part === "string";

			if ( isPartStr && !/\W/.test(part) ) {
				part = isXML ? part : part.toUpperCase();

				for ( var i = 0, l = checkSet.length; i < l; i++ ) {
					var elem = checkSet[i];
					if ( elem ) {
						var parent = elem.parentNode;
						checkSet[i] = parent.nodeName === part ? parent : false;
					}
				}
			} else {
				for ( var i = 0, l = checkSet.length; i < l; i++ ) {
					var elem = checkSet[i];
					if ( elem ) {
						checkSet[i] = isPartStr ?
							elem.parentNode :
							elem.parentNode === part;
					}
				}

				if ( isPartStr ) {
					Sizzle.filter( part, checkSet, true );
				}
			}
		},
		"": function(checkSet, part, isXML){
			var doneName = done++, checkFn = dirCheck;

			if ( !/\W/.test(part) ) {
				var nodeCheck = part = isXML ? part : part.toUpperCase();
				checkFn = dirNodeCheck;
			}

			checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
		},
		"~": function(checkSet, part, isXML){
			var doneName = done++, checkFn = dirCheck;

			if ( typeof part === "string" && !/\W/.test(part) ) {
				var nodeCheck = part = isXML ? part : part.toUpperCase();
				checkFn = dirNodeCheck;
			}

			checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
		}
	},
	find: {
		ID: function(match, context, isXML){
			if ( typeof context.getElementById !== "undefined" && !isXML ) {
				var m = context.getElementById(match[1]);
				return m ? [m] : [];
			}
		},
		NAME: function(match, context, isXML){
			if ( typeof context.getElementsByName !== "undefined" ) {
				var ret = [], results = context.getElementsByName(match[1]);

				for ( var i = 0, l = results.length; i < l; i++ ) {
					if ( results[i].getAttribute("name") === match[1] ) {
						ret.push( results[i] );
					}
				}

				return ret.length === 0 ? null : ret;
			}
		},
		TAG: function(match, context){
			return context.getElementsByTagName(match[1]);
		}
	},
	preFilter: {
		CLASS: function(match, curLoop, inplace, result, not, isXML){
			match = " " + match[1].replace(/\\/g, "") + " ";

			if ( isXML ) {
				return match;
			}

			for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
				if ( elem ) {
					if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) {
						if ( !inplace )
							result.push( elem );
					} else if ( inplace ) {
						curLoop[i] = false;
					}
				}
			}

			return false;
		},
		ID: function(match){
			return match[1].replace(/\\/g, "");
		},
		TAG: function(match, curLoop){
			for ( var i = 0; curLoop[i] === false; i++ ){}
			return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
		},
		CHILD: function(match){
			if ( match[1] == "nth" ) {
				var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
					match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||
					!/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);

				match[2] = (test[1] + (test[2] || 1)) - 0;
				match[3] = test[3] - 0;
			}

			match[0] = done++;

			return match;
		},
		ATTR: function(match, curLoop, inplace, result, not, isXML){
			var name = match[1].replace(/\\/g, "");

			if ( !isXML && Expr.attrMap[name] ) {
				match[1] = Expr.attrMap[name];
			}

			if ( match[2] === "~=" ) {
				match[4] = " " + match[4] + " ";
			}

			return match;
		},
		PSEUDO: function(match, curLoop, inplace, result, not){
			if ( match[1] === "not" ) {
				if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
					match[3] = Sizzle(match[3], null, null, curLoop);
				} else {
					var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
					if ( !inplace ) {
						result.push.apply( result, ret );
					}
					return false;
				}
			} else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
				return true;
			}

			return match;
		},
		POS: function(match){
			match.unshift( true );
			return match;
		}
	},
	filters: {
		enabled: function(elem){
			return elem.disabled === false && elem.type !== "hidden";
		},
		disabled: function(elem){
			return elem.disabled === true;
		},
		checked: function(elem){
			return elem.checked === true;
		},
		selected: function(elem){
			elem.parentNode.selectedIndex;
			return elem.selected === true;
		},
		parent: function(elem){
			return !!elem.firstChild;
		},
		empty: function(elem){
			return !elem.firstChild;
		},
		has: function(elem, i, match){
			return !!Sizzle( match[3], elem ).length;
		},
		header: function(elem){
			return /h\d/i.test( elem.nodeName );
		},
		text: function(elem){
			return "text" === elem.type;
		},
		radio: function(elem){
			return "radio" === elem.type;
		},
		checkbox: function(elem){
			return "checkbox" === elem.type;
		},
		file: function(elem){
			return "file" === elem.type;
		},
		password: function(elem){
			return "password" === elem.type;
		},
		submit: function(elem){
			return "submit" === elem.type;
		},
		image: function(elem){
			return "image" === elem.type;
		},
		reset: function(elem){
			return "reset" === elem.type;
		},
		button: function(elem){
			return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";
		},
		input: function(elem){
			return /input|select|textarea|button/i.test(elem.nodeName);
		}
	},
	setFilters: {
		first: function(elem, i){
			return i === 0;
		},
		last: function(elem, i, match, array){
			return i === array.length - 1;
		},
		even: function(elem, i){
			return i % 2 === 0;
		},
		odd: function(elem, i){
			return i % 2 === 1;
		},
		lt: function(elem, i, match){
			return i < match[3] - 0;
		},
		gt: function(elem, i, match){
			return i > match[3] - 0;
		},
		nth: function(elem, i, match){
			return match[3] - 0 == i;
		},
		eq: function(elem, i, match){
			return match[3] - 0 == i;
		}
	},
	filter: {
		PSEUDO: function(elem, match, i, array){
			var name = match[1], filter = Expr.filters[ name ];

			if ( filter ) {
				return filter( elem, i, match, array );
			} else if ( name === "contains" ) {
				return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0;
			} else if ( name === "not" ) {
				var not = match[3];

				for ( var i = 0, l = not.length; i < l; i++ ) {
					if ( not[i] === elem ) {
						return false;
					}
				}

				return true;
			}
		},
		CHILD: function(elem, match){
			var type = match[1], node = elem;
			switch (type) {
				case 'only':
				case 'first':
					while ( (node = node.previousSibling) )  {
						if ( node.nodeType === 1 ) return false;
					}
					if ( type == 'first') return true;
					node = elem;
				case 'last':
					while ( (node = node.nextSibling) )  {
						if ( node.nodeType === 1 ) return false;
					}
					return true;
				case 'nth':
					var first = match[2], last = match[3];

					if ( first == 1 && last == 0 ) {
						return true;
					}

					var doneName = match[0],
						parent = elem.parentNode;

					if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
						var count = 0;
						for ( node = parent.firstChild; node; node = node.nextSibling ) {
							if ( node.nodeType === 1 ) {
								node.nodeIndex = ++count;
							}
						}
						parent.sizcache = doneName;
					}

					var diff = elem.nodeIndex - last;
					if ( first == 0 ) {
						return diff == 0;
					} else {
						return ( diff % first == 0 && diff / first >= 0 );
					}
			}
		},
		ID: function(elem, match){
			return elem.nodeType === 1 && elem.getAttribute("id") === match;
		},
		TAG: function(elem, match){
			return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;
		},
		CLASS: function(elem, match){
			return (" " + (elem.className || elem.getAttribute("class")) + " ")
				.indexOf( match ) > -1;
		},
		ATTR: function(elem, match){
			var name = match[1],
				result = Expr.attrHandle[ name ] ?
					Expr.attrHandle[ name ]( elem ) :
					elem[ name ] != null ?
						elem[ name ] :
						elem.getAttribute( name ),
				value = result + "",
				type = match[2],
				check = match[4];

			return result == null ?
				type === "!=" :
				type === "=" ?
				value === check :
				type === "*=" ?
				value.indexOf(check) >= 0 :
				type === "~=" ?
				(" " + value + " ").indexOf(check) >= 0 :
				!check ?
				value && result !== false :
				type === "!=" ?
				value != check :
				type === "^=" ?
				value.indexOf(check) === 0 :
				type === "$=" ?
				value.substr(value.length - check.length) === check :
				type === "|=" ?
				value === check || value.substr(0, check.length + 1) === check + "-" :
				false;
		},
		POS: function(elem, match, i, array){
			var name = match[2], filter = Expr.setFilters[ name ];

			if ( filter ) {
				return filter( elem, i, match, array );
			}
		}
	}
};

var origPOS = Expr.match.POS;

for ( var type in Expr.match ) {
	Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
	Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source );
}

var makeArray = function(array, results) {
	array = Array.prototype.slice.call( array, 0 );

	if ( results ) {
		results.push.apply( results, array );
		return results;
	}

	return array;
};

try {
	Array.prototype.slice.call( document.documentElement.childNodes, 0 );

} catch(e){
	makeArray = function(array, results) {
		var ret = results || [];

		if ( toString.call(array) === "[object Array]" ) {
			Array.prototype.push.apply( ret, array );
		} else {
			if ( typeof array.length === "number" ) {
				for ( var i = 0, l = array.length; i < l; i++ ) {
					ret.push( array[i] );
				}
			} else {
				for ( var i = 0; array[i]; i++ ) {
					ret.push( array[i] );
				}
			}
		}

		return ret;
	};
}

var sortOrder;

if ( document.documentElement.compareDocumentPosition ) {
	sortOrder = function( a, b ) {
		if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
			if ( a == b ) {
				hasDuplicate = true;
			}
			return 0;
		}

		var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
		if ( ret === 0 ) {
			hasDuplicate = true;
		}
		return ret;
	};
} else if ( "sourceIndex" in document.documentElement ) {
	sortOrder = function( a, b ) {
		if ( !a.sourceIndex || !b.sourceIndex ) {
			if ( a == b ) {
				hasDuplicate = true;
			}
			return 0;
		}

		var ret = a.sourceIndex - b.sourceIndex;
		if ( ret === 0 ) {
			hasDuplicate = true;
		}
		return ret;
	};
} else if ( document.createRange ) {
	sortOrder = function( a, b ) {
		if ( !a.ownerDocument || !b.ownerDocument ) {
			if ( a == b ) {
				hasDuplicate = true;
			}
			return 0;
		}

		var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
		aRange.setStart(a, 0);
		aRange.setEnd(a, 0);
		bRange.setStart(b, 0);
		bRange.setEnd(b, 0);
		var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
		if ( ret === 0 ) {
			hasDuplicate = true;
		}
		return ret;
	};
}

(function(){
	var form = document.createElement("div"),
		id = "script" + (new Date).getTime();
	form.innerHTML = "<a name='" + id + "'/>";

	var root = document.documentElement;
	root.insertBefore( form, root.firstChild );

	if ( !!document.getElementById( id ) ) {
		Expr.find.ID = function(match, context, isXML){
			if ( typeof context.getElementById !== "undefined" && !isXML ) {
				var m = context.getElementById(match[1]);
				return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
			}
		};

		Expr.filter.ID = function(elem, match){
			var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
			return elem.nodeType === 1 && node && node.nodeValue === match;
		};
	}

	root.removeChild( form );
	root = form = null; // release memory in IE
})();

(function(){

	var div = document.createElement("div");
	div.appendChild( document.createComment("") );

	if ( div.getElementsByTagName("*").length > 0 ) {
		Expr.find.TAG = function(match, context){
			var results = context.getElementsByTagName(match[1]);

			if ( match[1] === "*" ) {
				var tmp = [];

				for ( var i = 0; results[i]; i++ ) {
					if ( results[i].nodeType === 1 ) {
						tmp.push( results[i] );
					}
				}

				results = tmp;
			}

			return results;
		};
	}

	div.innerHTML = "<a href='#'></a>";
	if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
			div.firstChild.getAttribute("href") !== "#" ) {
		Expr.attrHandle.href = function(elem){
			return elem.getAttribute("href", 2);
		};
	}

	div = null; // release memory in IE
})();

if ( document.querySelectorAll ) (function(){
	var oldSizzle = Sizzle, div = document.createElement("div");
	div.innerHTML = "<p class='TEST'></p>";

	if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
		return;
	}

	Sizzle = function(query, context, extra, seed){
		context = context || document;

		if ( !seed && context.nodeType === 9 && !isXML(context) ) {
			try {
				return makeArray( context.querySelectorAll(query), extra );
			} catch(e){}
		}

		return oldSizzle(query, context, extra, seed);
	};

	for ( var prop in oldSizzle ) {
		Sizzle[ prop ] = oldSizzle[ prop ];
	}

	div = null; // release memory in IE
})();

if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
	var div = document.createElement("div");
	div.innerHTML = "<div class='test e'></div><div class='test'></div>";

	if ( div.getElementsByClassName("e").length === 0 )
		return;

	div.lastChild.className = "e";

	if ( div.getElementsByClassName("e").length === 1 )
		return;

	Expr.order.splice(1, 0, "CLASS");
	Expr.find.CLASS = function(match, context, isXML) {
		if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
			return context.getElementsByClassName(match[1]);
		}
	};

	div = null; // release memory in IE
})();

function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
	var sibDir = dir == "previousSibling" && !isXML;
	for ( var i = 0, l = checkSet.length; i < l; i++ ) {
		var elem = checkSet[i];
		if ( elem ) {
			if ( sibDir && elem.nodeType === 1 ){
				elem.sizcache = doneName;
				elem.sizset = i;
			}
			elem = elem[dir];
			var match = false;

			while ( elem ) {
				if ( elem.sizcache === doneName ) {
					match = checkSet[elem.sizset];
					break;
				}

				if ( elem.nodeType === 1 && !isXML ){
					elem.sizcache = doneName;
					elem.sizset = i;
				}

				if ( elem.nodeName === cur ) {
					match = elem;
					break;
				}

				elem = elem[dir];
			}

			checkSet[i] = match;
		}
	}
}

function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
	var sibDir = dir == "previousSibling" && !isXML;
	for ( var i = 0, l = checkSet.length; i < l; i++ ) {
		var elem = checkSet[i];
		if ( elem ) {
			if ( sibDir && elem.nodeType === 1 ) {
				elem.sizcache = doneName;
				elem.sizset = i;
			}
			elem = elem[dir];
			var match = false;

			while ( elem ) {
				if ( elem.sizcache === doneName ) {
					match = checkSet[elem.sizset];
					break;
				}

				if ( elem.nodeType === 1 ) {
					if ( !isXML ) {
						elem.sizcache = doneName;
						elem.sizset = i;
					}
					if ( typeof cur !== "string" ) {
						if ( elem === cur ) {
							match = true;
							break;
						}

					} else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
						match = elem;
						break;
					}
				}

				elem = elem[dir];
			}

			checkSet[i] = match;
		}
	}
}

var contains = document.compareDocumentPosition ?  function(a, b){
	return a.compareDocumentPosition(b) & 16;
} : function(a, b){
	return a !== b && (a.contains ? a.contains(b) : true);
};

var isXML = function(elem){
	return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
		!!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML";
};

var posProcess = function(selector, context){
	var tmpSet = [], later = "", match,
		root = context.nodeType ? [context] : context;

	while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
		later += match[0];
		selector = selector.replace( Expr.match.PSEUDO, "" );
	}

	selector = Expr.relative[selector] ? selector + "*" : selector;

	for ( var i = 0, l = root.length; i < l; i++ ) {
		Sizzle( selector, root[i], tmpSet );
	}

	return Sizzle.filter( later, tmpSet );
};


window.Sizzle = Sizzle;

})();

;(function(engine) {
  var extendElements = Prototype.Selector.extendElements;

  function select(selector, scope) {
    return extendElements(engine(selector, scope || document));
  }

  function match(element, selector) {
    return engine.matches(selector, [element]).length == 1;
  }

  Prototype.Selector.engine = engine;
  Prototype.Selector.select = select;
  Prototype.Selector.match = match;
})(Sizzle);

window.Sizzle = Prototype._original_property;
delete Prototype._original_property;

var Form = {
  reset: function(form) {
    form = $(form);
    form.reset();
    return form;
  },

  serializeElements: function(elements, options) {
    if (typeof options != 'object') options = { hash: !!options };
    else if (Object.isUndefined(options.hash)) options.hash = true;
    var key, value, submitted = false, submit = options.submit, accumulator, initial;

    if (options.hash) {
      initial = {};
      accumulator = function(result, key, value) {
        if (key in result) {
          if (!Object.isArray(result[key])) result[key] = [result[key]];
          result[key].push(value);
        } else result[key] = value;
        return result;
      };
    } else {
      initial = '';
      accumulator = function(result, key, value) {
        return result + (result ? '&' : '') + encodeURIComponent(key) + '=' + encodeURIComponent(value);
      }
    }

    return elements.inject(initial, function(result, element) {
      if (!element.disabled && element.name) {
        key = element.name; value = $(element).getValue();
        if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&
            submit !== false && (!submit || key == submit) && (submitted = true)))) {
          result = accumulator(result, key, value);
        }
      }
      return result;
    });
  }
};

Form.Methods = {
  serialize: function(form, options) {
    return Form.serializeElements(Form.getElements(form), options);
  },

  getElements: function(form) {
    var elements = $(form).getElementsByTagName('*'),
        element,
        arr = [ ],
        serializers = Form.Element.Serializers;
    for (var i = 0; element = elements[i]; i++) {
      arr.push(element);
    }
    return arr.inject([], function(elements, child) {
      if (serializers[child.tagName.toLowerCase()])
        elements.push(Element.extend(child));
      return elements;
    })
  },

  getInputs: function(form, typeName, name) {
    form = $(form);
    var inputs = form.getElementsByTagName('input');

    if (!typeName && !name) return $A(inputs).map(Element.extend);

    for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
      var input = inputs[i];
      if ((typeName && input.type != typeName) || (name && input.name != name))
        continue;
      matchingInputs.push(Element.extend(input));
    }

    return matchingInputs;
  },

  disable: function(form) {
    form = $(form);
    Form.getElements(form).invoke('disable');
    return form;
  },

  enable: function(form) {
    form = $(form);
    Form.getElements(form).invoke('enable');
    return form;
  },

  findFirstElement: function(form) {
    var elements = $(form).getElements().findAll(function(element) {
      return 'hidden' != element.type && !element.disabled;
    });
    var firstByIndex = elements.findAll(function(element) {
      return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
    }).sortBy(function(element) { return element.tabIndex }).first();

    return firstByIndex ? firstByIndex : elements.find(function(element) {
      return /^(?:input|select|textarea)$/i.test(element.tagName);
    });
  },

  focusFirstElement: function(form) {
    form = $(form);
    var element = form.findFirstElement();
    if (element) element.activate();
    return form;
  },

  request: function(form, options) {
    form = $(form), options = Object.clone(options || { });

    var params = options.parameters, action = form.readAttribute('action') || '';
    if (action.blank()) action = window.location.href;
    options.parameters = form.serialize(true);

    if (params) {
      if (Object.isString(params)) params = params.toQueryParams();
      Object.extend(options.parameters, params);
    }

    if (form.hasAttribute('method') && !options.method)
      options.method = form.method;

    return new Ajax.Request(action, options);
  }
};

/*--------------------------------------------------------------------------*/


Form.Element = {
  focus: function(element) {
    $(element).focus();
    return element;
  },

  select: function(element) {
    $(element).select();
    return element;
  }
};

Form.Element.Methods = {

  serialize: function(element) {
    element = $(element);
    if (!element.disabled && element.name) {
      var value = element.getValue();
      if (value != undefined) {
        var pair = { };
        pair[element.name] = value;
        return Object.toQueryString(pair);
      }
    }
    return '';
  },

  getValue: function(element) {
    element = $(element);
    var method = element.tagName.toLowerCase();
    return Form.Element.Serializers[method](element);
  },

  setValue: function(element, value) {
    element = $(element);
    var method = element.tagName.toLowerCase();
    Form.Element.Serializers[method](element, value);
    return element;
  },

  clear: function(element) {
    $(element).value = '';
    return element;
  },

  present: function(element) {
    return $(element).value != '';
  },

  activate: function(element) {
    element = $(element);
    try {
      element.focus();
      if (element.select && (element.tagName.toLowerCase() != 'input' ||
          !(/^(?:button|reset|submit)$/i.test(element.type))))
        element.select();
    } catch (e) { }
    return element;
  },

  disable: function(element) {
    element = $(element);
    element.disabled = true;
    return element;
  },

  enable: function(element) {
    element = $(element);
    element.disabled = false;
    return element;
  }
};

/*--------------------------------------------------------------------------*/

var Field = Form.Element;

var $F = Form.Element.Methods.getValue;

/*--------------------------------------------------------------------------*/

Form.Element.Serializers = (function() {
  function input(element, value) {
    switch (element.type.toLowerCase()) {
      case 'checkbox':
      case 'radio':
        return inputSelector(element, value);
      default:
        return valueSelector(element, value);
    }
  }

  function inputSelector(element, value) {
    if (Object.isUndefined(value))
      return element.checked ? element.value : null;
    else element.checked = !!value;
  }

  function valueSelector(element, value) {
    if (Object.isUndefined(value)) return element.value;
    else element.value = value;
  }

  function select(element, value) {
    if (Object.isUndefined(value))
      return (element.type === 'select-one' ? selectOne : selectMany)(element);

    var opt, currentValue, single = !Object.isArray(value);
    for (var i = 0, length = element.length; i < length; i++) {
      opt = element.options[i];
      currentValue = this.optionValue(opt);
      if (single) {
        if (currentValue == value) {
          opt.selected = true;
          return;
        }
      }
      else opt.selected = value.include(currentValue);
    }
  }

  function selectOne(element) {
    var index = element.selectedIndex;
    return index >= 0 ? optionValue(element.options[index]) : null;
  }

  function selectMany(element) {
    var values, length = element.length;
    if (!length) return null;

    for (var i = 0, values = []; i < length; i++) {
      var opt = element.options[i];
      if (opt.selected) values.push(optionValue(opt));
    }
    return values;
  }

  function optionValue(opt) {
    return Element.hasAttribute(opt, 'value') ? opt.value : opt.text;
  }

  return {
    input:         input,
    inputSelector: inputSelector,
    textarea:      valueSelector,
    select:        select,
    selectOne:     selectOne,
    selectMany:    selectMany,
    optionValue:   optionValue,
    button:        valueSelector
  };
})();

/*--------------------------------------------------------------------------*/


Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
  initialize: function($super, element, frequency, callback) {
    $super(callback, frequency);
    this.element   = $(element);
    this.lastValue = this.getValue();
  },

  execute: function() {
    var value = this.getValue();
    if (Object.isString(this.lastValue) && Object.isString(value) ?
        this.lastValue != value : String(this.lastValue) != String(value)) {
      this.callback(this.element, value);
      this.lastValue = value;
    }
  }
});

Form.Element.Observer = Class.create(Abstract.TimedObserver, {
  getValue: function() {
    return Form.Element.getValue(this.element);
  }
});

Form.Observer = Class.create(Abstract.TimedObserver, {
  getValue: function() {
    return Form.serialize(this.element);
  }
});

/*--------------------------------------------------------------------------*/

Abstract.EventObserver = Class.create({
  initialize: function(element, callback) {
    this.element  = $(element);
    this.callback = callback;

    this.lastValue = this.getValue();
    if (this.element.tagName.toLowerCase() == 'form')
      this.registerFormCallbacks();
    else
      this.registerCallback(this.element);
  },

  onElementEvent: function() {
    var value = this.getValue();
    if (this.lastValue != value) {
      this.callback(this.element, value);
      this.lastValue = value;
    }
  },

  registerFormCallbacks: function() {
    Form.getElements(this.element).each(this.registerCallback, this);
  },

  registerCallback: function(element) {
    if (element.type) {
      switch (element.type.toLowerCase()) {
        case 'checkbox':
        case 'radio':
          Event.observe(element, 'click', this.onElementEvent.bind(this));
          break;
        default:
          Event.observe(element, 'change', this.onElementEvent.bind(this));
          break;
      }
    }
  }
});

Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
  getValue: function() {
    return Form.Element.getValue(this.element);
  }
});

Form.EventObserver = Class.create(Abstract.EventObserver, {
  getValue: function() {
    return Form.serialize(this.element);
  }
});
(function() {

  var Event = {
    KEY_BACKSPACE: 8,
    KEY_TAB:       9,
    KEY_RETURN:   13,
    KEY_ESC:      27,
    KEY_LEFT:     37,
    KEY_UP:       38,
    KEY_RIGHT:    39,
    KEY_DOWN:     40,
    KEY_DELETE:   46,
    KEY_HOME:     36,
    KEY_END:      35,
    KEY_PAGEUP:   33,
    KEY_PAGEDOWN: 34,
    KEY_INSERT:   45,

    cache: {}
  };

  var docEl = document.documentElement;
  var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl
    && 'onmouseleave' in docEl;



  var isIELegacyEvent = function(event) { return false; };

  if (window.attachEvent) {
    if (window.addEventListener) {
      isIELegacyEvent = function(event) {
        return !(event instanceof window.Event);
      };
    } else {
      isIELegacyEvent = function(event) { return true; };
    }
  }

  var _isButton;

  function _isButtonForDOMEvents(event, code) {
    return event.which ? (event.which === code + 1) : (event.button === code);
  }

  var legacyButtonMap = { 0: 1, 1: 4, 2: 2 };
  function _isButtonForLegacyEvents(event, code) {
    return event.button === legacyButtonMap[code];
  }

  function _isButtonForWebKit(event, code) {
    switch (code) {
      case 0: return event.which == 1 && !event.metaKey;
      case 1: return event.which == 2 || (event.which == 1 && event.metaKey);
      case 2: return event.which == 3;
      default: return false;
    }
  }

  if (window.attachEvent) {
    if (!window.addEventListener) {
      _isButton = _isButtonForLegacyEvents;
    } else {
      _isButton = function(event, code) {
        return isIELegacyEvent(event) ? _isButtonForLegacyEvents(event, code) :
         _isButtonForDOMEvents(event, code);
      }
    }
  } else if (Prototype.Browser.WebKit) {
    _isButton = _isButtonForWebKit;
  } else {
    _isButton = _isButtonForDOMEvents;
  }

  function isLeftClick(event)   { return _isButton(event, 0) }

  function isMiddleClick(event) { return _isButton(event, 1) }

  function isRightClick(event)  { return _isButton(event, 2) }

  function element(event) {
    event = Event.extend(event);

    var node = event.target, type = event.type,
     currentTarget = event.currentTarget;

    if (currentTarget && currentTarget.tagName) {
      if (type === 'load' || type === 'error' ||
        (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
          && currentTarget.type === 'radio'))
            node = currentTarget;
    }

    if (node.nodeType == Node.TEXT_NODE)
      node = node.parentNode;

    return Element.extend(node);
  }

  function findElement(event, expression) {
    var element = Event.element(event);

    if (!expression) return element;
    while (element) {
      if (Object.isElement(element) && Prototype.Selector.match(element, expression)) {
        return Element.extend(element);
      }
      element = element.parentNode;
    }
  }

  function pointer(event) {
    return { x: pointerX(event), y: pointerY(event) };
  }

  function pointerX(event) {
    var docElement = document.documentElement,
     body = document.body || { scrollLeft: 0 };

    return event.pageX || (event.clientX +
      (docElement.scrollLeft || body.scrollLeft) -
      (docElement.clientLeft || 0));
  }

  function pointerY(event) {
    var docElement = document.documentElement,
     body = document.body || { scrollTop: 0 };

    return  event.pageY || (event.clientY +
       (docElement.scrollTop || body.scrollTop) -
       (docElement.clientTop || 0));
  }


  function stop(event) {
    Event.extend(event);
    event.preventDefault();
    event.stopPropagation();

    event.stopped = true;
  }


  Event.Methods = {
    isLeftClick:   isLeftClick,
    isMiddleClick: isMiddleClick,
    isRightClick:  isRightClick,

    element:     element,
    findElement: findElement,

    pointer:  pointer,
    pointerX: pointerX,
    pointerY: pointerY,

    stop: stop
  };

  var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
    m[name] = Event.Methods[name].methodize();
    return m;
  });

  if (window.attachEvent) {
    function _relatedTarget(event) {
      var element;
      switch (event.type) {
        case 'mouseover':
        case 'mouseenter':
          element = event.fromElement;
          break;
        case 'mouseout':
        case 'mouseleave':
          element = event.toElement;
          break;
        default:
          return null;
      }
      return Element.extend(element);
    }

    var additionalMethods = {
      stopPropagation: function() { this.cancelBubble = true },
      preventDefault:  function() { this.returnValue = false },
      inspect: function() { return '[object Event]' }
    };

    Event.extend = function(event, element) {
      if (!event) return false;

      if (!isIELegacyEvent(event)) return event;

      if (event._extendedByPrototype) return event;
      event._extendedByPrototype = Prototype.emptyFunction;

      var pointer = Event.pointer(event);

      Object.extend(event, {
        target: event.srcElement || element,
        relatedTarget: _relatedTarget(event),
        pageX:  pointer.x,
        pageY:  pointer.y
      });

      Object.extend(event, methods);
      Object.extend(event, additionalMethods);

      return event;
    };
  } else {
    Event.extend = Prototype.K;
  }

  if (window.addEventListener) {
    Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__;
    Object.extend(Event.prototype, methods);
  }

  function _createResponder(element, eventName, handler) {
    var registry = Element.retrieve(element, 'prototype_event_registry');

    if (Object.isUndefined(registry)) {
      CACHE.push(element);
      registry = Element.retrieve(element, 'prototype_event_registry', $H());
    }

    var respondersForEvent = registry.get(eventName);
    if (Object.isUndefined(respondersForEvent)) {
      respondersForEvent = [];
      registry.set(eventName, respondersForEvent);
    }

    if (respondersForEvent.pluck('handler').include(handler)) return false;

    var responder;
    if (eventName.include(":")) {
      responder = function(event) {
        if (Object.isUndefined(event.eventName))
          return false;

        if (event.eventName !== eventName)
          return false;

        Event.extend(event, element);
        handler.call(element, event);
      };
    } else {
      if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED &&
       (eventName === "mouseenter" || eventName === "mouseleave")) {
        if (eventName === "mouseenter" || eventName === "mouseleave") {
          responder = function(event) {
            Event.extend(event, element);

            var parent = event.relatedTarget;
            while (parent && parent !== element) {
              try { parent = parent.parentNode; }
              catch(e) { parent = element; }
            }

            if (parent === element) return;

            handler.call(element, event);
          };
        }
      } else {
        responder = function(event) {
          Event.extend(event, element);
          handler.call(element, event);
        };
      }
    }

    responder.handler = handler;
    respondersForEvent.push(responder);
    return responder;
  }

  function _destroyCache() {
    for (var i = 0, length = CACHE.length; i < length; i++) {
      Event.stopObserving(CACHE[i]);
      CACHE[i] = null;
    }
  }

  var CACHE = [];

  if (Prototype.Browser.IE)
    window.attachEvent('onunload', _destroyCache);

  if (Prototype.Browser.WebKit)
    window.addEventListener('unload', Prototype.emptyFunction, false);


  var _getDOMEventName = Prototype.K,
      translations = { mouseenter: "mouseover", mouseleave: "mouseout" };

  if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) {
    _getDOMEventName = function(eventName) {
      return (translations[eventName] || eventName);
    };
  }

  function observe(element, eventName, handler) {
    element = $(element);

    var responder = _createResponder(element, eventName, handler);

    if (!responder) return element;

    if (eventName.include(':')) {
      if (element.addEventListener)
        element.addEventListener("dataavailable", responder, false);
      else {
        element.attachEvent("ondataavailable", responder);
        element.attachEvent("onlosecapture", responder);
      }
    } else {
      var actualEventName = _getDOMEventName(eventName);

      if (element.addEventListener)
        element.addEventListener(actualEventName, responder, false);
      else
        element.attachEvent("on" + actualEventName, responder);
    }

    return element;
  }

  function stopObserving(element, eventName, handler) {
    element = $(element);

    var registry = Element.retrieve(element, 'prototype_event_registry');
    if (!registry) return element;

    if (!eventName) {
      registry.each( function(pair) {
        var eventName = pair.key;
        stopObserving(element, eventName);
      });
      return element;
    }

    var responders = registry.get(eventName);
    if (!responders) return element;

    if (!handler) {
      responders.each(function(r) {
        stopObserving(element, eventName, r.handler);
      });
      return element;
    }

    var i = responders.length, responder;
    while (i--) {
      if (responders[i].handler === handler) {
        responder = responders[i];
        break;
      }
    }
    if (!responder) return element;

    if (eventName.include(':')) {
      if (element.removeEventListener)
        element.removeEventListener("dataavailable", responder, false);
      else {
        element.detachEvent("ondataavailable", responder);
        element.detachEvent("onlosecapture", responder);
      }
    } else {
      var actualEventName = _getDOMEventName(eventName);
      if (element.removeEventListener)
        element.removeEventListener(actualEventName, responder, false);
      else
        element.detachEvent('on' + actualEventName, responder);
    }

    registry.set(eventName, responders.without(responder));

    return element;
  }

  function fire(element, eventName, memo, bubble) {
    element = $(element);

    if (Object.isUndefined(bubble))
      bubble = true;

    if (element == document && document.createEvent && !element.dispatchEvent)
      element = document.documentElement;

    var event;
    if (document.createEvent) {
      event = document.createEvent('HTMLEvents');
      event.initEvent('dataavailable', bubble, true);
    } else {
      event = document.createEventObject();
      event.eventType = bubble ? 'ondataavailable' : 'onlosecapture';
    }

    event.eventName = eventName;
    event.memo = memo || { };

    if (document.createEvent)
      element.dispatchEvent(event);
    else
      element.fireEvent(event.eventType, event);

    return Event.extend(event);
  }

  Event.Handler = Class.create({
    initialize: function(element, eventName, selector, callback) {
      this.element   = $(element);
      this.eventName = eventName;
      this.selector  = selector;
      this.callback  = callback;
      this.handler   = this.handleEvent.bind(this);
    },

    start: function() {
      Event.observe(this.element, this.eventName, this.handler);
      return this;
    },

    stop: function() {
      Event.stopObserving(this.element, this.eventName, this.handler);
      return this;
    },

    handleEvent: function(event) {
      var element = Event.findElement(event, this.selector);
      if (element) this.callback.call(this.element, event, element);
    }
  });

  function on(element, eventName, selector, callback) {
    element = $(element);
    if (Object.isFunction(selector) && Object.isUndefined(callback)) {
      callback = selector, selector = null;
    }

    return new Event.Handler(element, eventName, selector, callback).start();
  }

  Object.extend(Event, Event.Methods);

  Object.extend(Event, {
    fire:          fire,
    observe:       observe,
    stopObserving: stopObserving,
    on:            on
  });

  Element.addMethods({
    fire:          fire,

    observe:       observe,

    stopObserving: stopObserving,

    on:            on
  });

  Object.extend(document, {
    fire:          fire.methodize(),

    observe:       observe.methodize(),

    stopObserving: stopObserving.methodize(),

    on:            on.methodize(),

    loaded:        false
  });

  if (window.Event) Object.extend(window.Event, Event);
  else window.Event = Event;
})();

(function() {
  /* Support for the DOMContentLoaded event is based on work by Dan Webb,
     Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */

  var timer;

  function fireContentLoadedEvent() {
    if (document.loaded) return;
    if (timer) window.clearTimeout(timer);
    document.loaded = true;
    document.fire('dom:loaded');
  }

  function checkReadyState() {
    if (document.readyState === 'complete') {
      document.stopObserving('readystatechange', checkReadyState);
      fireContentLoadedEvent();
    }
  }

  function pollDoScroll() {
    try { document.documentElement.doScroll('left'); }
    catch(e) {
      timer = pollDoScroll.defer();
      return;
    }
    fireContentLoadedEvent();
  }

  if (document.addEventListener) {
    document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false);
  } else {
    document.observe('readystatechange', checkReadyState);
    if (window == top)
      timer = pollDoScroll.defer();
  }

  Event.observe(window, 'load', fireContentLoadedEvent);
})();

Element.addMethods();

/*------------------------------- DEPRECATED -------------------------------*/

Hash.toQueryString = Object.toQueryString;

var Toggle = { display: Element.toggle };

Element.Methods.childOf = Element.Methods.descendantOf;

var Insertion = {
  Before: function(element, content) {
    return Element.insert(element, {before:content});
  },

  Top: function(element, content) {
    return Element.insert(element, {top:content});
  },

  Bottom: function(element, content) {
    return Element.insert(element, {bottom:content});
  },

  After: function(element, content) {
    return Element.insert(element, {after:content});
  }
};

var $continue = new Error('"throw $continue" is deprecated, use "return" instead');

var Position = {
  includeScrollOffsets: false,

  prepare: function() {
    this.deltaX =  window.pageXOffset
                || document.documentElement.scrollLeft
                || document.body.scrollLeft
                || 0;
    this.deltaY =  window.pageYOffset
                || document.documentElement.scrollTop
                || document.body.scrollTop
                || 0;
  },

  within: function(element, x, y) {
    if (this.includeScrollOffsets)
      return this.withinIncludingScrolloffsets(element, x, y);
    this.xcomp = x;
    this.ycomp = y;
    this.offset = Element.cumulativeOffset(element);

    return (y >= this.offset[1] &&
            y <  this.offset[1] + element.offsetHeight &&
            x >= this.offset[0] &&
            x <  this.offset[0] + element.offsetWidth);
  },

  withinIncludingScrolloffsets: function(element, x, y) {
    var offsetcache = Element.cumulativeScrollOffset(element);

    this.xcomp = x + offsetcache[0] - this.deltaX;
    this.ycomp = y + offsetcache[1] - this.deltaY;
    this.offset = Element.cumulativeOffset(element);

    return (this.ycomp >= this.offset[1] &&
            this.ycomp <  this.offset[1] + element.offsetHeight &&
            this.xcomp >= this.offset[0] &&
            this.xcomp <  this.offset[0] + element.offsetWidth);
  },

  overlap: function(mode, element) {
    if (!mode) return 0;
    if (mode == 'vertical')
      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
        element.offsetHeight;
    if (mode == 'horizontal')
      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
        element.offsetWidth;
  },


  cumulativeOffset: Element.Methods.cumulativeOffset,

  positionedOffset: Element.Methods.positionedOffset,

  absolutize: function(element) {
    Position.prepare();
    return Element.absolutize(element);
  },

  relativize: function(element) {
    Position.prepare();
    return Element.relativize(element);
  },

  realOffset: Element.Methods.cumulativeScrollOffset,

  offsetParent: Element.Methods.getOffsetParent,

  page: Element.Methods.viewportOffset,

  clone: function(source, target, options) {
    options = options || { };
    return Element.clonePosition(target, source, options);
  }
};

/*--------------------------------------------------------------------------*/

if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
  function iter(name) {
    return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
  }

  instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
  function(element, className) {
    className = className.toString().strip();
    var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
    return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
  } : function(element, className) {
    className = className.toString().strip();
    var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
    if (!classNames && !className) return elements;

    var nodes = $(element).getElementsByTagName('*');
    className = ' ' + className + ' ';

    for (var i = 0, child, cn; child = nodes[i]; i++) {
      if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
          (classNames && classNames.all(function(name) {
            return !name.toString().blank() && cn.include(' ' + name + ' ');
          }))))
        elements.push(Element.extend(child));
    }
    return elements;
  };

  return function(className, parentElement) {
    return $(parentElement || document.body).getElementsByClassName(className);
  };
}(Element.Methods);

/*--------------------------------------------------------------------------*/

Element.ClassNames = Class.create();
Element.ClassNames.prototype = {
  initialize: function(element) {
    this.element = $(element);
  },

  _each: function(iterator) {
    this.element.className.split(/\s+/).select(function(name) {
      return name.length > 0;
    })._each(iterator);
  },

  set: function(className) {
    this.element.className = className;
  },

  add: function(classNameToAdd) {
    if (this.include(classNameToAdd)) return;
    this.set($A(this).concat(classNameToAdd).join(' '));
  },

  remove: function(classNameToRemove) {
    if (!this.include(classNameToRemove)) return;
    this.set($A(this).without(classNameToRemove).join(' '));
  },

  toString: function() {
    return $A(this).join(' ');
  }
};

Object.extend(Element.ClassNames.prototype, Enumerable);

/*--------------------------------------------------------------------------*/

(function() {
  window.Selector = Class.create({
    initialize: function(expression) {
      this.expression = expression.strip();
    },

    findElements: function(rootElement) {
      return Prototype.Selector.select(this.expression, rootElement);
    },

    match: function(element) {
      return Prototype.Selector.match(element, this.expression);
    },

    toString: function() {
      return this.expression;
    },

    inspect: function() {
      return "#<Selector: " + this.expression + ">";
    }
  });

  Object.extend(Selector, {
    matchElements: function(elements, expression) {
      var match = Prototype.Selector.match,
          results = [];

      for (var i = 0, length = elements.length; i < length; i++) {
        var element = elements[i];
        if (match(element, expression)) {
          results.push(Element.extend(element));
        }
      }
      return results;
    },

    findElement: function(elements, expression, index) {
      index = index || 0;
      var matchIndex = 0, element;
      for (var i = 0, length = elements.length; i < length; i++) {
        element = elements[i];
        if (Prototype.Selector.match(element, expression) && index === matchIndex++) {
          return Element.extend(element);
        }
      }
    },

    findChildElements: function(element, expressions) {
      var selector = expressions.toArray().join(', ');
      return Prototype.Selector.select(selector, element || document);
    }
  });
})();


/*--------------------------------------------------------------------------*/

/* -------------------------------------------------*/
/* --->>> DEPRECATED!!  onDOMReady Extension <<<----*/
/* -------------------------------------------------*/
Event.onDOMReady = function(f) {
    document.observe("dom:loaded", f);
};
// script.aculo.us scriptaculous.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010

// Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// For details, see the script.aculo.us web site: http://script.aculo.us/

var Scriptaculous = {
  Version: '1.9.0',
  require: function(libraryName) {
    try{
      // inserting via DOM fails in Safari 2.0, so brute force approach
      document.write('<script type="text/javascript" src="'+libraryName+'"><\/script>');
    } catch(e) {
      // for xhtml+xml served content, fall back to DOM methods
      var script = document.createElement('script');
      script.type = 'text/javascript';
      script.src = libraryName;
      document.getElementsByTagName('head')[0].appendChild(script);
    }
  },
  REQUIRED_PROTOTYPE: '1.6.0.3',
  load: function() {
    function convertVersionString(versionString) {
      var v = versionString.replace(/_.*|\./g, '');
      v = parseInt(v + '0'.times(4-v.length));
      return versionString.indexOf('_') > -1 ? v-1 : v;
    }

    if((typeof Prototype=='undefined') ||
       (typeof Element == 'undefined') ||
       (typeof Element.Methods=='undefined') ||
       (convertVersionString(Prototype.Version) <
        convertVersionString(Scriptaculous.REQUIRED_PROTOTYPE)))
       throw("script.aculo.us requires the Prototype JavaScript framework >= " +
        Scriptaculous.REQUIRED_PROTOTYPE);

    var js = /scriptaculous\.js(\?.*)?$/;
    $$('script[src]').findAll(function(s) {
      return s.src.match(js);
    }).each(function(s) {
      var path = s.src.replace(js, ''),
      includes = s.src.match(/\?.*load=([a-z,]*)/);
      (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider,sound').split(',').each(
       function(include) { Scriptaculous.require(path+include+'.js') });
    });
  }
};

Scriptaculous.load();
// script.aculo.us builder.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010

// Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/

var Builder = {
  NODEMAP: {
    AREA: 'map',
    CAPTION: 'table',
    COL: 'table',
    COLGROUP: 'table',
    LEGEND: 'fieldset',
    OPTGROUP: 'select',
    OPTION: 'select',
    PARAM: 'object',
    TBODY: 'table',
    TD: 'table',
    TFOOT: 'table',
    TH: 'table',
    THEAD: 'table',
    TR: 'table'
  },
  // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
  //       due to a Firefox bug
  node: function(elementName) {
    elementName = elementName.toUpperCase();

    // try innerHTML approach
    var parentTag = this.NODEMAP[elementName] || 'div';
    var parentElement = document.createElement(parentTag);
    try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
      parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
    } catch(e) {}
    var element = parentElement.firstChild || null;

    // see if browser added wrapping tags
    if(element && (element.tagName.toUpperCase() != elementName))
      element = element.getElementsByTagName(elementName)[0];

    // fallback to createElement approach
    if(!element) element = document.createElement(elementName);

    // abort if nothing could be created
    if(!element) return;

    // attributes (or text)
    if(arguments[1])
      if(this._isStringOrNumber(arguments[1]) ||
        (arguments[1] instanceof Array) ||
        arguments[1].tagName) {
          this._children(element, arguments[1]);
        } else {
          var attrs = this._attributes(arguments[1]);
          if(attrs.length) {
            try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
              parentElement.innerHTML = "<" +elementName + " " +
                attrs + "></" + elementName + ">";
            } catch(e) {}
            element = parentElement.firstChild || null;
            // workaround firefox 1.0.X bug
            if(!element) {
              element = document.createElement(elementName);
              for(attr in arguments[1])
                element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
            }
            if(element.tagName.toUpperCase() != elementName)
              element = parentElement.getElementsByTagName(elementName)[0];
          }
        }

    // text, or array of children
    if(arguments[2])
      this._children(element, arguments[2]);

     return $(element);
  },
  _text: function(text) {
     return document.createTextNode(text);
  },

  ATTR_MAP: {
    'className': 'class',
    'htmlFor': 'for'
  },

  _attributes: function(attributes) {
    var attrs = [];
    for(attribute in attributes)
      attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) +
          '="' + attributes[attribute].toString().escapeHTML().gsub(/"/,'&quot;') + '"');
    return attrs.join(" ");
  },
  _children: function(element, children) {
    if(children.tagName) {
      element.appendChild(children);
      return;
    }
    if(typeof children=='object') { // array can hold nodes and text
      children.flatten().each( function(e) {
        if(typeof e=='object')
          element.appendChild(e);
        else
          if(Builder._isStringOrNumber(e))
            element.appendChild(Builder._text(e));
      });
    } else
      if(Builder._isStringOrNumber(children))
        element.appendChild(Builder._text(children));
  },
  _isStringOrNumber: function(param) {
    return(typeof param=='string' || typeof param=='number');
  },
  build: function(html) {
    var element = this.node('div');
    $(element).update(html.strip());
    return element.down();
  },
  dump: function(scope) {
    if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope

    var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " +
      "BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " +
      "FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+
      "KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+
      "PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+
      "TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/);

    tags.each( function(tag){
      scope[tag] = function() {
        return Builder.node.apply(Builder, [tag].concat($A(arguments)));
      };
    });
  }
};
// script.aculo.us effects.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010

// Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// Contributors:
//  Justin Palmer (http://encytemedia.com/)
//  Mark Pilgrim (http://diveintomark.org/)
//  Martin Bialasinki
//
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/

// converts rgb() and #xxx to #xxxxxx format,
// returns self (or first argument) if not convertable
String.prototype.parseColor = function() {
  var color = '#';
  if (this.slice(0,4) == 'rgb(') {
    var cols = this.slice(4,this.length-1).split(',');
    var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
  } else {
    if (this.slice(0,1) == '#') {
      if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();
      if (this.length==7) color = this.toLowerCase();
    }
  }
  return (color.length==7 ? color : (arguments[0] || this));
};

/*--------------------------------------------------------------------------*/

Element.collectTextNodes = function(element) {
  return $A($(element).childNodes).collect( function(node) {
    return (node.nodeType==3 ? node.nodeValue :
      (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
  }).flatten().join('');
};

Element.collectTextNodesIgnoreClass = function(element, className) {
  return $A($(element).childNodes).collect( function(node) {
    return (node.nodeType==3 ? node.nodeValue :
      ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
        Element.collectTextNodesIgnoreClass(node, className) : ''));
  }).flatten().join('');
};

Element.setContentZoom = function(element, percent) {
  element = $(element);
  element.setStyle({fontSize: (percent/100) + 'em'});
  if (Prototype.Browser.WebKit) window.scrollBy(0,0);
  return element;
};

Element.getInlineOpacity = function(element){
  return $(element).style.opacity || '';
};

Element.forceRerendering = function(element) {
  try {
    element = $(element);
    var n = document.createTextNode(' ');
    element.appendChild(n);
    element.removeChild(n);
  } catch(e) { }
};

/*--------------------------------------------------------------------------*/

var Effect = {
  _elementDoesNotExistError: {
    name: 'ElementDoesNotExistError',
    message: 'The specified DOM element does not exist, but is required for this effect to operate'
  },
  Transitions: {
    linear: Prototype.K,
    sinoidal: function(pos) {
      return (-Math.cos(pos*Math.PI)/2) + .5;
    },
    reverse: function(pos) {
      return 1-pos;
    },
    flicker: function(pos) {
      var pos = ((-Math.cos(pos*Math.PI)/4) + .75) + Math.random()/4;
      return pos > 1 ? 1 : pos;
    },
    wobble: function(pos) {
      return (-Math.cos(pos*Math.PI*(9*pos))/2) + .5;
    },
    pulse: function(pos, pulses) {
      return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5;
    },
    spring: function(pos) {
      return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
    },
    none: function(pos) {
      return 0;
    },
    full: function(pos) {
      return 1;
    }
  },
  DefaultOptions: {
    duration:   1.0,   // seconds
    fps:        100,   // 100= assume 66fps max.
    sync:       false, // true for combining
    from:       0.0,
    to:         1.0,
    delay:      0.0,
    queue:      'parallel'
  },
  tagifyText: function(element) {
    var tagifyStyle = 'position:relative';
    if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';

    element = $(element);
    $A(element.childNodes).each( function(child) {
      if (child.nodeType==3) {
        child.nodeValue.toArray().each( function(character) {
          element.insertBefore(
            new Element('span', {style: tagifyStyle}).update(
              character == ' ' ? String.fromCharCode(160) : character),
              child);
        });
        Element.remove(child);
      }
    });
  },
  multiple: function(element, effect) {
    var elements;
    if (((typeof element == 'object') ||
        Object.isFunction(element)) &&
       (element.length))
      elements = element;
    else
      elements = $(element).childNodes;

    var options = Object.extend({
      speed: 0.1,
      delay: 0.0
    }, arguments[2] || { });
    var masterDelay = options.delay;

    $A(elements).each( function(element, index) {
      new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
    });
  },
  PAIRS: {
    'slide':  ['SlideDown','SlideUp'],
    'blind':  ['BlindDown','BlindUp'],
    'appear': ['Appear','Fade']
  },
  toggle: function(element, effect, options) {
    element = $(element);
    effect  = (effect || 'appear').toLowerCase();
    
    return Effect[ Effect.PAIRS[ effect ][ element.visible() ? 1 : 0 ] ](element, Object.extend({
      queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
    }, options || {}));
  }
};

Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;

/* ------------- core effects ------------- */

Effect.ScopedQueue = Class.create(Enumerable, {
  initialize: function() {
    this.effects  = [];
    this.interval = null;
  },
  _each: function(iterator) {
    this.effects._each(iterator);
  },
  add: function(effect) {
    var timestamp = new Date().getTime();

    var position = Object.isString(effect.options.queue) ?
      effect.options.queue : effect.options.queue.position;

    switch(position) {
      case 'front':
        // move unstarted effects after this effect
        this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
            e.startOn  += effect.finishOn;
            e.finishOn += effect.finishOn;
          });
        break;
      case 'with-last':
        timestamp = this.effects.pluck('startOn').max() || timestamp;
        break;
      case 'end':
        // start effect after last queued effect has finished
        timestamp = this.effects.pluck('finishOn').max() || timestamp;
        break;
    }

    effect.startOn  += timestamp;
    effect.finishOn += timestamp;

    if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
      this.effects.push(effect);

    if (!this.interval)
      this.interval = setInterval(this.loop.bind(this), 15);
  },
  remove: function(effect) {
    this.effects = this.effects.reject(function(e) { return e==effect });
    if (this.effects.length == 0) {
      clearInterval(this.interval);
      this.interval = null;
    }
  },
  loop: function() {
    var timePos = new Date().getTime();
    for(var i=0, len=this.effects.length;i<len;i++)
      this.effects[i] && this.effects[i].loop(timePos);
  }
});

Effect.Queues = {
  instances: $H(),
  get: function(queueName) {
    if (!Object.isString(queueName)) return queueName;

    return this.instances.get(queueName) ||
      this.instances.set(queueName, new Effect.ScopedQueue());
  }
};
Effect.Queue = Effect.Queues.get('global');

Effect.Base = Class.create({
  position: null,
  start: function(options) {
    if (options && options.transition === false) options.transition = Effect.Transitions.linear;
    this.options      = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { });
    this.currentFrame = 0;
    this.state        = 'idle';
    this.startOn      = this.options.delay*1000;
    this.finishOn     = this.startOn+(this.options.duration*1000);
    this.fromToDelta  = this.options.to-this.options.from;
    this.totalTime    = this.finishOn-this.startOn;
    this.totalFrames  = this.options.fps*this.options.duration;

    this.render = (function() {
      function dispatch(effect, eventName) {
        if (effect.options[eventName + 'Internal'])
          effect.options[eventName + 'Internal'](effect);
        if (effect.options[eventName])
          effect.options[eventName](effect);
      }

      return function(pos) {
        if (this.state === "idle") {
          this.state = "running";
          dispatch(this, 'beforeSetup');
          if (this.setup) this.setup();
          dispatch(this, 'afterSetup');
        }
        if (this.state === "running") {
          pos = (this.options.transition(pos) * this.fromToDelta) + this.options.from;
          this.position = pos;
          dispatch(this, 'beforeUpdate');
          if (this.update) this.update(pos);
          dispatch(this, 'afterUpdate');
        }
      };
    })();

    this.event('beforeStart');
    if (!this.options.sync)
      Effect.Queues.get(Object.isString(this.options.queue) ?
        'global' : this.options.queue.scope).add(this);
  },
  loop: function(timePos) {
    if (timePos >= this.startOn) {
      if (timePos >= this.finishOn) {
        this.render(1.0);
        this.cancel();
        this.event('beforeFinish');
        if (this.finish) this.finish();
        this.event('afterFinish');
        return;
      }
      var pos   = (timePos - this.startOn) / this.totalTime,
          frame = (pos * this.totalFrames).round();
      if (frame > this.currentFrame) {
        this.render(pos);
        this.currentFrame = frame;
      }
    }
  },
  cancel: function() {
    if (!this.options.sync)
      Effect.Queues.get(Object.isString(this.options.queue) ?
        'global' : this.options.queue.scope).remove(this);
    this.state = 'finished';
  },
  event: function(eventName) {
    if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
    if (this.options[eventName]) this.options[eventName](this);
  },
  inspect: function() {
    var data = $H();
    for(property in this)
      if (!Object.isFunction(this[property])) data.set(property, this[property]);
    return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
  }
});

Effect.Parallel = Class.create(Effect.Base, {
  initialize: function(effects) {
    this.effects = effects || [];
    this.start(arguments[1]);
  },
  update: function(position) {
    this.effects.invoke('render', position);
  },
  finish: function(position) {
    this.effects.each( function(effect) {
      effect.render(1.0);
      effect.cancel();
      effect.event('beforeFinish');
      if (effect.finish) effect.finish(position);
      effect.event('afterFinish');
    });
  }
});

Effect.Tween = Class.create(Effect.Base, {
  initialize: function(object, from, to) {
    object = Object.isString(object) ? $(object) : object;
    var args = $A(arguments), method = args.last(),
      options = args.length == 5 ? args[3] : null;
    this.method = Object.isFunction(method) ? method.bind(object) :
      Object.isFunction(object[method]) ? object[method].bind(object) :
      function(value) { object[method] = value };
    this.start(Object.extend({ from: from, to: to }, options || { }));
  },
  update: function(position) {
    this.method(position);
  }
});

Effect.Event = Class.create(Effect.Base, {
  initialize: function() {
    this.start(Object.extend({ duration: 0 }, arguments[0] || { }));
  },
  update: Prototype.emptyFunction
});

Effect.Opacity = Class.create(Effect.Base, {
  initialize: function(element) {
    this.element = $(element);
    if (!this.element) throw(Effect._elementDoesNotExistError);
    // make this work on IE on elements without 'layout'
    if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
      this.element.setStyle({zoom: 1});
    var options = Object.extend({
      from: this.element.getOpacity() || 0.0,
      to:   1.0
    }, arguments[1] || { });
    this.start(options);
  },
  update: function(position) {
    this.element.setOpacity(position);
  }
});

Effect.Move = Class.create(Effect.Base, {
  initialize: function(element) {
    this.element = $(element);
    if (!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({
      x:    0,
      y:    0,
      mode: 'relative'
    }, arguments[1] || { });
    this.start(options);
  },
  setup: function() {
    this.element.makePositioned();
    this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
    this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');
    if (this.options.mode == 'absolute') {
      this.options.x = this.options.x - this.originalLeft;
      this.options.y = this.options.y - this.originalTop;
    }
  },
  update: function(position) {
    this.element.setStyle({
      left: (this.options.x  * position + this.originalLeft).round() + 'px',
      top:  (this.options.y  * position + this.originalTop).round()  + 'px'
    });
  }
});

// for backwards compatibility
Effect.MoveBy = function(element, toTop, toLeft) {
  return new Effect.Move(element,
    Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));
};

Effect.Scale = Class.create(Effect.Base, {
  initialize: function(element, percent) {
    this.element = $(element);
    if (!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({
      scaleX: true,
      scaleY: true,
      scaleContent: true,
      scaleFromCenter: false,
      scaleMode: 'box',        // 'box' or 'contents' or { } with provided values
      scaleFrom: 100.0,
      scaleTo:   percent
    }, arguments[2] || { });
    this.start(options);
  },
  setup: function() {
    this.restoreAfterFinish = this.options.restoreAfterFinish || false;
    this.elementPositioning = this.element.getStyle('position');

    this.originalStyle = { };
    ['top','left','width','height','fontSize'].each( function(k) {
      this.originalStyle[k] = this.element.style[k];
    }.bind(this));

    this.originalTop  = this.element.offsetTop;
    this.originalLeft = this.element.offsetLeft;

    var fontSize = this.element.getStyle('font-size') || '100%';
    ['em','px','%','pt'].each( function(fontSizeType) {
      if (fontSize.indexOf(fontSizeType)>0) {
        this.fontSize     = parseFloat(fontSize);
        this.fontSizeType = fontSizeType;
      }
    }.bind(this));

    this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;

    this.dims = null;
    if (this.options.scaleMode=='box')
      this.dims = [this.element.offsetHeight, this.element.offsetWidth];
    if (/^content/.test(this.options.scaleMode))
      this.dims = [this.element.scrollHeight, this.element.scrollWidth];
    if (!this.dims)
      this.dims = [this.options.scaleMode.originalHeight,
                   this.options.scaleMode.originalWidth];
  },
  update: function(position) {
    var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
    if (this.options.scaleContent && this.fontSize)
      this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
    this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
  },
  finish: function(position) {
    if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
  },
  setDimensions: function(height, width) {
    var d = { };
    if (this.options.scaleX) d.width = width.round() + 'px';
    if (this.options.scaleY) d.height = height.round() + 'px';
    if (this.options.scaleFromCenter) {
      var topd  = (height - this.dims[0])/2;
      var leftd = (width  - this.dims[1])/2;
      if (this.elementPositioning == 'absolute') {
        if (this.options.scaleY) d.top = this.originalTop-topd + 'px';
        if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
      } else {
        if (this.options.scaleY) d.top = -topd + 'px';
        if (this.options.scaleX) d.left = -leftd + 'px';
      }
    }
    this.element.setStyle(d);
  }
});

Effect.Highlight = Class.create(Effect.Base, {
  initialize: function(element) {
    this.element = $(element);
    if (!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { });
    this.start(options);
  },
  setup: function() {
    // Prevent executing on elements not in the layout flow
    if (this.element.getStyle('display')=='none') { this.cancel(); return; }
    // Disable background image during the effect
    this.oldStyle = { };
    if (!this.options.keepBackgroundImage) {
      this.oldStyle.backgroundImage = this.element.getStyle('background-image');
      this.element.setStyle({backgroundImage: 'none'});
    }
    if (!this.options.endcolor)
      this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
    if (!this.options.restorecolor)
      this.options.restorecolor = this.element.getStyle('background-color');
    // init color calculations
    this._base  = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
    this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
  },
  update: function(position) {
    this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
      return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) });
  },
  finish: function() {
    this.element.setStyle(Object.extend(this.oldStyle, {
      backgroundColor: this.options.restorecolor
    }));
  }
});

Effect.ScrollTo = function(element) {
  var options = arguments[1] || { },
  scrollOffsets = document.viewport.getScrollOffsets(),
  elementOffsets = $(element).cumulativeOffset();

  if (options.offset) elementOffsets[1] += options.offset;

  return new Effect.Tween(null,
    scrollOffsets.top,
    elementOffsets[1],
    options,
    function(p){ scrollTo(scrollOffsets.left, p.round()); }
  );
};

/* ------------- combination effects ------------- */

Effect.Fade = function(element) {
  element = $(element);
  var oldOpacity = element.getInlineOpacity();
  var options = Object.extend({
    from: element.getOpacity() || 1.0,
    to:   0.0,
    afterFinishInternal: function(effect) {
      if (effect.options.to!=0) return;
      effect.element.hide().setStyle({opacity: oldOpacity});
    }
  }, arguments[1] || { });
  return new Effect.Opacity(element,options);
};

Effect.Appear = function(element) {
  element = $(element);
  var options = Object.extend({
  from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
  to:   1.0,
  // force Safari to render floated elements properly
  afterFinishInternal: function(effect) {
    effect.element.forceRerendering();
  },
  beforeSetup: function(effect) {
    effect.element.setOpacity(effect.options.from).show();
  }}, arguments[1] || { });
  return new Effect.Opacity(element,options);
};

Effect.Puff = function(element) {
  element = $(element);
  var oldStyle = {
    opacity: element.getInlineOpacity(),
    position: element.getStyle('position'),
    top:  element.style.top,
    left: element.style.left,
    width: element.style.width,
    height: element.style.height
  };
  return new Effect.Parallel(
   [ new Effect.Scale(element, 200,
      { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),
     new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
     Object.extend({ duration: 1.0,
      beforeSetupInternal: function(effect) {
        Position.absolutize(effect.effects[0].element);
      },
      afterFinishInternal: function(effect) {
         effect.effects[0].element.hide().setStyle(oldStyle); }
     }, arguments[1] || { })
   );
};

Effect.BlindUp = function(element) {
  element = $(element);
  element.makeClipping();
  return new Effect.Scale(element, 0,
    Object.extend({ scaleContent: false,
      scaleX: false,
      restoreAfterFinish: true,
      afterFinishInternal: function(effect) {
        effect.element.hide().undoClipping();
      }
    }, arguments[1] || { })
  );
};

Effect.BlindDown = function(element) {
  element = $(element);
  var elementDimensions = element.getDimensions();
  return new Effect.Scale(element, 100, Object.extend({
    scaleContent: false,
    scaleX: false,
    scaleFrom: 0,
    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
    restoreAfterFinish: true,
    afterSetup: function(effect) {
      effect.element.makeClipping().setStyle({height: '0px'}).show();
    },
    afterFinishInternal: function(effect) {
      effect.element.undoClipping();
    }
  }, arguments[1] || { }));
};

Effect.SwitchOff = function(element) {
  element = $(element);
  var oldOpacity = element.getInlineOpacity();
  return new Effect.Appear(element, Object.extend({
    duration: 0.4,
    from: 0,
    transition: Effect.Transitions.flicker,
    afterFinishInternal: function(effect) {
      new Effect.Scale(effect.element, 1, {
        duration: 0.3, scaleFromCenter: true,
        scaleX: false, scaleContent: false, restoreAfterFinish: true,
        beforeSetup: function(effect) {
          effect.element.makePositioned().makeClipping();
        },
        afterFinishInternal: function(effect) {
          effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
        }
      });
    }
  }, arguments[1] || { }));
};

Effect.DropOut = function(element) {
  element = $(element);
  var oldStyle = {
    top: element.getStyle('top'),
    left: element.getStyle('left'),
    opacity: element.getInlineOpacity() };
  return new Effect.Parallel(
    [ new Effect.Move(element, {x: 0, y: 100, sync: true }),
      new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
    Object.extend(
      { duration: 0.5,
        beforeSetup: function(effect) {
          effect.effects[0].element.makePositioned();
        },
        afterFinishInternal: function(effect) {
          effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
        }
      }, arguments[1] || { }));
};

Effect.Shake = function(element) {
  element = $(element);
  var options = Object.extend({
    distance: 20,
    duration: 0.5
  }, arguments[1] || {});
  var distance = parseFloat(options.distance);
  var split = parseFloat(options.duration) / 10.0;
  var oldStyle = {
    top: element.getStyle('top'),
    left: element.getStyle('left') };
    return new Effect.Move(element,
      { x:  distance, y: 0, duration: split, afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {
        effect.element.undoPositioned().setStyle(oldStyle);
  }}); }}); }}); }}); }}); }});
};

Effect.SlideDown = function(element) {
  element = $(element).cleanWhitespace();
  // SlideDown need to have the content of the element wrapped in a container element with fixed height!
  var oldInnerBottom = element.down().getStyle('bottom');
  var elementDimensions = element.getDimensions();
  return new Effect.Scale(element, 100, Object.extend({
    scaleContent: false,
    scaleX: false,
    scaleFrom: window.opera ? 0 : 1,
    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
    restoreAfterFinish: true,
    afterSetup: function(effect) {
      effect.element.makePositioned();
      effect.element.down().makePositioned();
      if (window.opera) effect.element.setStyle({top: ''});
      effect.element.makeClipping().setStyle({height: '0px'}).show();
    },
    afterUpdateInternal: function(effect) {
      effect.element.down().setStyle({bottom:
        (effect.dims[0] - effect.element.clientHeight) + 'px' });
    },
    afterFinishInternal: function(effect) {
      effect.element.undoClipping().undoPositioned();
      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
    }, arguments[1] || { })
  );
};

Effect.SlideUp = function(element) {
  element = $(element).cleanWhitespace();
  var oldInnerBottom = element.down().getStyle('bottom');
  var elementDimensions = element.getDimensions();
  return new Effect.Scale(element, window.opera ? 0 : 1,
   Object.extend({ scaleContent: false,
    scaleX: false,
    scaleMode: 'box',
    scaleFrom: 100,
    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
    restoreAfterFinish: true,
    afterSetup: function(effect) {
      effect.element.makePositioned();
      effect.element.down().makePositioned();
      if (window.opera) effect.element.setStyle({top: ''});
      effect.element.makeClipping().show();
    },
    afterUpdateInternal: function(effect) {
      effect.element.down().setStyle({bottom:
        (effect.dims[0] - effect.element.clientHeight) + 'px' });
    },
    afterFinishInternal: function(effect) {
      effect.element.hide().undoClipping().undoPositioned();
      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom});
    }
   }, arguments[1] || { })
  );
};

// Bug in opera makes the TD containing this element expand for a instance after finish
Effect.Squish = function(element) {
  return new Effect.Scale(element, window.opera ? 1 : 0, {
    restoreAfterFinish: true,
    beforeSetup: function(effect) {
      effect.element.makeClipping();
    },
    afterFinishInternal: function(effect) {
      effect.element.hide().undoClipping();
    }
  });
};

Effect.Grow = function(element) {
  element = $(element);
  var options = Object.extend({
    direction: 'center',
    moveTransition: Effect.Transitions.sinoidal,
    scaleTransition: Effect.Transitions.sinoidal,
    opacityTransition: Effect.Transitions.full
  }, arguments[1] || { });
  var oldStyle = {
    top: element.style.top,
    left: element.style.left,
    height: element.style.height,
    width: element.style.width,
    opacity: element.getInlineOpacity() };

  var dims = element.getDimensions();
  var initialMoveX, initialMoveY;
  var moveX, moveY;

  switch (options.direction) {
    case 'top-left':
      initialMoveX = initialMoveY = moveX = moveY = 0;
      break;
    case 'top-right':
      initialMoveX = dims.width;
      initialMoveY = moveY = 0;
      moveX = -dims.width;
      break;
    case 'bottom-left':
      initialMoveX = moveX = 0;
      initialMoveY = dims.height;
      moveY = -dims.height;
      break;
    case 'bottom-right':
      initialMoveX = dims.width;
      initialMoveY = dims.height;
      moveX = -dims.width;
      moveY = -dims.height;
      break;
    case 'center':
      initialMoveX = dims.width / 2;
      initialMoveY = dims.height / 2;
      moveX = -dims.width / 2;
      moveY = -dims.height / 2;
      break;
  }

  return new Effect.Move(element, {
    x: initialMoveX,
    y: initialMoveY,
    duration: 0.01,
    beforeSetup: function(effect) {
      effect.element.hide().makeClipping().makePositioned();
    },
    afterFinishInternal: function(effect) {
      new Effect.Parallel(
        [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
          new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
          new Effect.Scale(effect.element, 100, {
            scaleMode: { originalHeight: dims.height, originalWidth: dims.width },
            sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
        ], Object.extend({
             beforeSetup: function(effect) {
               effect.effects[0].element.setStyle({height: '0px'}).show();
             },
             afterFinishInternal: function(effect) {
               effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);
             }
           }, options)
      );
    }
  });
};

Effect.Shrink = function(element) {
  element = $(element);
  var options = Object.extend({
    direction: 'center',
    moveTransition: Effect.Transitions.sinoidal,
    scaleTransition: Effect.Transitions.sinoidal,
    opacityTransition: Effect.Transitions.none
  }, arguments[1] || { });
  var oldStyle = {
    top: element.style.top,
    left: element.style.left,
    height: element.style.height,
    width: element.style.width,
    opacity: element.getInlineOpacity() };

  var dims = element.getDimensions();
  var moveX, moveY;

  switch (options.direction) {
    case 'top-left':
      moveX = moveY = 0;
      break;
    case 'top-right':
      moveX = dims.width;
      moveY = 0;
      break;
    case 'bottom-left':
      moveX = 0;
      moveY = dims.height;
      break;
    case 'bottom-right':
      moveX = dims.width;
      moveY = dims.height;
      break;
    case 'center':
      moveX = dims.width / 2;
      moveY = dims.height / 2;
      break;
  }

  return new Effect.Parallel(
    [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
      new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
      new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
    ], Object.extend({
         beforeStartInternal: function(effect) {
           effect.effects[0].element.makePositioned().makeClipping();
         },
         afterFinishInternal: function(effect) {
           effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
       }, options)
  );
};

Effect.Pulsate = function(element) {
  element = $(element);
  var options    = arguments[1] || { },
    oldOpacity = element.getInlineOpacity(),
    transition = options.transition || Effect.Transitions.linear,
    reverser   = function(pos){
      return 1 - transition((-Math.cos((pos*(options.pulses||5)*2)*Math.PI)/2) + .5);
    };

  return new Effect.Opacity(element,
    Object.extend(Object.extend({  duration: 2.0, from: 0,
      afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
    }, options), {transition: reverser}));
};

Effect.Fold = function(element) {
  element = $(element);
  var oldStyle = {
    top: element.style.top,
    left: element.style.left,
    width: element.style.width,
    height: element.style.height };
  element.makeClipping();
  return new Effect.Scale(element, 5, Object.extend({
    scaleContent: false,
    scaleX: false,
    afterFinishInternal: function(effect) {
    new Effect.Scale(element, 1, {
      scaleContent: false,
      scaleY: false,
      afterFinishInternal: function(effect) {
        effect.element.hide().undoClipping().setStyle(oldStyle);
      } });
  }}, arguments[1] || { }));
};

Effect.Morph = Class.create(Effect.Base, {
  initialize: function(element) {
    this.element = $(element);
    if (!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({
      style: { }
    }, arguments[1] || { });

    if (!Object.isString(options.style)) this.style = $H(options.style);
    else {
      if (options.style.include(':'))
        this.style = options.style.parseStyle();
      else {
        this.element.addClassName(options.style);
        this.style = $H(this.element.getStyles());
        this.element.removeClassName(options.style);
        var css = this.element.getStyles();
        this.style = this.style.reject(function(style) {
          return style.value == css[style.key];
        });
        options.afterFinishInternal = function(effect) {
          effect.element.addClassName(effect.options.style);
          effect.transforms.each(function(transform) {
            effect.element.style[transform.style] = '';
          });
        };
      }
    }
    this.start(options);
  },

  setup: function(){
    function parseColor(color){
      if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
      color = color.parseColor();
      return $R(0,2).map(function(i){
        return parseInt( color.slice(i*2+1,i*2+3), 16 );
      });
    }
    this.transforms = this.style.map(function(pair){
      var property = pair[0], value = pair[1], unit = null;

      if (value.parseColor('#zzzzzz') != '#zzzzzz') {
        value = value.parseColor();
        unit  = 'color';
      } else if (property == 'opacity') {
        value = parseFloat(value);
        if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
          this.element.setStyle({zoom: 1});
      } else if (Element.CSS_LENGTH.test(value)) {
          var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/);
          value = parseFloat(components[1]);
          unit = (components.length == 3) ? components[2] : null;
      }

      var originalValue = this.element.getStyle(property);
      return {
        style: property.camelize(),
        originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0),
        targetValue: unit=='color' ? parseColor(value) : value,
        unit: unit
      };
    }.bind(this)).reject(function(transform){
      return (
        (transform.originalValue == transform.targetValue) ||
        (
          transform.unit != 'color' &&
          (isNaN(transform.originalValue) || isNaN(transform.targetValue))
        )
      );
    });
  },
  update: function(position) {
    var style = { }, transform, i = this.transforms.length;
    while(i--)
      style[(transform = this.transforms[i]).style] =
        transform.unit=='color' ? '#'+
          (Math.round(transform.originalValue[0]+
            (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
          (Math.round(transform.originalValue[1]+
            (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +
          (Math.round(transform.originalValue[2]+
            (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
        (transform.originalValue +
          (transform.targetValue - transform.originalValue) * position).toFixed(3) +
            (transform.unit === null ? '' : transform.unit);
    this.element.setStyle(style, true);
  }
});

Effect.Transform = Class.create({
  initialize: function(tracks){
    this.tracks  = [];
    this.options = arguments[1] || { };
    this.addTracks(tracks);
  },
  addTracks: function(tracks){
    tracks.each(function(track){
      track = $H(track);
      var data = track.values().first();
      this.tracks.push($H({
        ids:     track.keys().first(),
        effect:  Effect.Morph,
        options: { style: data }
      }));
    }.bind(this));
    return this;
  },
  play: function(){
    return new Effect.Parallel(
      this.tracks.map(function(track){
        var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options');
        var elements = [$(ids) || $$(ids)].flatten();
        return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) });
      }).flatten(),
      this.options
    );
  }
});

Element.CSS_PROPERTIES = $w(
  'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' +
  'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
  'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
  'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
  'fontSize fontWeight height left letterSpacing lineHeight ' +
  'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
  'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
  'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
  'right textIndent top width wordSpacing zIndex');

Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;

String.__parseStyleElement = document.createElement('div');
String.prototype.parseStyle = function(){
  var style, styleRules = $H();
  if (Prototype.Browser.WebKit)
    style = new Element('div',{style:this}).style;
  else {
    String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>';
    style = String.__parseStyleElement.childNodes[0].style;
  }

  Element.CSS_PROPERTIES.each(function(property){
    if (style[property]) styleRules.set(property, style[property]);
  });

  if (Prototype.Browser.IE && this.include('opacity'))
    styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]);

  return styleRules;
};

if (document.defaultView && document.defaultView.getComputedStyle) {
  Element.getStyles = function(element) {
    var css = document.defaultView.getComputedStyle($(element), null);
    return Element.CSS_PROPERTIES.inject({ }, function(styles, property) {
      styles[property] = css[property];
      return styles;
    });
  };
} else {
  Element.getStyles = function(element) {
    element = $(element);
    var css = element.currentStyle, styles;
    styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) {
      results[property] = css[property];
      return results;
    });
    if (!styles.opacity) styles.opacity = element.getOpacity();
    return styles;
  };
}

Effect.Methods = {
  morph: function(element, style) {
    element = $(element);
    new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { }));
    return element;
  },
  visualEffect: function(element, effect, options) {
    element = $(element);
    var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);
    new Effect[klass](element, options);
    return element;
  },
  highlight: function(element, options) {
    element = $(element);
    new Effect.Highlight(element, options);
    return element;
  }
};

$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
  'pulsate shake puff squish switchOff dropOut').each(
  function(effect) {
    Effect.Methods[effect] = function(element, options){
      element = $(element);
      Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);
      return element;
    };
  }
);

$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each(
  function(f) { Effect.Methods[f] = Element[f]; }
);

Element.addMethods(Effect.Methods);
// script.aculo.us dragdrop.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010

// Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/

if(Object.isUndefined(Effect))
  throw("dragdrop.js requires including script.aculo.us' effects.js library");

var Droppables = {
  drops: [],

  remove: function(element) {
    this.drops = this.drops.reject(function(d) { return d.element==$(element) });
  },

  add: function(element) {
    element = $(element);
    var options = Object.extend({
      greedy:     true,
      hoverclass: null,
      tree:       false
    }, arguments[1] || { });

    // cache containers
    if(options.containment) {
      options._containers = [];
      var containment = options.containment;
      if(Object.isArray(containment)) {
        containment.each( function(c) { options._containers.push($(c)) });
      } else {
        options._containers.push($(containment));
      }
    }

    if(options.accept) options.accept = [options.accept].flatten();

    Element.makePositioned(element); // fix IE
    options.element = element;

    this.drops.push(options);
  },

  findDeepestChild: function(drops) {
    deepest = drops[0];

    for (i = 1; i < drops.length; ++i)
      if (Element.isParent(drops[i].element, deepest.element))
        deepest = drops[i];

    return deepest;
  },

  isContained: function(element, drop) {
    var containmentNode;
    if(drop.tree) {
      containmentNode = element.treeNode;
    } else {
      containmentNode = element.parentNode;
    }
    return drop._containers.detect(function(c) { return containmentNode == c });
  },

  isAffected: function(point, element, drop) {
    return (
      (drop.element!=element) &&
      ((!drop._containers) ||
        this.isContained(element, drop)) &&
      ((!drop.accept) ||
        (Element.classNames(element).detect(
          function(v) { return drop.accept.include(v) } ) )) &&
      Position.within(drop.element, point[0], point[1]) );
  },

  deactivate: function(drop) {
    if(drop.hoverclass)
      Element.removeClassName(drop.element, drop.hoverclass);
    this.last_active = null;
  },

  activate: function(drop) {
    if(drop.hoverclass)
      Element.addClassName(drop.element, drop.hoverclass);
    this.last_active = drop;
  },

  show: function(point, element) {
    if(!this.drops.length) return;
    var drop, affected = [];

    this.drops.each( function(drop) {
      if(Droppables.isAffected(point, element, drop))
        affected.push(drop);
    });

    if(affected.length>0)
      drop = Droppables.findDeepestChild(affected);

    if(this.last_active && this.last_active != drop) this.deactivate(this.last_active);
    if (drop) {
      Position.within(drop.element, point[0], point[1]);
      if(drop.onHover)
        drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));

      if (drop != this.last_active) Droppables.activate(drop);
    }
  },

  fire: function(event, element) {
    if(!this.last_active) return;
    Position.prepare();

    if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
      if (this.last_active.onDrop) {
        this.last_active.onDrop(element, this.last_active.element, event);
        return true;
      }
  },

  reset: function() {
    if(this.last_active)
      this.deactivate(this.last_active);
  }
};

var Draggables = {
  drags: [],
  observers: [],

  register: function(draggable) {
    if(this.drags.length == 0) {
      this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
      this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
      this.eventKeypress  = this.keyPress.bindAsEventListener(this);

      Event.observe(document, "mouseup", this.eventMouseUp);
      Event.observe(document, "mousemove", this.eventMouseMove);
      Event.observe(document, "keypress", this.eventKeypress);
    }
    this.drags.push(draggable);
  },

  unregister: function(draggable) {
    this.drags = this.drags.reject(function(d) { return d==draggable });
    if(this.drags.length == 0) {
      Event.stopObserving(document, "mouseup", this.eventMouseUp);
      Event.stopObserving(document, "mousemove", this.eventMouseMove);
      Event.stopObserving(document, "keypress", this.eventKeypress);
    }
  },

  activate: function(draggable) {
    if(draggable.options.delay) {
      this._timeout = setTimeout(function() {
        Draggables._timeout = null;
        window.focus();
        Draggables.activeDraggable = draggable;
      }.bind(this), draggable.options.delay);
    } else {
      window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
      this.activeDraggable = draggable;
    }
  },

  deactivate: function() {
    this.activeDraggable = null;
  },

  updateDrag: function(event) {
    if(!this.activeDraggable) return;
    var pointer = [Event.pointerX(event), Event.pointerY(event)];
    // Mozilla-based browsers fire successive mousemove events with
    // the same coordinates, prevent needless redrawing (moz bug?)
    if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
    this._lastPointer = pointer;

    this.activeDraggable.updateDrag(event, pointer);
  },

  endDrag: function(event) {
    if(this._timeout) {
      clearTimeout(this._timeout);
      this._timeout = null;
    }
    if(!this.activeDraggable) return;
    this._lastPointer = null;
    this.activeDraggable.endDrag(event);
    this.activeDraggable = null;
  },

  keyPress: function(event) {
    if(this.activeDraggable)
      this.activeDraggable.keyPress(event);
  },

  addObserver: function(observer) {
    this.observers.push(observer);
    this._cacheObserverCallbacks();
  },

  removeObserver: function(element) {  // element instead of observer fixes mem leaks
    this.observers = this.observers.reject( function(o) { return o.element==element });
    this._cacheObserverCallbacks();
  },

  notify: function(eventName, draggable, event) {  // 'onStart', 'onEnd', 'onDrag'
    if(this[eventName+'Count'] > 0)
      this.observers.each( function(o) {
        if(o[eventName]) o[eventName](eventName, draggable, event);
      });
    if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
  },

  _cacheObserverCallbacks: function() {
    ['onStart','onEnd','onDrag'].each( function(eventName) {
      Draggables[eventName+'Count'] = Draggables.observers.select(
        function(o) { return o[eventName]; }
      ).length;
    });
  }
};

/*--------------------------------------------------------------------------*/

var Draggable = Class.create({
  initialize: function(element) {
    var defaults = {
      handle: false,
      reverteffect: function(element, top_offset, left_offset) {
        var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
        new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
          queue: {scope:'_draggable', position:'end'}
        });
      },
      endeffect: function(element) {
        var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0;
        new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
          queue: {scope:'_draggable', position:'end'},
          afterFinish: function(){
            Draggable._dragging[element] = false
          }
        });
      },
      zindex: 1000,
      revert: false,
      quiet: false,
      scroll: false,
      scrollSensitivity: 20,
      scrollSpeed: 15,
      snap: false,  // false, or xy or [x,y] or function(x,y){ return [x,y] }
      delay: 0
    };

    if(!arguments[1] || Object.isUndefined(arguments[1].endeffect))
      Object.extend(defaults, {
        starteffect: function(element) {
          element._opacity = Element.getOpacity(element);
          Draggable._dragging[element] = true;
          new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
        }
      });

    var options = Object.extend(defaults, arguments[1] || { });

    this.element = $(element);

    if(options.handle && Object.isString(options.handle))
      this.handle = this.element.down('.'+options.handle, 0);

    if(!this.handle) this.handle = $(options.handle);
    if(!this.handle) this.handle = this.element;

    if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
      options.scroll = $(options.scroll);
      this._isScrollChild = Element.childOf(this.element, options.scroll);
    }

    Element.makePositioned(this.element); // fix IE

    this.options  = options;
    this.dragging = false;

    this.eventMouseDown = this.initDrag.bindAsEventListener(this);
    Event.observe(this.handle, "mousedown", this.eventMouseDown);

    Draggables.register(this);
  },

  destroy: function() {
    Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
    Draggables.unregister(this);
  },

  currentDelta: function() {
    return([
      parseInt(Element.getStyle(this.element,'left') || '0'),
      parseInt(Element.getStyle(this.element,'top') || '0')]);
  },

  initDrag: function(event) {
    if(!Object.isUndefined(Draggable._dragging[this.element]) &&
      Draggable._dragging[this.element]) return;
    if(Event.isLeftClick(event)) {
      // abort on form elements, fixes a Firefox issue
      var src = Event.element(event);
      if((tag_name = src.tagName.toUpperCase()) && (
        tag_name=='INPUT' ||
        tag_name=='SELECT' ||
        tag_name=='OPTION' ||
        tag_name=='BUTTON' ||
        tag_name=='TEXTAREA')) return;

      var pointer = [Event.pointerX(event), Event.pointerY(event)];
      var pos     = this.element.cumulativeOffset();
      this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });

      Draggables.activate(this);
      Event.stop(event);
    }
  },

  startDrag: function(event) {
    this.dragging = true;
    if(!this.delta)
      this.delta = this.currentDelta();

    if(this.options.zindex) {
      this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
      this.element.style.zIndex = this.options.zindex;
    }

    if(this.options.ghosting) {
      this._clone = this.element.cloneNode(true);
      this._originallyAbsolute = (this.element.getStyle('position') == 'absolute');
      if (!this._originallyAbsolute)
        Position.absolutize(this.element);
      this.element.parentNode.insertBefore(this._clone, this.element);
    }

    if(this.options.scroll) {
      if (this.options.scroll == window) {
        var where = this._getWindowScroll(this.options.scroll);
        this.originalScrollLeft = where.left;
        this.originalScrollTop = where.top;
      } else {
        this.originalScrollLeft = this.options.scroll.scrollLeft;
        this.originalScrollTop = this.options.scroll.scrollTop;
      }
    }

    Draggables.notify('onStart', this, event);

    if(this.options.starteffect) this.options.starteffect(this.element);
  },

  updateDrag: function(event, pointer) {
    if(!this.dragging) this.startDrag(event);

    if(!this.options.quiet){
      Position.prepare();
      Droppables.show(pointer, this.element);
    }

    Draggables.notify('onDrag', this, event);

    this.draw(pointer);
    if(this.options.change) this.options.change(this);

    if(this.options.scroll) {
      this.stopScrolling();

      var p;
      if (this.options.scroll == window) {
        with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
      } else {
        p = Position.page(this.options.scroll).toArray();
        p[0] += this.options.scroll.scrollLeft + Position.deltaX;
        p[1] += this.options.scroll.scrollTop + Position.deltaY;
        p.push(p[0]+this.options.scroll.offsetWidth);
        p.push(p[1]+this.options.scroll.offsetHeight);
      }
      var speed = [0,0];
      if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
      if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
      if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
      if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
      this.startScrolling(speed);
    }

    // fix AppleWebKit rendering
    if(Prototype.Browser.WebKit) window.scrollBy(0,0);

    Event.stop(event);
  },

  finishDrag: function(event, success) {
    this.dragging = false;

    if(this.options.quiet){
      Position.prepare();
      var pointer = [Event.pointerX(event), Event.pointerY(event)];
      Droppables.show(pointer, this.element);
    }

    if(this.options.ghosting) {
      if (!this._originallyAbsolute)
        Position.relativize(this.element);
      delete this._originallyAbsolute;
      Element.remove(this._clone);
      this._clone = null;
    }

    var dropped = false;
    if(success) {
      dropped = Droppables.fire(event, this.element);
      if (!dropped) dropped = false;
    }
    if(dropped && this.options.onDropped) this.options.onDropped(this.element);
    Draggables.notify('onEnd', this, event);

    var revert = this.options.revert;
    if(revert && Object.isFunction(revert)) revert = revert(this.element);

    var d = this.currentDelta();
    if(revert && this.options.reverteffect) {
      if (dropped == 0 || revert != 'failure')
        this.options.reverteffect(this.element,
          d[1]-this.delta[1], d[0]-this.delta[0]);
    } else {
      this.delta = d;
    }

    if(this.options.zindex)
      this.element.style.zIndex = this.originalZ;

    if(this.options.endeffect)
      this.options.endeffect(this.element);

    Draggables.deactivate(this);
    Droppables.reset();
  },

  keyPress: function(event) {
    if(event.keyCode!=Event.KEY_ESC) return;
    this.finishDrag(event, false);
    Event.stop(event);
  },

  endDrag: function(event) {
    if(!this.dragging) return;
    this.stopScrolling();
    this.finishDrag(event, true);
    Event.stop(event);
  },

  draw: function(point) {
    var pos = this.element.cumulativeOffset();
    if(this.options.ghosting) {
      var r   = Position.realOffset(this.element);
      pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
    }

    var d = this.currentDelta();
    pos[0] -= d[0]; pos[1] -= d[1];

    if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
      pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
      pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
    }

    var p = [0,1].map(function(i){
      return (point[i]-pos[i]-this.offset[i])
    }.bind(this));

    if(this.options.snap) {
      if(Object.isFunction(this.options.snap)) {
        p = this.options.snap(p[0],p[1],this);
      } else {
      if(Object.isArray(this.options.snap)) {
        p = p.map( function(v, i) {
          return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this));
      } else {
        p = p.map( function(v) {
          return (v/this.options.snap).round()*this.options.snap }.bind(this));
      }
    }}

    var style = this.element.style;
    if((!this.options.constraint) || (this.options.constraint=='horizontal'))
      style.left = p[0] + "px";
    if((!this.options.constraint) || (this.options.constraint=='vertical'))
      style.top  = p[1] + "px";

    if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
  },

  stopScrolling: function() {
    if(this.scrollInterval) {
      clearInterval(this.scrollInterval);
      this.scrollInterval = null;
      Draggables._lastScrollPointer = null;
    }
  },

  startScrolling: function(speed) {
    if(!(speed[0] || speed[1])) return;
    this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
    this.lastScrolled = new Date();
    this.scrollInterval = setInterval(this.scroll.bind(this), 10);
  },

  scroll: function() {
    var current = new Date();
    var delta = current - this.lastScrolled;
    this.lastScrolled = current;
    if(this.options.scroll == window) {
      with (this._getWindowScroll(this.options.scroll)) {
        if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
          var d = delta / 1000;
          this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
        }
      }
    } else {
      this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
      this.options.scroll.scrollTop  += this.scrollSpeed[1] * delta / 1000;
    }

    Position.prepare();
    Droppables.show(Draggables._lastPointer, this.element);
    Draggables.notify('onDrag', this);
    if (this._isScrollChild) {
      Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
      Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
      Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
      if (Draggables._lastScrollPointer[0] < 0)
        Draggables._lastScrollPointer[0] = 0;
      if (Draggables._lastScrollPointer[1] < 0)
        Draggables._lastScrollPointer[1] = 0;
      this.draw(Draggables._lastScrollPointer);
    }

    if(this.options.change) this.options.change(this);
  },

  _getWindowScroll: function(w) {
    var T, L, W, H;
    with (w.document) {
      if (w.document.documentElement && documentElement.scrollTop) {
        T = documentElement.scrollTop;
        L = documentElement.scrollLeft;
      } else if (w.document.body) {
        T = body.scrollTop;
        L = body.scrollLeft;
      }
      if (w.innerWidth) {
        W = w.innerWidth;
        H = w.innerHeight;
      } else if (w.document.documentElement && documentElement.clientWidth) {
        W = documentElement.clientWidth;
        H = documentElement.clientHeight;
      } else {
        W = body.offsetWidth;
        H = body.offsetHeight;
      }
    }
    return { top: T, left: L, width: W, height: H };
  }
});

Draggable._dragging = { };

/*--------------------------------------------------------------------------*/

var SortableObserver = Class.create({
  initialize: function(element, observer) {
    this.element   = $(element);
    this.observer  = observer;
    this.lastValue = Sortable.serialize(this.element);
  },

  onStart: function() {
    this.lastValue = Sortable.serialize(this.element);
  },

  onEnd: function() {
    Sortable.unmark();
    if(this.lastValue != Sortable.serialize(this.element))
      this.observer(this.element)
  }
});

var Sortable = {
  SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,

  sortables: { },

  _findRootElement: function(element) {
    while (element.tagName.toUpperCase() != "BODY") {
      if(element.id && Sortable.sortables[element.id]) return element;
      element = element.parentNode;
    }
  },

  options: function(element) {
    element = Sortable._findRootElement($(element));
    if(!element) return;
    return Sortable.sortables[element.id];
  },

  destroy: function(element){
    element = $(element);
    var s = Sortable.sortables[element.id];

    if(s) {
      Draggables.removeObserver(s.element);
      s.droppables.each(function(d){ Droppables.remove(d) });
      s.draggables.invoke('destroy');

      delete Sortable.sortables[s.element.id];
    }
  },

  create: function(element) {
    element = $(element);
    var options = Object.extend({
      element:     element,
      tag:         'li',       // assumes li children, override with tag: 'tagname'
      dropOnEmpty: false,
      tree:        false,
      treeTag:     'ul',
      overlap:     'vertical', // one of 'vertical', 'horizontal'
      constraint:  'vertical', // one of 'vertical', 'horizontal', false
      containment: element,    // also takes array of elements (or id's); or false
      handle:      false,      // or a CSS class
      only:        false,
      delay:       0,
      hoverclass:  null,
      ghosting:    false,
      quiet:       false,
      scroll:      false,
      scrollSensitivity: 20,
      scrollSpeed: 15,
      format:      this.SERIALIZE_RULE,

      // these take arrays of elements or ids and can be
      // used for better initialization performance
      elements:    false,
      handles:     false,

      onChange:    Prototype.emptyFunction,
      onUpdate:    Prototype.emptyFunction
    }, arguments[1] || { });

    // clear any old sortable with same element
    this.destroy(element);

    // build options for the draggables
    var options_for_draggable = {
      revert:      true,
      quiet:       options.quiet,
      scroll:      options.scroll,
      scrollSpeed: options.scrollSpeed,
      scrollSensitivity: options.scrollSensitivity,
      delay:       options.delay,
      ghosting:    options.ghosting,
      constraint:  options.constraint,
      handle:      options.handle };

    if(options.starteffect)
      options_for_draggable.starteffect = options.starteffect;

    if(options.reverteffect)
      options_for_draggable.reverteffect = options.reverteffect;
    else
      if(options.ghosting) options_for_draggable.reverteffect = function(element) {
        element.style.top  = 0;
        element.style.left = 0;
      };

    if(options.endeffect)
      options_for_draggable.endeffect = options.endeffect;

    if(options.zindex)
      options_for_draggable.zindex = options.zindex;

    // build options for the droppables
    var options_for_droppable = {
      overlap:     options.overlap,
      containment: options.containment,
      tree:        options.tree,
      hoverclass:  options.hoverclass,
      onHover:     Sortable.onHover
    };

    var options_for_tree = {
      onHover:      Sortable.onEmptyHover,
      overlap:      options.overlap,
      containment:  options.containment,
      hoverclass:   options.hoverclass
    };

    // fix for gecko engine
    Element.cleanWhitespace(element);

    options.draggables = [];
    options.droppables = [];

    // drop on empty handling
    if(options.dropOnEmpty || options.tree) {
      Droppables.add(element, options_for_tree);
      options.droppables.push(element);
    }

    (options.elements || this.findElements(element, options) || []).each( function(e,i) {
      var handle = options.handles ? $(options.handles[i]) :
        (options.handle ? $(e).select('.' + options.handle)[0] : e);
      options.draggables.push(
        new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
      Droppables.add(e, options_for_droppable);
      if(options.tree) e.treeNode = element;
      options.droppables.push(e);
    });

    if(options.tree) {
      (Sortable.findTreeElements(element, options) || []).each( function(e) {
        Droppables.add(e, options_for_tree);
        e.treeNode = element;
        options.droppables.push(e);
      });
    }

    // keep reference
    this.sortables[element.identify()] = options;

    // for onupdate
    Draggables.addObserver(new SortableObserver(element, options.onUpdate));

  },

  // return all suitable-for-sortable elements in a guaranteed order
  findElements: function(element, options) {
    return Element.findChildren(
      element, options.only, options.tree ? true : false, options.tag);
  },

  findTreeElements: function(element, options) {
    return Element.findChildren(
      element, options.only, options.tree ? true : false, options.treeTag);
  },

  onHover: function(element, dropon, overlap) {
    if(Element.isParent(dropon, element)) return;

    if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
      return;
    } else if(overlap>0.5) {
      Sortable.mark(dropon, 'before');
      if(dropon.previousSibling != element) {
        var oldParentNode = element.parentNode;
        element.style.visibility = "hidden"; // fix gecko rendering
        dropon.parentNode.insertBefore(element, dropon);
        if(dropon.parentNode!=oldParentNode)
          Sortable.options(oldParentNode).onChange(element);
        Sortable.options(dropon.parentNode).onChange(element);
      }
    } else {
      Sortable.mark(dropon, 'after');
      var nextElement = dropon.nextSibling || null;
      if(nextElement != element) {
        var oldParentNode = element.parentNode;
        element.style.visibility = "hidden"; // fix gecko rendering
        dropon.parentNode.insertBefore(element, nextElement);
        if(dropon.parentNode!=oldParentNode)
          Sortable.options(oldParentNode).onChange(element);
        Sortable.options(dropon.parentNode).onChange(element);
      }
    }
  },

  onEmptyHover: function(element, dropon, overlap) {
    var oldParentNode = element.parentNode;
    var droponOptions = Sortable.options(dropon);

    if(!Element.isParent(dropon, element)) {
      var index;

      var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
      var child = null;

      if(children) {
        var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);

        for (index = 0; index < children.length; index += 1) {
          if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
            offset -= Element.offsetSize (children[index], droponOptions.overlap);
          } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
            child = index + 1 < children.length ? children[index + 1] : null;
            break;
          } else {
            child = children[index];
            break;
          }
        }
      }

      dropon.insertBefore(element, child);

      Sortable.options(oldParentNode).onChange(element);
      droponOptions.onChange(element);
    }
  },

  unmark: function() {
    if(Sortable._marker) Sortable._marker.hide();
  },

  mark: function(dropon, position) {
    // mark on ghosting only
    var sortable = Sortable.options(dropon.parentNode);
    if(sortable && !sortable.ghosting) return;

    if(!Sortable._marker) {
      Sortable._marker =
        ($('dropmarker') || Element.extend(document.createElement('DIV'))).
          hide().addClassName('dropmarker').setStyle({position:'absolute'});
      document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
    }
    var offsets = dropon.cumulativeOffset();
    Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});

    if(position=='after')
      if(sortable.overlap == 'horizontal')
        Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
      else
        Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});

    Sortable._marker.show();
  },

  _tree: function(element, options, parent) {
    var children = Sortable.findElements(element, options) || [];

    for (var i = 0; i < children.length; ++i) {
      var match = children[i].id.match(options.format);

      if (!match) continue;

      var child = {
        id: encodeURIComponent(match ? match[1] : null),
        element: element,
        parent: parent,
        children: [],
        position: parent.children.length,
        container: $(children[i]).down(options.treeTag)
      };

      /* Get the element containing the children and recurse over it */
      if (child.container)
        this._tree(child.container, options, child);

      parent.children.push (child);
    }

    return parent;
  },

  tree: function(element) {
    element = $(element);
    var sortableOptions = this.options(element);
    var options = Object.extend({
      tag: sortableOptions.tag,
      treeTag: sortableOptions.treeTag,
      only: sortableOptions.only,
      name: element.id,
      format: sortableOptions.format
    }, arguments[1] || { });

    var root = {
      id: null,
      parent: null,
      children: [],
      container: element,
      position: 0
    };

    return Sortable._tree(element, options, root);
  },

  /* Construct a [i] index for a particular node */
  _constructIndex: function(node) {
    var index = '';
    do {
      if (node.id) index = '[' + node.position + ']' + index;
    } while ((node = node.parent) != null);
    return index;
  },

  sequence: function(element) {
    element = $(element);
    var options = Object.extend(this.options(element), arguments[1] || { });

    return $(this.findElements(element, options) || []).map( function(item) {
      return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
    });
  },

  setSequence: function(element, new_sequence) {
    element = $(element);
    var options = Object.extend(this.options(element), arguments[2] || { });

    var nodeMap = { };
    this.findElements(element, options).each( function(n) {
        if (n.id.match(options.format))
            nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
        n.parentNode.removeChild(n);
    });

    new_sequence.each(function(ident) {
      var n = nodeMap[ident];
      if (n) {
        n[1].appendChild(n[0]);
        delete nodeMap[ident];
      }
    });
  },

  serialize: function(element) {
    element = $(element);
    var options = Object.extend(Sortable.options(element), arguments[1] || { });
    var name = encodeURIComponent(
      (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);

    if (options.tree) {
      return Sortable.tree(element, arguments[1]).children.map( function (item) {
        return [name + Sortable._constructIndex(item) + "[id]=" +
                encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
      }).flatten().join('&');
    } else {
      return Sortable.sequence(element, arguments[1]).map( function(item) {
        return name + "[]=" + encodeURIComponent(item);
      }).join('&');
    }
  }
};

// Returns true if child is contained within element
Element.isParent = function(child, element) {
  if (!child.parentNode || child == element) return false;
  if (child.parentNode == element) return true;
  return Element.isParent(child.parentNode, element);
};

Element.findChildren = function(element, only, recursive, tagName) {
  if(!element.hasChildNodes()) return null;
  tagName = tagName.toUpperCase();
  if(only) only = [only].flatten();
  var elements = [];
  $A(element.childNodes).each( function(e) {
    if(e.tagName && e.tagName.toUpperCase()==tagName &&
      (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
        elements.push(e);
    if(recursive) {
      var grandchildren = Element.findChildren(e, only, recursive, tagName);
      if(grandchildren) elements.push(grandchildren);
    }
  });

  return (elements.length>0 ? elements.flatten() : []);
};

Element.offsetSize = function (element, type) {
  return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
};
// script.aculo.us controls.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010

// Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//           (c) 2005-2010 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
//           (c) 2005-2010 Jon Tirsen (http://www.tirsen.com)
// Contributors:
//  Richard Livsey
//  Rahul Bhargava
//  Rob Wills
//
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/

// Autocompleter.Base handles all the autocompletion functionality
// that's independent of the data source for autocompletion. This
// includes drawing the autocompletion menu, observing keyboard
// and mouse events, and similar.
//
// Specific autocompleters need to provide, at the very least,
// a getUpdatedChoices function that will be invoked every time
// the text inside the monitored textbox changes. This method
// should get the text for which to provide autocompletion by
// invoking this.getToken(), NOT by directly accessing
// this.element.value. This is to allow incremental tokenized
// autocompletion. Specific auto-completion logic (AJAX, etc)
// belongs in getUpdatedChoices.
//
// Tokenized incremental autocompletion is enabled automatically
// when an autocompleter is instantiated with the 'tokens' option
// in the options parameter, e.g.:
// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
// will incrementally autocomplete with a comma as the token.
// Additionally, ',' in the above example can be replaced with
// a token array, e.g. { tokens: [',', '\n'] } which
// enables autocompletion on multiple tokens. This is most
// useful when one of the tokens is \n (a newline), as it
// allows smart autocompletion after linebreaks.

if(typeof Effect == 'undefined')
  throw("controls.js requires including script.aculo.us' effects.js library");

var Autocompleter = { };
Autocompleter.Base = Class.create({
  baseInitialize: function(element, update, options) {
    element          = $(element);
    this.element     = element;
    this.update      = $(update);
    this.hasFocus    = false;
    this.changed     = false;
    this.active      = false;
    this.index       = 0;
    this.entryCount  = 0;
    this.oldElementValue = this.element.value;

    if(this.setOptions)
      this.setOptions(options);
    else
      this.options = options || { };

    this.options.paramName    = this.options.paramName || this.element.name;
    this.options.tokens       = this.options.tokens || [];
    this.options.frequency    = this.options.frequency || 0.4;
    this.options.minChars     = this.options.minChars || 1;
    this.options.onShow       = this.options.onShow ||
      function(element, update){
        if(!update.style.position || update.style.position=='absolute') {
          update.style.position = 'absolute';
          Position.clone(element, update, {
            setHeight: false,
            offsetTop: element.offsetHeight
          });
        }
        Effect.Appear(update,{duration:0.15});
      };
    this.options.onHide = this.options.onHide ||
      function(element, update){ new Effect.Fade(update,{duration:0.15}) };

    if(typeof(this.options.tokens) == 'string')
      this.options.tokens = new Array(this.options.tokens);
    // Force carriage returns as token delimiters anyway
    if (!this.options.tokens.include('\n'))
      this.options.tokens.push('\n');

    this.observer = null;

    this.element.setAttribute('autocomplete','off');

    Element.hide(this.update);

    Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
    Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
  },

  show: function() {
    if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
    if(!this.iefix &&
      (Prototype.Browser.IE) &&
      (Element.getStyle(this.update, 'position')=='absolute')) {
      new Insertion.After(this.update,
       '<iframe id="' + this.update.id + '_iefix" '+
       'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
       'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
      this.iefix = $(this.update.id+'_iefix');
    }
    if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
  },

  fixIEOverlapping: function() {
    Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
    this.iefix.style.zIndex = 1;
    this.update.style.zIndex = 2;
    Element.show(this.iefix);
  },

  hide: function() {
    this.stopIndicator();
    if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
    if(this.iefix) Element.hide(this.iefix);
  },

  startIndicator: function() {
    if(this.options.indicator) Element.show(this.options.indicator);
  },

  stopIndicator: function() {
    if(this.options.indicator) Element.hide(this.options.indicator);
  },

  onKeyPress: function(event) {
    if(this.active)
      switch(event.keyCode) {
       case Event.KEY_TAB:
       case Event.KEY_RETURN:
         this.selectEntry();
         Event.stop(event);
       case Event.KEY_ESC:
         this.hide();
         this.active = false;
         Event.stop(event);
         return;
       case Event.KEY_LEFT:
       case Event.KEY_RIGHT:
         return;
       case Event.KEY_UP:
         this.markPrevious();
         this.render();
         Event.stop(event);
         return;
       case Event.KEY_DOWN:
         this.markNext();
         this.render();
         Event.stop(event);
         return;
      }
     else
       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
         (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;

    this.changed = true;
    this.hasFocus = true;

    if(this.observer) clearTimeout(this.observer);
      this.observer =
        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
  },

  activate: function() {
    this.changed = false;
    this.hasFocus = true;
    this.getUpdatedChoices();
  },

  onHover: function(event) {
    var element = Event.findElement(event, 'LI');
    if(this.index != element.autocompleteIndex)
    {
        this.index = element.autocompleteIndex;
        this.render();
    }
    Event.stop(event);
  },

  onClick: function(event) {
    var element = Event.findElement(event, 'LI');
    this.index = element.autocompleteIndex;
    this.selectEntry();
    this.hide();
  },

  onBlur: function(event) {
    // needed to make click events working
    setTimeout(this.hide.bind(this), 250);
    this.hasFocus = false;
    this.active = false;
  },

  render: function() {
    if(this.entryCount > 0) {
      for (var i = 0; i < this.entryCount; i++)
        this.index==i ?
          Element.addClassName(this.getEntry(i),"selected") :
          Element.removeClassName(this.getEntry(i),"selected");
      if(this.hasFocus) {
        this.show();
        this.active = true;
      }
    } else {
      this.active = false;
      this.hide();
    }
  },

  markPrevious: function() {
    if(this.index > 0) this.index--;
      else this.index = this.entryCount-1;
    this.getEntry(this.index).scrollIntoView(true);
  },

  markNext: function() {
    if(this.index < this.entryCount-1) this.index++;
      else this.index = 0;
    this.getEntry(this.index).scrollIntoView(false);
  },

  getEntry: function(index) {
    return this.update.firstChild.childNodes[index];
  },

  getCurrentEntry: function() {
    return this.getEntry(this.index);
  },

  selectEntry: function() {
    this.active = false;
    this.updateElement(this.getCurrentEntry());
  },

  updateElement: function(selectedElement) {
    if (this.options.updateElement) {
      this.options.updateElement(selectedElement);
      return;
    }
    var value = '';
    if (this.options.select) {
      var nodes = $(selectedElement).select('.' + this.options.select) || [];
      if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
    } else
      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');

    var bounds = this.getTokenBounds();
    if (bounds[0] != -1) {
      var newValue = this.element.value.substr(0, bounds[0]);
      var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
      if (whitespace)
        newValue += whitespace[0];
      this.element.value = newValue + value + this.element.value.substr(bounds[1]);
    } else {
      this.element.value = value;
    }
    this.oldElementValue = this.element.value;
    this.element.focus();

    if (this.options.afterUpdateElement)
      this.options.afterUpdateElement(this.element, selectedElement);
  },

  updateChoices: function(choices) {
    if(!this.changed && this.hasFocus) {
      this.update.innerHTML = choices;
      Element.cleanWhitespace(this.update);
      Element.cleanWhitespace(this.update.down());

      if(this.update.firstChild && this.update.down().childNodes) {
        this.entryCount =
          this.update.down().childNodes.length;
        for (var i = 0; i < this.entryCount; i++) {
          var entry = this.getEntry(i);
          entry.autocompleteIndex = i;
          this.addObservers(entry);
        }
      } else {
        this.entryCount = 0;
      }

      this.stopIndicator();
      this.index = 0;

      if(this.entryCount==1 && this.options.autoSelect) {
        this.selectEntry();
        this.hide();
      } else {
        this.render();
      }
    }
  },

  addObservers: function(element) {
    Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
    Event.observe(element, "click", this.onClick.bindAsEventListener(this));
  },

  onObserverEvent: function() {
    this.changed = false;
    this.tokenBounds = null;
    if(this.getToken().length>=this.options.minChars) {
      this.getUpdatedChoices();
    } else {
      this.active = false;
      this.hide();
    }
    this.oldElementValue = this.element.value;
  },

  getToken: function() {
    var bounds = this.getTokenBounds();
    return this.element.value.substring(bounds[0], bounds[1]).strip();
  },

  getTokenBounds: function() {
    if (null != this.tokenBounds) return this.tokenBounds;
    var value = this.element.value;
    if (value.strip().empty()) return [-1, 0];
    var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);
    var offset = (diff == this.oldElementValue.length ? 1 : 0);
    var prevTokenPos = -1, nextTokenPos = value.length;
    var tp;
    for (var index = 0, l = this.options.tokens.length; index < l; ++index) {
      tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);
      if (tp > prevTokenPos) prevTokenPos = tp;
      tp = value.indexOf(this.options.tokens[index], diff + offset);
      if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;
    }
    return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);
  }
});

Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
  var boundary = Math.min(newS.length, oldS.length);
  for (var index = 0; index < boundary; ++index)
    if (newS[index] != oldS[index])
      return index;
  return boundary;
};

Ajax.Autocompleter = Class.create(Autocompleter.Base, {
  initialize: function(element, update, url, options) {
    this.baseInitialize(element, update, options);
    this.options.asynchronous  = true;
    this.options.onComplete    = this.onComplete.bind(this);
    this.options.defaultParams = this.options.parameters || null;
    this.url                   = url;
  },

  getUpdatedChoices: function() {
    this.startIndicator();

    var entry = encodeURIComponent(this.options.paramName) + '=' +
      encodeURIComponent(this.getToken());

    this.options.parameters = this.options.callback ?
      this.options.callback(this.element, entry) : entry;

    if(this.options.defaultParams)
      this.options.parameters += '&' + this.options.defaultParams;

    new Ajax.Request(this.url, this.options);
  },

  onComplete: function(request) {
    this.updateChoices(request.responseText);
  }
});

// The local array autocompleter. Used when you'd prefer to
// inject an array of autocompletion options into the page, rather
// than sending out Ajax queries, which can be quite slow sometimes.
//
// The constructor takes four parameters. The first two are, as usual,
// the id of the monitored textbox, and id of the autocompletion menu.
// The third is the array you want to autocomplete from, and the fourth
// is the options block.
//
// Extra local autocompletion options:
// - choices - How many autocompletion choices to offer
//
// - partialSearch - If false, the autocompleter will match entered
//                    text only at the beginning of strings in the
//                    autocomplete array. Defaults to true, which will
//                    match text at the beginning of any *word* in the
//                    strings in the autocomplete array. If you want to
//                    search anywhere in the string, additionally set
//                    the option fullSearch to true (default: off).
//
// - fullSsearch - Search anywhere in autocomplete array strings.
//
// - partialChars - How many characters to enter before triggering
//                   a partial match (unlike minChars, which defines
//                   how many characters are required to do any match
//                   at all). Defaults to 2.
//
// - ignoreCase - Whether to ignore case when autocompleting.
//                 Defaults to true.
//
// It's possible to pass in a custom function as the 'selector'
// option, if you prefer to write your own autocompletion logic.
// In that case, the other options above will not apply unless
// you support them.

Autocompleter.Local = Class.create(Autocompleter.Base, {
  initialize: function(element, update, array, options) {
    this.baseInitialize(element, update, options);
    this.options.array = array;
  },

  getUpdatedChoices: function() {
    this.updateChoices(this.options.selector(this));
  },

  setOptions: function(options) {
    this.options = Object.extend({
      choices: 10,
      partialSearch: true,
      partialChars: 2,
      ignoreCase: true,
      fullSearch: false,
      selector: function(instance) {
        var ret       = []; // Beginning matches
        var partial   = []; // Inside matches
        var entry     = instance.getToken();
        var count     = 0;

        for (var i = 0; i < instance.options.array.length &&
          ret.length < instance.options.choices ; i++) {

          var elem = instance.options.array[i];
          var foundPos = instance.options.ignoreCase ?
            elem.toLowerCase().indexOf(entry.toLowerCase()) :
            elem.indexOf(entry);

          while (foundPos != -1) {
            if (foundPos == 0 && elem.length != entry.length) {
              ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
                elem.substr(entry.length) + "</li>");
              break;
            } else if (entry.length >= instance.options.partialChars &&
              instance.options.partialSearch && foundPos != -1) {
              if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
                partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
                  elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
                  foundPos + entry.length) + "</li>");
                break;
              }
            }

            foundPos = instance.options.ignoreCase ?
              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
              elem.indexOf(entry, foundPos + 1);

          }
        }
        if (partial.length)
          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length));
        return "<ul>" + ret.join('') + "</ul>";
      }
    }, options || { });
  }
});

// AJAX in-place editor and collection editor
// Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).

// Use this if you notice weird scrolling problems on some browsers,
// the DOM might be a bit confused when this gets called so do this
// waits 1 ms (with setTimeout) until it does the activation
Field.scrollFreeActivate = function(field) {
  setTimeout(function() {
    Field.activate(field);
  }, 1);
};

Ajax.InPlaceEditor = Class.create({
  initialize: function(element, url, options) {
    this.url = url;
    this.element = element = $(element);
    this.prepareOptions();
    this._controls = { };
    arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
    Object.extend(this.options, options || { });
    if (!this.options.formId && this.element.id) {
      this.options.formId = this.element.id + '-inplaceeditor';
      if ($(this.options.formId))
        this.options.formId = '';
    }
    if (this.options.externalControl)
      this.options.externalControl = $(this.options.externalControl);
    if (!this.options.externalControl)
      this.options.externalControlOnly = false;
    this._originalBackground = this.element.getStyle('background-color') || 'transparent';
    this.element.title = this.options.clickToEditText;
    this._boundCancelHandler = this.handleFormCancellation.bind(this);
    this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
    this._boundFailureHandler = this.handleAJAXFailure.bind(this);
    this._boundSubmitHandler = this.handleFormSubmission.bind(this);
    this._boundWrapperHandler = this.wrapUp.bind(this);
    this.registerListeners();
  },
  checkForEscapeOrReturn: function(e) {
    if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;
    if (Event.KEY_ESC == e.keyCode)
      this.handleFormCancellation(e);
    else if (Event.KEY_RETURN == e.keyCode)
      this.handleFormSubmission(e);
  },
  createControl: function(mode, handler, extraClasses) {
    var control = this.options[mode + 'Control'];
    var text = this.options[mode + 'Text'];
    if ('button' == control) {
      var btn = document.createElement('input');
      btn.type = 'submit';
      btn.value = text;
      btn.className = 'editor_' + mode + '_button';
      if ('cancel' == mode)
        btn.onclick = this._boundCancelHandler;
      this._form.appendChild(btn);
      this._controls[mode] = btn;
    } else if ('link' == control) {
      var link = document.createElement('a');
      link.href = '#';
      link.appendChild(document.createTextNode(text));
      link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
      link.className = 'editor_' + mode + '_link';
      if (extraClasses)
        link.className += ' ' + extraClasses;
      this._form.appendChild(link);
      this._controls[mode] = link;
    }
  },
  createEditField: function() {
    var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
    var fld;
    if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) {
      fld = document.createElement('input');
      fld.type = 'text';
      var size = this.options.size || this.options.cols || 0;
      if (0 < size) fld.size = size;
    } else {
      fld = document.createElement('textarea');
      fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);
      fld.cols = this.options.cols || 40;
    }
    fld.name = this.options.paramName;
    fld.value = text; // No HTML breaks conversion anymore
    fld.className = 'editor_field';
    if (this.options.submitOnBlur)
      fld.onblur = this._boundSubmitHandler;
    this._controls.editor = fld;
    if (this.options.loadTextURL)
      this.loadExternalText();
    this._form.appendChild(this._controls.editor);
  },
  createForm: function() {
    var ipe = this;
    function addText(mode, condition) {
      var text = ipe.options['text' + mode + 'Controls'];
      if (!text || condition === false) return;
      ipe._form.appendChild(document.createTextNode(text));
    };
    this._form = $(document.createElement('form'));
    this._form.id = this.options.formId;
    this._form.addClassName(this.options.formClassName);
    this._form.onsubmit = this._boundSubmitHandler;
    this.createEditField();
    if ('textarea' == this._controls.editor.tagName.toLowerCase())
      this._form.appendChild(document.createElement('br'));
    if (this.options.onFormCustomization)
      this.options.onFormCustomization(this, this._form);
    addText('Before', this.options.okControl || this.options.cancelControl);
    this.createControl('ok', this._boundSubmitHandler);
    addText('Between', this.options.okControl && this.options.cancelControl);
    this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');
    addText('After', this.options.okControl || this.options.cancelControl);
  },
  destroy: function() {
    if (this._oldInnerHTML)
      this.element.innerHTML = this._oldInnerHTML;
    this.leaveEditMode();
    this.unregisterListeners();
  },
  enterEditMode: function(e) {
    if (this._saving || this._editing) return;
    this._editing = true;
    this.triggerCallback('onEnterEditMode');
    if (this.options.externalControl)
      this.options.externalControl.hide();
    this.element.hide();
    this.createForm();
    this.element.parentNode.insertBefore(this._form, this.element);
    if (!this.options.loadTextURL)
      this.postProcessEditField();
    if (e) Event.stop(e);
  },
  enterHover: function(e) {
    if (this.options.hoverClassName)
      this.element.addClassName(this.options.hoverClassName);
    if (this._saving) return;
    this.triggerCallback('onEnterHover');
  },
  getText: function() {
    return this.element.innerHTML.unescapeHTML();
  },
  handleAJAXFailure: function(transport) {
    this.triggerCallback('onFailure', transport);
    if (this._oldInnerHTML) {
      this.element.innerHTML = this._oldInnerHTML;
      this._oldInnerHTML = null;
    }
  },
  handleFormCancellation: function(e) {
    this.wrapUp();
    if (e) Event.stop(e);
  },
  handleFormSubmission: function(e) {
    var form = this._form;
    var value = $F(this._controls.editor);
    this.prepareSubmission();
    var params = this.options.callback(form, value) || '';
    if (Object.isString(params))
      params = params.toQueryParams();
    params.editorId = this.element.id;
    if (this.options.htmlResponse) {
      var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
      Object.extend(options, {
        parameters: params,
        onComplete: this._boundWrapperHandler,
        onFailure: this._boundFailureHandler
      });
      new Ajax.Updater({ success: this.element }, this.url, options);
    } else {
      var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
      Object.extend(options, {
        parameters: params,
        onComplete: this._boundWrapperHandler,
        onFailure: this._boundFailureHandler
      });
      new Ajax.Request(this.url, options);
    }
    if (e) Event.stop(e);
  },
  leaveEditMode: function() {
    this.element.removeClassName(this.options.savingClassName);
    this.removeForm();
    this.leaveHover();
    this.element.style.backgroundColor = this._originalBackground;
    this.element.show();
    if (this.options.externalControl)
      this.options.externalControl.show();
    this._saving = false;
    this._editing = false;
    this._oldInnerHTML = null;
    this.triggerCallback('onLeaveEditMode');
  },
  leaveHover: function(e) {
    if (this.options.hoverClassName)
      this.element.removeClassName(this.options.hoverClassName);
    if (this._saving) return;
    this.triggerCallback('onLeaveHover');
  },
  loadExternalText: function() {
    this._form.addClassName(this.options.loadingClassName);
    this._controls.editor.disabled = true;
    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
    Object.extend(options, {
      parameters: 'editorId=' + encodeURIComponent(this.element.id),
      onComplete: Prototype.emptyFunction,
      onSuccess: function(transport) {
        this._form.removeClassName(this.options.loadingClassName);
        var text = transport.responseText;
        if (this.options.stripLoadedTextTags)
          text = text.stripTags();
        this._controls.editor.value = text;
        this._controls.editor.disabled = false;
        this.postProcessEditField();
      }.bind(this),
      onFailure: this._boundFailureHandler
    });
    new Ajax.Request(this.options.loadTextURL, options);
  },
  postProcessEditField: function() {
    var fpc = this.options.fieldPostCreation;
    if (fpc)
      $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();
  },
  prepareOptions: function() {
    this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);
    Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);
    [this._extraDefaultOptions].flatten().compact().each(function(defs) {
      Object.extend(this.options, defs);
    }.bind(this));
  },
  prepareSubmission: function() {
    this._saving = true;
    this.removeForm();
    this.leaveHover();
    this.showSaving();
  },
  registerListeners: function() {
    this._listeners = { };
    var listener;
    $H(Ajax.InPlaceEditor.Listeners).each(function(pair) {
      listener = this[pair.value].bind(this);
      this._listeners[pair.key] = listener;
      if (!this.options.externalControlOnly)
        this.element.observe(pair.key, listener);
      if (this.options.externalControl)
        this.options.externalControl.observe(pair.key, listener);
    }.bind(this));
  },
  removeForm: function() {
    if (!this._form) return;
    this._form.remove();
    this._form = null;
    this._controls = { };
  },
  showSaving: function() {
    this._oldInnerHTML = this.element.innerHTML;
    this.element.innerHTML = this.options.savingText;
    this.element.addClassName(this.options.savingClassName);
    this.element.style.backgroundColor = this._originalBackground;
    this.element.show();
  },
  triggerCallback: function(cbName, arg) {
    if ('function' == typeof this.options[cbName]) {
      this.options[cbName](this, arg);
    }
  },
  unregisterListeners: function() {
    $H(this._listeners).each(function(pair) {
      if (!this.options.externalControlOnly)
        this.element.stopObserving(pair.key, pair.value);
      if (this.options.externalControl)
        this.options.externalControl.stopObserving(pair.key, pair.value);
    }.bind(this));
  },
  wrapUp: function(transport) {
    this.leaveEditMode();
    // Can't use triggerCallback due to backward compatibility: requires
    // binding + direct element
    this._boundComplete(transport, this.element);
  }
});

Object.extend(Ajax.InPlaceEditor.prototype, {
  dispose: Ajax.InPlaceEditor.prototype.destroy
});

Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
  initialize: function($super, element, url, options) {
    this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;
    $super(element, url, options);
  },

  createEditField: function() {
    var list = document.createElement('select');
    list.name = this.options.paramName;
    list.size = 1;
    this._controls.editor = list;
    this._collection = this.options.collection || [];
    if (this.options.loadCollectionURL)
      this.loadCollection();
    else
      this.checkForExternalText();
    this._form.appendChild(this._controls.editor);
  },

  loadCollection: function() {
    this._form.addClassName(this.options.loadingClassName);
    this.showLoadingText(this.options.loadingCollectionText);
    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
    Object.extend(options, {
      parameters: 'editorId=' + encodeURIComponent(this.element.id),
      onComplete: Prototype.emptyFunction,
      onSuccess: function(transport) {
        var js = transport.responseText.strip();
        if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
          throw('Server returned an invalid collection representation.');
        this._collection = eval(js);
        this.checkForExternalText();
      }.bind(this),
      onFailure: this.onFailure
    });
    new Ajax.Request(this.options.loadCollectionURL, options);
  },

  showLoadingText: function(text) {
    this._controls.editor.disabled = true;
    var tempOption = this._controls.editor.firstChild;
    if (!tempOption) {
      tempOption = document.createElement('option');
      tempOption.value = '';
      this._controls.editor.appendChild(tempOption);
      tempOption.selected = true;
    }
    tempOption.update((text || '').stripScripts().stripTags());
  },

  checkForExternalText: function() {
    this._text = this.getText();
    if (this.options.loadTextURL)
      this.loadExternalText();
    else
      this.buildOptionList();
  },

  loadExternalText: function() {
    this.showLoadingText(this.options.loadingText);
    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
    Object.extend(options, {
      parameters: 'editorId=' + encodeURIComponent(this.element.id),
      onComplete: Prototype.emptyFunction,
      onSuccess: function(transport) {
        this._text = transport.responseText.strip();
        this.buildOptionList();
      }.bind(this),
      onFailure: this.onFailure
    });
    new Ajax.Request(this.options.loadTextURL, options);
  },

  buildOptionList: function() {
    this._form.removeClassName(this.options.loadingClassName);
    this._collection = this._collection.map(function(entry) {
      return 2 === entry.length ? entry : [entry, entry].flatten();
    });
    var marker = ('value' in this.options) ? this.options.value : this._text;
    var textFound = this._collection.any(function(entry) {
      return entry[0] == marker;
    }.bind(this));
    this._controls.editor.update('');
    var option;
    this._collection.each(function(entry, index) {
      option = document.createElement('option');
      option.value = entry[0];
      option.selected = textFound ? entry[0] == marker : 0 == index;
      option.appendChild(document.createTextNode(entry[1]));
      this._controls.editor.appendChild(option);
    }.bind(this));
    this._controls.editor.disabled = false;
    Field.scrollFreeActivate(this._controls.editor);
  }
});

//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****
//**** This only  exists for a while,  in order to  let ****
//**** users adapt to  the new API.  Read up on the new ****
//**** API and convert your code to it ASAP!            ****

Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {
  if (!options) return;
  function fallback(name, expr) {
    if (name in options || expr === undefined) return;
    options[name] = expr;
  };
  fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :
    options.cancelLink == options.cancelButton == false ? false : undefined)));
  fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :
    options.okLink == options.okButton == false ? false : undefined)));
  fallback('highlightColor', options.highlightcolor);
  fallback('highlightEndColor', options.highlightendcolor);
};

Object.extend(Ajax.InPlaceEditor, {
  DefaultOptions: {
    ajaxOptions: { },
    autoRows: 3,                                // Use when multi-line w/ rows == 1
    cancelControl: 'link',                      // 'link'|'button'|false
    cancelText: 'cancel',
    clickToEditText: 'Click to edit',
    externalControl: null,                      // id|elt
    externalControlOnly: false,
    fieldPostCreation: 'activate',              // 'activate'|'focus'|false
    formClassName: 'inplaceeditor-form',
    formId: null,                               // id|elt
    highlightColor: '#ffff99',
    highlightEndColor: '#ffffff',
    hoverClassName: '',
    htmlResponse: true,
    loadingClassName: 'inplaceeditor-loading',
    loadingText: 'Loading...',
    okControl: 'button',                        // 'link'|'button'|false
    okText: 'ok',
    paramName: 'value',
    rows: 1,                                    // If 1 and multi-line, uses autoRows
    savingClassName: 'inplaceeditor-saving',
    savingText: 'Saving...',
    size: 0,
    stripLoadedTextTags: false,
    submitOnBlur: false,
    textAfterControls: '',
    textBeforeControls: '',
    textBetweenControls: ''
  },
  DefaultCallbacks: {
    callback: function(form) {
      return Form.serialize(form);
    },
    onComplete: function(transport, element) {
      // For backward compatibility, this one is bound to the IPE, and passes
      // the element directly.  It was too often customized, so we don't break it.
      new Effect.Highlight(element, {
        startcolor: this.options.highlightColor, keepBackgroundImage: true });
    },
    onEnterEditMode: null,
    onEnterHover: function(ipe) {
      ipe.element.style.backgroundColor = ipe.options.highlightColor;
      if (ipe._effect)
        ipe._effect.cancel();
    },
    onFailure: function(transport, ipe) {
      alert('Error communication with the server: ' + transport.responseText.stripTags());
    },
    onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.
    onLeaveEditMode: null,
    onLeaveHover: function(ipe) {
      ipe._effect = new Effect.Highlight(ipe.element, {
        startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,
        restorecolor: ipe._originalBackground, keepBackgroundImage: true
      });
    }
  },
  Listeners: {
    click: 'enterEditMode',
    keydown: 'checkForEscapeOrReturn',
    mouseover: 'enterHover',
    mouseout: 'leaveHover'
  }
});

Ajax.InPlaceCollectionEditor.DefaultOptions = {
  loadingCollectionText: 'Loading options...'
};

// Delayed observer, like Form.Element.Observer,
// but waits for delay after last key input
// Ideal for live-search fields

Form.Element.DelayedObserver = Class.create({
  initialize: function(element, delay, callback) {
    this.delay     = delay || 0.5;
    this.element   = $(element);
    this.callback  = callback;
    this.timer     = null;
    this.lastValue = $F(this.element);
    Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
  },
  delayedListener: function(event) {
    if(this.lastValue == $F(this.element)) return;
    if(this.timer) clearTimeout(this.timer);
    this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
    this.lastValue = $F(this.element);
  },
  onTimerEvent: function() {
    this.timer = null;
    this.callback(this.element, $F(this.element));
  }
});
// script.aculo.us slider.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010

// Copyright (c) 2005-2010 Marty Haught, Thomas Fuchs
//
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/

if (!Control) var Control = { };

// options:
//  axis: 'vertical', or 'horizontal' (default)
//
// callbacks:
//  onChange(value)
//  onSlide(value)
Control.Slider = Class.create({
  initialize: function(handle, track, options) {
    var slider = this;

    if (Object.isArray(handle)) {
      this.handles = handle.collect( function(e) { return $(e) });
    } else {
      this.handles = [$(handle)];
    }

    this.track   = $(track);
    this.options = options || { };

    this.axis      = this.options.axis || 'horizontal';
    this.increment = this.options.increment || 1;
    this.step      = parseInt(this.options.step || '1');
    this.range     = this.options.range || $R(0,1);

    this.value     = 0; // assure backwards compat
    this.values    = this.handles.map( function() { return 0 });
    this.spans     = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false;
    this.options.startSpan = $(this.options.startSpan || null);
    this.options.endSpan   = $(this.options.endSpan || null);

    this.restricted = this.options.restricted || false;

    this.maximum   = this.options.maximum || this.range.end;
    this.minimum   = this.options.minimum || this.range.start;

    // Will be used to align the handle onto the track, if necessary
    this.alignX = parseInt(this.options.alignX || '0');
    this.alignY = parseInt(this.options.alignY || '0');

    this.trackLength = this.maximumOffset() - this.minimumOffset();

    this.handleLength = this.isVertical() ?
      (this.handles[0].offsetHeight != 0 ?
        this.handles[0].offsetHeight : this.handles[0].style.height.replace(/px$/,"")) :
      (this.handles[0].offsetWidth != 0 ? this.handles[0].offsetWidth :
        this.handles[0].style.width.replace(/px$/,""));

    this.active   = false;
    this.dragging = false;
    this.disabled = false;

    if (this.options.disabled) this.setDisabled();

    // Allowed values array
    this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false;
    if (this.allowedValues) {
      this.minimum = this.allowedValues.min();
      this.maximum = this.allowedValues.max();
    }

    this.eventMouseDown = this.startDrag.bindAsEventListener(this);
    this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
    this.eventMouseMove = this.update.bindAsEventListener(this);

    // Initialize handles in reverse (make sure first handle is active)
    this.handles.each( function(h,i) {
      i = slider.handles.length-1-i;
      slider.setValue(parseFloat(
        (Object.isArray(slider.options.sliderValue) ?
          slider.options.sliderValue[i] : slider.options.sliderValue) ||
         slider.range.start), i);
      h.makePositioned().observe("mousedown", slider.eventMouseDown);
    });

    this.track.observe("mousedown", this.eventMouseDown);
    document.observe("mouseup", this.eventMouseUp);
    document.observe("mousemove", this.eventMouseMove);

    this.initialized = true;
  },
  dispose: function() {
    var slider = this;
    Event.stopObserving(this.track, "mousedown", this.eventMouseDown);
    Event.stopObserving(document, "mouseup", this.eventMouseUp);
    Event.stopObserving(document, "mousemove", this.eventMouseMove);
    this.handles.each( function(h) {
      Event.stopObserving(h, "mousedown", slider.eventMouseDown);
    });
  },
  setDisabled: function(){
    this.disabled = true;
  },
  setEnabled: function(){
    this.disabled = false;
  },
  getNearestValue: function(value){
    if (this.allowedValues){
      if (value >= this.allowedValues.max()) return(this.allowedValues.max());
      if (value <= this.allowedValues.min()) return(this.allowedValues.min());

      var offset = Math.abs(this.allowedValues[0] - value);
      var newValue = this.allowedValues[0];
      this.allowedValues.each( function(v) {
        var currentOffset = Math.abs(v - value);
        if (currentOffset <= offset){
          newValue = v;
          offset = currentOffset;
        }
      });
      return newValue;
    }
    if (value > this.range.end) return this.range.end;
    if (value < this.range.start) return this.range.start;
    return value;
  },
  setValue: function(sliderValue, handleIdx){
    if (!this.active) {
      this.activeHandleIdx = handleIdx || 0;
      this.activeHandle    = this.handles[this.activeHandleIdx];
      this.updateStyles();
    }
    handleIdx = handleIdx || this.activeHandleIdx || 0;
    if (this.initialized && this.restricted) {
      if ((handleIdx>0) && (sliderValue<this.values[handleIdx-1]))
        sliderValue = this.values[handleIdx-1];
      if ((handleIdx < (this.handles.length-1)) && (sliderValue>this.values[handleIdx+1]))
        sliderValue = this.values[handleIdx+1];
    }
    sliderValue = this.getNearestValue(sliderValue);
    this.values[handleIdx] = sliderValue;
    this.value = this.values[0]; // assure backwards compat

    this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] =
      this.translateToPx(sliderValue);

    this.drawSpans();
    if (!this.dragging || !this.event) this.updateFinished();
  },
  setValueBy: function(delta, handleIdx) {
    this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta,
      handleIdx || this.activeHandleIdx || 0);
  },
  translateToPx: function(value) {
    return Math.round(
      ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) *
      (value - this.range.start)) + "px";
  },
  translateToValue: function(offset) {
    return ((offset/(this.trackLength-this.handleLength) *
      (this.range.end-this.range.start)) + this.range.start);
  },
  getRange: function(range) {
    var v = this.values.sortBy(Prototype.K);
    range = range || 0;
    return $R(v[range],v[range+1]);
  },
  minimumOffset: function(){
    return(this.isVertical() ? this.alignY : this.alignX);
  },
  maximumOffset: function(){
    return(this.isVertical() ?
      (this.track.offsetHeight != 0 ? this.track.offsetHeight :
        this.track.style.height.replace(/px$/,"")) - this.alignY :
      (this.track.offsetWidth != 0 ? this.track.offsetWidth :
        this.track.style.width.replace(/px$/,"")) - this.alignX);
  },
  isVertical:  function(){
    return (this.axis == 'vertical');
  },
  drawSpans: function() {
    var slider = this;
    if (this.spans)
      $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) });
    if (this.options.startSpan)
      this.setSpan(this.options.startSpan,
        $R(0, this.values.length>1 ? this.getRange(0).min() : this.value ));
    if (this.options.endSpan)
      this.setSpan(this.options.endSpan,
        $R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum));
  },
  setSpan: function(span, range) {
    if (this.isVertical()) {
      span.style.top = this.translateToPx(range.start);
      span.style.height = this.translateToPx(range.end - range.start + this.range.start);
    } else {
      span.style.left = this.translateToPx(range.start);
      span.style.width = this.translateToPx(range.end - range.start + this.range.start);
    }
  },
  updateStyles: function() {
    this.handles.each( function(h){ Element.removeClassName(h, 'selected') });
    Element.addClassName(this.activeHandle, 'selected');
  },
  startDrag: function(event) {
    if (Event.isLeftClick(event)) {
      if (!this.disabled){
        this.active = true;

        var handle = Event.element(event);
        var pointer  = [Event.pointerX(event), Event.pointerY(event)];
        var track = handle;
        if (track==this.track) {
          var offsets  = this.track.cumulativeOffset();
          this.event = event;
          this.setValue(this.translateToValue(
           (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2)
          ));
          var offsets  = this.activeHandle.cumulativeOffset();
          this.offsetX = (pointer[0] - offsets[0]);
          this.offsetY = (pointer[1] - offsets[1]);
        } else {
          // find the handle (prevents issues with Safari)
          while((this.handles.indexOf(handle) == -1) && handle.parentNode)
            handle = handle.parentNode;

          if (this.handles.indexOf(handle)!=-1) {
            this.activeHandle    = handle;
            this.activeHandleIdx = this.handles.indexOf(this.activeHandle);
            this.updateStyles();

            var offsets  = this.activeHandle.cumulativeOffset();
            this.offsetX = (pointer[0] - offsets[0]);
            this.offsetY = (pointer[1] - offsets[1]);
          }
        }
      }
      Event.stop(event);
    }
  },
  update: function(event) {
   if (this.active) {
      if (!this.dragging) this.dragging = true;
      this.draw(event);
      if (Prototype.Browser.WebKit) window.scrollBy(0,0);
      Event.stop(event);
   }
  },
  draw: function(event) {
    var pointer = [Event.pointerX(event), Event.pointerY(event)];
    var offsets = this.track.cumulativeOffset();
    pointer[0] -= this.offsetX + offsets[0];
    pointer[1] -= this.offsetY + offsets[1];
    this.event = event;
    this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] ));
    if (this.initialized && this.options.onSlide)
      this.options.onSlide(this.values.length>1 ? this.values : this.value, this);
  },
  endDrag: function(event) {
    if (this.active && this.dragging) {
      this.finishDrag(event, true);
      Event.stop(event);
    }
    this.active = false;
    this.dragging = false;
  },
  finishDrag: function(event, success) {
    this.active = false;
    this.dragging = false;
    this.updateFinished();
  },
  updateFinished: function() {
    if (this.initialized && this.options.onChange)
      this.options.onChange(this.values.length>1 ? this.values : this.value, this);
    this.event = null;
  }
});
// script.aculo.us sound.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010

// Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//
// Based on code created by Jules Gravinese (http://www.webveteran.com/)
//
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/

Sound = {
  tracks: {},
  _enabled: true,
  template:
    new Template('<embed style="height:0" id="sound_#{track}_#{id}" src="#{url}" loop="false" autostart="true" hidden="true"/>'),
  enable: function(){
    Sound._enabled = true;
  },
  disable: function(){
    Sound._enabled = false;
  },
  play: function(url){
    if(!Sound._enabled) return;
    var options = Object.extend({
      track: 'global', url: url, replace: false
    }, arguments[1] || {});

    if(options.replace && this.tracks[options.track]) {
      $R(0, this.tracks[options.track].id).each(function(id){
        var sound = $('sound_'+options.track+'_'+id);
        sound.Stop && sound.Stop();
        sound.remove();
      });
      this.tracks[options.track] = null;
    }

    if(!this.tracks[options.track])
      this.tracks[options.track] = { id: 0 };
    else
      this.tracks[options.track].id++;

    options.id = this.tracks[options.track].id;
    $$('body')[0].insert(
      Prototype.Browser.IE ? new Element('bgsound',{
        id: 'sound_'+options.track+'_'+options.id,
        src: options.url, loop: 1, autostart: true
      }) : Sound.template.evaluate(options));
  }
};

if(Prototype.Browser.Gecko && navigator.userAgent.indexOf("Win") > 0){
  if(navigator.plugins && $A(navigator.plugins).detect(function(p){ return p.name.indexOf('QuickTime') != -1 }))
    Sound.template = new Template('<object id="sound_#{track}_#{id}" width="0" height="0" type="audio/mpeg" data="#{url}"/>');
  else if(navigator.plugins && $A(navigator.plugins).detect(function(p){ return p.name.indexOf('Windows Media') != -1 }))
    Sound.template = new Template('<object id="sound_#{track}_#{id}" type="application/x-mplayer2" data="#{url}"></object>');
  else if(navigator.plugins && $A(navigator.plugins).detect(function(p){ return p.name.indexOf('RealPlayer') != -1 }))
    Sound.template = new Template('<embed type="audio/x-pn-realaudio-plugin" style="height:0" id="sound_#{track}_#{id}" src="#{url}" loop="false" autostart="true" hidden="true"/>');
  else
    Sound.play = function(){};
} 
/**
 * SWFObject v1.5: Flash Player detection and embed - http://blog.deconcept.com/swfobject/
 *
 * SWFObject is (c) 2007 Geoff Stearns and is released under the MIT License:
 * http://www.opensource.org/licenses/mit-license.php
 *
 */
if (typeof deconcept == "undefined") {
    var deconcept = new Object();
}
if (typeof deconcept.util == "undefined") {
    deconcept.util = new Object();
}
if (typeof deconcept.SWFObjectUtil == "undefined") {
    deconcept.SWFObjectUtil = new Object();
}
deconcept.SWFObject = function (_1, id, w, h, _5, c, _7, _8, _9, _a) {
    if (!document.getElementById) {
        return;
    }
    this.DETECT_KEY = _a ? _a : "detectflash";
    this.skipDetect = deconcept.util.getRequestParameter(this.DETECT_KEY);
    this.params = new Object();
    this.variables = new Object();
    this.attributes = new Array();
    if (_1) {
        this.setAttribute("swf", _1);
    }
    if (id) {
        this.setAttribute("id", id);
    }
    if (w) {
        this.setAttribute("width", w);
    }
    if (h) {
        this.setAttribute("height", h);
    }
    if (_5) {
        this.setAttribute("version", new deconcept.PlayerVersion(_5.toString().split(".")));
    }
    this.installedVer = deconcept.SWFObjectUtil.getPlayerVersion();
    if (!window.opera && document.all && this.installedVer.major > 7) {
        deconcept.SWFObject.doPrepUnload = true;
    }
    if (c) {
        this.addParam("bgcolor", c);
    }
    var q = _7 ? _7 : "high";
    this.addParam("quality", q);
    this.setAttribute("useExpressInstall", false);
    this.setAttribute("doExpressInstall", false);
    var _c = (_8) ? _8 : window.location;
    this.setAttribute("xiRedirectUrl", _c);
    this.setAttribute("redirectUrl", "");
    if (_9) {
        this.setAttribute("redirectUrl", _9);
    }
};
deconcept.SWFObject.prototype = {
    useExpressInstall: function (_d) {
        this.xiSWFPath = !_d ? "expressinstall.swf" : _d;
        this.setAttribute("useExpressInstall", true);
    }, setAttribute: function (_e, _f) {
        this.attributes[_e] = _f;
    }, getAttribute: function (_10) {
        return this.attributes[_10];
    }, addParam: function (_11, _12) {
        this.params[_11] = _12;
    }, getParams: function () {
        return this.params;
    }, addVariable: function (_13, _14) {
        this.variables[_13] = _14;
    }, getVariable: function (_15) {
        return this.variables[_15];
    }, getVariables: function () {
        return this.variables;
    }, getVariablePairs: function () {
        var _16 = new Array();
        var key;
        var _18 = this.getVariables();
        for (key in _18) {
            _16[_16.length] = key + "=" + _18[key];
        }
        return _16;
    }, getSWFHTML: function () {
        var _19 = "";
        if (navigator.plugins && navigator.mimeTypes && navigator.mimeTypes.length) {
            if (this.getAttribute("doExpressInstall")) {
                this.addVariable("MMplayerType", "PlugIn");
                this.setAttribute("swf", this.xiSWFPath);
            }
            _19 = "<embed type=\"application/x-shockwave-flash\" src=\"" + this.getAttribute("swf") + "\" width=\"" + this.getAttribute("width") + "\" height=\"" + this.getAttribute("height") + "\" style=\"" + this.getAttribute("style") + "\"";
            _19 += " id=\"" + this.getAttribute("id") + "\" name=\"" + this.getAttribute("id") + "\" ";
            var _1a = this.getParams();
            for (var key in _1a) {
                _19 += [key] + "=\"" + _1a[key] + "\" ";
            }
            var _1c = this.getVariablePairs().join("&");
            if (_1c.length > 0) {
                _19 += "flashvars=\"" + _1c + "\"";
            }
            _19 += "/>";
        } else {
            if (this.getAttribute("doExpressInstall")) {
                this.addVariable("MMplayerType", "ActiveX");
                this.setAttribute("swf", this.xiSWFPath);
            }
            _19 = "<object id=\"" + this.getAttribute("id") + "\" classid=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\" width=\"" + this.getAttribute("width") + "\" height=\"" + this.getAttribute("height") + "\" style=\"" + this.getAttribute("style") + "\">";
            _19 += "<param name=\"movie\" value=\"" + this.getAttribute("swf") + "\" />";
            var _1d = this.getParams();
            for (var key in _1d) {
                _19 += "<param name=\"" + key + "\" value=\"" + _1d[key] + "\" />";
            }
            var _1f = this.getVariablePairs().join("&");
            if (_1f.length > 0) {
                _19 += "<param name=\"flashvars\" value=\"" + _1f + "\" />";
            }
            _19 += "</object>";
        }
        return _19;
    }, write: function (_20) {
        if (this.getAttribute("useExpressInstall")) {
            var _21 = new deconcept.PlayerVersion([6, 0, 65]);
            if (this.installedVer.versionIsValid(_21) && !this.installedVer.versionIsValid(this.getAttribute("version"))) {
                this.setAttribute("doExpressInstall", true);
                this.addVariable("MMredirectURL", escape(this.getAttribute("xiRedirectUrl")));
                document.title = document.title.slice(0, 47) + " - Flash Player Installation";
                this.addVariable("MMdoctitle", document.title);
            }
        }
        if (this.skipDetect || this.getAttribute("doExpressInstall") || this.installedVer.versionIsValid(this.getAttribute("version"))) {
            var n = (typeof _20 == "string") ? document.getElementById(_20) : _20;
            n.innerHTML = this.getSWFHTML();
            return true;
        } else {
            if (this.getAttribute("redirectUrl") != "") {
                document.location.replace(this.getAttribute("redirectUrl"));
            }
        }
        return false;
    }
};
deconcept.SWFObjectUtil.getPlayerVersion = function () {
    var _23 = new deconcept.PlayerVersion([0, 0, 0]);
    if (navigator.plugins && navigator.mimeTypes.length) {
        var x = navigator.plugins["Shockwave Flash"];
        if (x && x.description) {
            _23 = new deconcept.PlayerVersion(x.description.replace(/([a-zA-Z]|\s)+/, "").replace(/(\s+r|\s+b[0-9]+)/, ".").split("."));
        }
    } else {
        if (navigator.userAgent && navigator.userAgent.indexOf("Windows CE") >= 0) {
            var axo = 1;
            var _26 = 3;
            while (axo) {
                try {
                    _26++;
                    axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash." + _26);
                    _23 = new deconcept.PlayerVersion([_26, 0, 0]);
                } catch (e) {
                    axo = null;
                }
            }
        } else {
            try {
                var axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");
            } catch (e) {
                try {
                    var axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");
                    _23 = new deconcept.PlayerVersion([6, 0, 21]);
                    axo.AllowScriptAccess = "always";
                } catch (e) {
                    if (_23.major == 6) {
                        return _23;
                    }
                }
                try {
                    axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
                } catch (e) {}
            }
            if (axo != null) {
                _23 = new deconcept.PlayerVersion(axo.GetVariable("$version").split(" ")[1].split(","));
            }
        }
    }
    return _23;
};
deconcept.PlayerVersion = function (_29) {
    this.major = _29[0] != null ? parseInt(_29[0]) : 0;
    this.minor = _29[1] != null ? parseInt(_29[1]) : 0;
    this.rev = _29[2] != null ? parseInt(_29[2]) : 0;
};
deconcept.PlayerVersion.prototype.versionIsValid = function (fv) {
    if (this.major < fv.major) {
        return false;
    }
    if (this.major > fv.major) {
        return true;
    }
    if (this.minor < fv.minor) {
        return false;
    }
    if (this.minor > fv.minor) {
        return true;
    }
    if (this.rev < fv.rev) {
        return false;
    }
    return true;
};
deconcept.util = {
    getRequestParameter: function (_2b) {
        var q = document.location.search || document.location.hash;
        if (_2b == null) {
            return q;
        }
        if (q) {
            var _2d = q.substring(1).split("&");
            for (var i = 0; i < _2d.length; i++) {
                if (_2d[i].substring(0, _2d[i].indexOf("=")) == _2b) {
                    return _2d[i].substring((_2d[i].indexOf("=") + 1));
                }
            }
        }
        return "";
    }
};
deconcept.SWFObjectUtil.cleanupSWFs = function () {
    var _2f = document.getElementsByTagName("OBJECT");
    for (var i = _2f.length - 1; i >= 0; i--) {
        _2f[i].style.display = "none";
        for (var x in _2f[i]) {
            if (typeof _2f[i][x] == "function") {
                _2f[i][x] = function () {};
            }
        }
    }
};
if (deconcept.SWFObject.doPrepUnload) {
    if (!deconcept.unloadSet) {
        deconcept.SWFObjectUtil.prepUnload = function () {
            __flash_unloadHandler = function () {};
            __flash_savedUnloadHandler = function () {};
            window.attachEvent("onunload", deconcept.SWFObjectUtil.cleanupSWFs);
        };
        window.attachEvent("onbeforeunload", deconcept.SWFObjectUtil.prepUnload);
        deconcept.unloadSet = true;
    }
}
if (!document.getElementById && document.all) {
    document.getElementById = function (id) {
        return document.all[id];
    };
}
var getQueryParamValue = deconcept.util.getRequestParameter;
var FlashObject = deconcept.SWFObject;
var SWFObject = deconcept.SWFObject;
/*****************************************************************************
 * @title:   gidFunctions.js
 * @author:  Andrew Southwick, Byung Kim
 * @date:    10-24-2007
 * @rev:     4.20
 * @desc:    Core GID Javascript Library
 * @assumes: prototype.js 1.5 rel., scriptaculous.js, brandConstants.js
 *
 * (C) Copyright 2004-2007 by Gap Inc.
 *  All Rights Reserved.
 *
 * This software is the confidential and proprietary information
 * of Gap Inc. ("Confidential Information"). Redistribution of the source
 * code or binary form is not permitted without prior authorization
 * from the Gap Inc.
 *
 *****************************************************************************/

/**
 * ClientBrowser is a class that defines our existing browser support.
 * It defines variables that can be accessed at runtime to determine
 * a user's browser type and version.
 * Instantiated as clientBrowser.
 *
 * Based on PLONE Browser Support document (10/9/2007)
 * Safari applewebkit versions from http://developer.apple.com/internet/safari/uamatrix.html
 *
 * @constructor
 * @author Byung Kim
 * @date 10/24/2007
 */
var ClientBrowser = Class.create();
ClientBrowser.prototype = {
	userAgent:navigator.userAgent,
	product:navigator.product,
	mimeTypes:navigator.mimeTypes,
	supportLevel:null,

	/* All currently supported browsers.
	 *
	 * name - string we use to name the new properties
	 * key - userAgent search string. Can be a partial RegExp string
	 * versions - browser versions supported.
	 * 		alias - the string used to name the property
	 * 		key - the RegExp to match all version numbers represented
	 * 		baseVersion - the minimum version number represented for the browser version we're checking.  This is used to determine the "Up" boolean.
	 */
	browsers:[
		{name:"IE",key:"MSIE ",versions:[
			{alias:"55",key:"5.5",baseVersion:"5.5"}, // IE 5.5
			{alias:"6",key:"6",baseVersion:"6"}, // IE 6
			{alias:"7",key:"7",baseVersion:"7"}, // IE 7
			{alias:"8",key:"8",baseVersion:"8"} // IE 8
		]},
		{name:"Firefox",key:"Firefox/",versions:[
			{alias:"1",key:"^1\.0[0-9\.]*",baseVersion:"1"}, // Firefox 1
			{alias:"15",key:"^1\.5[0-9\.]*",baseVersion:"1.5"}, // Firefox 1.5
			{alias:"2",key:"^2[0-9\.]*",baseVersion:"2"}, // Firefox 2
			{alias:"3",key:"^3[0-9\.]*",baseVersion:"3"} // Firefox 3
		]},
		{name:"Safari",key:"AppleWebKit/",versions:[
			{alias:"132",key:"^(312\.8)|(312\.8\.1)",baseVersion:"312.8"}, // Safari 1.3.2 (appleWebKit versions 312.8 - 312.8.1)
			{alias:"203",key:"^(417\.9)|(418)",baseVersion:"417.9"}, // Safari 2.0.3 (appleWebKit versions 417.9 - 418)
			{alias:"204",key:"^(418\.8)|(418\.9)|(418\.9\.1)|(419)|(419\.2\.1)|(419\.3)",baseVersion:"418.8"}, // Safari 2.0.4 (appleWebKit versions 418.8 - 419.3)
			{alias:"304",key:"^(523\.10)",baseVersion:"523.10"} // Safari 3.0.4 (appleWebKit versions 523.10+)
		]}
	],

	/**
	 * initialize clientBrowser
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	initialize:function() {
		this.setBrowserInfo();
//		this.setFlashDetection();
		this.setSupportLevel();
	},

	/**
	 * setBrowserInfo sets browser info properties
	 * Code flow:
	 * 1) check if the browser is in the userAgent string. If the browser wasn't found, we end here and no property is created.
	 * 2) create a new boolean property to denote that the browser was found in the userAgent.
	 * 3) get the full browser version string
	 * 4) create a new property for the full browser version string
	 * 5) check each supported version defined for the browser and set booleans for each version
	 * 6) set the boolean for each browser version (e.g.  isFirefox2, isIE55, isSafari203 )
	 *    The property is only set when it is not true.  This is to allow multiple checks to the same alias with different keys.
	 *    Useful for Safari versions which are based on multiple applewebkit versions.
	 * 7) set the boolean for each browser version and above (e.g. isFirefox2up, isIE55Up, isSafari203Up )
	 *    The property is only set when it doesn't exist.  The "Up" boolean is only set with the initial version number for a given alias.
	 *    Useful for Safari versions which are based on multiple applewebkit versions.
	 * 8) If the browser is detected, break the loop to minimize iterations.
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	setBrowserInfo:function() {
		var client = this;
		var iterator = function(browser) {
			var inUA = (client.userAgent.match(browser.key) != null); // 1
			if (inUA) {
				client["is"+browser.name] = inUA; // 2
				var uaVer = client.getVersion(browser.key); // 3
				if (uaVer) {
					client["ver"+browser.name] = uaVer; // 4
					if (browser.versions) {
						browser.versions.each( // 5
							function(version) {
								var prop = "is"+browser.name+version.alias;
								if (!client[prop]) client[prop] = (uaVer.match(new RegExp(version.key)) != null); // 6
								if (client[prop+"Up"] == undefined) client[prop+"Up"] = (uaVer >= version.baseVersion); // 7
							}
						);
					}
				}
				$break; // 8
			}
		}
		this.browsers.each(iterator);

		// Check for Gecko
		this.isGecko = (navigator.product == "Gecko");
		if (this.isGecko) {
			this.verGecko = this.getVersion("rv:");
		}

		// Set Platform
		this.isMac = this.userAgent.include("Mac");
		this.isWin = this.userAgent.include("Windows");

	},

	/**
	 * getVersion returns the full version string from the userAgent
	 * @param {string} key The key used to search the userAgent string
	 * @return a string representing the version of the browser
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	getVersion:function(key) {
		var keyMatch = this.userAgent.match(new RegExp(key+"[0-9\.]*"));
		var keyValue = null;
		if (keyMatch && keyMatch.length > 0) {
			keyValue = keyMatch[0].match(/[0-9\.]+/)[0];	
		}
		return keyValue;
	},

	/**
	 * setSupportLevel sets the business support level
	 * 0 = no support
	 * 1 = semi support
	 * 2 = full support
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	setSupportLevel:function(){
		if (this.isFirefox15Up || this.isIE6Up || this.isSafari204Up) {
			this.supportLevel = 2;
		} else if (this.isSafari132Up || this.isFirefox1Up || this.isNetscape7Up) {
			this.supportLevel = 1;
		} else {
			this.supportLevel = 0;
		}

	},

	/**
	 * setFlashDetection detects the flash plugin
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	setFlashDetection:function() {
		var mimeTypes = this.mimeTypes;
		if (mimeTypes && mimeTypes["application/x-shockwave-flash"] && mimeTypes["application/x-shockwave-flash"].enabledPlugin) {
			var flashPlugin = mimeTypes["application/x-shockwave-flash"].enabledPlugin;
			this.isFlash = true;
			this.verFlash = flashPlugin.description.match(/[\d.]+/)[0];
		} else if (this.isWin && this.isIE6Up) {
			document.write('<scr' + 'ipt language=VBScript>' + '\n' +
			'Dim hasPlayer, playerversion' + '\n' +
			'hasPlayer = false' + '\n' +
			'playerversion = 10' + '\n' +
			'Do While playerversion > 0' + '\n' +
			'On Error Resume Next' + '\n' +
			'hasPlayer = (IsObject(CreateObject("ShockwaveFlash.ShockwaveFlash." & playerversion)))' + '\n' +
			'If hasPlayer = true Then Exit Do' + '\n' +
			'playerversion = playerversion - 1' + '\n' +
			'Loop' + '\n' +
			'client.verFlash = playerversion' + '\n' +
			'client.isFlash = hasPlayer' + '\n' +
			'<\/sc' + 'ript>');
		}
	}

};

var clientBrowser = new ClientBrowser();

/**
 * GidLib is a class containing the core javascript library for GID.
 * Instantiated as gidLib.
 *
 * @constructor
 * @author Byung Kim
 * @date 10/24/2007
 */
var LastKnownHiddenDropDowns = [];

var GidLib = Class.create();
GidLib.prototype = {

	/**
	 * GID constants
	 * @base GidLib
	 *
	 * @author Byung Kim
	 * @date 12/19/2007
	 */
	constants:{

	},

	/**
	 * initialize for GidLib
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	initialize:function() {
		this.addPrototypeMethods();
	},

    /**
     * clone - makes a shallow copy of desired obj, only sets the prototype to point to the original obj
     * this is different than prototypes own clone function which copies basic type attributes of original obj
     * @author yoshi
     */
     clone: function(obj) {
         function F() {}
         F.prototype = obj;
         return new F;
     },

    loadDomObjMap : function(obj, map) {
		Object.keys(map).each(function(key) {
			obj[key] = $(map[key]);
		});
	},

	loadBtnImgMap : function(obj, map, path) {
		Object.keys(map).each(function(key) { obj[key] = {src:path + map[key]}; });
	},

    /**
	 * addPrototypeMethods is used to add prototype methods to native classes
	 *
	 * @author Byung Kim
	 * @date 10/31/2007
	 */
	addPrototypeMethods:function() {
        /**
         * extend Array method to return index of found element instead of element value
         */
        Object.extend(Array.prototype, {findIndex : function(iterator) {
            var result;
            this.each(function(value, index) {
              if (iterator(value, index)) {
                result = index;
                $break;
              }
            });
            return result;
        }});

        /**
		 * add hasOwnProperty method to Object class if it doesn't exist. (e.g. Safari 1.3.2)
		 */
		if(!Object.prototype.hasOwnProperty) {
			Object.prototype.hasOwnProperty = function(prop) {
				return this.constructor.prototype[prop] === undefined;
			}
		}

		/**
		 * add setSrc method to IMG and INPUT tags via Prototype Element.addMethods();
		 */
		 Element.addMethods(['IMG','INPUT'],{
			setSrc:function(element,src) {
				$(element)["src"] = src;
			}
		 });

		/**
		 * add trim methods to String class
		 */
		 String.prototype.lTrim = function() {
		 	return this.replace(/^\s+/,'');
		 }
		 String.prototype.rTrim = function() {
		 	return this.replace(/\s+$/,'');
		 }
		 String.prototype.trim = function() {
		 	return this.replace(/^\s+|\s+$/g,'');
		 }
	},

	/**
	 * onLoadHandler is a collection of methods to execute on load of a page.  This is called globally
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	onLoadHandler:function() {
		if (window["GIDPageViewAdapter"]) {
			objGIDPageViewAdapter = new GIDPageViewAdapter();
			objGIDPageViewAdapter.objGIDProducts = new GIDProducts();
		}
	    var disableLayer = $('disableLayer');
        if(disableLayer) disableLayer.style.display = 'none';
//		this.buttonEvents.processQueue();

		var path = location.pathname;
        if (!/\/browse\//.exec(path)) {
        	this.setButtonEvents();
        }

        // Top nav and universal bar for all pages
    	this.setButtonEvents($('universalBar'));
    	this.setButtonEvents($('topNav'));

		this.setLabelOnClick();

		/* pageOnLoadFunctions() is deprecated - use Event.observe() to attach events */
		pageOnLoadFunctions();
	},

    /**
	 * stop events from bubbling up
	 *
	 * @author yoshi
	 * @date 07/09/2008
	 */
    stopBubbling: function(e) {
        if (!e) var e = window.event
        e.cancelBubble = true;
        if (e.stopPropagation) e.stopPropagation();
    },

    /**
	 * onUnloadHandler is a collection of methods to execute on unload of a page.  This is called globally
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	onUnloadHandler:function() {
		/* pageOnLoadFunctions() is deprecated - use Event.observe() to attach events */
        var disableLayer = $('disableLayer');
        if(disableLayer) disableLayer.style.display = 'block';
        pageOnUnloadFunctions();
	},


	/**
	 * hideDropDownsUnderElement hides all select boxes under a specified element for IE6
	 * @param {object} element A pointer reference to the element we want to hide <select> elements under
	 * @param {string} optional css selector
	 * @return an array of <select> elements that were hidden
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */

	hideDropDownsUnderElement:function(element, exp) {
		var hiddenDropDowns = [];

		if 	(LastKnownHiddenDropDowns.length > 0) {
			hiddenDropDowns = LastKnownHiddenDropDowns;
		}

		if (element && clientBrowser.isIE6 == true) {
			var dropDowns = $$(exp||'select');
			var iterator = function(dropDown) {
				var isHidden = (dropDown.getStyle("visibility") == "hidden" || dropDown.visible == false);
				if (dropDown && isHidden == false) {
					var dropDownPosition = Position.cumulativeOffset(dropDown);
					var dropDownWidth = dropDown.offsetWidth;
					var dropDownHeight = dropDown.offsetHeight;
					var isTopLeftUnderElement = Position.within(element,dropDownPosition[0],dropDownPosition[1]);
					var isTopRightUnderElement = Position.within(element,dropDownPosition[0]+dropDownWidth,dropDownPosition[1]);
					var isBottomLeftUnderElement = Position.within(element,dropDownPosition[0],dropDownPosition[1]+dropDownHeight);
					var isBottomRightUnderElement = Position.within(element,dropDownPosition[0]+dropDownWidth,dropDownPosition[1]+dropDownHeight);
					if (isTopLeftUnderElement || isTopRightUnderElement || isBottomLeftUnderElement || isBottomRightUnderElement) {
						dropDown.style.visibility = "hidden";
						hiddenDropDowns.push(dropDown);
					}
				}
			}
			dropDowns.each(iterator);
		}
		LastKnownHiddenDropDowns = hiddenDropDowns;
		return hiddenDropDowns;
	},

	/**
	 * showDropDowns shows all select boxes in the array created by the result of hideDropDownsUnderElement
	 * @param {object} hiddenDropDowns An array of <select> elements to set style visibility = visible
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	showDropDowns:function(hiddenDropDowns) {
		if (hiddenDropDowns&& hiddenDropDowns.length > 0 && clientBrowser.isIE6 == true) {
			hiddenDropDowns.invoke("setStyle", {visibility:"visible"});
		}
	},

	/**
	 * setFocus tries to focus on an element. If an exception is caught, return false.
	 * This method is necessary for IE which throws a JS error when you try to focus on
	 * an element where the element or any parent element is hidden or disabled.
	 *
	 * TODO: Only use the try..catch for IE.
	 *
	 * @param {object} element The DOM element we want to try to set focus() to.
	 * @return true or false depending on if the method was successful in setting the focus() on the specified element.
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	setFocus:function(element) {
		var isFocusable = true;
		try {
			element.focus();
		} catch(e) {
			// the element could not be focused on. return false.
			isFocusable = false;
		}
		return isFocusable;
	},


	buttonEvents : {
		nodeQueue : [],
       	spriteBaseRegExp : /(universalButtonSprite\S+)(On|Over|Off)/,
       	spriteClassRegExp : /universalButtonSprite\S+/,

       	addToQueue : function(elementNode) {
			this.nodeQueue.push(elementNode);
			elementNode.onload = null;
		},

       	setEvents : function(elementNode) {
			if (elementNode.buttonEventsCompleted != true) {
				var imgsrc = elementNode.src;
				var searchIndex = elementNode.src.indexOf("?");
				var imgIndex = elementNode.src.indexOf(".gif");
				if (searchIndex == -1 || searchIndex > imgIndex) { // ensure the match is not part of a param or the start of params is after the img name
					var suffix = (imgsrc.indexOf("_sm.gif") > 0 ? "_sm.gif" : ".gif");
					if (imgsrc.search(/(_off|_on)/) != -1) {
						elementNode.offState = imgsrc;
			            elementNode.onState = imgsrc.search(/_on(_sm)?\.gif/) != -1 ? imgsrc : imgsrc.replace(/(_off)?(_sm)?\.gif/, "_on" + suffix);
			            elementNode.overState = imgsrc.replace(/(_off|_on)?(_sm)?\.gif/,"_over" + suffix);
						elementNode.isSpriteBased = false;

						Event.observe(elementNode,"mouseover",this.setOverState.bind(this));
						Event.observe(elementNode,"focus",this.setOnState.bind(this));
						Event.observe(elementNode,"mouseout",this.setOffState.bind(this));
						Event.observe(elementNode,"blur",this.setOffState.bind(this));
					}
					// Handle sprites
					else if (this.spriteClassRegExp.test(elementNode.className)) {
						var matches = this.spriteBaseRegExp.exec(elementNode.className);
						elementNode.isSpriteBased = true;
						elementNode.spriteBase = matches[1];

						Event.observe(elementNode,"mouseover",this.setOverState.bind(this));
						Event.observe(elementNode,"focus",this.setOnState.bind(this));
						Event.observe(elementNode,"mouseout",this.setOffState.bind(this));
						Event.observe(elementNode,"blur",this.setOffState.bind(this));
					}

				}
				elementNode.buttonEventsCompleted = true;
			}
		},

		setOverState : function(event) {
			var targetElement = Event.element(event);
            var isDisabled = targetElement.getAttribute('isDisabled')
            if(isDisabled) return;

            if (targetElement.isSpriteBased) {
            	targetElement.className = targetElement.className.replace(this.spriteClassRegExp,targetElement.spriteBase + "Over");
            } else {
	            targetElement.src = targetElement.overState;
	        }
		},

		setOnState : function(event) {
			var targetElement = Event.element(event);
            var isDisabled = targetElement.getAttribute('isDisabled')
            if(isDisabled) return;

            if (targetElement.isSpriteBased) {
            	targetElement.className = targetElement.className.replace(this.spriteClassRegExp,targetElement.spriteBase + "On");
            } else {
	            targetElement.src = targetElement.onState;
	        }
		},

        setOffState : function(event) {
            var targetElement = Event.element(event);
            var isDisabled = targetElement.getAttribute('isDisabled')
            if(isDisabled) return;

            if (targetElement.isSpriteBased) {
            	targetElement.className = targetElement.className.replace(this.spriteClassRegExp,targetElement.spriteBase + "On");
            } else {
	            targetElement.src = isDisabled ? targetElement.offState : targetElement.onState;
	        }
        },

        processQueue : function() {
        	this.nodeQueue.each(this.setEvents.bind(this));
        }

	},


	/**
	 * setButtonEvents sets button mouseover, mouseout, focus, and blur events for all navigational buttons
	 * @param {object} root The root DOM element to start searching for elements to modify
	 *
	 * Modified 11/12/2007 Byung Kim - fixed to support "_sm" type buttons
	 * Modified 7/22/2008 Byung Kim - added support for setting button events on specific root nodes even with HAS_BUTTON_MOUSE_OVERS = false
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	setButtonEvents:function(root) {
		var elements = [];

		var addElements = function(attr) {
			var rootElement = (root ? $(root) : $('mainContent'));
			//var rootElement = (root ? $(root) : Element.extend(document.body));
			elements = elements.concat(rootElement ? rootElement.getElementsBySelector('img'+attr,'input'+attr) : $$('img'+attr,'input'+attr));
		}

		// a specific root node is specified
		if (root != null && root != undefined && $(root)) {
			addElements("[src*=/common/buttons/en/button_]");

		// search all buttons in the DOM
		} else {
			// branded buttons
			if (brandConst.HAS_BUTTON_MOUSE_OVERS) addElements("[src*=/common/buttons/en/button_]");
		}

		// universal buttons and universal buttons from sprites
		if (brandConst.UNIVERSAL_BUTTON_CONTENT_PATH && brandConst.UNIVERSAL_BUTTON_CONTENT_PATH != "") {
			addElements("[src*=" + brandConst.UNIVERSAL_BUTTON_CONTENT_PATH + "]");
			addElements("[class*=universalButtonSprite]");
		}

		elements.each(gidLib.buttonEvents.setEvents.bind(gidLib.buttonEvents));
	},

	/**
	 * selectOption selects a form field option based on the provided value.
	 * Works for select, select-multiple, radio, and checkboxes.
	 * @param {object} element The DOM Form element we want to modify
	 * @param {string} value The value of the option we want to set as selected
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	selectOption:function(element,value) {
		var fieldType = element.type;
		if (!fieldType && element[0]) fieldType = element[0].type;
		if (fieldType == "select-one" || fieldType == "select-multiple") {
			for (i=0;i<element.options.length;i++) {
				if (element.options[i].value == value) {
					element.options[i].selected = true;
					break;
				}
			}
		} else if (fieldType == "radio" || fieldType == "checkbox") {
			for (i=0;i<element.length;i++) {
				if (element[i].value == value) {
					element[i].checked = true;
					break;
				}
			}
		}
	},

	/**
	 * setLabelOnClick fixes an IE defect where the label tag doesn't act to focus on the respective form element when clicked
	 *
	 * Modified 12/18/2007 Byung Kim - refactored to work better
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	setLabelOnClick:function() {
		if (clientBrowser.isIE) {
			var labels = $$('label');

			var eventMethod = function(e) {
				var element = $(Event.element(e).htmlFor);
				if (element) gidLib.setFocus(element);
				return false;
			}

			var iterator = function(element) {
				Event.observe(element, "click", eventMethod.bindAsEventListener(this));
			}

			labels.each(iterator);
		}
	},


	/**
	 * setCookie sets a cookie to the document object
	 * @param {string} name The cookie name
	 * @param {string} value The cookie value.  This needs to be an escape() value under most cases.
	 * @param {object} expires The cookie expire date.  It should be a Date() object.  If its the millisecond representation, it is converted to the Date() object.
	 * @param {string} path The cookie path
	 * @param {string} domain The cookie domain
	 * @param {boolean} secure Whether the cookie is set secure
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	setCookie:function(name,value,expires,path,domain,secure) {
		if (typeof expires != "object") expires = new Date(expires);
		document.cookie = name + "=" + value +
			((expires) ? "; expires=" + expires.toGMTString() : "") +
			((path) ? "; path=" + path : "") +
			((domain) ? "; domain=" + domain : "") +
			((secure) ? "; secure" : "");
	},

	/**
	 * getCookie retrieves the value of the specified document cookie
	 * @param {string} cookieName The name of the cookie we want to retrieve the value for
	 * @return the value of the cookie.  If no cookie is found, it returns zero (0).
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	getCookie:function(cookieName) {
		var cookie = document.cookie;
	  	var key = cookieName + "=";
	  	var value = 0;
		var keyMatch = cookie.match(new RegExp(key+"[^;]*"));
		if (keyMatch && keyMatch.length > 0) {
			value = unescape(keyMatch[0].substr(key.length));
		}

	 	return value;
	},

	/**
	 * getCookieVar returns the value of a key-value pair within the specified document.cookie
	 * @param {string} cookieName The cookie we want to get the value from
	 * @param {string} key The key to check in the cookie
	 * @return the value of the key-value pair.  If the key is not found, it returns a blank string ("")
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	getCookieVar:function(cookieName, key) {
		var cookieValue = gidLib.getCookie(cookieName);
		var keyValue = "";
		if (cookieValue != 0 && key && key != "") {
			var keyMatch = cookieValue.match(new RegExp(key+"=[^&]*"));
			if (keyMatch && keyMatch.length > 0) {
				keyValue = unescape(keyMatch[0].substr(key.length+1));
			}
		}
		return keyValue;
	},

	/**
	 * setCookieParamsHelper return the appropriate values for cookie expiration, domain, path, and secure flag.
	 * @param {string} cookieName The cookie name we want to use.
	 * @param {object} expDate The Date() object representing the date we want to expire the cookie
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	setCookieParamsHelper : function(cookieName,expDate) {
		var exp = null;
		var domain = null;
		var path = "/";
		var isSecure = (location.protocol == "https:");

		// set cookieDomain
		var securePrefix = "secure-"
		var domain  = null;
		var  renderOnOldDomain = true;
		var h = location.hostname;
		if (window["gidBrandSiteConstruct"]) {
			if (gidBrandSiteConstruct.sslPrefix && gidBrandSiteConstruct.sslPrefix != "") securePrefix = gidBrandSiteConstruct.sslPrefix;
			domain = gidBrandSiteConstruct.cookieDomain;
			renderOnOldDomain = gidBrandSiteConstruct.renderOnCurrentBrandUrl;
			if(h.indexOf(domain) == -1){
				renderOnOldDomain =  true;
			}
		}

		if(renderOnOldDomain){
			securePrefix = "secure."
			domain = (h == "localhost" ?  null : "." + (h.indexOf(securePrefix) == 0 ? h.substr(securePrefix.length+(securePrefix.indexOf(".") != -1 ? 0 : 1)) : h));
		}
		// set cookieExp for persistent cookies
		if ((cookieName.toLowerCase()).indexOf("persist") != -1) {
			exp = (expDate != null ? expDate : gidLib.getFutureDate({"years":5}));
		}

		return {exp:exp,domain:domain,path:path,isSecure:isSecure};
	},

	/**
	 * setCookieVar sets a key value pair in the specified cookie.
	 * If the key already exists, it updates the key value.  If the cookie doesn't exist, it creates a new one.
	 * @param {string} cookieName The cookie name we want to use.  Include "persist" in the name to make it a persistent cookie.  Include "session" in the name to make it a session cookie.
	 * @param {string} cookieVarKey The key of the key-value pair we want to set
	 * @param {string} cookieVarValue The value of the key-value pair we want to set
	 * @param {object} expDate The Date() object representing the date we want to expire the cookie
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	setCookieVar:function(cookieName,cookieVarKey,cookieVarValue,expDate) {
		/*
		if (window["cookieManagementService"]) {
			if (cookieManagementService.api.isCookieManagementServiceReady()) {
				
			} else {
				Event.observe(document, "cookieManagementService:ready", cookieManagementService.controller.cookieManagementServiceReadyHandler);
			}
		}
		*/
		// initialize params
		var newCookieValue = "";

		// Set newCookieValue
		var cookieValue = gidLib.getCookie(cookieName);
		var cookieKeyValuePair = cookieVarKey + "=" + escape(cookieVarValue);
		if (cookieValue == 0 || cookieValue == "") {
			newCookieValue = cookieKeyValuePair;
		} else {
			if (cookieValue.indexOf(cookieVarKey + "=") != -1) {
				newCookieValue = cookieValue.replace(new RegExp(cookieVarKey+"=[^&]*"),cookieKeyValuePair);
			} else {
				newCookieValue = cookieValue + "&" + cookieKeyValuePair;
			}
		}

		var cookieConfiguration = cookieManagementService.api.getCookieConfigurationByCookieName(cookieName);
		if (cookieConfiguration) {
			/* Cookie is managed by cookieManagementService so set all values based on cookieConfiguration */
			gidLib.setCookie(cookieConfiguration.name,
					escape(newCookieValue),
					cookieConfiguration.expirationDate,
					cookieConfiguration.path,
					cookieConfiguration.domain,
					cookieConfiguration.isSecure);
		} else {
			/* Cookie is not managed by cookieManagementService so proceed with default execution */
			var cookieParams = gidLib.setCookieParamsHelper(cookieName,expDate);
			/* Set cookie */
			gidLib.setCookie(cookieName,escape(newCookieValue),cookieParams.exp,cookieParams.path,cookieParams.domain,cookieParams.isSecure);
		}
	},

	/**
	 * getCookieKeyValuePairs returns an array of key value pairs for a specified cookie
	 * @param {string} cookieName The cookie name we want to use.
	 * @return an array of JSON objects that represent the key value pairs in the cookie
	 *
	 * @author Byung Kim
	 * @date 8/11/2008
	 */
	getCookieKeyValuePairs : function(cookieName) {
		var cookieValue = gidLib.getCookie(cookieName);
		var keyValuePairs = [];
		if (cookieValue != 0) {
			var cookieKeyValuePairs = unescape(cookieValue).split("&");
			cookieKeyValuePairs.each(function(keyValuePair) {
				var pair = keyValuePair.split("=");
				keyValuePairs.push({key:pair[0],value:pair[1]});
			});
		}
		return keyValuePairs;
	},

	/**
	 * removeCookieVar removes a key value pair from a cookie
	 * @param {string} cookieName The cookie name we want to use.
	 * @param {string} cookieVarKey The key of the key-value pair we want to remove
	 * @param {object} expDate The Date() object representing the date we want to expire the cookie
	 *
	 * @date 8/11/2008
	 * @author Byung Kim
	 */
	removeCookieVar : function(cookieName, cookieVarKey, expDate) {
		var cookieValue = gidLib.getCookie(cookieName);
		if (cookieValue != 0) {
			cookieValue = cookieValue.replace(new RegExp(cookieVarKey+"=[^&]*&?"),"").replace(/&$/,""); // 2nd replace() removes the trailing "&" if necessary
			var cookieParams = gidLib.setCookieParamsHelper(cookieName,expDate);
			gidLib.setCookie(cookieName,escape(cookieValue),cookieParams.exp,cookieParams.path,cookieParams.domain,cookieParams.isSecure);
		}
	},

	/**
	 * getFutureDate gets the future time based on the current time and a given incremented time.
	 * @param {object} futureDate A JSON Object that represents the incremented time.
	 * 		Valid parameters are: days, weeks, years, hours, minutes, seconds, milliseconds
	 * 		e.g  {"days":7,"years":1,"months":5}  -- denotes a date one year, five months, and seven days in the future
	 * @return newDate The future time in milliseconds
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	getFutureDate:function(futureDate) {
		var newDate = new Date();
		var c = [];
		c["milliseconds"] = 1;
		c["seconds"] = c["milliseconds"]*1000;
		c["minutes"] = c["seconds"]*60;
		c["hours"] = c["minutes"]*60;
		c["days"] = c["hours"]*24;
		c["weeks"] = c["days"]*7;
		c["years"] = c["weeks"]*52;
		var ms = 0;
		var params = Object.keys(futureDate);
		if (params && params.length) {
			params.each(function(param) {
				ms += c[param]*futureDate[param];
			});
		}
		newDate = newDate.setTime(newDate.getTime() + ms);
		return newDate;
	},

	/**
	 * loadImage loads an image into memory. Useful for postloading images.
	 * Not to be used if you only want to create an image object.  Use {src:"/image/path/imageName.gif"} for that.
	 * @param {string} src The path to the image
	 * @return the Image object with the src set
	 *
	 * @author Byung Kim
	 * @date 10/25/2007
	 */
	 loadImage:function(src) {
	 	var obj = document.createElement("img");
	 	$(obj).setSrc(src);
	 	return obj;
	 },

	/**
	 * setObjPosition sets a DOM elements style.top and style.left CSS properties
	 * @param {object} element The DOM element we want to set position
	 * @param {integer} intX The X coordinate value
	 * @param {integer} intY The Y coordinate value
	 *
	 * Modified 12/19/2007 Byung Kim - refactored to use setStyle
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	setObjPosition:function(element,intX,intY) {
		strX = String(intX);
		strY = String(intY);
		$(element).setStyle({
			top: intY + (strY.indexOf("px") == -1 ? "px" : ""),
			left: intX + (strX.indexOf("px") == -1 ? "px" : "")
		});
	},

	/**
	 * setObjCenter sets a DOM element absolutely positioned to the center of the screen
	 * @param {object} target The DOM element we want to modify
	 *
	 * Modified 12/19/2007 Byung Kim - refactored to use setStyle
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	setObjCenter:function(target) {
	    var halfW = (document.body.clientWidth ? document.body.clientWidth : window.innerWidth) * 0.5;
	    var halfH = (document.body.clientHeight ? document.body.clientHeight : window.innerHeight)  * 0.5;
	    $(target).setStyle({
	    	position:"absolute",
	    	left:(halfW - target.offsetWidth * 0.5) + 'px',
	    	top:(halfH - target.offsetHeight * 0.5) + 'px'
	    });
	},

	/**
	 * getQuerystringParam gets the value of the querystrings key-value pair
	 * @param {string} keyArg The key of the key-value pair
	 * @param {boolean} preserveCase Whether to preserve the case of the querystring & keyArg
	 * @return the value of the key-value pair
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	getQuerystringParam:function(keyArg,preserveCase) {
		var query = (preserveCase ? location.search : location.search.toLowerCase());
		var paramKey = (preserveCase ? keyArg : keyArg.toLowerCase());
		var paramVal = "";

	  	var regex = new RegExp("[\\?&]"+paramKey+"=([^&#]*)");
	  	var keyVal = regex.exec(query);
	  	if (keyVal != null) paramVal = keyVal[1];

	  	paramVal = unescape(paramVal);
	  	return paramVal;
	},

	/**
	 * removeQueryStringParam removes a key-value pair from the specified string
	 * @param {string} url The url string to remove the key-value pair from
	 * @param {string} param The key of the key-value pair to remove
	 * @return the modified string
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	removeQueryStringParam:function(url,param) {
		var regExp =  new RegExp("&"+param+"=[^&]*");
		var regExp2 =  new RegExp("/?"+param+"=[^&]*&");

		// param to remove may be a first (preceeded by ?) or subsequent parameter (preceeded by &)
		return (url.match(regExp) ? url.replace(regExp,"") : url.replace(regExp2,""));
	},

	/**
	 * addCurrentDomain takes a relative url and returns a fully qualified url string
	 * @param {string} url The relative url to modify
	 * @return a fully qualified absolute path url based on the current page location protocol & location.host
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	addCurrentDomain:function(url) {
		var input = 'http';
		if(url.substring(0, input.length) === input)
			return url;
		return "http://www.oldnavy.com" + url;
	},

	/**
	 * openWindow opens a popup window
	 * @param {string} url The url of the popup
	 * @param {integer} width The width of the popup.  If one isn't specified, it defaults to the value in brandConst
	 * @param {integer} height The height of the popup.  If one isn't specified, it defaults to the value in brandConst
	 * @param {string} title The name of the popup window
	 * @param {integer} left The X coordinate for where the popup should appear on the screen. If one isn't specified, it defaults to the value in brandConst
	 * @param {integer} top The Y coordinate for where the popup should appear on the screen. If one isn't specified, it defaults to the value in brandConst
	 * @param {boolean} location Whether to display the location bar in the popup. If this isn't specified, it defaults to no.
	 * @param {boolean} resizable Whether the popup is resizable. If this isn't specified, it defaults to no.
	 * @param {boolean} scrollbars Whether to show scrollbars. If this isn't specified, it defaults to no.
	 * @param {boolean} status Whether to display the status bar. If this isn't specified, it defaults to no.
	 * @param {boolean} toolbar Whether to display the toolbar. If this isn't specified, it defaults to no.
	 * @param {boolean} menubar Whether to display the menubar. If this isn't specified, it defaults to no.
	 * @return the window object
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	openWindow:function(url,width,height,title,left,top,location,resizable,scrollbars,status,toolbar,menubar) {
		width = parseInt(width);
		height = parseInt(height);
		width = (isNaN(width))? brandConst.POPUP_DEFAULT_WIDTH : width;
		height = (isNaN(height))? brandConst.POPUP_DEFAULT_HEIGHT : height;
		title = (title)? title : "popup";
		if (!left) {
			screenWidth = screen.availWidth;
			left = (screenWidth)? Math.max((screenWidth/2 - width/2),0) : 100;
		}
		if (!top) {
			screenHeight = screen.availHeight;
			top = (screenHeight)? Math.max((screenHeight/2 - height/2),0) : 100;
		}
		var params = "width=" + width + ",height=" + height + ",left=" +left  + ",top=" + top;
		params += (resizable)? ",resizable=yes" : "";
		params += (scrollbars)? ",scrollbars=yes" : "";
		params += (status)? ",status=yes" : "";
		params += (location)? ",location=yes" : "";
		params += (toolbar)? ",toolbar=yes" : "";
		params += (menubar) ? ",menubar=yes" : "";

		var obj = window.open(url,title,params);
		gidLib.setFocus(obj);
		return obj;
	},

	/**
	 * set800pxUniversalNav sets the Tab Navigation to resize and hide the marketing content
	 * so the width of 5 Tabs can fit in a 800px Screen Width
	 *
	 * @author Keo Keonorasak
	 * @date 01/01/2009
	 */
	set800pxUniversalNav:function() {
	    var screenWidth = screen.width;
	    if((screenWidth != null) && (screenWidth <= 800)){
	    	if (gidBrandSiteConstruct.gidBrandSites){
	    		if (gidBrandSiteConstruct.gidBrandSites.compact().uniq().length > 4){
			    	var universalBarCenter = $("universalBarCenter");
			    	var universalBarCenterContainer = $("universalBarContainer");
			    	var universalMarketingContainerTop = $("universalMarketingContainerTop");
			    	universalBarCenter.className = "universalBarContainer800Screen";
			    	universalBarCenterContainer.className = "universalBarContainer800Screen";
			    	universalMarketingContainerTop.setStyle({width: "54px"});
			    	universalMarketingContainerTop.firstDescendant().setStyle({visibility: "hidden"});
			    }
	    	}
	    }
	},

	/**
	 * closeWindow closes the current window
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	closeWindow:function() {
		if (window.opener) window.opener.focus();
		window.close();
	},

	/**
	 * contentItemLink takes the user to a url but first appends values for reporting
	 * @param {string} domTarget The content item element id
	 * @param {string} strURL The url to modify and add the appropriate parameters
	 * @param {string} linkId Used to distinguish between similar links originating from the same content item
	 * @param {string} urlTarget Used to specify a window target
	 * @return a boolean depending on urlTarget.  It returns false if it needs to target a different window
	 *
	 * Modified 12/18/2007 Byung Kim - check objTarget is null before looking up the className
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	contentItemLink:function(domTarget,strURL,linkId,urlTarget) {
		var objTarget = null;
		var contentItemId = "";
		var strContentItemContainerPrefix = "contentItemContainer";
		var refBusinessId = null;
		var isHardCodedId = (typeof domTarget == "string" || typeof domTarget == "number");
		var isHardCodedURL = (strURL && strURL != '');
		if(!(reportingService||{}).isActive){
			refBusinessId = (window["omni"] ? omni.strCurrentBusinessId : "");
		}
		else {
			refBusinessId = reportingService.controller.viewManagers.commonViewManager.model.commonCurrentBusinessId;
		}
		if (isHardCodedId) {
			objTarget = (contentItemId = domTarget);
		} else {
			objTarget = domTarget;
			do {
				isFound = ((objTarget && objTarget.id && objTarget.id.match(new RegExp(strContentItemContainerPrefix))) || objTarget == null);
				if (!isFound) objTarget = objTarget.parentNode;
			} while (!isFound);
			if ($(objTarget)) contentItemId = $(objTarget).classNames();
		}
		var updateURLString = function(strURL) {
			strURL += (strURL.indexOf("?") == -1 ? "?" : "&") +
				"mlink=" +
				refBusinessId + "," +
				contentItemId +
				(linkId && linkId != "" ? "," + linkId : "") +
				"&clink=" + contentItemId;
			return strURL;
		}
		if (objTarget) {
			if (!isHardCodedURL) {
				if (domTarget.useMap && document.getElementsByName(domTarget.useMap.substr(1)).length > 0) {
					strLink = document.getElementsByName(domTarget.useMap.substr(1))[0].areas[0].href;
				} else if (domTarget.href) {
					strLink = domTarget.href;
				} else if (domTarget.parentNode && domTarget.parentNode.href) {
					strLink = domTarget.parentNode.href;
				}
				strURL = strLink.replace(new RegExp(".+"+location.host),"");
			}

			var isUrl = strURL.match(/^(\/|http|about)/);
			if (isUrl) {
				if (strURL.match(/^\//)) strURL = updateURLString(strURL);
			} else {
				var expURL = new RegExp(/[^']*\.do\?[^']*/g);
				var arrayURLs = strURL.match(expURL);
				if (arrayURLs) {
					for (i=0;i<arrayURLs.length;i++) {
						strURL = strURL.replace(arrayURLs[i],updateURLString(arrayURLs[i]));
					}
				}
			}
			if (urlTarget && urlTarget == "_new") {
					var newWindow = window.open(strURL,'');
					newWindow.focus();
			} else {
				var objTargetWindow = (urlTarget && window[urlTarget] ? window[urlTarget] : window);
					objTargetWindow.location.href = strURL;
			}
			if (!isHardCodedURL) return false;
		} else {
			if (!isHardCodedURL) return true;
		}
	},

	/**
	 * unUnicode converts all unicode characters to ascii
	 * @param {string} str The string to modify
	 * @return the modified string
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	unUnicode:function(str) {
		if (str) {
			var arrayMatch = str.match(/&#[0-9]+;/g);
			if (arrayMatch) {
				for (var i=0;i<arrayMatch.length;i++) {
					str = str.replace(arrayMatch[i],String.fromCharCode(arrayMatch[i].match(/[0-9]+/)));
				}
			}
		}
		return str;
	},

	/**
	 * openGiftCardWindow opens a layered popup with the giftcard balance page
	 * @param {boolean} hasDisplayButton determines whether to show the close button
	 *
	 * Modified 2/15/2008 Byung Kim - updated openLayeredPopup call to new API
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	openGiftCardWindow:function(hasDisplayButton) {
		var shouldDisplayButton = "";
		if (hasDisplayButton) shouldDisplayButton = 'displayButton=true';
		gidLib.layeredPopup.openLayeredPopup({
			str:'/buy/giftCardBalance.do?'+shouldDisplayButton,
			id:'giftCardPopupContent',
			width:brandConst.POPUP_GIFTCARD_WIDTH,
			height:brandConst.POPUP_GIFTCARD_HEIGHT,
			title:'GiftCard'
		});
	},

	/**
	 * isMouseOut
	 *
	 * TODO: Yoshi to add documentation
	 *
	 */
    isMouseOut: function(x, y, boundBox) {
		return (x < boundBox.left ) || (x > boundBox.right) || (y < boundBox.top ) || (y > boundBox.btm) ;
	},

	/**
	 *  isTrueMouseOutEvent is used to determine if the mouseout event occured
	 *  as the user moused out of the given element and not by mousing
	 *  over a childNode within the element.
	 *
	 *  returns true when the user moused out of the element
	 *  returns false when the user moused over a childNode within element
	 *
	 *  accepts optional method to execute when true
	 */
	isTrueMouseOutEvent : function(e,element,method) {
		var eventElement = Event.element(e);
		var mouseOutTarget = $(e.relatedTarget);
		var parentNodes = mouseOutTarget.ancestors();
		var isChildNode = parentNodes.include(element);
		var isTrueMouseOut = (eventElement == element && isChildNode == false);
		if (isTrueMouseOut && method) {
			method();
		}
		return isTrueMouseOut;
	},


	/**
	 * loadScript uses a dynamic script tag to load JS into the site.  Used for cross-domain AJAX.
	 * @param {object} param JSON object with all the required parameters
	 * 			callerObject : The class/object that's calling loadScript
	 * 			src : The src attribute value of the script tag.
	 * 			timeout : JSON object with the handler and args for timing out
	 * 			callBack: JSON object with the handler and args for the callback
	 *
	 * @author Byung Kim
	 * @date 11/2/2007
	 */
	loadScript:function(param) {
		var script = $(document.createElement("script"));
		var targetId = "dynamicScriptLoader";
		script.type = "text/javascript";
		script.src = param.src;
		var target = $(targetId);
		if (!target) {
			target = $(document.createElement("div"));
			target.id = targetId;
			target.setStyle({display:"none"});
		}
		document.body.appendChild(target);
		target.appendChild(script);

		param.callerObject.loadScriptTimedOut = false;
		param.callerObject.loadScriptSuccess = false;

		// Timeout handler
		var timeout = param.timeout;
		if (timeout && timeout.handler) {
			new PeriodicalExecuter(function(periodicalExecuterRef) {
				if (!param.callerObject.loadScriptSuccess) {
					param.callerObject.loadScriptTimedOut = timeout.handler(timeout.args);
				}
				periodicalExecuterRef.stop();
			},timeout.timeDelay);
		}

		// Callback handler
		var callback = param.callback;
		if (callback && callback.handler) {
			new PeriodicalExecuter(function(periodicalExecuterRef) {
				if (param.callerObject.loadScriptTimedOut) {
					periodicalExecuterRef.stop();
				} else {
					param.callerObject.loadScriptSuccess = callback.handler(callback.args,periodicalExecuterRef);
				}
			},callback.timeDelay);
		}
	},

    /**
	 * check if target element contains ele, use for mouseout and mouse over testing
	 * @base GidLib
	 *
	 * @author yoshi
	 * @date 10/21/2008
	 */
    checkMouseEvent: function(ele, target) {
        var isContain;
        if(target.contains) { isContain = !target.contains(ele); }
        if(isContain == undefined && target.compareDocumentPosition) { isContain = target.compareDocumentPosition(ele); }

        return target.contains ? isContain : isContain < 16;
    },

	/**
	 * layeredPopup is a subclass of GidLib for all things related to layered popup functionality
	 * @base GidLib
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	layeredPopup: {
		dragger:null,
		hiddenDropDowns:null,
		effects:null,
		isOpen:false,
		popupId:"",

		/**
		 * layeredPopups can have different styles.  Each object within styles contains everything required to display differently.
		 *
		 * @author Byung Kim
		 * @date 1/8/2008
		 */
		styles : {

			/**
			 * commonTemplates contains templates that are common to all styles
			 *
			 * @author Byung Kim
			 * @date 1/8/2008
			 */
			commonTemplates : {
				iFrameContentTemplate : new Template('<iframe id="#{id}PopupContent" scrolling="#{scrolling}" name="#{id}PopupContent" src="#{src}" class="content" style="width:#{width}px;height:#{height}px;" frameborder="0"></iframe>'),
				markupContentTemplate : new Template('<div id="#{id}PopupContent" class="content" style="width:#{width}px;height:#{height}px;">#{content}</div>'),
				closeAnchorTemplate : new Template('<a href="#" onclick="return gidLib.layeredPopup.closeLayeredPopup();">#{text}</a>'),
				universalCloseButtonTemplate : new Template('<img src="/assets/common/clear.gif" alt="#{altText}" class="universalButtonSprite universalButtonSpriteCloseWindowOn"/>')
			},

			/**
			 * brandedPopup is a style type for layeredPopup display
			 *
			 * @author Byung Kim
			 * @date 1/8/2008
			 */
			brandedPopup : {
				constants : brandConst,
				templates : {
					mainTemplate : new Template(
						'<div id="popupMain" style="width:#{totalWidth}px;">' +
							'<div class="topBorder clearfix">' +
								'<div class="topLeftCorner"></div>' +
								'<div id="layeredPopup#{topId}FrameTop" class="topMiddle#{topClass}" style="width:#{topWidth}px;">#{title}</div>' +
								'<div class="topCloseButton"><a href="#" onclick="return gidLib.layeredPopup.closeLayeredPopup();">&#160;</a></div>' +
								'<div class="topRightCorner"></div>' +
							'</div>' +
							'<div class="mainContent clearfix">' +
								'<div class="leftBar" style="height:#{borderHeight}px;"></div>' +
								'#{content}' +
								'<div class="rightBar" style="height:#{borderHeight}px;"></div>' +
							'</div>' +
							'<div class="bottom clearfix ">' +
								'<div class="bottomLeftCorner"></div>' +
								'<div class="bottomMiddle" style="width:#{innerContentWidth}px;">#{bottonCloseButton}</div>' +
								'<div class="bottomRightCorner"></div>' +
							'</div>' +
						'</div>'
					)
				},

				/**
				 * getMarkup returns the markup for brandedPopup styled popups
				 *
				 * @author Byung Kim
				 * @date 1/8/2008
				 */
				getMarkup:function(useIFrame, width, height, title, strContent, id, style, scrolling) {
					var constants = this.constants;
					var templates = this.templates;
					var commonTemplates = gidLib.layeredPopup.styles.commonTemplates;
					var topId = ""
					var topClass = "";
					var content = "";
					var bottomCloseButton = (brandConst.LAYERED_POPUP_CLOSE_COPY != "" ? commonTemplates.closeAnchorTemplate.evaluate({copy:brandConst.LAYERED_POPUP_CLOSE_COPY}) : "");
					var topTitle = brandConst.LAYERED_POPUP_BRAND_NAME + (title && title != "" ? " - " + title : "");
					if (!topTitle || topTitle == "") topTitle = "&#160;";

					var innerContentWidth = width - constants.LAYERED_POPUP_SIDE_IMAGE_WIDTH;
					var borderHeight = height + constants.LAYERED_POPUP_CONTENT_COMBINED_BORDER_HEIGHT;

					if (useIFrame) {
						topId = "NoDrag";
						content = commonTemplates.iFrameContentTemplate.evaluate({
							id:id,
							src:gidLib.addCurrentDomain(strContent),
							width:innerContentWidth,
							height:height,
							scrolling:scrolling
						});
					} else {
						topClass = " cursorMove";
						content = commonTemplates.markupContentTemplate.evaluate({
							id:id,
							content:strContent,
							width:innerContentWidth,
							height:height
						});
					}

					return templates.mainTemplate.evaluate({
						topId:topId,
						topClass:topClass,
						totalWidth:width+constants.LAYERED_POPUP_COMBINED_BORDER_WIDTH+constants.LAYERED_POPUP_CONTENT_COMBINED_BORDER_WIDTH+constants.LAYERED_POPUP_SHADOW_WIDTH,
						topWidth:width-constants.LAYERED_POPUP_MAIN_WIDTH_OFFSET,
						innerContentWidth:innerContentWidth,
						borderHeight:borderHeight,
						title:topTitle,
						content:content,
						bottomCloseButton:bottomCloseButton
					});
				}
			},

			/**
			 * universalPopup is a style type for layeredPopup display
			 *
			 * @author Byung Kim
			 * @date 1/8/2008
			 */
			universalPopup : {
				constants : {
					LAYERED_POPUP_COMBINED_BORDER_WIDTH: 21, // left and right border combined widths
					LAYERED_POPUP_COMBINED_BORDER_HEIGHT: 59, // top and bottom border combined heights
					LAYERED_POPUP_MAIN_WIDTH_OFFSET:19 // close window button width
				},
				templates : {
					mainTemplate : new Template(
						'<div id="popupMain" class="universalLayeredPopup" style="width:#{popupWidth}px;">' +
							'<div class="topBorder clearfix">' +
								'<div class="pop-sprites topLeftCorner"></div>' +
								'<div id="layeredPopup#{topId}FrameTop" class=" pop-sprites topMiddle#{topClass}" style="width:#{topWidth}px;">#{title}</div>' +
								'<div class="topCloseButton">#{topCloseButton}</div>' +
								'<div class="pop-sprites topRightCorner"></div>' +
							'</div>' +
							'<div class="mainContent clearfix">' +
								'<div class="leftBar" style="height:#{borderHeight}px;"></div>' +
								'#{content}' +
								'<div class="rightBar" style="height:#{borderHeight}px;"></div>' +
							'</div>' +
							'<div class="bottom clearfix ">' +
								'<div class="pop-sprites bottomLeftCorner"></div>' +
								'<div class="pop-sprites bottomMiddle" style="width:#{innerContentWidth}px;"></div>' +
								'<div class="pop-sprites bottomRightCorner"></div>' +
							'</div>' +
						'</div>'
					)
				},

				/**
				 * getMarkup returns the markup for universalPopup styled popups
				 *
				 * @author Byung Kim
				 * @date 1/8/2008
				 */
				getMarkup:function(useIFrame, width, height, title, strContent, id, style, scrolling) {
					var constants = this.constants;
					var templates = this.templates;
					var commonTemplates = gidLib.layeredPopup.styles.commonTemplates;
					var topId = ""
					var topClass = "";
					var content = "";
					var topCloseButton = commonTemplates.closeAnchorTemplate.evaluate({text:commonTemplates.universalCloseButtonTemplate.evaluate({path:brandConst.UNIVERSAL_BUTTON_CONTENT_PATH,altText:resourceBundleValues.infoPopupsAltTextClose })});
					var topTitle = title;
					if (!topTitle || topTitle == "") topTitle = "&#160;";

					if (useIFrame) {
						topId = "NoDrag";
						content = commonTemplates.iFrameContentTemplate.evaluate({
							id:id,
							src:gidLib.addCurrentDomain(strContent),
							width:width,
							height:height,
							scrolling:scrolling
						});
					} else {
						topClass = " cursorMove";
						content = commonTemplates.markupContentTemplate.evaluate({
							id:id,
							content:strContent,
							width:width,
							height:height
						});
					}

					return templates.mainTemplate.evaluate({
						popupWidth:width + constants.LAYERED_POPUP_COMBINED_BORDER_WIDTH,
						topId:topId,
						topClass:topClass,
						topWidth:width-constants.LAYERED_POPUP_MAIN_WIDTH_OFFSET,
						innerContentWidth:width,
						borderHeight:height,
						title:topTitle,
						content:content,
						topCloseButton:topCloseButton
					});
				}
			},

			/**
			 * universalPanel is a style type for layeredPopup display
			 *
			 * @author Byung Kim
			 * @date 1/8/2008
			 */
			universalPanel : {
				constants : {
					PANEL_COMBINED_BORDER_WIDTH: 14, // left and right border combined widths
					PANEL_MAIN_WIDTH_OFFSET:27, // to calculate the main width from the content width
					PANEL_TITLE_WIDTH_OFFSET:50 // to calculate the title width
				},
				templates : {
					mainTemplate : new Template(
						'<div id="popupMain" class="universalPanel" style="width:#{mainWidth}px;">' +
							'<div class="row top clearfix" style="width:#{topBottomWidth}px;">' +
								'<div class="pop-sprites topLeft">&#160;</div>' +
								'<div class="pop-sprites topCenter" style="width:#{topBottomCenterWidth}px;">&#160;</div>' +
								'<div class="pop-sprites topRight">&#160;</div>' +
							'</div>' +
							'<div class="row">' +
								'<div class="leftCenter clearfix" style="width:#{leftCenterWidth}px;">' +
									'#{calloutLeft}' +
									'<div class="rightCenter" style="width:#{rightCenterWidth}px;#{offset}">' +
										'<div class="close" style="width:#{closeWidth}px;">' +
											'#{topBar}' +
											'<div class="panelContent" style="width:#{contentWidth}px;height:#{contentHeight}px;">#{content}</div>' +
										'</div>' +
									'</div>' +
								'</div>' +
							'</div>' +
							'<div class="row bottom clearfix" style="width:#{topBottomWidth}px;">' +
								'<div class="pop-sprites bottomLeft">&#160;</div>' +
								'<div class="pop-sprites bottomCenter" style="width:#{topBottomCenterWidth}px;">&#160;</div>' +
								'<div class="pop-sprites bottomRight">&#160;</div>' +
							'</div>' +
							'#{calloutBottom}' +
						'</div>'
					),

					panelCalloutBottomTemplate : new Template('<div class="pop-sprites row callout" style="#{offset}">&#160;</div>'),
					panelCalloutLeftTemplate : new Template('<div id="universalPanelLeftCallout" class="pop-sprites calloutLeft" style="#{offset}">&#160;</div>'),
					panelTitleTemplate : new Template('<div class="panelTitle" style="width:#{width}px;">#{title}</div>'),
					panelCloseButtonTemplate : new Template('<div class="closeButton">#{topCloseButton}</div>'),
					panelTopBarTemplate : new Template('<div class="clearfix">#{titleTemplate}#{buttonTemplate}</div>')
				},

				/**
				 * getMarkup returns the markup for universalPanel styled popups
				 *
				 * @author Byung Kim
				 * @date 1/8/2008
				 */
				getMarkup:function(useIFrame, width, height, title, strContent, id, style, scrolling) {
					var constants = this.constants;
					var templates = this.templates;
					var commonTemplates = gidLib.layeredPopup.styles.commonTemplates;
					var hasCloseButton = true;
					var callout = null;
					var calloutOffset = null;
					var content = "";
					var mainWidth = width+constants.PANEL_MAIN_WIDTH_OFFSET;
					if (style && style.hasCloseButton != undefined) hasCloseButton = style.hasCloseButton;
					if (style && style.callout) callout = style.callout;
					if (style && style.calloutOffset) calloutOffset = style.calloutOffset;
					var topTitle = "";
					var topCloseButton = "";
					var calloutLeft = "";
					var calloutBottom = "";
					var topBar = "";

					if (title) {
						topTitle = templates.panelTitleTemplate.evaluate({
							title:title,
							width:mainWidth-constants.PANEL_TITLE_WIDTH_OFFSET
						})
					}

					if (hasCloseButton) {
						topCloseButton = templates.panelCloseButtonTemplate.evaluate({
							topCloseButton:commonTemplates.closeAnchorTemplate.evaluate({
								text:commonTemplates.universalCloseButtonTemplate.evaluate({
									path:brandConst.UNIVERSAL_BUTTON_CONTENT_PATH, altText:resourceBundleValues.infoPopupsAltTextClose
								})
							})
						})
					}

					if (title || hasCloseButton) {
						topBar = templates.panelTopBarTemplate.evaluate({
							titleTemplate:topTitle,
							buttonTemplate:topCloseButton
						});
					}

					if (callout == "left") {
						calloutOffset = "top:"+calloutOffset+"px;"
						calloutLeft = templates.panelCalloutLeftTemplate.evaluate({offset:calloutOffset});
					} else if (callout == "bottom") {
						calloutOffset = "left:"+calloutOffset+"px;"
						calloutBottom = templates.panelCalloutBottomTemplate.evaluate({offset:calloutOffset});
					}

					if (useIFrame) {
						topId = "NoDrag";
						content = commonTemplates.iFrameContentTemplate.evaluate({
							id:id,
							src:gidLib.addCurrentDomain(strContent),
							width:width,
							height:height,
							scrolling:scrolling
						});
					} else {
						topClass = " cursorMove";
						content = commonTemplates.markupContentTemplate.evaluate({
							id:id,
							content:strContent,
							width:width,
							height:height
						});
					}

					return templates.mainTemplate.evaluate({
						mainWidth:mainWidth,
						topBottomWidth:mainWidth,
						topBottomCenterWidth:(mainWidth-constants.PANEL_COMBINED_BORDER_WIDTH),
						leftCenterWidth:mainWidth,
						rightCenterWidth:mainWidth,
						closeWidth:width,
						topBar:topBar,
						contentWidth:width,
						contentHeight:height,
						content:content,
						calloutLeft:calloutLeft,
						calloutBottom:calloutBottom
					});
				}
			}
		},

		/**
		 * openLayeredPopup opens a DHTML layer that looks like a popup.
		 * @param {object} argsGraph The JSON object with values for the popup
		 * 		str : The content of the layered Popup.  This can be a string of HTML, a URL to open, or a form to submit
		 * 		id : The id for the layer parent element
		 * 		width : The width of the popup
		 * 		height : The height of the popup
		 * 		title : The title to display in the fake titlebar
		 * 		left : The x coordinate to position the popup
		 * 		top : The y coordinate to position the popup
		 * 		calleeElement : The calling element for the event
		 *		isDraggable : Whether the popover is draggable by clicking on it
		 * 		style : A JSON object for the look and feel of the popup.
		 * 			name : which template to use (optional, default is the brandedPopup template)
		 * 			hasCloseButton : whether to display the close button for the universalPanel (optional, default is true)
		 * 			callout : used for displaying a callout for the universalPanel. values are 'left' or 'bottom' (optional, default is no callout)
		 * 			calloutOffset : used to offset the callout on the left or top depending on which is used.
		 *			isDraggable : Whether the poopup is draggagle (defaults to true)
		 *		scrolling : The scrolling attribute of the iframe popup (eg: yes no, auto)
		 * 		interstitial : JSON object -- when it exists, display as an interstitial (rest of the page greys out)
		 * 			color: hex value color of the interstitial (e.g. "#fff")
		 * 			opacity: opacity of the interstitial (e.g. "0.7" is 70%)
		 * 			buzz: whether to buzz the popup if a user clicks on the interstitial (e.g. true or false)
		 * 		effects : JSON object describing how to show and hide the layeredPopup.  Default is CSS visibility visible/hidden
		 * 			show : JSON object for showing the layered popup
		 * 				method : Scriptaculous Effect method
		 * 				args : JSON object of arguments for the method
		 * 			hide : JSON object for hiding the layered popup
		 * 				method : Scriptaculous Effect method
		 * 				args : JSON object of arguments for the method
		 *
		 *
		 * Modified 12/19/2007 Byung Kim - converted method arguments to JSON with legacy API support
		 * Modified 1/1/2008 Byung Kim - reorganized to accept different styles
		 * Modified 1/8/2008 Byung Kim - updated FORM submit implementation
		 * Modified 1/31/2008 Byung Kim - added interstitial option
		 * Modified 7/25/2008 Byung Kim - added effects option for showing / hiding layered popup
		 * Modified 7/31/2008 Byung Kim - added callee element arg for returning focus to the callee once the popup closes
		 * Modified 9/17/2010 Prabhakar Doraisamy - added scrolling arg to turn on or off the iframe scrolling (for IEs)
		 *
		 * TODO: refactor implementation to use only JSON
		 * TODO: implement a dynamic root element for the popups
		 *
		 * @author Byung Kim
		 * @date 10/24/2007
		 */
		openLayeredPopup:function(argsGraph) {
			var args = arguments;
			if (args.length > 1) {
				// FOR LEGACY SUPPORT ONLY.  New implementations should use JSON object graph for arguments
				var str = args[0];
				var id = args[1];
				var width = args[2];
				var height = args[3];
				var title = args[4];
				var left = args[5];
				var top = args[6];
			} else {
				var str = argsGraph.str;
				var id = argsGraph.id;
				var width = argsGraph.width;
				var height = argsGraph.height;
				var title = argsGraph.title;
				var left = argsGraph.left;
				var top = argsGraph.top;
				var scrolling = argsGraph.scrolling;
				var isDraggable = argsGraph.isDraggable;
				var style = argsGraph.style;
				var interstitial = argsGraph.interstitial;
				this.effects = argsGraph.effects;
				this.calleeElement = argsGraph.calleeElement;
			}
			var popup = $("popupContent");

			var layerStyle = this.styles[(style && style.name ? style.name : "brandedPopup")];
			var constants = layerStyle.constants;
			var useIFrame = false;
			var isForm = false;
			var strContent = "";
			var strIgnoredFormTypes = "submit,button,image,file,reset";
			this.popupId = id;

			popup.setStyle({visibility:"hidden",display:"block"});

			if (str.match(/^(\/|http|about)/)) {
				useIFrame = true;
				strContent = str;
				var cid = str.substr(str.indexOf('?')+1, str.length);
			} else {
				var targetElement = $(str);
				if (targetElement) {
					if (targetElement.tagName && targetElement.tagName.toLowerCase() == 'form') {
						useIFrame = true;
						isForm = true;
						strContent = "/gid/html/en/blank.html";
						targetElement.target = id + "PopupContent";
					} else {
						// Copy the innerHTML
						strContent = targetElement.innerHTML;
					}
				} else {
					// Copy the string literally
					strContent = str;
				}
			}
			popup.update(layerStyle.getMarkup(useIFrame,width,height,title,strContent,id,style,scrolling));
			if (!useIFrame && isDraggable) {
				gidLib.setButtonEvents("popupContent");
				this.dragger = new Draggable('popupContent',{handle:'layeredPopupFrameTop'});
			}
			this.hiddenDropDowns = gidLib.hideDropDownsUnderElement(popup);
			if (interstitial) this.openInterstitialDisplay(interstitial);
			this.setPosition(popup,left,top);
			this.launchLayeredPopup(id);
			if (isForm) targetElement.submit();
			return false;
		},

		/**
		 * setPosition sets the position of the layeredPopup
		 *
		 * Modified 7/23/2008 - Byung Kim : removed scrollTop, scrollLeft calculation on x,y coordinates.  values passed to openlayeredpopup should account for the scroll values
		 *
		 * @author Byung Kim
		 * @date 1/8/2008
		 */
		setPosition : function(popup,x,y) {
			var popupX = parseInt(x);
			var popupY = parseInt(y);
			var dimensions = popup.getDimensions();
			if (isNaN(popupX) || isNaN(popupY)) {
				var clientWidth = (document.documentElement && document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth);
				var clientHeight = (document.documentElement && document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight);
				var scrollLeft = (document.documentElement && document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft);
				var scrollTop = (document.documentElement && document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop);
				if (clientBrowser.isSafari) {
					/*
					TD 14933: Safari does not seem to properly support the document.documentElement.clientHeight -
					reporting a constant value of 1330.
					*/
					var clientHeight = window.innerHeight;
				}
				popupX = Math.max(0,clientWidth/2 - dimensions.width/2)+scrollLeft;
				popupY = Math.max(0,clientHeight/2 - dimensions.height/2)+scrollTop;
				// alert("clientBrowser.isSafari = " + clientBrowser.isSafari + "\rwindow.innerHeight = " + window.innerHeight + "\rdocument.documentElement.clientHeight = " + document.documentElement.clientHeight + "\r\rclientHeight = " + clientHeight + "\rclientWidth = " + clientWidth + "\r\rpopupX = " + popupX + "\rpopupY = " + popupY);
			}
			gidLib.setObjPosition(popup,popupX,popupY);
		},

		/**
		 * launchLayeredPopup reveals the layered popup
		 *
		 * @author Byung Kim
		 * @date 7/25/2008
		 */
		launchLayeredPopup : function(id) {
			var effects = this.effects;
			var popup = $("popupContent");

			var afterFinish = function(id) {
				setTimeout("gidLib.setFocus($('popupContent'))",100);
				this.isOpen = true;
			};

			if (effects && effects.show && effects.show.method) {
				popup.setStyle({display:"none",visibility:"visible"});
				var method = effects.show.method;
				var args = effects.show.args;
				var aF = {afterFinish:afterFinish.bind(this,id)};
				if (args) {
					Object.extend(args,aF);
					method(popup,args);
				} else {
					method(popup,aF);
				}
			} else {
				popup.setStyle({visibility:"visible",display:"block"});
				afterFinish(id);
			}
		},

		/**
		 * closeLayeredPopup closes the layered popup
		 *
		 * Modified 7/25/2008 Byung Kim - added effect
		 * Modified 7/31/2008 Byung Kim - added returning focus to the callee element
		 *
		 * @author Byung Kim
		 * @date 10/24/2007
		 */
		closeLayeredPopup:function() {
			var popup = $("popupContent");
			this.closeInterstitialDisplay();
			var afterFinish = function() {
				while(popup.hasChildNodes()) {
					popup.removeChild(popup.firstChild);
				}
				gidLib.showDropDowns(this.hiddenDropDowns);
				this.hiddenDropDowns = null;
				if (this.dragger) this.dragger.destroy();
				if (this.calleeElement) {
					gidLib.setFocus(this.calleeElement);
				}
				this.isOpen = false;
				this.popupId = "";
			};

			var effects = this.effects;
			if (effects && effects.hide && effects.hide.method) {
				var method = effects.hide.method;
				var args = effects.hide.args;
				var aF = {afterFinish:afterFinish.bind(this)};
				if (args) {
					Object.extend(args,aF);
					method(popup,args);
				} else {
					method(popup,aF);
				}
			} else {
				popup.setStyle({visibility:"hidden",display:"block"});
				afterFinish();
			}
			return false;
		},

		/**
		 * openInterstitialDisplay adds an interstitial layer behind the layeredPopup to grey out the page.
		 *
		 * @author Byung Kim
		 * @date 1/30/2008
		 */
		openInterstitialDisplay:function(args) {
			var color = (args.color ? args.color : "#fff");
			var opacity = (args.opacity ? args.opacity : "0.65");
			this.hasBuzz = (args.buzz != undefined ? args.buzz : true);
			var id = "layeredPopupInterstitial";
			var interstitial = $(id);
			if (!interstitial) {
				var interstitial = $(document.createElement("div"));
				interstitial.id = id;
				document.body.appendChild(interstitial);
				Event.observe(interstitial,"click",this.interstitialBuzzEffect.bind(this));
			}
			var clientWidth = (document.documentElement && document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth);
			var clientHeight = (document.documentElement && document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight);
			var scrollLeft = (document.documentElement && document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft);
			var scrollTop = (document.documentElement && document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop);
			var docW = clientWidth + scrollLeft;
			var docH = (clientBrowser.isIE ? document.body.offsetHeight : (document.documentElement ? document.documentElement.offsetHeight : document.body.offsetHeight));
			if (clientHeight > docH) docH = clientHeight;
			interstitial.setStyle({
				position:"absolute",
				top:"0px",
				left:"0px",
				width:docW+"px",
				height:docH+"px",
				display:"block",
				background:color,
				zIndex:"98",
				opacity:opacity,
				filter:"alpha(opacity=" + (opacity*100) + ")"
			});
		},

		/**
		 * closeInterstitialDisplay hides the interstitial layer
		 *
		 * @author Byung Kim
		 * @date 1/30/2008
		 */
		closeInterstitialDisplay:function() {
			var interstitial = $("layeredPopupInterstitial");
			if (interstitial) {
				interstitial.setStyle({display:"none"});
				Event.stopObserving(interstitial,"click",this.interstitialBuzzEffect.bind(this));
			}
		},

		/**
		 * interstitialBuzzEffect does the effect for the popup
		 *
		 * @author Byung Kim
		 * @date 1/31/2008
		 */
		interstitialBuzzEffect:function(hasBuzz) {
			if (this.hasBuzz) Effect.Shake("popupContent");
		}
	},

	/**
	 * rolloverBubble is a subclass of GidLib for all functionality related to rollover bubbles.
 	 * Usage Example:
 	 * 		<a href="#" onmouseover="gidLib.rolloverBubble.openBubble(event,{content:'Hello World!',align:'left',vposition:'top'});" onmouseout="gidLib.rolloverBubble.closeBubble();" onclick="return false;"><img .../></a>
 	 *
 	 * Note on accessibility:
 	 * 		The text that goes in the bubble should also be represented in the alt tag of the image or as a visually hidden element.
 	 *
	 * @base GidLib
	 *
	 * @author Byung Kim
	 * @date 12/19/2007
	 * @edited Keo
	 * @date 03/21/2008
	 * Note: Added the vertical positioning of the bubble's location
	 */
	 rolloverBubble:{
	 	hiddenDropDowns:null,
	 	targetElement:null,
	 	bubbleElement:null,
	 	calloutSide:"",
	 	calloutVert:"",

	 	constants:{
	 		CONTAINER_ID:"rolloverBubbleContainer",
	 		CONTENT_ID:"rolloverBubbleContent",
	 		LEFT_POSITION_OFFSET:(clientBrowser.isIE ? 4 : 12),
	 		RIGHT_POSITION_OFFSET:(clientBrowser.isIE ? 30 : 23),
	 		TOP_POSITION_OFFSET:2,
	 		BOTTOM_POSITION_OFFSET:20
	 	},

	 	templates:{
		 	mainTemplate : new Template(
		 		'<div class="bubbleCallout callout#{calloutTop}"></div>' +
		 		'<div class="bubbleTop clearfix">' +
		 			'<div class="bubble-sprites left"></div>' +
		 			'<div class="bubble-sprites center"></div>' +
		 			'<div class="bubble-sprites right"></div>' +
		 		'</div>' +
		 		'<div id=#{contentId} class="bubbleMiddle #{calloutTextAlign} clearfix">#{content}</div>' +
		 		'<div class="bubbleBottom clearfix">' +
		 			'<div class="bubble-sprites left"></div>' +
		 			'<div class="bubble-sprites center"></div>' +
		 			'<div class="bubble-sprites right"></div>' +
		 		'</div>' +
		 		'<div class="bubble-sprites bubbleCallout callout#{calloutBottom}"></div>'
		 	)
	 	},

	 	/**
	 	 * openBubble opens the rollover bubble. It dynamically creates the containing element if it doesn't exist in the DOM.
	 	 * The content of the rollover bubble is not refreshed and the position is not recalculated if the user rolls over the same bubble again.
	 	 * We use visibility instead of display because the offsetHeight needs to be calculated.  display:none returns an offsetHeight of 0px.
	 	 * @param {object} e The event object
	 	 * @param {object} args A JSON object containing:
	 	 * 		content : the string to display in the bubble
	 	 *		align : optional - determines where the callout appears on the bubble.  Values are "left" or "right".  "left" is the default value
	 	 *		vposition : optional - determines to appear above or belove the element  "top" is the default value
	 	 *
	 	 * @author Byung Kim
	 	 * @date 12/20/2007
	 	 * @edit Keo
	 	 * @date 03/21/2008
	 	 */
	 	openBubble:function(e,args) {
	 		var constants = this.constants;
	 		var containerId = constants.CONTAINER_ID;
	 		var contentId = constants.CONTENT_ID;
	 		var content = $(contentId);
	 		var container = $(containerId);
	 		if (!container) {
	 			container = $(document.createElement("div"));
	 			container.id = containerId;
	 			container.setStyle({
	 				visibility:"hidden",
	 				position:"absolute"
	 			});
	 			document.body.appendChild(container);
	 		}
	 		if (!content || (content && content.innerHTML != args.content) ) {
		 		this.targetElement = Event.element(e);
		 		this.calloutSide = (args.align ? args.align.capitalize() : "Left");
		 		var calloutSide = this.calloutSide;
		 		var calloutTextAlign = (args.textalign ? args.textalign.capitalize() : "Left");
		 		var calloutTop = "None";
		 		var calloutBottom = "Bottom"+calloutSide;
		 		this.calloutVert = (args.vposition ? args.vposition.capitalize() : "Top");
		 		var calloutVert = this.calloutVert;
	 			if(calloutVert == "Bottom"){
	 				calloutTop = "Top"+calloutSide;
	 				calloutBottom = "None";
	 			}
	 			container.update(this.templates.mainTemplate.evaluate({
	 				contentId:contentId,
	 				content:args.content,
	 				calloutTop:calloutTop,
	 				calloutBottom:calloutBottom,
	 				calloutTextAlign:calloutTextAlign
	 			}));
	 			this.bubbleElement = container;

	 			this.setBubblePosition();
	 		}
			this.hiddenDropDowns = gidLib.hideDropDownsUnderElement(container);
 			container.setStyle({visibility:"visible"});

 			Event.observe(window,"resize",this.setBubblePosition.bind(this));
	 	},

	 	/**
	 	 * setBubblePosition sets the position of the bubble
	 	 *
	 	 * @author Byung Kim
	 	 */
	 	setBubblePosition:function() {
	 		var position = Position.cumulativeOffset(this.targetElement);
	 		var constants = this.constants;
	 		var bubbleElement = this.bubbleElement;
 			gidLib.setObjPosition(bubbleElement,
 				position[0]-(this.calloutSide == "Left" ? constants.LEFT_POSITION_OFFSET : (bubbleElement.offsetWidth - constants.RIGHT_POSITION_OFFSET)),
 				this.calloutVert == "Top" ? position[1]-bubbleElement.offsetHeight-constants.TOP_POSITION_OFFSET : position[1]+constants.BOTTOM_POSITION_OFFSET
 			);
	 	},

	 	/**
	 	 * closeBubble closes the rollover bubble.  Note that it does not clear the markup contents incase the user rolls over the same bubble again.
	 	 *
	 	 * @author Byung Kim
	 	 * @date 12/20/2007
	 	 */
	 	closeBubble:function() {
	 		var container = $(this.constants.CONTAINER_ID);
 			container.setStyle({visibility:"hidden"});
			gidLib.showDropDowns(this.hiddenDropDowns);
			this.hiddenDropDowns = null;
	 	}
	 },

	 inProgressIndicator : {
		 hiddenDropDowns : null,
		 args : null,

		 setPosition : function() {
		 	var args = gidLib.inProgressIndicator.args;
			var requestInProgressMaskNode = $("gidLibRequestInProgressMask");

			if (args.isFullScreen == true) {
				var maskNodeX = 0;
				var maskNodeY = 0;
				var maskNodeWidth = (document.body.clientWidth ? document.body.clientWidth : window.innerWidth);
				var maskNodeHeight = (document.body.clientHeight ? document.body.clientHeight : window.innerHeight);
			} else {
				var targetElement = $(args.targetElement);
				var targetElementDimensions = targetElement.getDimensions();
				var targetElementPosition = Position.cumulativeOffset(targetElement);
				var maskNodeX = targetElementPosition[0];
				var maskNodeY = targetElementPosition[1];
				var maskNodeWidth = targetElementDimensions.width;
				var maskNodeHeight = targetElementDimensions.height;
			}

			requestInProgressMaskNode.style.width = maskNodeWidth + "px";
			requestInProgressMaskNode.style.height = maskNodeHeight + "px";
			requestInProgressMaskNode.style.left = maskNodeX + "px";
			requestInProgressMaskNode.style.top = maskNodeY + "px";

			if (args.messageText) {
				var yOffset = ((maskNodeHeight / 2) + (args.messageYOffset ? Number(args.messageYOffset) : 0)) + "px";
				var xOffset = (args.messageXOffset ? args.messageXOffset : 0) + "px";
				$("gidLibRequestInProgressMaskMessage").setStyle({
					"position" : "relative",
					"top" :  yOffset,
					"left" : xOffset
				});
			}
		},

		createRequestInProgressNode : function() {
			var requestInProgressMaskNode = $("gidLibRequestInProgressMask");
			var args = gidLib.inProgressIndicator.args;
			if (!requestInProgressMaskNode) {
				var element = $(document.createElement("div"));
				element.id = "gidLibRequestInProgressMask";
				document.body.appendChild(element);
				requestInProgressMaskNode = $("gidLibRequestInProgressMask");
			}

			requestInProgressMaskNode.setStyle({
				"background" : "white url(/gid/assets/common/en_US/loadIndicator24.gif) no-repeat scroll center center",
				"height" : "100px",
				"width" : "250px",
				"left" : "0px",
				"top" : "0px",
				"opacity" : (args.opacity ? args.opacity : 0.7),
				"position" : "absolute",
				"zIndex" : 99,
				"textAlign" : "center"
			});

			return requestInProgressMaskNode;
		},

		setInProgressNodeMessage : function() {
			var requestInProgressMaskNode = $("gidLibRequestInProgressMask");
			var args = gidLib.inProgressIndicator.args;
			var message = "";
			if (args.messageText) {
				message = '<div id="gidLibRequestInProgressMaskMessage" style="' + (args.messageStyle ? args.messageStyle : "") + '">' + args.messageText + '</div>';
			}
			requestInProgressMaskNode.update(message);
		},

		/**
		 * parameters for args are as follows:
		 *   'isFullScreen'	 : whether to place the in progress indicator over the entire screen.  If omitted, it assumes we are targetting an element
		 *   'targetElement' : the element to place the in progress indicator over.  Not used when isFullScreen is true.
		 *   'messageText'       : adds a message below the spinny graphic.  Accepts HTML for custom styling.
		 *   'messageYOffset' : offsets the position of the message vertically (by default, it is centered)
		 *   'messageXOffset' : offsets the position of the message horizontally (by default, it is centered)
		 *   'messageStyle'  : additional CSS styling for the message
		 *   'opacity'       : sets the opacity of the interstitial
		 */
		open : function(args) {
			this.args = args;
			var requestInProgressMaskNode = this.createRequestInProgressNode();
			this.setInProgressNodeMessage();
			this.setPosition();
			requestInProgressMaskNode.setStyle({"display":"block"});
			this.hiddenDropDownNodes = gidLib.hideDropDownsUnderElement(requestInProgressMaskNode); // IE 6x layer bug - select inputs show through layers.
			Event.observe(window,"resize",gidLib.inProgressIndicator.setPosition);
	 	},
	 	close : function() {
	 		var requestInProgressMaskNode = $("gidLibRequestInProgressMask");
	 		if (requestInProgressMaskNode) {
		 		requestInProgressMaskNode.setStyle({"display":"none"});
		 		gidLib.showDropDowns(this.hiddenDropDownNodes);  // IE 6x layer bug - select inputs show through layers.
				Event.stopObserving(window,"resize",gidLib.inProgressIndicator.setPosition);
	 		}
	 	}
	 },

	/**
	 * reporting is a subclass of GidLib for functionality specifically for reporting purposes
	 * @base GidLib
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	reporting:{

		/**
		 * getContentItemIds returns contentItemIds array.  used by sOmni.js
		 * @return an array of all contentItemContainer elements in the DOM
		 *
		 * @author Byung Kim
		 * @date 10/24/2007
		 */
		getContentItemIds:function() {
			var elements = $$("[id^=contentItemContainer]");
			var contentItemIds = new Array();
			elementsIterator = function(element) {
				contentItemIds.push(element.className);
			}
			elements.each(elementsIterator);
			return contentItemIds;
		},

		/**
		 * getBrandSite returns the brand site object given the brandCode.  This reads from gidBrandSiteConstruct so it must exist for this to work.
		 * This should be used when trying to retrieve the brandSite object since it performs the check to see if gidBrandSiteConstruct exists or not.
		 * @param brandCode
		 * @return the brand object
		 *
		 * @author Byung Kim
		 * @date 1/29/2008
		 */
		getBrandSite:function(brandCode) {
			var brandSite = null;
			if (window["gidBrandSiteConstruct"] && brandCode) {
				if (gidBrandSiteConstruct.gidBrandSites) brandSite = gidBrandSiteConstruct.gidBrandSites[brandCode];
			}
			return brandSite;
		}
	}
}
var gidLib = new GidLib();


/**
 * InlineBag is a class for functionality related to the inline shopping bag.
 * Instantiated as inlineBag.
 * @constructor
 *
 * @author Byung Kim
 * @date 10/24/2007
 */
var InlineBag = Class.create();
InlineBag.prototype = {
	isOpen:false,
	doOpenBag:false,
	isAnimating:false,
	hasMarketing:false,
	closeDelay:60,
	marketingDelay:0.25,
	headerElementId:"inlineBagHeader",
	headerOpenElementId:"inlineBagHeaderOpen",
	contentClipElementId:"inlineBagClip",
	contentElementId:"inlineBagContent",
	marketingClipElementId:"inlineBagMarketingClip",
	marketingContentElementId:"inlineBagMarketingContent",
	priceElementId:"inlineBagTopPriceLayer",
	priceOpenElementId:"inlineBagTopPriceLayerOpen",
	universalBarElementId:"universalBar",
	contentDataElementId:brandConst.inlineBag.CONTENT_DATA_ELEMENT_ID,
	marketingDataElementId:brandConst.inlineBag.MARKETING_DATA_ELEMENT_ID,
	contentXOffset:brandConst.inlineBag.CONTENT_X_OFFSET,
	contentYOffset:brandConst.inlineBag.CONTENT_Y_OFFSET,
	marketingContentXOffset:brandConst.inlineBag.MARKETING_CONTENT_X_OFFSET,
	marketingContentYOffset:brandConst.inlineBag.MARKETING_CONTENT_Y_OFFSET,
	hiddenDropDowns:[],
	labelColor:brandProperties.inLineBag.labelColor,
	labelSize:brandProperties.inLineBag.labelSize,
	labelQuantity:brandProperties.inLineBag.labelQuantity,
	labelPrice:brandProperties.inLineBag.labelPrice,
	itemInSingular:brandProperties.inLineBag.itemInSingular,
	itemInPlural:brandProperties.inLineBag.itemInPlural,
	lineItemBag:brandProperties.inLineBag.lineItemBag,
	addedTo:brandProperties.inLineBag.addedTo,
	yourBag:brandProperties.inLineBag.yourBag,

	// Holds the data, controller, etc for inlineBag ajax requests
	model:{
	    data:{}
	},
	controller:{
	},
	constants:{
	},

	/**
	 * initialize inlineBag
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	initialize:function() {},

	setWindowResizeHandler:function() {
		if (objInlineBag.isOpen) {
		    objInlineBag.positionInlineBag()

		    if (objInlineBag.hasMarketing) {
			    objInlineBag.positionInlineBagMarketing();
			}
		}
	},

	/**
	 * initializeElements sets pointer references to DOM elements
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	initializeElements:function() {
		this.headerBlock = $(this.headerElementId);
		this.headerBlockOpen = $(this.headerOpenElementId);
		this.inlineBagWrapper = $(this.contentClipElementId);
		this.inlineBagContent = $(this.contentElementId);
	},

	/**
	 * positionInlineBag positions the inline bag display
	 *
	 * Modified 3/17/09 -- subtract the universalbar Y position to fix inlinebag positioning in IE when the preview bar is enabled
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	positionInlineBag:function() {
		if (this.inlineBagWrapper.style && this.inlineBagContent.style) {
			var position = Position.cumulativeOffset($(this.headerBlock));

            var xValue = position[0];
			var yValue = position[1];

			// Need to use the open header for position if the bag is already open
			if (this.isOpen) {
    			position = Position.cumulativeOffset($(this.headerBlockOpen));

	        	xValue = position[0];
			    yValue = position[1] + this.headerBlockOpen.offsetHeight;
	        } else {
    	        yValue += this.headerBlock.offsetHeight;
	        }

			var universalBarPositionY = 0;
	        if ($(this.universalBarElementId) != null && clientBrowser.isIE) {
		        universalBarPositionY = Position.cumulativeOffset($(this.universalBarElementId))[1];
	        }

			var x = xValue + "px";
			var y = (yValue-universalBarPositionY) + "px";
			gidLib.setObjPosition(this.inlineBagWrapper,x,y);
		} else {
			var inlineBag = this;
			new PeriodicalExecuter(function(pe) {
				inlineBag.positionInlineBag();
				pe.stop();
			},0.1);
		}
	},

	/**
	 * positionInlineBagMarketing positions the marketing portion of the inline bag display
	 *
	 * Modified 3/19/09 -- subtract the universalbar Y position to fix inlinebag positioning in IE when the preview bar is enabled
	 *
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	positionInlineBagMarketing:function() {
		var position = Position.cumulativeOffset(this.inlineBagContent);

		var xValue = position[0];
		var yValue = position[1];

		var universalBarPositionY = 0;
        if ($(this.universalBarElementId) != null && clientBrowser.isIE) {
	        universalBarPositionY = Position.cumulativeOffset($(this.universalBarElementId))[1];
        }

		var x = xValue + "px";
		var y = (yValue + this.inlineBagContent.offsetHeight - universalBarPositionY) + "px";
		gidLib.setObjPosition($(this.marketingClipElementId),x,y);
	},

	/**
	 * openInlineBag opens the inline bag display
	 * @param {boolean} isAuto ?? not sure what this is for
	 *
	 * Modified 12/04/2007 Byung Kim - added reference to openInlineBagAfterFinish
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	openInlineBag:function(isAuto) {
		if ((!this.isOpen || $(this.contentDataElementId).innerHTML != "") && (!this.isAnimating || isAuto)) {
			this.isAnimating = true;
			this.headerBlock.hide();
			this.headerBlockOpen.show();

            // Explicitly hide the search section under the inline bag
    		//if (IS_SISTERSITE_TABS_ON == "true") {
            var searchInputs = $('topSearchInputs');
            if(searchInputs) { searchInputs.hide(); }
            //}

            //     Handle dynamic repositioning if browser resized
    		Event.observe(
	    		window,
		    	"resize",
			    this.setWindowResizeHandler
		    );

			Effect.SlideDown(inlineBag.contentElementId,
				{
					duration:0.5,
					afterFinish:this.openInlineBagAfterFinish.bind(this)
				}
			);
		}
 	},

	/**
	 * openInlineBagAfterFinish executes after the inline bag opening animation finishes
	 *
	 * @author Byung Kim
	 * @date 12/04/2007
	 */
 	openInlineBagAfterFinish:function() {
		this.hiddenDropDowns = gidLib.hideDropDownsUnderElement(this.inlineBagWrapper);

		if (this.hasMarketing) {
			this.positionInlineBagMarketing();
			var inlineBag = this;
			new PeriodicalExecuter(function(pe) {
				inlineBag.openInlineBagMarketing();
				pe.stop();
			},this.marketingDelay);
		} else {
			this.openAfterFinish();
		}
 	},

 	/**
 	 * openInlineBagMarketing opens the marketing message portion of the inline bag display
 	 *
	 * Modified 12/04/2007 Byung Kim - added bind() to openAfterFinish

	 * 	 * @author Byung Kim
	 * @date 10/24/2007
 	 */
	openInlineBagMarketing:function() {
		Effect.SlideDown(inlineBag.marketingContentElementId,
			{
				duration:0.5,
				afterFinish:this.openAfterFinish.bind(this)
			}
		);
	},

	/**
	 * openAfterFinish is code executed after the completion of the opening effect
	 *
	 * Modified 12/04/2007 Byung Kim - replaced parent references with "this"
	 * Modified 12/10/2007 Byung Kim - wrapped setFocusToInlineBag call with a PE to fix an IE problem where the inline bag will blank out

	 * 	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	openAfterFinish:function() {
		this.isOpen = true;
		this.isAnimating = false;
		var inlineBag = this;
		new PeriodicalExecuter(function(pe) {
			inlineBag.closeInlineBag();
			pe.stop();
		},this.closeDelay);
		Event.observe(document,"mousedown",this.checkInlineBag.bind(this));
		new PeriodicalExecuter(function(pe) {
			inlineBag.setFocusToInlineBag();
			pe.stop();
		},0.1);
	},

	/**
	 * closeInlineBag closes the inline bag
	 * @param {boolean} isAuto ?? not sure what this is for
	 *
	 * Modified 12/04/2007 Byung Kim - added reference to closeInlineBagAfterFinish
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	closeInlineBag:function(isAuto) {
		if (this.isOpen && (!this.isAnimating || isAuto)) {
			this.isAnimating = true;
			if (this.hasMarketing) {
				this.closeInlineBagMarketing();
			} else {
				Effect.SlideUp(inlineBag.contentElementId,
					{
						duration:0.5,
						afterFinish:this.closeInlineBagAfterFinish.bind(this)
					}
				);
			}
		}
	},

	/**
	 * closeInlineBagAfterFinish executes after the inline bag closing animation finishes
	 *
	 * @author Byung Kim
	 * @date 12/04/2007
	 */
	closeInlineBagAfterFinish:function() {
		this.isOpen = false;
		this.isAnimating = false;
		this.headerBlock.show();
		this.headerBlockOpen.hide();

        // Explicitly redisplay search
        //if (IS_SISTERSITE_TABS_ON == "true") {
        var searchInputs = $('topSearchInputs');
        if(searchInputs) { $('topSearchInputs').show(); }
        //}

		gidLib.showDropDowns(this.hiddenDropDowns);

		Event.stopObserving(document,"resize",this.checkInlineBag.bind(this));

		Event.stopObserving(document,"mousedown",this.checkInlineBag.bind(this));
		this.removeFocusFromInlineBag();
	},

	/**
	 * closeInlineBagMarketing closes the marketing portion of the inline bag display
	 *
	 * Modified 12/04/2007 Byung Kim - added reference to closeInlineBagMarketingAfterFinish
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	closeInlineBagMarketing:function() {
		Effect.SlideUp(inlineBag.marketingContentElementId,
			{
				duration:0.5,
				afterFinish:this.closeInlineBagMarketingAfterFinish.bind(this)
			}
		);
	},

	/**
	 * closeInlineBagMarketingAfterFinish executes after the inline bag marketing closing animation finishes
	 *
	 * @author Byung Kim
	 * @date 12/04/2007
	 */
	closeInlineBagMarketingAfterFinish:function() {
		this.hasMarketing = false;
		var inlineBag = this;
		new PeriodicalExecuter(function(pe) {
			inlineBag.closeInlineBag(true);
			pe.stop();
		},this.marketingDelay);
	},

	/**
	 * setInlineShoppingBagData updates the DOM with the response from the inlineBag data call.
	 * All markup passed to this method is generated within the data call.
	 * @param {string} subTotal The markup for the subTotal
	 * @param {string} markup The markup for the inlinebag display
	 * @param {string} openTotal The markup for the total when the inlinebag is open
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	setInlineShoppingBagData:function(subTotal,markup,openTotal) {
		this.setSubTotal(subTotal,openTotal);

		/*
		 *	Hacked resetting of "absolute" position for inside bag items.
		 *  This is a hacky workaround for a problem with IE6 in which the
		 *  absolutely styled elements were not having their height and width
		 *  set initially
		 */
		Element.setStyle(this.contentClipElementId,{position: 'absolute'});
		Element.setStyle(this.marketingClipElementId,{position: 'absolute'});

		if (markup) $(this.contentDataElementId).update(markup);
		this.positionInlineBag();
	},

	/**
	 * setInlineShoppingBagMarketing updates the marketing message portion of the inline bag display from the data call
	 * @param {string} markup The markup for the marketing message.  The markup is generated in the data call.
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	setInlineShoppingBagMarketing:function(markup) {
		if (markup) $(this.marketingDataElementId).update(markup);
		this.hasMarketing = true;
	},

	/**
	 * setSubTotal sets the subTotal display in the close and open state of the inline bag header
	 * @param {string} subTotal The closed header markup
	 * @param {string} openTotal the open header markup
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	setSubTotal:function(subTotal,openTotal) {
		$(this.priceElementId).update(subTotal);
		if (openTotal) $(this.priceOpenElementId).update(openTotal);
	},

	/**
	 * checkInlineBag checks if the inlinebag is open and closes it.  This is set as a document.mousedown event when the inline bag is open.
	 * This event is removed when the inline bag is closed.
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	checkInlineBag:function() {
		if (this.isOpen) {
			var inlineBag = this;
			new PeriodicalExecuter(function(pe) {
				inlineBag.closeInlineBag();
				pe.stop();
			},0.5);
		}
	},

	/**
	 * Set focus to inline bag for accessibilty story #216
	 *
	 * Modified 12/04/2007 Byung Kim - added local variable and updated references
	 * Modified 2/2/08 Amy Tang - changed focus to inlineBagItemMessage for screen reader
	 *
	 * @author Hillel Familant
	 * @date 12/1/2007
	 */
	setFocusToInlineBag:function() {
		gidLib.setFocus($('inlineBagItemMessageItem'));
	},

	/**
	 * Set focus to inline bag Item Message Item for screenreader story #747
	 *
	 *
	 * @author Amy Tang
	 * @date 2/2/2009
	 */
	setInLineBagMessage:function(message){
		$('inlineBagItemMessageItem').title = message;
	},


	/**
	 * Remove focus from inline bag to one of the main containers:
	 * mainContainer, bodyContainer, etc.. for accessibilty story #216
	 *
	 * Modified 12/04/2007 Byung Kim - added local variables and updated references
	 *
	 * @author Hillel Familant
	 * @date 12/1/2007
	 */
	removeFocusFromInlineBag:function() {
		var mainContent = $('mainContent');
		if (mainContent) gidLib.setFocus(mainContent);
	}
}
var inlineBag = new InlineBag();


/**
 * Site Navigation is a class for tools related to navigating the site.
 * Instantiated as siteNavigation.
 *
 * @constructor
 * @author Andrew Southwick
 * @date 10/24/2007
 */
var SiteNavigation = Class.create();
SiteNavigation.prototype = {
	shoppingBagUrl:"/buy/shopping_bag.do",
	signInUrlTemplate:new Template("#{url}?context=nav&targetURL=#{targetUrl}"),
	signOutUrlTemplate:new Template("#{url}?returnURL=#{returnUrl}&targetURL=#{targetUrl}"),
	joinEmailUrlTemplate:new Template("/profile/join_email_list_confirm.do?targetURL=#{returnUrl}&src=#{src}#{email}"),
	relativeUrl:(top != self ? "/browse/home.do" : window.location.pathname + window.location.search + window.location.hash),

	/**
	 * initialize SiteNavigation
	 *
	 * @author Andrew Southwick
	 * @date 10/24/2007
	 */
	initialize:function() {
		this.initializeSignInUrls();
	},

	/**
	 * initializeSignInUrls initializes the signIn Urls for profile
	 *
	 * @author Byung Kim
	 * @date 11/5/2007
	 */
	initializeSignInUrls:function() {

	    this.relativeUrl = this.relativeUrl.replace("sem=true","");
		this.signInReturnUrl = escape(this.relativeUrl);
		this.signInTargetUrl = escape(this.relativeUrl);
		this.signInShoppingUrl = escape(this.relativeUrl);
	},

	/**
	 * onLoadHandler declares SiteNavigation related methods to be called onload of the page.
	 *
	 * @author Byung Kim
	 * @date 10/30/2007
	 */
	onLoadHandler:function() {
		this.setPreviousPageUrl();
		this.footer.setFooterImages();
	},

	/**
	 * getAccessibleCheckout takes a user to the more accessible checkout path.  To be removed with the 4.2 release
	 * @deprecated
	 *
	 * @author Andrew Southwick
	 * @date 10/24/2007
	 */
	getAccessibleCheckout:function() {
		if (window.location.pathname == this.shoppingBagUrl) {
			if (checkout) {
				if (shoppingBag.stateMonitor.isBuyActive == true) {
					checkout();
				}
			}
		}
		return false;
	},

	/**
	 * goToShoppingBag sends the user to the shopping bag page.  This is currently used in the TopNav.
	 * Only use when an anchor tag cannot be used.  Try to write out the URL as much as possible for SEO & accessibility reasons.
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	goToShoppingBag:function() {
		if (location.pathname != this.shoppingBagUrl) {
			location.href = this.shoppingBagUrl;
		}
	},

	/**
	 * goSignIn takes the user to the sign in page
	 * @param {string} signInHref The url for the sign in page
	 * @return false for cancelling the href or form submit
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	goSignIn:function(signInHref) {
		parent.location.href = this.signInUrlTemplate.evaluate({
	    	url:signInHref,
	    	targetUrl:this.signInTargetUrl
	    });
		return false;
	},

	/**
	 * goSignOut takes the user to the sign out page
	 * @param {string} signOutHref The url for the signout page
	 * @return false for cancelling the href or form submit
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	goSignOut:function(signOutHref) {
		parent.location.href = this.signOutUrlTemplate.evaluate({
			url:signOutHref,
			returnUrl:this.signInReturnUrl,
			targetUrl:this.signInTargetUrl
		});
		return false;
	},

	/**
	 * showShipingOptInOverlay shows the overlay shipping country and currency selection
	 * @return false for cancelling the href or form submit
	 *
	 * @author Filipe Sabella
	 * @date 04/08/2010
	 */
	showShippingOptInOverlay: function() {
		function getLeftMargin() {
			return ((document.body.clientWidth ? document.body.clientWidth : window.innerWidth) / 2) - (width / 2);
		}

		var width = 1016;
		var top = 45;
	    var height = 664;

		new Ajax.Request('/profile/internationalShippingOptInList.do', {
			onComplete: function(response) {
				var currentPopUp = $('internationalShippingPopUp');
				if (currentPopUp) currentPopUp.remove();

				// we were facing some serious ie 6 css loading issues. this is the only place
				// to load the css in a way that works every time without adding the css
				// itself to all pages
				$$('head')[0].appendChild(new Element('link', {
					type : 'text/css',
					rel  : 'stylesheet',
					href : '/gid/css/profile/internationalShipping/en/internationalShipping.css',
					media: 'screen'
				}));

				var style = 'top:' + top + 'px;left:' + getLeftMargin() + 'px;width:' + width + 'px;height: ' + height + 'px';
				var div = new Element('div', {'id': 'internationalShippingPopUp', 'style': style})
							.update(response.responseText);

				document.body.appendChild(div);

				window.onresize = function () {
					div.style.left = getLeftMargin() + 'px';
				};
			}
		});

		return false;
	},
	/**
	 * setCurrentShippingToCountry sets the current selected shipping country on the header
	 * @param countryCode
	 * @param countryName to show on the alt text of the flag's icon
	 * @return false for cancelling the href or form submit
	 *
	 * @author Filipe Sabella
	 * @date 04/12/2010
	 */
	setCurrentShippingToCountry: function(countryCode, countryName) {
		var shippingToCountryFlag = $('shippingToCountryFlag');
		var src = shippingToCountryFlag.src;
		src = src.substring(0, src.lastIndexOf('/') + 1) + countryCode + '.gif';
		shippingToCountryFlag.src = src;

		shippingToCountryFlag.setAttribute('title', countryName);
	},
	/**
	 * goJoinEmail opens the join email url
	 * @param {string} ref The referring content item element id for contentItemLink
	 * @param {string} srcValue A value to pass to the popup url
	 * @param {string} emailAddress A email address value
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	goJoinEmail:function(ref,srcValue,emailAddress) {
			var optionalEmail = (emailAddress != null && emailAddress != "" ? "&emailAddress=" + emailAddress : "");
			var strGoURL = this.joinEmailUrlTemplate.evaluate({
				returnUrl:this.signInReturnUrl,
				src:srcValue,
				email:optionalEmail
			});
			gidLib.contentItemLink(ref,strGoURL);
	},

	/**
	 * setPreviousPageUrl creates a URL for the current page to be set in a cookie
	 * for use to navigate back to the page.  This is done ONLY if the page is one of
	 * the following:
	 *
	 *	home
	 *	division or subdivision
	 *	category
	 *	outfit
	 *
	 * 10/30/2007 Byung Kim - moved method to SiteNavigation Class.
	 *
	 * @author Aaron Liebling
	 * @date 10/25/2007
	 */
	setPreviousPageUrl:function() {
		var path = location.pathname;

		if (/^\/$/.exec(path) ||
			/\/browse\/home\.do/.exec(path) ||
			/division\.do/.exec(path) ||
			/subDivision\.do/.exec(path) ||
			/category.*\.do/.exec(path) ||
			/outfit\.do/.exec(path)) {
			var url = path + location.search;
			gidLib.setCookieVar("globalSession","previousPageUrl",url);
		}
	},


	/**
	 * footer is a subclass of SiteNavigation for footer related functionality.
	 * @base SiteNavigation
	 *
	 * @author Byung Kim
	 * @date 10/25/2007
	 */
	footer:{

		/**
		 * setFooterImages sets properties for footer image swapping
		 * Modified: Byung Kim 11/5/07 -- moved footer image definition from brandFunctions to brandConstants.  Now just read if the object exists in brandConst and extend properties.
		 *
		 * @author Byung Kim
		 * @date 10/25/2007
		 */
		setFooterImages:function() {
			if (brandConst.footer && brandConst.footer.IMAGES) Object.extend(this,brandConst.footer.IMAGES);
		}
	},

	/**
	 * topNav is a subclass of SiteNavigation for TopNav related functionality.
	 * @base SiteNavigation
	 *
	 * @author Byung Kim
	 * @date 10/31/2007
	 */
	topNav: {
		activeDivision:"",
		divisionTimer:0,
		subDivisionTimer:0,

		/**
		 * divisionsOver handles the mouseover event for Division Navigation.
		 * This is to be extended in brandFunctions.js
		 *
		 * @author Byung Kim
		 * @date 10/31/2007
		 */
		divisionOver:function() {},

		/**
		 * divisionsOut handles the mouseout event for Division Navigation.
		 * This is to be extended in brandFunctions.js
		 *
		 * @author Byung Kim
		 * @date 10/31/2007
		 */
		divisionOut:function() {},

		/**
		 * subDivisionOver handles the mouseover event for SubDivision Navigation.
		 * This is to be extended in brandFunctions.js
		 *
		 * @author Byung Kim
		 * @date 10/31/2007
		 */
		subDivisionOver:function() {},

		/**
		 * subDivisionOut handles the mouseout event for SubDivision Navigation.
		 * This is to be extended in brandFunctions.js
		 *
		 * @author Byung Kim
		 * @date 10/31/2007
		 */
		subDivisionOut:function() {},

		/**
		 * subDivisionsOver handles the mouseover event for the whole SubDivision Navigation Menu.
		 * This is to be extended in brandFunctions.js
		 *
		 * @author Byung Kim
		 * @date 10/31/2007
		 */
		subDivisionsOver:function() {},

		/**
		 * subDivisionsOut handles the mouseout event for the whole SubDivision Navigation Menu.
		 * This is to be extended in brandFunctions.js
		 *
		 * @author Byung Kim
		 * @date 10/31/2007
		 */
		subDivisionsOut:function() {},

		/**
		 * hideSubDivisions hides the subdivision navigation menu.
		 * This is to be extended in brandFunctions.js
		 *
		 * @author Byung Kim
		 * @date 10/31/2007
		 */
		hideSubDivisions:function() {}
	},

	/**
	 * searchBox is a subclass of SiteNavigation for functionality related to the site search form
	 * @base SiteNavigation
	 */
	searchBox: {
		hasSearchText:false,

		/**
		 * checkSearchText
		 * once you are on a search page the hasSearchText variable will aways be true - so check the default text explicitly
		 * on clearing the field - also get rid of any error class on the field - so when the user types, it is standard text
		 *
		 */
		checkSearchText:function(objFormField) {
			if (objFormField.value == this.strDefaultText) {
				if (objFormField.className != "searchTextStandard") {
					objFormField.className = "searchTextStandard";
				}
				objFormField.value = "";
			}
		},

		/**
		 * checkInput checks that the user didn't click go with an invalid search term (blank or the default text)
		 *
		 */
		checkInput:function (objForm) {
			//trim whitespace first
			var searchTextInput = objForm.searchText.value.trim();

			if ((searchTextInput == "") || (searchTextInput == this.strDefaultText)) {
				objForm.searchText.value = this.strDefaultText;
				objForm.searchText.className = "searchTextError";
				return false;
			} else {
				/**
 				* Modified:  Keo 07/28/08 - Added wrapper for G to H
 				*/
				if(!(reportingService||{}).isActive){
					omni.objSearchProcessor.setKeyword(objForm.searchText.value, objForm.searchDivName.options[objForm.searchDivName.selectedIndex].text, omni.strCurrentPageName);
				} else {
					var searchAppManager = reportingService.controller.appManagers.searchAppManager;
					var commonModel = reportingService.controller.viewManagers.commonViewManager.model;
					var text = objForm && objForm.searchDivName ? objForm.searchDivName.options[objForm.searchDivName.selectedIndex].text : '';
					searchAppManager.setKeyword(objForm.searchText.value, text, commonModel.commonCurrentPageName);
				}
				return true;
			}
		}
	},

	/**
	 * sideNav is a subclass of SiteNavigation for functionality related to the sidenav.
	 *
	 * TODO: cleanup
	 */
	sideNav: {
		rootElement:$("sideNavCategories"),
		strSelectedHeaderNodeId:"5003",
		strUserExpandedHeaderNodeId:"",
		objCategoryListElement:null,
		objCategoryNavElement:null,

		setCategoryGroupDisplay:function(strCategoryGroupId, strElementExtension) {
			var strExtension = "";
			if (strElementExtension) strExtension = strElementExtension;
			if (strCategoryGroupId != this.strSelectedHeaderNodeId) {
				this.setPreviousCategoryElements(strElementExtension);
				this.setCategoryElements(strCategoryGroupId, strExtension);
				if (this.strUserExpandedHeaderNodeId == "") {
					this.setDisplay(this.objCategoryListElement, this.objCategoryNavElement, true, strElementExtension);
					this.strUserExpandedHeaderNodeId = strCategoryGroupId;
				}
				else if (strCategoryGroupId == this.strUserExpandedHeaderNodeId) {
					// User has expanded a node in the navigation, and now wants to close the same node.
					// setPreviousCategoryElements()
					this.setDisplay(this.objPreviousCategoryListElement, this.objPreviousCategoryNavElement, false, strElementExtension);
					this.strUserExpandedHeaderNodeId = "";
				}
				else if (strCategoryGroupId != this.strUserExpandedHeaderNodeId) {
					// User has expanded a node in the navigation, need to close this node, then expand their new selected node.
					// setPreviousCategoryElements()
					this.setDisplay(this.objPreviousCategoryListElement, this.objPreviousCategoryNavElement, false, this.strPreviousElementExtension);
					this.setDisplay(this.objCategoryListElement, this.objCategoryNavElement, true, strElementExtension);
					this.strUserExpandedHeaderNodeId = strCategoryGroupId;
				}
			}
		},

		setCategoryElements:function(strCategoryGroupId, strExtension) {
			this.objCategoryListElement = $("categoryList" + strCategoryGroupId + strExtension);
			this.objCategoryNavElement = $("categoryNav" + strCategoryGroupId + strExtension);
		},

		setPreviousCategoryElements:function(strElementExtension) {
			this.objPreviousCategoryListElement = this.objCategoryListElement;
			this.objPreviousCategoryNavElement = this.objCategoryNavElement;

			this.strPreviousElementExtension = strElementExtension;
		},

		setDisplay:function(objCategoryListElement, objCategoryNavElement, isVisible, strElementExtension) {
			// alert("inner setDisplay method running");
			if (objCategoryListElement && objCategoryNavElement) {
				var strCssClassSuffix = "";
				if (strElementExtension == "Scrolling") {
					// scrolling header category detected - set class suffix to "scrolling"
					strCssClassSuffix = " scrolling";
					// alert("strCssClassSuffix = " + strCssClassSuffix);
				}

				if (isVisible) {
					objCategoryNavElement.className = "header expanded" + strCssClassSuffix;
					objCategoryListElement.style.display = "list-item";
				}
				else {
					objCategoryNavElement.className = "header collapsed" + strCssClassSuffix;
					objCategoryListElement.style.display = "none";
				}
				// alert("objCategoryNavElement.className = " + objCategoryNavElement.className);
	 		}
		},

		setTrimHeaderSeeAll:function(strCategoryGroupId) {
			this.objCategoryNavElementLimited = $("categoryNav" + strCategoryGroupId + "Limited");
			this.objCategoryNavElementScrolling = $("categoryNav" + strCategoryGroupId + "Scrolling");

			if ((this.objCategoryNavElementLimited != null) || (this.objCategoryNavElementScrolling != null)) {
				this.objCategoryNavElementLimited.style.display = "none";
				this.objCategoryNavElementScrolling.style.display = "list-item";
			}
		},

		setSideNavHeight : function() {
			// standard categories
			var objSideNavCategories = $('sideNavCategories');

			// search categories
			if (!objSideNavCategories) {
				objSideNavCategories = $('sideNavCategoryHeaders');
			}

			// site search refinements
			if (!objSideNavCategories) {
				objSideNavCategories = $('sideNavRefinements');
			}

			var objSideNav = $('sideNav');
			var objContent = $('mainContent');
			
			// Reset styles for sidenav and content
			if (objSideNavCategories) {
				objSideNavCategories.style.height = "";
				objSideNavCategories.style.minHeight = "";
			}
			objContent.style.height = "";
			objContent.style.minHeight = "";
			
			if (objSideNavCategories && objSideNav && objContent) {
				if (objContent.offsetHeight > objSideNav.offsetHeight) {
					var intSideNavOuterHeight = objSideNav.offsetHeight - objSideNavCategories.offsetHeight;
					if (clientBrowser.isIE6) {
						objSideNavCategories.style.height = (objContent.offsetHeight-intSideNavOuterHeight) + 'px';
					} else {
						objSideNavCategories.style.minHeight = (objContent.offsetHeight-intSideNavOuterHeight) + 'px';
					}
				} else if (objSideNav.offsetHeight > objContent.offsetHeight) {
					var strHeight = objContent.offsetHeight + (objSideNav.offsetHeight-objContent.offsetHeight) + 'px';
					if (clientBrowser.isIE6) {
						objContent.style.height = strHeight;
					} else {
						objContent.style.minHeight = strHeight;
					}
				}
			}
		}
	}
};
var siteNavigation = new SiteNavigation();


/**
 * GIDBrandSiteConstructor is a class that is a data store for sister GID site data.
 * Instantiated as gidBrandSiteConstruct in JsGlobalFunctions.jsp by a method in NavLogicTag.java
 * @constructor
 *
 * @author Byung Kim
 * @date 10/24/2007
 */
var GIDBrandSiteConstructor = Class.create();
GIDBrandSiteConstructor.prototype = {

	/**
	 * initialize GIDBrandSiteConstructor
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	initialize:function(
		currentBrandCode,
		cookieDomain,
		currentBrandSiteId,
		sbsViewAllState,
		sitesVisited,
		sbsPreferences,
		sslPrefix,
		isCommonDomainOn,
		renderOnCurrentBrandUrl,
		sisterSiteActionPath,
		gidBrandSites) {
		this.currentBrandCode = currentBrandCode;
		this.cookieDomain  = cookieDomain;
		this.currentBrandSiteId = currentBrandSiteId;
		this.sbsViewAllState = sbsViewAllState;
		this.sbsPreferences = sbsPreferences;
		this.sslPrefix = sslPrefix;
		this.isCommonDomainOn  = isCommonDomainOn;
		this.renderOnCurrentBrandUrl  = renderOnCurrentBrandUrl;
		this.sisterSiteActionPath = sisterSiteActionPath;
		this.hasGIDBrandSites = (gidBrandSites && gidBrandSites.length > 0);
		this.sisterSiteUrlParams = "?isViewAll=" + this.sbsViewAllState + this.sbsPreferences;
		this.tempBrandSites = gidBrandSites;
		this.gidBrandSites = new Array();
		this.setBrandSites();
		this.visitedSites = sitesVisited;

		// Fire sisterSiteSync.syncUser when the DOM is ready except on checkout page
		if (this.hasGIDBrandSites && !this.isCommonDomainOn) {
			processingService.api.addApplicationMethodToRegistry(sisterSiteSync.syncUser.bind(sisterSiteSync), "gidFunctions sisterSiteSync");
		}
	},

	/**
	 * setBrandSites adds brand site objects to gidBrandSites array. The index is the brandCode.
	 * This is done for easy referencing.
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	setBrandSites:function() {
		for (var i=0;i<this.tempBrandSites.length;i++) {
			var brandSite = this.tempBrandSites[i];
			this.gidBrandSites[Number(brandSite.brandCode)] = brandSite;
			var badgePath = brandConst["BRAND"+brandSite.brandCode+"_BADGE_CONTENT_PATH"];
			if (badgePath) this.gidBrandSites[Number(brandSite.brandCode)].badgeContentPath = badgePath;
		}
	},

	/**
	 * openBrand opens another brand's site in the same browser window
	 * @param {integer} brandcode The brand to open
	 * @param {string} trackingId The tracking Id
	 * @return false to cancel the href
	 *
	 * @author Byung Kim
	 * @date 2/5/2008
	 */
	openBrand:function(brandcode,trackingId) {
		var visited = (this.visitedSites != null ? this.visitedSites : "");
		var brandSite = this.gidBrandSites[brandcode];
		var currentBrandSite = this.gidBrandSites[this.currentBrandCode];
		var url = "";
		if (brandSite) {
			if(this.renderOnCurrentBrandUrl){
				url = currentBrandSite.unsecureUrl + this.sisterSiteActionPath;
			}else{
				url = brandSite.unsecureUrl + this.sisterSiteActionPath;
			}
			url += "?ssiteID=" + (trackingId ? trackingId : this.currentBrandSiteId)+ "&lBrdCd=" + brandcode;

			parent.location.href = url;
		}
		return false;
	}
}


/**
 * SisterSiteSync is a class used for cross domain user data synchronization.
 * Instantiated as sisterSiteSync.
 * @constructor
 *
 * @author Byung Kim
 * @date 10/24/2007
 */
var SisterSiteSync = Class.create();
SisterSiteSync.prototype = {
	hasUserSyncFailed:false,
	synchronizationPage:"/gid/html/en/userSynchronization.html",
	serverCookies:{
		jSessionId:{param:"jsid",cookieName:"JSESSIONID",isPersist:false,isSecure:false,isServerCookie:true},
		gidSecureToken:{param:"gst",cookieName:"gidSecureToken",isPersist:false,isSecure:true,isServerCookie:true},
		unknownShopperId:{param:"usid",cookieName:"unknownShopperId",isPersist:true,isSecure:false,isServerCookie:true,expTime:{"days":72}},
		customerId:{param:"cid",cookieName:"customerId",isPersist:true,isSecure:false,isServerCookie:true,expTime:{"days":72}},
		customerIdVerified:{param:"cidv",cookieName:"customerIdVerified",isPersist:true,isSecure:false,isServerCookie:true,expTime:{"days":72}},
		mktUniversalPersist:{param:"mup",cookieName:"mktUniversalPersist",isPersist:true,isSecure:false,isServerCookie:false,expTime:{"years":5}},
        brandTidMap:{param:"brandTidMap",cookieName:"BRAND_TID_MAP",isPersist:true,isSecure:false,isServerCookie:true,expTime:{"days":7}},
		partner:{param:"partner",cookieName:"PARTNER",isPersist:true,isSecure:false,isServerCookie:true,expTime:{"days":7}},
        locale:{param:"locale",cookieName:"locale",isPersist:true,isSecure:false,isServerCookie:true,expTime:{"days":72}}
	},

	/**
	 * initialize for SisterSiteSync
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	initialize:function() {
	},


	/**
	 * createIFrame creates an IFrame and calls the userSynchronization.html page to synchronize data
	 * @param {object} gidBrandSite An object with all the relevant info about the brand
	 * @param {string} params The querystring parameters to attach to the url
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	createIFrame:function(gidBrandSite,params) {
		var frameID = "brand"+gidBrandSite.brandCode+"DataSync";
		var cont = document.body;
		var isSsl = (location.protocol == "https:");
		var brandSiteUrl = (isSsl ? gidBrandSite.secureUrl : gidBrandSite.unsecureUrl);
		if (brandSiteUrl && brandSiteUrl.length > 0 && cont) {
			var newIFrame = $(document.createElement("IFRAME"));
			newIFrame.id = frameID;
			newIFrame.style.display = "none";
			newIFrame.src = brandSiteUrl + this.synchronizationPage + "?" + params;
			cont.appendChild(newIFrame);
		}
	},

	/**
	 * syncUser synchronizes cookie information to all the GID sister brand sites
	 *
	 * Modified 2/12/2008 Byung Kim - added mktuniversalpersist cookie and cleaned up code
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	syncUser:function() {
		if (navigator.cookieEnabled && gidBrandSiteConstruct.hasGIDBrandSites && !gidBrandSiteConstruct.isCommonDomainOn) {
			var aParams = [];
  			var securePrefix = null;
			var unknownShopperId = this.serverCookies.unknownShopperId;
			var jSessionId = this.serverCookies.jSessionId;
			var customerId = this.serverCookies.customerId;
			var customerIdVerified = this.serverCookies.customerIdVerified;
			var mktUniversalPersist = this.serverCookies.mktUniversalPersist;
			var gidSecureToken = this.serverCookies.gidSecureToken;
            var partner = this.serverCookies.partner;
            var brandTidMap = this.serverCookies.brandTidMap;
            var locale = this.serverCookies.locale;

			// pass the originating brand host
			aParams.push("brand=" + escape(location.host));

			// unknownshopperid cookie
			var usid = gidLib.getCookie(unknownShopperId.cookieName);
			if (usid && usid != "") aParams.push(unknownShopperId.param+"="+escape(usid));

			// jsession cookie
			var jsid = gidLib.getCookie(jSessionId.cookieName);
			if (jsid && jsid != "") aParams.push(jSessionId.param+"="+escape(jsid));


			// customerid cookie
			var cid = gidLib.getCookie(customerId.cookieName);
			if (cid && cid != "") aParams.push(customerId.param+"="+escape(cid));

			// customeridverified cookie
			var cidv = gidLib.getCookie(customerIdVerified.cookieName);
			if (cidv && cidv != "") aParams.push(customerIdVerified.param+"="+escape(cidv));

			// mktUniversalPersist cookie
			var mup = gidLib.getCookie(mktUniversalPersist.cookieName);
			if (mup && mup != "") aParams.push(mktUniversalPersist.param+"="+escape(mup));

			// gidsecuretoken cookie
			var gst = gidLib.getCookie(gidSecureToken.cookieName);
			if (gst && gst != "") aParams.push(gidSecureToken.param+"="+escape(gst));

            // PARTNER cookie
            var partnerCookie = gidLib.getCookie(partner.cookieName);
            if (partnerCookie && partnerCookie != "") aParams.push(partner.param+"="+escape(partnerCookie));

            // BRANDTIDMAP cookie
            var brandTidMapCookie = gidLib.getCookie(brandTidMap.cookieName);
            if (brandTidMapCookie && brandTidMapCookie != "") aParams.push(brandTidMap.param+"="+escape(brandTidMapCookie));

            // localeCode cookie
			var localeCode = gidLib.getCookie(locale.cookieName);
			if (localeCode && localeCode != "") aParams.push(locale.param + "=" + escape(localeCode));

			// pass the flag states
			aParams.push("sslPrefix=" + gidBrandSiteConstruct.sslPrefix);

			var sParams = aParams.join("&");

			for (i=0;i<gidBrandSiteConstruct.gidBrandSites.length;i++) {
				var gidBrandSite = gidBrandSiteConstruct.gidBrandSites[i];
				if (gidBrandSite && gidBrandSite.brandCode != gidBrandSiteConstruct.currentBrandCode) {
					this.createIFrame(gidBrandSite,sParams);
				}
			}
		}
	},
	/**
	 * userSyncFailed displays a message stating that cookie synchronization failed.
	 * It also sets a global boolean flag hasUserSyncFailed.
	 * Finally it sets the appropriate reporting metrics for G and H code.
	 *
	 * Modified 8/19/2008 Andrew Southwick - moved to gidFunctions to re-enable broken
	 * functionality related to synch failure.  This was broken when this method was
	 * moved to userSynchronization.html which redirects in the event of failure to
	 * userSyncFailure.html which then calls the parent userSyncFailed method below.
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	userSyncFailed:function() {
		var message = $('thirdPartyCookieBlockedMessage');
		if (!this.hasUserSyncFailed) {
			if (message) {
				var cookieMessage = $(document.createElement("div"));
				cookieMessage.setStyle({
					padding:"10px",
					margin:"10px 0px",
					background:"#fff",
					border:"2px solid #000",
					fontSize:"12px",
					fontWeight:"bold",
					color:"#000"
				});
				cookieMessage.update(message.innerHTML);
				$('topNav').insertBefore(cookieMessage,$('universalTopNav'));
			}
			if (getCookieVar("globalSession","userSyncFailed") != "true") {
				setCookieVar("globalSession","userSyncFailed","true");
				if(!(reportingService||{}).isActive){
					// G code.
					var strViewType = omni.strViewType;
					omni.strViewType = "thirdPartyCookieBlocked";
					omni.setTransmitData();
					omni.strViewType = strViewType;
				}
				else {
					// H code.
					var commonViewManager = reportingService.controller.viewManagers.commonViewManager;
					var commonModel = reportingService.controller.viewManagers.commonViewManager.model;
					commonModel.commonIsThirdPartyCookieBlocked = true;
					commonViewManager.controller.getReportRequest();
				}
			}
		}
		this.hasUserSyncFailed = true;
	}
};
var sisterSiteSync = new SisterSiteSync();

/**
 * Survey is a class to manage all of the pixel tracking
 *
 * TODO: move survey code out of brandFunctions to this class.
 * TODO: instantiate the class
 *
 * @constructor
 *
 * @author Byung Kim
 * @date 11/1/2007
 */
var Survey = Class.create();
Survey.prototype = {
	cookieExpiration:null,

	/**
	 * initialize Survey
	 *
	 * @author Byung Kim
	 * @date 11/1/2007
	 */
	initialize:function() {
		this.cookieExpirationTime = gidLib.getFutureDate({"weeks":26}); // six months
	}

};


var DateUtils = Class.create();

DateUtils.prototype = {
	currentDate:null,
	expireDate:null,
	expireDateFromCookie:null,
	initialize:function() {
		//initialize properties here
	},

	getDateFromCookieVar:function(cookieName,varName) {
		var cookieVarTimeValue = gidLib.getCookieVar(cookieName,varName);
		var date = null;
		var currentDate = new Date();
		var dateMsValue = null;

		// Check if the value in the cookie is valid with isNaN.
		// If in No valid value has been set in the cookie - return null.
		if ((isNaN(cookieVarTimeValue) == false) && (cookieVarTimeValue != "")) {
			// Value from cookie is valid - create date object by parsing out ms value.
			dateMsValue = parseInt(cookieVarTimeValue);
			currentDate.setTime(dateMsValue);
			date = currentDate;
		}

		return date;

	},
	setDateToCookieVar:function(cookieName, varName, date) {
		// Convert time to ms, and then to string.
		var dateTimeString = date.getTime() + "";
		setCookieVar(cookieName, varName, dateTimeString);
	},
	getDateComparisonPresentOrFutureByDay:function(expirationDate,compareDate) {
		// Returns true if dates by day are equal and false otherwise.
		var isDatePresentOrFutureByDay = false;
		var expirationDateDay = expirationDate.getDate();
		var expirationDateMonth = expirationDate.getMonth();
		var expirationDateYear = expirationDate.getYear();
		var compareDateDay = compareDate.getDate();
		var compareDateMonth = compareDate.getMonth();
		var compareDateYear = compareDate.getYear();
		if ((expirationDateDay == compareDateDay) && (expirationDateMonth == compareDateMonth) && (expirationDateYear == compareDateYear)) {
			isDatePresentOrFutureByDay = true;
		}
		else if (compareDate > expirationDate) {
			isDatePresentOrFutureByDay = true;
		}
		return isDatePresentOrFutureByDay;
	},
	getFutureDateByDay:function(daysToAdd) {
		// Returns future Date object.
		var futureDate = new Date();
		futureDate.setDate(futureDate.getDate()+daysToAdd);
		return futureDate;
	}
}

gidLib.dateUtils = new DateUtils();

Event.observe(window, 'load', function() {
	gidLib.onLoadHandler();
	gidLib.set800pxUniversalNav();
	siteNavigation.onLoadHandler();
	inlineBag.initializeElements();
});

Event.observe(window, 'beforeunload', function() {
	gidLib.onUnloadHandler();
});

var LocaleUtils = Class.create();

LocaleUtils.prototype = {
	initialize:function() {
		//initialize properties here
	},
	constants : {
					EMPTY_STRING: "", // Empty String
					PARAMETER_LOCALE: "locale", // locale parameter
					QUESTIONMARK: "?", // URL Sparater
					PARAMETER_DELIMITER: "&", // parameter separator
					EQUAL_SIGN: "=",
					ASH_SIGN: "#"
	},
	/**
	*  toggle Locale function is used to switch the locale value.
	*/
	toggleLocale : function(locale){
	    var constants = this.constants;
	    var newUrl = constants.EMPTY_STRING;
	    var localeParameter = constants.PARAMETER_LOCALE+constants.EQUAL_SIGN;
	    var previousUrl = window.location;
	    var temporaryUrl = previousUrl+constants.EMPTY_STRING;
	    if(temporaryUrl.indexOf(constants.ASH_SIGN)!=-1){
	      temporaryUrl = temporaryUrl.substring(0,temporaryUrl.length-1);
	    }
		var removedLocaleUrl = gidLib.removeQueryStringParam(temporaryUrl,constants.PARAMETER_LOCALE);
		var urlArray = removedLocaleUrl.split(constants.QUESTIONMARK);
		if(urlArray.length>=2){
		    var parameterArray=urlArray[1].split(constants.PARAMETER_DELIMITER);
			if(parameterArray.length>=1 && parameterArray[0].indexOf(constants.PARAMETER_LOCALE)==-1){
			 newURL=removedLocaleUrl+constants.PARAMETER_DELIMITER+localeParameter+locale;
			}else{
				newURL=urlArray[0]+constants.QUESTIONMARK+localeParameter+locale;
			}
		}else{
		 newURL=removedLocaleUrl+constants.QUESTIONMARK+localeParameter+locale;
		}
		window.location=newURL;
	}
}

gidLib.localeUtils = new LocaleUtils();

var InternationalShippingService = Class.create();
InternationalShippingService.prototype = {
	initialize: function() {
	},
	constants: {
		FORM_ACTION_URI: '/profile/internationalShippingOptIn.do',
		SELECTED_COUNTRY_CLASS: 'selected-country',
		FIFTY_ONE_FORM: 'fiftyOneForm',
		EXPLANATORY_COPY: 'explanatoryCopy',
		SHOPPING_SITE_SELECTION: 'shoppingSiteSelection',
		CURRENCY_FIFTY_ONE_SELECT: 'currency51',
		SITE_SEL_CURRENCY_FIFTY_ONE_SELECT: 'siteSelectionCurrency51',
		DEFAULT_COUNTRY_CODE: 'us',
		DEFAULT_LANGUAGE: 'en',
		US_MARKET_CODE: 'US',
		LOCALE_PARAM_NAME: 'locale'
	},
	model: {
		selectedCountry: null,
		redirectCrossMarketUrl: null,
		businessUnitId: null,
		isShippCountryCodeInUrl: null,
		shoppingBagHeader: null,
		currentCountryCode: null,
		currentCurrencyCode: null,
		isCountryRedirectNeeded: false,
		isSiteSelectionAvailable: false,
		currentLanguageCode: false,
		marketCode: null,
		borderImagesSources: []

		//countryUrlMap:null
	},
	view: {
		hiddenSelects: [],
		initializeHandlersToDomElements: function() {
			var lis = $$('#countries51 li');
			lis.each(function(e) {
				if (!e.classNames().include('disable')) {
					e.observe('click', function() {
						internationalShippingService.view.selectCountry(e);
					});

					// disables the links
					$(e.children[0]).observe('click', function(e) {
						e.preventDefault();
						return false;
					});
					if (clientBrowser.isIE6Up) {
						e.observe('mouseover', function() {
							e.classNames().add('hover');
						});
						e.observe('mouseout', function() {
							e.classNames().remove('hover');
						});
					}
				}
			});
			if (clientBrowser.isIE6) {
				var shopSiteSel = $('shoppingSiteSelection');
				if (shopSiteSel) {
					shopSiteSel.classNames().add('fixedShopSiteSelHeight');
				}
			}
			var goButtons = $('buttons51').getElementsByClassName('goButton');
			for (i = 0; i < goButtons.length; i++) {
				goButtons[i].observe('click', function(e) { internationalShippingService.controller.submit51(); e.preventDefault(); });
			}

			$('shopCountry').observe('click', function(e) { internationalShippingService.view.setSelectedSite(e); });
			$('shopUS').observe('click', function(e) { internationalShippingService.view.setSelectedSite(e); });

			var closeFunction = function(e) { internationalShippingService.controller.closeLayer(); e.preventDefault(); };
			$('cancel51').observe('click', closeFunction);
			$('closeButton').observe('click', closeFunction);
		},
		handler:{
		},
		selectCurrency: function(currencyCode) {
			$('currency51').value = currencyCode;
		},
		showRememberMySelection: function() {
			$('rememberMySelectionLabel').classNames().remove('invisible');
		},
		hideRememberMySelection: function() {
			$('rememberMySelectionLabel').classNames().add('invisible');
		},
		getCalloutText: function() {
			var calloutText = '';
			var messageValue = resourceBundleValues.intlShipSiteRedirectContent[internationalShippingService.model.selectedCountry.code];
			if (messageValue) {
				calloutText = messageValue.calloutMessage;
			}
			$('countryCalloutImg').setAttribute('alt', calloutText);
			return calloutText;
		},
		setSelectedSite: function(e) {
			var countryCode = internationalShippingService.constants.DEFAULT_COUNTRY_CODE;

			if ($('shopCountry').checked) {
				countryCode = internationalShippingService.model.selectedCountry.code;
				internationalShippingService.view.hideRememberMySelection();
			} else {
				internationalShippingService.view.showRememberMySelection();
			}

			var doesRedirectURLExistForCountry = internationalShippingService.controller.doesRedirectURLExistForCountry(countryCode);

			if (doesRedirectURLExistForCountry) {
				internationalShippingService.model.isCountryRedirectNeeded = true;
				internationalShippingService.controller.setRedirectToCrossMarketUrl(countryCode, internationalShippingService.model.currentLanguageCode, internationalShippingService.model.selectedCountry.code);
			} else {
				internationalShippingService.model.isCountryRedirectNeeded = false;
			}
			internationalShippingService.view.showButtonForSelection();
		},
		setShoppingSiteSelectionContent: function(countryCode) {
			var messageValue = resourceBundleValues.intlShipSiteRedirectContent[countryCode];
			if (messageValue) {
				$('shopOtherSiteLabel').update(messageValue.siteSelectorLabel);
				$('shopOtherSiteContentMsg1').update(messageValue.contentMessage1);
				$('shopOtherSiteContentMsg2').update(messageValue.contentMessage2);
				$('shopOtherSiteContentMsg3').update(messageValue.contentMessage3);
			}
		},
		setShoppingSiteSelectionCurrencyCode: function(currencyCode) {
			$('siteSelectionCurrency51').value = currencyCode;
		},
		enableSiteSelectionArea: function(countryCode, currencyCode, languageCode) {
			//set country specific message content in selection area
			internationalShippingService.view.setShoppingSiteSelectionContent(countryCode);

			var constants = internationalShippingService.constants;
			$(constants.EXPLANATORY_COPY).hide();
			$(constants.SHOPPING_SITE_SELECTION).show();
			$(constants.FIFTY_ONE_FORM).classNames().add('hidden');

			if ($(constants.SITE_SEL_CURRENCY_FIFTY_ONE_SELECT).childElements().length == 0) {
				//copy currency select options to select in site redirect area
				$(constants.SITE_SEL_CURRENCY_FIFTY_ONE_SELECT).update($(internationalShippingService.constants.CURRENCY_FIFTY_ONE_SELECT).innerHTML);
			}

			//set default selection to Shop Country Site
			$('shopCountry').checked = true;
			internationalShippingService.view.hideRememberMySelection();
			var doesRedirectURLExistForCountry = internationalShippingService.controller.doesRedirectURLExistForCountry(countryCode);
			if (doesRedirectURLExistForCountry) {
				internationalShippingService.model.isCountryRedirectNeeded = true;
				internationalShippingService.controller.setRedirectToCrossMarketUrl(countryCode, languageCode, countryCode);
			} else {
				internationalShippingService.model.isCountryRedirectNeeded = false;
				//set default redirect url to us
				internationalShippingService.controller.setRedirectToCrossMarketUrl(internationalShippingService.constants.DEFAULT_COUNTRY_CODE, languageCode, countryCode);
			}

			internationalShippingService.view.setShoppingSiteSelectionCurrencyCode(currencyCode);
		},
		showButtonForSelection: function() {
			var goButtons = $('buttons51').getElementsByClassName('goButton');
			for (i = 0; i < goButtons.length; i++) {
				goButtons[i].hide();
			}
			if (internationalShippingService.model.isCountryRedirectNeeded) {
				var countryCode = internationalShippingService.model.selectedCountry.code;
				if (internationalShippingService.model.isSiteSelectionAvailable && $('shopUS').checked) {
					countryCode = internationalShippingService.constants.DEFAULT_COUNTRY_CODE;
				}
				var doesRedirectURLExistForCountry = internationalShippingService.controller.doesRedirectURLExistForCountry(countryCode);
				if (doesRedirectURLExistForCountry) {
					switch (countryCode) {
						case 'ca':
							$('goToCanada').show();
							break;
						case 'gb':
							$('goToEU').show();
							break;
						default:
							$('goToUS').show();
					}
				} else {
					$('goToUS').show();
				}
			} else {
				$('go51').show();
			}
		},
		selectCountry: function(li) {
			var link = li.childNodes[0];
			var countryName = link.innerHTML;
			var countryCode = link.id;
			if (countryCode.indexOf("_") != -1) {
				var countryCodeIDArray = countryCode.split("_");
				countryCode = countryCodeIDArray[0];
			}

			internationalShippingService.model.selectedCountry = {name: countryName, code: countryCode};

			var constants = internationalShippingService.constants;
			var selectedCountryClass = constants.SELECTED_COUNTRY_CLASS;
			$$('#countries51 li.' + selectedCountryClass).each(function(e) { e.removeClassName(selectedCountryClass); });

			li.addClassName(selectedCountryClass);
			// hides the forms
			$(constants.FIFTY_ONE_FORM).classNames().add('hidden');
			$(constants.EXPLANATORY_COPY).classNames().add('invisible');
			$(constants.EXPLANATORY_COPY).show();
			$(constants.SHOPPING_SITE_SELECTION).hide();

			// remove other currencies if it is US
			var isUsd = countryCode == internationalShippingService.constants.DEFAULT_COUNTRY_CODE;
			var isCountryRedirectNeeded = internationalShippingService.controller.isCountryRedirectNeeded(countryCode);
			var isSiteSelectionAvailable = (countryCode == 'ca' || countryCode == 'gb');
			internationalShippingService.model.isCountryRedirectNeeded = isCountryRedirectNeeded;
			internationalShippingService.model.isSiteSelectionAvailable = isSiteSelectionAvailable;
			var isCurrencyNeeded =  internationalShippingService.controller.isCurrencyNeeded(countryCode);
			internationalShippingService.view.showRememberMySelection();
			var isShipCountryInUrl = internationalShippingService.controller.isShipCountryCodeInRedirectedURL();
			internationalShippingService.model.isShippCountryCodeInUrl = isShipCountryInUrl;
			var currencyCode = 'usd';
			var languageCode = 'en';
			if (!isUsd) {
				// class names for the <li>s include a currency_<currencyCode>
				link.classNames().each(function(e) {
					if (/currency_/.test(e)) {
						currencyCode = e.split('_')[1];
					}
				});
				if (isCurrencyNeeded) {
					$(constants.FIFTY_ONE_FORM).classNames().remove('hidden');
				}
				$(constants.EXPLANATORY_COPY).classNames().remove('invisible');
			}
			if (isCountryRedirectNeeded) {
				// class names for the <li>s include a language_<languageCode>
				link.classNames().each(function(e) {
					if (/language_/.test(e)) {
						languageCode = e.split('_')[1];
					}
				});
			}
			if (isCurrencyNeeded) {
				internationalShippingService.view.selectCurrency(currencyCode);
			}
			internationalShippingService.model.currentLanguageCode = languageCode;

			if (isSiteSelectionAvailable) {
				internationalShippingService.view.enableSiteSelectionArea(countryCode, currencyCode, languageCode);
			} else {
				if (isCountryRedirectNeeded) {
					internationalShippingService.controller.setRedirectToCrossMarketUrl(countryCode, languageCode, countryCode);
				} else if (isShipCountryInUrl) {
					internationalShippingService.controller.updateShipCountryCodeInUrl(countryCode, currencyCode);
				}
			}

			internationalShippingService.view.showButtonForSelection();

			internationalShippingService.controller.setMarketingContentForCountry(countryCode, languageCode, currencyCode);
		}
	},
	controller: {
		init: {
			main: function() {
				internationalShippingService.view.initializeHandlersToDomElements();

				var currentLocaleCode = brandConst.BRAND_LOCALE;
				var currentCountry = internationalShippingService.constants.DEFAULT_COUNTRY_CODE;
				var currentLanguage = internationalShippingService.constants.DEFAULT_LANGUAGE;

				if (currentLocaleCode != -1) {
					var currentLocaleCodeArray = currentLocaleCode.split("_");
					currentLanguage = currentLocaleCodeArray[0];
					currentCountry = currentLocaleCodeArray[1].toLowerCase();
				}
				currentCountry = internationalShippingService.model.currentCountryCode || currentCountry;
				var countryId = currentCountry;
				var isCountryCodeCA = currentCountry == 'ca';
				if (isCountryCodeCA) {
					countryId = currentCountry + "_" + currentLanguage;
				}
				if ($(countryId) != null) {
				internationalShippingService.view.selectCountry($(countryId).parentNode);
				}
				var currentCurrency = internationalShippingService.model.currentCurrencyCode || 'usd';
				internationalShippingService.view.selectCurrency(currentCurrency);
				if (internationalShippingService.model.isSiteSelectionAvailable && internationalShippingService.model.currentCurrencyCode) {
					$('siteSelectionCurrency51').value = currentCurrency;
				}

				gidLib.setButtonEvents('internationalShippingPopUp');

				var borderImagesSources = internationalShippingService.model.borderImagesSources;
				var preloadedImages = 0;
				for (var i = 0; i < borderImagesSources.length; i++) {
					var img = new Image();
					img.onload = function() {
						if (++preloadedImages == borderImagesSources.length) {
							internationalShippingService.view.hiddenSelects = gidLib.hideDropDownsUnderElement($('internationalShippingPopUp'), 'select:not(select[id=currency51])'); // to not hide the currency select
						}
					}
					img.src = borderImagesSources[i];
				}
				$('currency51').setStyle({visibility:'visible'});
				$('siteSelectionCurrency51').setStyle({visibility:'visible'});
			}
		},
		setRedirectToCrossMarketUrl: function(countryCode, languageCode, shipToCountryCode) {
			var redirectCrossMarketUrl = null;
			var shipToCurrencyCode = null;
			var isDefaultRedirectUrlActive = false;
			var businessUnitId = internationalShippingService.model.businessUnitId;
			var brandCode = brandConst.BRAND_ID.toLowerCase();
			var localeCode = languageCode + "_" + countryCode.toUpperCase();
			var ssiteId = "cs_" + countryCode + "_" + languageCode + "_" + brandCode;
			var baseUrl = internationalShippingService.model.countryUrlMap[businessUnitId + "_" + countryCode];
			if (baseUrl == null) {
				baseUrl = internationalShippingService.model.countryUrlMap[businessUnitId + "_" + "default"];
				if (baseUrl != null) {
					isDefaultRedirectUrlActive = true;
				}
			}
			redirectCrossMarketUrl = baseUrl + "?";
				shipToCurrencyCode = $('currency51').value;
				redirectCrossMarketUrl += "ssiteID=" + ssiteId ;

				//cannot pass locale currently because US site can only pass 'en',
				//which will reset any previously selected locale on an international site
//				if (!isDefaultRedirectUrlActive) {
//					redirectCrossMarketUrl += "&locale=" + localeCode;
//				}
			redirectCrossMarketUrl += (shipToCountryCode ? "&shipToCountryCode=" + shipToCountryCode : '') +
									  (shipToCurrencyCode ? "&shipToCurrencyCode=" + shipToCurrencyCode : '');
			internationalShippingService.model.redirectCrossMarketUrl = redirectCrossMarketUrl;
		},
		redirectToCrossMarketUrl: function() {
			window.location = internationalShippingService.model.redirectCrossMarketUrl;
		},
		updateShipCountryCodeInUrl: function(countryCode, currencyCode) {
			var constants = internationalShippingService.constants;
			var redirectCrossMarketUrl = null;
			var currentUrl = window.location+'';
			var removedCountryCodeUrl = gidLib.removeQueryStringParam(currentUrl,'shipToCountryCode');
			var removedCountryCurrencyCodeUrl = gidLib.removeQueryStringParam(removedCountryCodeUrl,'shipToCurrencyCode');
			var contryCode = countryCode;
			var currencyCode = null;
			if (!$(constants.FIFTY_ONE_FORM).classNames().include('hidden')) {
				currencyCode = $('currency51').value;
			}
			redirectCrossMarketUrl = removedCountryCurrencyCodeUrl + (contryCode ? "&shipToCountryCode=" + contryCode : '') +
			  (currencyCode ? "&shipToCurrencyCode=" + currencyCode : '');
			internationalShippingService.model.redirectCrossMarketUrl = redirectCrossMarketUrl;

		},
		updateShipCurrencyCodeInUrl: function(currencyCode, rememberMySelection) {
			var redirectCrossMarketUrl = null;
			var currentUrl = internationalShippingService.model.redirectCrossMarketUrl;
			var removedMySelection = gidLib.removeQueryStringParam(currentUrl,'rememberMySelection');
			var removedCurrencyCodeUrl = gidLib.removeQueryStringParam(removedMySelection,'shipToCurrencyCode');

			redirectCrossMarketUrl = removedCurrencyCodeUrl +
			  (currencyCode ? "&shipToCurrencyCode=" + currencyCode : '') +
			  (rememberMySelection ? "&rememberMySelection=" + rememberMySelection : '');
			internationalShippingService.model.redirectCrossMarketUrl = redirectCrossMarketUrl;

		},
		getRefreshUrl: function() {
			var refreshUrl = window.location+'';
			var queryStringBegPos = refreshUrl.indexOf('?');
			//remove existing query strings
			if (queryStringBegPos > 0) {
				refreshUrl = refreshUrl.substr(0, queryStringBegPos);
			}
			var redirectUrl = internationalShippingService.model.redirectCrossMarketUrl;
			queryStringBegPos = redirectUrl.indexOf('?');
			if (queryStringBegPos > 0) {
				refreshUrl+= redirectUrl.substr(queryStringBegPos);
			}
			return refreshUrl;
		},
		isCountryRedirectNeeded: function(countryCode) {
			var isCountryRedirectNeeded = false;
			var businessUnitId = internationalShippingService.model.businessUnitId;
			var crossMarketUrl = internationalShippingService.model.countryUrlMap[businessUnitId + "_" + countryCode];
			var crossMarketDefaultUrl = null;
			if ( crossMarketUrl != null ) {
				isCountryRedirectNeeded = true;
			} else {
				crossMarketDefaultUrl = internationalShippingService.model.countryUrlMap[businessUnitId + "_" + "default"];
				//default is used for FiftyOne countries to redirect to US site
				if (crossMarketDefaultUrl) {
					isCountryRedirectNeeded = true;
				}
			}
			return isCountryRedirectNeeded;
		},
		doesRedirectURLExistForCountry: function(countryCode) {
			var doesRedirectURLExistForCountry = false;
			var businessUnitId = internationalShippingService.model.businessUnitId;
			var crossMarketUrl = internationalShippingService.model.countryUrlMap[businessUnitId + "_" + countryCode];
			if ( crossMarketUrl != null ) {
				doesRedirectURLExistForCountry = true;
			}
			return doesRedirectURLExistForCountry;
		},
		isCurrencyNeeded: function(countryCode) {
			var isCurrencyNeeded = true;
			var businessUnitId = internationalShippingService.model.businessUnitId;
			var crossMarketUrl = internationalShippingService.model.countryUrlMap[businessUnitId + "_" + countryCode];
			if ( crossMarketUrl != null ) {
				isCurrencyNeeded = false;
			}
			return isCurrencyNeeded;
		},
		isShipCountryCodeInRedirectedURL: function() {
			var isRedirectedSite = false;
			var currentURL = window.location + '';
			if(currentURL.indexOf('shipToCountryCode')!=-1) {
				isRedirectedSite = true;
			}
			return isRedirectedSite;
		},
		submit51: function() {
			internationalShippingService.controller.submit51 = function() { return false; }
			var selectedCountry = internationalShippingService.model.selectedCountry;
			var isShipCountryInURL = internationalShippingService.model.isShippCountryCodeInUrl;
			var newCurrencyCode = null;
			var rememberMySelection = 'false';
			if (internationalShippingService.model.isCountryRedirectNeeded || isShipCountryInURL) {
				var constants = internationalShippingService.constants;
				if (internationalShippingService.model.isSiteSelectionAvailable) {
					newCurrencyCode = $(constants.SITE_SEL_CURRENCY_FIFTY_ONE_SELECT).value;
				} else {
					if (!$(constants.FIFTY_ONE_FORM).classNames().include('hidden')) {
						newCurrencyCode = $('currency51').value;
					}
				}
				rememberMySelection =  $('rememberMySelection').checked+'';
				internationalShippingService.controller.updateShipCurrencyCodeInUrl(newCurrencyCode, rememberMySelection);
				if (internationalShippingService.model.isCountryRedirectNeeded) {
					internationalShippingService.controller.redirectToCrossMarketUrl();
				} else {
					var refreshUrl = internationalShippingService.controller.getRefreshUrl();
					window.location = refreshUrl;
				}
			} else {
				//We only want to update country and currency if US site selected
				if (internationalShippingService.model.marketCode != internationalShippingService.constants.US_MARKET_CODE) {
			    	internationalShippingService.controller.saved();
					return false;
				}
				var currencyCode = null;
				if (internationalShippingService.model.isSiteSelectionAvailable) {
					currencyCode = $('siteSelectionCurrency51').value;
				} else {
					currencyCode = $('currency51').value;
				}
				new Ajax.Request(internationalShippingService.constants.FORM_ACTION_URI, {
				    method: 'post',
				    parameters: {
				        currencyCode: currencyCode,
				        countryCode: selectedCountry.code,
				        remember: $('rememberMySelection').checked
				    },
				    onComplete: function() {
					    // function is defined on the parent page
				    	siteNavigation.setCurrentShippingToCountry(selectedCountry.code, selectedCountry.name);
				    	internationalShippingService.controller.saved();
				    }
				});
			}
			return false;
		},
		saved: function() {
			var header = $$('.pageHeader');
			header = header.length > 0 ? header[0].innerHTML.trim() : '';
			if (header == internationalShippingService.model.shoppingBagHeader) {
				window.location = siteNavigation.shoppingBagUrl;
			} else {
				/* ideally we should only close the layer, but the current page layout
					might be affected by the currency choice. We'll refresh the page
					until a better solution comes up.

					//internationalShippingService.controller.closeLayer();
				*/
					window.location.reload();
				}
		},
		closeLayer: function() {
			gidLib.showDropDowns(internationalShippingService.view.hiddenSelects);
	       	$('internationalShippingPopUp').remove();
		},
		setMarketingContentForCountry: function(countryCode, languageCode, currencyCode) {

		}
	}
}
var internationalShippingService = new InternationalShippingService();

/*********************************************
DEPRECATED METHODS - do not use. do not remove
*********************************************/

/**
 * returnImg - returns an object with a src property
 * @deprecated - use JSON syntax to create this instead (e.g. {src:"/image/path/image.jpg"}  ).  Wrapper function is redundant.
 * 				 To preload an image, use gidLib.loadImage()
 */
function returnImg(strSrc) {
	return gidLib.loadImage(strSrc);
}

/**
 * returnObjById
 * @deprecated - use prototype method $() instead
 */
function returnObjById(strId) {
	return $(strId);
}

/**
 * replaceHTML
 * @deprecated
 */
function replaceHTML(obj, html) {
	if(obj) obj.update(html);
	return obj;
}

/**
 * setObjInnerHTML
 * @deprecated - use prototype method update() instead
 */
function setObjInnerHTML(objLayer,strHTML) {
	if(typeof(objLayer) == 'string') {
        $(objLayer).innerHTML = strHTML;
    } else { objLayer.innerHTML = strHTML; };
}

/**
 * returnObjPosition
 * @deprecated - use prototype method Position.cumulativeOffset()
 */
function returnObjPosition(target){
	var pos = Position.cumulativeOffset($(target));
	return {x:pos[0], y:pos[1]};
}

/**
 * isPrototypeSafe
 * @deprecated - use hasOwnProperty instead to check if the key isn't a method.
 */
function isPrototypeSafe(key, object) {
	return object.hasOwnProperty(key);
}

/**
 * getElementsByAttribute
 * @deprecated - use prototype methods $$() or Element.getElementsBySelector() instead
 */
function getElementsByAttribute(objElement,strAttribute,searchValue,arrayResults) {
	if (clientBrowser.isIE && strAttribute == "class") strAttribute = "className";
	if (!arrayResults) arrayResults = new Array();
	if (objElement.hasChildNodes()) {
		for (var i in objElement.childNodes) {
			if (objElement.childNodes.hasOwnProperty(i)) {
				var objChild = objElement.childNodes[i];
				if (objChild.getAttribute &&
						objChild.getAttribute(strAttribute) &&
						(typeof searchValue != "string" ? objChild.getAttribute(strAttribute).search(searchValue) != -1 : objChild.getAttribute(strAttribute) == searchValue))
						arrayResults[arrayResults.length] = objChild;
				if (objChild.hasChildNodes && objChild.hasChildNodes()) getElementsByAttribute(objChild,strAttribute,searchValue,arrayResults);
			}
		}
	}
	return arrayResults;
}

/**
 * stringFilter - removes certain characters from a form value
 * @deprecated - use regExp matching instead
 */
function stringFilter (input, filteredValues) {
	s = input.value;
	var i;
	var returnString = "";
		for (i = 0; i < s.length; i++) {
		var c = s.charAt(i);
			if (filteredValues.indexOf(c) == -1) returnString += c;
		}
	input.value = returnString;
}

/**
 * getElementsWithMatchingId
 * @deprecated - use prototype method $$() or Element.getElementsBySelector()
 */
function getElementsWithMatchingId(idName) {
	var objContentItemContainerElements = new Array();
	var divs = document.getElementsByTagName("div");
	var y = 0;
	for (var i = 0; i < divs.length; i++) {
		var id = divs[i].getAttribute("id");
		if (id && id.match(idName)) {
			objContentItemContainerElements[y] = divs[i];
			y++;
		}
	}
	return objContentItemContainerElements;
}

/**
 * changeFormDropDownVisibility
 * @deprecated use gidLib.hideDropDownsUnderElement() instead
 */
function changeFormDropDownVisibility(state) {
	if (clientBrowser.isIE6 || clientBrowser.isIE55) {
		var arrayDropDowns = $$("select");
		var iterator = function(element) {
			if (element && element.style) element.style.visibility = state;
		}
		arrayDropDowns.each(iterator);
	}
}

/**
 * setObjVisibility
 * @deprecated use Prototype method setStyle instead
 */
function setObjVisibility(element,state) {
	if (element) element.setStyle({visibility:state});
}

/**
 * setImgSrc
 * @deprecated use prototype method setSrc() native to IMG and INPUT elements instead
 */
function setImgSrc(id, src) {
	$(id).setSrc(src);
}


var pageOnLoadFunctions = function() {}
var pageOnUnloadFunctions = function() {}
/*
 * Legacy pointer references to new gidLib methods
 */
var setCookieVar = gidLib.setCookieVar.bind(gidLib);
var setCookie = gidLib.setCookie.bind(gidLib);
var getCookie = gidLib.getCookie.bind(gidLib);
var getCookieVar = gidLib.getCookieVar.bind(gidLib);
var setObjPosition = gidLib.setObjPosition.bind(gidLib);
var setObjCenter = gidLib.setObjCenter.bind(gidLib);
var getQuerystringParam = gidLib.getQuerystringParam.bind(gidLib);
var removeQueryStringParam = gidLib.removeQueryStringParam.bind(gidLib);
var openLayeredPopup = function(a1,a2,a3,a4,a5,a6,a7,a8) {
	/* This is to capture the "return false" of the new method.
	 * Legacy implementations aren't handling the return properly.
	 */
	gidLib.layeredPopup.openLayeredPopup(a1,a2,a3,a4,a5,a6,a7,a8);
}
var closeLayeredPopup = function() {
	/* This is to capture the "return false" of the new method.
	 * Legacy implementations aren't handling the return properly.
	 */
	gidLib.layeredPopup.closeLayeredPopup();
}
var addCurrentDomain = gidLib.addCurrentDomain.bind(gidLib);
var openWindow = gidLib.openWindow.bind(gidLib);
var closeWindow = gidLib.closeWindow.bind(gidLib);
var contentItemLink = gidLib.contentItemLink.bind(gidLib);
var unUnicode = gidLib.unUnicode.bind(gidLib);
var openGiftCardWindow = gidLib.openGiftCardWindow.bind(gidLib);
var setButtonEvents = gidLib.setButtonEvents.bind(gidLib);
var setLabelOnClick = gidLib.setLabelOnClick.bind(gidLib);
var getContentItemIds = gidLib.reporting.getContentItemIds.bind(gidLib.reporting);
var selectOption = gidLib.selectOption.bind(gidLib);

var objInlineBag = inlineBag;

var goSignIn = siteNavigation.goSignIn.bind(siteNavigation);
var goSignOut = siteNavigation.goSignOut.bind(siteNavigation);
var goJoinEmail = siteNavigation.goJoinEmail.bind(siteNavigation);
var goToShoppingBag = siteNavigation.goToShoppingBag.bind(siteNavigation);
var objFooter = siteNavigation.footer;
var strRelativeURL = siteNavigation.relativeUrl;
var strSignInReturnURL = siteNavigation.signInReturnUrl;
var strSignInTargetURL = siteNavigation.signInTargetUrl;
var strSignInShoppingURL = siteNavigation.signInShoppingUrl;
var objTopNavController = siteNavigation.topNav;
var objSearchBox = siteNavigation.searchBox;
var objSideNav = siteNavigation.sideNav;
var setSideNavHeight = siteNavigation.sideNav.setSideNavHeight;

/*
 * The following Legacy references are wrapped with anonymous functions because the instantiated object is extended in brandFunctions.js
 */
var setFooterImages = function() {
	siteNavigation.footer.setFooterImages();
}
var divisionOver = function(a,b) {
	siteNavigation.topNav.divisionOver(a,b);
}
var divisionOut = function(a,b) {
	siteNavigation.topNav.divisionOut(a,b);
}
var subDivisionOver = function(a,b) {
	siteNavigation.topNav.subDivisionOver(a,b);
}
var subDivisionOut = function (a,b) {
	siteNavigation.topNav.subDivisionOut(a,b);
}
var subDivisionsOver = function(a,b) {
	siteNavigation.topNav.subDivisionsOver(a,b);
}
var subDivisionsOut = function(a,b) {
	siteNavigation.topNav.subDivisionsOut(a,b);
}
var hideSubDivisions = function(a,b) {
	siteNavigation.topNav.hideSubDivisions(a,b);
}



/*
 * Legacy Browser Detection & Properties
 */
var objClient = clientBrowser;
Object.extend(clientBrowser,{
		intFlashVer:parseInt(clientBrowser.verFlash),
		isNav:clientBrowser.isNetscape,
		isNav7:clientBrowser.isNetscape7,
		isNav7up:clientBrowser.isNetscape7Up,
		intGver:clientBrowser.verGecko,
		isIE6up:clientBrowser.isIE6Up,
		isIE5_5:clientBrowser.isIE55,
		isIE5_5up:clientBrowser.isIE55Up
	}
);
CookieManagementService = Class.create();

CookieManagementService.prototype = {
	initialize:function() {
	},
	constants:{
		cookieConfigurationParameters:{
			GLOBAL_SESSION_COOKIE_CONFIGURATION_PARAMETERS:{name:"globalSession"}
		}
	},
	constructors:{
		AbstractCookieConfiguration:function(cookieConfigurationParameters) {
			var cookieConfigurationParametersName = cookieConfigurationParameters["name"];
			var cookieConfigurationParametersValue = cookieConfigurationParameters["value"];
			var cookieConfigurationParametersPath = null;
			var cookieConfigurationParametersDomain = null;
			var cookieConfigurationParametersIsSecure = cookieConfigurationParameters["isSecure"];
			var cookieConfigurationParametersExpirationDate = cookieConfigurationParameters["expirationDate"];

			this.name = cookieConfigurationParametersName ? cookieConfigurationParametersName : null;
			this.value = cookieConfigurationParametersValue ? cookieConfigurationParametersValue : "";
			this.path = cookieConfigurationParametersPath ? cookieConfigurationParametersPath : "/";
			this.domain = cookieConfigurationParametersDomain ? cookieConfigurationParametersDomain : null;
			this.isSecure = cookieConfigurationParametersIsSecure ? cookieConfigurationParametersIsSecure : false;
			this.expirationDate = cookieConfigurationParametersExpirationDate ? cookieConfigurationParametersExpirationDate : null;
			
			if (this.domain == null) {
				this.domain = cookieManagementService.controller.getCookieDomain(this);
			}
			if (this.expirationDate == null) {
				this.expirationDate = cookieManagementService.controller.getCookieExpirationDate(this);
			}
			return this;
		}
	},
	model:{
		cookieConfigurationMap:{},
		cookieConfigurationList:[],
		isCookieManagementServiceReady:false
	},
	controller:{
		init:{
			main:function() {
				cookieManagementService.controller.init.setApplicationCookieConfigurations();
				cookieManagementService.model.isCookieManagementServiceReady = true;
				document.fire("cookieManagementService:ready");
			},
			setApplicationCookieConfigurations:function() {
				/* GLOBAL_SESSION_COOKIE_CONFIGURATION_PARAMETERS */
				var GLOBAL_SESSION_COOKIE_CONFIGURATION_PARAMETERS = cookieManagementService.constants.cookieConfigurationParameters.GLOBAL_SESSION_COOKIE_CONFIGURATION_PARAMETERS;
				var globalSessionCookieConfiguration = new cookieManagementService.constructors.AbstractCookieConfiguration(GLOBAL_SESSION_COOKIE_CONFIGURATION_PARAMETERS);
				cookieManagementService.api.addCookieConfiguration(globalSessionCookieConfiguration);
			}
		},
		addCookieConfiguration:function(cookieConfiguration) {
			var isAddCookieConfigurationSuccessful = false;
			var cookieConfigurationList = cookieManagementService.model.cookieConfigurationList;
			var cookieConfigurationMap = cookieManagementService.model.cookieConfigurationMap;
			var isCookieConfigurationValid = cookieManagementService.controller.isCookieConfigurationValid(cookieConfiguration);
			if (isCookieConfigurationValid) {
				var isCookieConfigurationAlreadySet = cookieManagementService.controller.isCookieConfigurationAlreadySet(cookieConfiguration);
				if (!isCookieConfigurationAlreadySet) {
					/* Cookie configuration is not already set so add this configuration to the model */
					cookieConfigurationList.push(cookieConfiguration);
					cookieConfigurationMap[cookieConfiguration.name] = cookieConfiguration;
					isAddCookieConfigurationSuccessful = true;
				}
			}
			return isAddCookieConfigurationSuccessful;
		},
		isCookieConfigurationValid:function(cookieConfiguration) {
			var isCookieConfigurationValid = false;
			var isErrorDetected = true;
			if (cookieConfiguration != null && cookieConfiguration != undefined) {
				if (cookieConfiguration.name) {
					if (cookieConfiguration.name.length > 0 && cookieConfiguration.name.length < 200) {
						isErrorDetected = false;
					}
				}
				if (!isErrorDetected) {
					isCookieConfigurationValid = true;
				}
			}
			return isCookieConfigurationValid;
		},
		isCookieConfigurationAlreadySet:function(cookieConfiguration) {
			var isCookieConfigurationAlreadySet = false;
			var cookieConfigurationMap = cookieManagementService.model.cookieConfigurationMap;
			var existingCookieConfiguration = cookieConfigurationMap[cookieConfiguration.name];
			if (existingCookieConfiguration) {
				isCookieConfigurationAlreadySet = true;
			}
			return isCookieConfigurationAlreadySet;
		},
		getCookieDomain:function(cookieConfiguration) {
			var cookieDomain = null;
			var hostname = window.location.hostname;
			var hostnameValues = hostname.split(".");
			var isWipDetected = false;
			var isAppDetected = false;
			var previewSubDomain = null;
			var cookieDomainBuffer = null;
			var cookieDomainSuffixIndex = hostnameValues.length - 1;
			if (cookieDomainSuffixIndex > 0) {
				if (hostname.indexOf(".wip.") != -1) {
					isWipDetected = true;
					previewSubDomain = ".wip.";
				}
				if (hostname.indexOf(".app.") != -1) {
					isAppDetected = true;
					previewSubDomain = ".app.";
				}
				if (isWipDetected || isAppDetected) {
					var beginIndex = hostname.indexOf(previewSubDomain);
					var endIndex = hostname.length;
					cookieDomainBuffer = hostname.substring(beginIndex, endIndex);
				} else {
					cookieDomainBuffer = "";
					for (var hostnameValuesIndex = 1; hostnameValuesIndex < hostnameValues.length; hostnameValuesIndex++) {
						cookieDomainBuffer = cookieDomainBuffer + "." + hostnameValues[hostnameValuesIndex];
					}
				}
				if (cookieDomainBuffer != null) {
					cookieDomain = cookieDomainBuffer;
				}
			}
			/*
			var cookieDomainSuffixIndex = hostnameValues.length - 1;
			var cookieDomainPrefixIndex = cookieDomainSuffixIndex - 1;
			var cookieDomainSuffix = hostnameValues[cookieDomainSuffixIndex];
			var cookieDomainPrefix = hostnameValues[cookieDomainPrefixIndex];
			cookieDomain = "." + cookieDomainPrefix + "." + cookieDomainSuffix;
			*/
			/*
			var gidBrandSiteConstructCookieDomain = gidBrandSiteConstruct.cookieDomain;
			if (window["gidBrandSiteConstruct"] && cookieConfiguration != null && cookieConfiguration != undefined) {
				if (cookieConfiguration.isSecure) {
					// cookieConfiguration is secure, so determine if cookie can be set securely.
					var isLocationSecure = (location.protocol == "https:");
					if (isLocationSecure) {
						var isRenderOnCurrentBrandUrl = gidBrandSiteConstruct.renderOnCurrentBrandUrl;
						if (isRenderOnCurrentBrandUrl){
							cookieDomain = hostname
						} else if (gidBrandSiteConstruct.sslPrefix && gidBrandSiteConstruct.sslPrefix != "") {
							var securePrefix = gidBrandSiteConstruct.sslPrefix;
							var securePrefixSuffix = null;
							if (hostname.indexOf(securePrefix) == 0) {
								var parseBeginIndex = securePrefix.length;
								var parseEndIndex = hostname.indexOf(parseBeginIndex, ".");
								securePrefixSuffix = hostname.substring(parseBeginIndex, parseEndIndex);
								cookieDomain = securePrefixSuffix + gidBrandSiteConstructCookieDomain;
							}
						}
					}
				} else {
					// cookieConfiguration is not secure.
					cookieDomain = gidBrandSiteConstructCookieDomain;
				}
			}
			*/
			return cookieDomain;
		},
		getCookieExpirationDate:function(cookieConfiguration) {
			var cookieExpirationDate = null;
			// set cookieExpirationDate for persistent cookies.
			if (cookieConfiguration != null && cookieConfiguration != undefined) {
				if ((cookieConfiguration.name.toLowerCase()).indexOf("persist") != -1) {
					cookieExpirationDate = gidLib.getFutureDate({"years":5});
				}
			}
			return cookieExpirationDate;
		},
		getCookieConfigurationByCookieName:function(cookieName) {
			var cookieConfiguration = null;
			var cookieConfigurationMap = cookieManagementService.model.cookieConfigurationMap;
			var existingCookieConfiguration = cookieConfigurationMap[cookieName];
			if (existingCookieConfiguration) {
				cookieConfiguration = existingCookieConfiguration;
			}
			return cookieConfiguration;
		}
	},
	api:{
		addCookieConfiguration:function(cookieConfiguration) {
			var addCookieConfiguration = cookieManagementService.controller.addCookieConfiguration;
			var isAddCookieConfigurationSuccessful = addCookieConfiguration(cookieConfiguration);
			return isAddCookieConfigurationSuccessful;
		},
		getCookieConfigurationByCookieName:function(cookieName) {
			var getCookieConfigurationByCookieName = cookieManagementService.controller.getCookieConfigurationByCookieName;
			var cookieConfiguration = getCookieConfigurationByCookieName(cookieName);
			return cookieConfiguration;
		}
	}
}
var cookieManagementService = new CookieManagementService();
cookieManagementService.controller.init.main();
/**
 * This Service Class is concerned with organizing processing logic related to the application
 * such that respective application and content related methods can be queued and executed in a 
 * controllable sequence.
 * @author Andrew Southwick, Filipe Sabella
 */
var ProcessingService = Class.create();
ProcessingService.prototype = {
	initialize: function() {
	},
	constants: {
	},
	constructors: {
		AbstractProcessingMethod: function(method, label) {
			this.label = label;
			this.method = method;

			this.execute = function() {
				this.method();
			};
		}
	},
	api: {
		addApplicationMethodToRegistry: function(applicationMethod, applicationMethodLabel) {
			processingService.controller.addApplicationMethodToRegistry(applicationMethod, applicationMethodLabel);
		},
		addContentMethodToRegistry: function(applicationMethod, contentMethodLabel) {
			processingService.controller.addContentMethodToRegistry(applicationMethod, contentMethodLabel);
		}
	},
	model: {
		isProcessingServiceActive: true,
		applicationProcessingRegistry: [],
		contentProcessingRegistry: []
	},
	controller: {
		init: {
			main: function() {
				var handler = processingService.controller.handlers.domReadyHandler.bind(processingService);
				Event.observe(document, "dom:loaded", handler);
			}
		},
		handlers: {
			/**
			 * Method is called on the domReady event.
			 * 
			 * Processing flow:
			 * 1) This method fires an event before and after the executeApplicationProcessingRegistry.
			 * "executeApplicationProcessingRegistryBegin" and "executeApplicationProcessingRegistryEnd"
			 * 2) Then the method fires and event before and after the executeContentProcessingRegistry.
			 * "executeContentProcessingRegistryBegin" and "executeContentProcessingRegistryEnd".
			 * The siteWidePerformanceMonitor will capture these events and record delta time so that these
			 * can be testable and recordable metrics.
			 */
			domReadyHandler: function() {
				document.fire("executeApplicationProcessingRegistryBegin");
				this.controller.executeApplicationProcessingRegistry()
				document.fire("executeApplicationProcessingRegistryEnd");

				document.fire("executeContentProcessingRegistryBegin");
				this.controller.executeContentProcessingRegistry()
				document.fire("executeContentProcessingRegistryEnd");
			}
		},
		executeApplicationProcessingRegistry: function() {
			var methods = processingService.model.applicationProcessingRegistry;
			methods.each(function(m) {
				m.execute();
			});
		},
		executeContentProcessingRegistry: function() {
			var methods = processingService.model.contentProcessingRegistry;
			methods.each(function(m) {
				m.execute();
			});
		},
		addApplicationMethodToRegistry: function(applicationMethod, applicationMethodLabel) {
			var method = new processingService.constructors.AbstractProcessingMethod(applicationMethod, applicationMethodLabel);
			processingService.model.applicationProcessingRegistry.push(method);
		},
		addContentMethodToRegistry: function(applicationMethod, contentMethodLabel) {
			var method = new processingService.constructors.AbstractProcessingMethod(applicationMethod, contentMethodLabel);
			processingService.model.contentProcessingRegistry.push(method);
		}
	}
}

var processingService = new ProcessingService();
processingService.controller.init.main();
/**
 * The PerformanceMonitorService is designed to capture performance metrics on a give page.
 * This is intended to be an extension of the TestJs open source test framework, and a part of 
 * the TestJs framework.
 * @author Andrew Southwick
 */
var PerformanceMonitorService = Class.create();
PerformanceMonitorService.prototype = {
	domReadyDate:null,
	domReadyTime:null,
	domLoadDate:null,
	domLoadTime:null,
	pageReadyDate:null,
	pageReadyTime:null,
	initDate:null,
	initTime:null,
	previousPageName:null,
	previousPageBusinessId:null,
	previousPageUnloadDate:null,
	previousPageUnloadTime:null,
	timeToReadyFromPreviousPage:null,
	timeToReadyFromInit:null,
	timeToCheckoutReadyFromPreviousPage:null,
	timeToLoadFromInit:null,
	timeToLoadFromPreviousPage:null,
	hasNoPreviousPage:false,
	isReferrerValid:false,
	isTestEnvironment:false,
	initialize:function() {
		//initialize properties her
	},
	reporting:{
		timeToReadyFromPreviousPage:null,
		timeToReadyFromPreviousPageFormatted:null,
		timeToLoadFromPreviousPage:null,
		timeToLoadFromPreviousPageFormatted:null,
		previousPage:{
			businessId:null,
			timeToLoadFromPreviousPage:null,
			timeToLoadFromPreviousPageFormatted:null
		}
	},
	constructors:{
		AbstractPagePerformanceMonitor:function() {
			this.initialize = function() {
				//initialize properties here
			};
			this.performanceMonitorName = null;
			this.performanceMonitorClassPath = null;
			this.controller = {
				init:{
					main:function() {
					}
				},
				handlers:{
					setEventHandlers:function() {
					}
				}
			};
			this.model = {

			};

		}
	},
	controller:{
		setCookieVar: function(varName, varValue) {
			var varNamePrefix = "pm-";
			var varNameWithPrefix = varNamePrefix + varName;
			gidLib.setCookieVar("globalSession", varNameWithPrefix, varValue);
		},
		getCookieVar: function(varName) {
			var varNamePrefix = "pm-";
			var varNameWithPrefix = varNamePrefix + varName;
			var val = gidLib.getCookieVar("globalSession", varNameWithPrefix);

			// we store null values when the performance measurement isn't set
			return val && val != "null" ? val : null;
		},
		getDeltaTimeResult: function(pagePerformanceMonitor) {
			var deltaTimeResult = {model: pagePerformanceMonitor.model, start: null, delta: null};

			var previousPageUnloadTime = performanceMonitorService.previousPageUnloadTime;
			if (pagePerformanceMonitor && pagePerformanceMonitor.model && previousPageUnloadTime) {
				var time = new Date().getTime();
				var delta = time - previousPageUnloadTime;

				deltaTimeResult.start = time;
				deltaTimeResult.delta = delta;
			}

			return deltaTimeResult;
		},
		/**
		 * This method gets a time delta metric and cleans / removes the original variable.
		 * @return Returns either null or the actual value from the cookie if there is a value.
		 */
		getTimeDeltaFromCookieVar: function(cookieVarName) {
			var timeDeltaFromCookieVar = performanceMonitor.controller.getCookieVar(cookieVarName);
			performanceMonitor.controller.setCookieVar(cookieVarName, "");
			return timeDeltaFromCookieVar || null;
		},
		init:{
			main:function() {
				var pm = performanceMonitor;
				processingService.api.addApplicationMethodToRegistry(pm.controller.pageOnDOMReadyHandler);
				Event.observe(window, 'load', pm.controller.pageOnLoadHandler);
				Event.observe(window, 'beforeunload', pm.controller.pageOnUnloadHandler);
				pm.initDate = new Date();
				pm.initTime = pm.initDate.getTime();
				pm.controller.init.getCookieData();
			},
			getCookieData:function() {
				var pm = performanceMonitor;
				var previousPageUnloadDate = gidLib.dateUtils.getDateFromCookieVar('globalSession', 'previousPageUnloadDate');
				if (previousPageUnloadDate != null) {
					pm.previousPageName = gidLib.getCookieVar('globalSession', 'previousPageName');
					pm.previousPageUnloadDate = gidLib.dateUtils.getDateFromCookieVar('globalSession', 'previousPageUnloadDate');
					if (pm.previousPageUnloadDate instanceof Date) {
						pm.previousPageUnloadTime = previousPageUnloadDate.getTime();
					}
					pm.reporting.previousPage.timeToReadyFromPreviousPage = pm.controller.getCookieVar('timeToReadyFromPreviousPage');
					pm.reporting.previousPage.timeToLoadFromPreviousPage = gidLib.getCookieVar('globalSession', 'timeToLoadFromPreviousPage');
					pm.reporting.previousPage.businessId = gidLib.getCookieVar('globalSession', 'previousPageBusinessId');
				}
				else {
					pm.hasNoPreviousPage = true;
				}
			}
		},
		monitorRegistry: [],

		addMonitorToRegistry: function(monitor) {
			if (monitor) {
				monitor.controller.init.main();
				monitor.controller.handlers.setEventHandlers();
				performanceMonitor.controller.monitorRegistry.push(monitor);
			}
		},
		monitors: {
			ecom: {
				siteWide: {
					siteWideMonitor: {}
				},
				browse: {
					productMonitor: {},
					categoryMonitor: {},
					homeMonitor: {},
					divisionMonitor: {},
					subDivisionMonitor: {}
				},
				buy: {
					shoppingBagMonitor: {}
				},
				checkout: {}
			}
		},
		pageOnUnloadHandler:function() {
			var dateUtils = gidLib.dateUtils;
			var date = new Date();
			var pageName = null;
			var businessId = null;
			var timeToReadyFromPreviousPage = (window["performanceMonitor"] ? performanceMonitor.reporting.timeToReadyFromPreviousPage : "");
			var timeToLoadFromPreviousPage = (window["performanceMonitor"] ? performanceMonitor.reporting.timeToLoadFromPreviousPage : "");
			if(!(window['reportingService']||{}).isActive) {
				pageName = (window["omni"] ? omni.strCurrentPageName : ""); // check if omni object exists for cookie propagator page
		        businessId = (window["omni"] ? omni.strCurrentBusinessId : "");
			}
			else {
				pageName = reportingService.controller.viewManagers.commonViewManager.model.commonCurrentPageName;
				businessId = reportingService.controller.viewManagers.commonViewManager.model.commonCurrentBusinessId;
			}
	        dateUtils.setDateToCookieVar('globalSession', 'previousPageUnloadDate', date);
	        gidLib.setCookieVar('globalSession', 'previousPageName', pageName);
			gidLib.setCookieVar('globalSession', 'previousPageBusinessId', businessId);
			performanceMonitor.controller.setCookieVar("timeToReadyFromPreviousPage", timeToReadyFromPreviousPage);
			gidLib.setCookieVar('globalSession', 'timeToLoadFromPreviousPage', timeToLoadFromPreviousPage);
		},
		pageOnDOMReadyHandler:function() {
			var pm = performanceMonitor;
			var timeToReadyFromPreviousPage = null;
			var timeToReadyFromInit = null;
			var domReadyDate = new Date();
			var domReadyTime = domReadyDate.getTime();
			pm.domReadyDate = domReadyDate;
			pm.domReadyTime = domReadyTime;

			if (pm.previousPageUnloadTime != null) {
				if (pm.controller.isReferrerValid()) {
					performanceMonitor.isReferrerValid = true;
					timeToReadyFromPreviousPage = domReadyTime - pm.previousPageUnloadTime;
					pm.timeToReadyFromPreviousPage = timeToReadyFromPreviousPage;
					pm.controller.setReportingMetrics();
				}
			}
			if (pm.initTime != null) {
				timeToReadyFromInit = domReadyTime - pm.initTime;
				pm.timeToReadyFromInit = timeToReadyFromInit;
			}
		},
		pageOnLoadHandler:function() {
			var pm = performanceMonitor;
			var timeToLoadFromPreviousPage = null;
			var timeToLoadFromInit = null;
			var domLoadDate = new Date();
			var domLoadTime = domLoadDate.getTime();
			pm.domLoadDate = domLoadDate;
			pm.domLoadTime = domLoadTime;

			if (pm.previousPageUnloadTime != null) {
				if (pm.isReferrerValid == true) {
					timeToLoadFromPreviousPage = domLoadTime - pm.previousPageUnloadTime;
					pm.timeToLoadFromPreviousPage = timeToLoadFromPreviousPage;
					pm.controller.setDomLoadMetrics();
				}
			}
			if (pm.initTime != null) {
				timeToLoadFromInit = domLoadTime - pm.initTime;
				pm.timeToLoadFromInit = timeToLoadFromInit;
			}
		},
		/**
		 * pageOnPageReadyHandler records the time it takes to for a page to be ready for interaction by the user on pages with post-load operations.
		 * This needs to be called manually once the post-load operations are completed but before the omniture transmission.
		 */
		pageOnPageReadyHandler:function() {
			var pm = performanceMonitor;
			var timeToPageReadyFromPreviousPage = null;
			var timeToPageReadyFromInit = null;
			var timeToPageReadyFromDomReady = null;
			var timeToPageReadyFromOnLoad = null;
			var pageReadyDate = new Date();
			var pageReadyTime = pageReadyDate.getTime();
			pm.pageReadyDate = pageReadyDate;
			pm.pageReadyTime = pageReadyTime;

			// Measures total page load from previous page unload time
			if (pm.previousPageUnloadTime != null) {
				if (pm.isReferrerValid == true) {
					timeToPageReadyFromPreviousPage = pageReadyTime - pm.previousPageUnloadTime;
					pm.timeToPageReadyFromPreviousPage = timeToPageReadyFromPreviousPage;
				}
			}

			// Measures total page load from the initialization time of this class
			if (pm.initTime != null) {
				timeToPageReadyFromInit = pageReadyTime - pm.initTime;
				pm.timeToPageReadyFromInit = timeToPageReadyFromInit;
			}

			// Measures total page load from DOM Ready
			if (pm.domReadyTime != null) {
				timeToPageReadyFromDomReady = pageReadyTime - pm.domReadyTime;
				pm.timeToPageReadyFromDomReady = timeToPageReadyFromDomReady;
			}

			// Measures total page load from onLoad
			if (pm.domLoadTime != null) {
				timeToPageReadyFromOnLoad = pageReadyTime - pm.domLoadTime;
				pm.timeToPageReadyFromOnLoad = timeToPageReadyFromOnLoad;
			}

		},

		/**
		 * setTimeToReadyAsTimeToPageReady overrides the timeToReadyFromPreviousPage value with the timeToPageReadyFromPreviousPage value
		 * This used to report more accurate page READY times on pages that have subsequent AJAX calls. e.g. product page
		 */
		setTimeToReadyAsTimeToPageReady : function() {
			var pm = performanceMonitor;
			pm.timeToReadyFromPreviousPage = pm.timeToPageReadyFromPreviousPage;
			pm.controller.setReportingMetrics();
		},

		/**
		 * setTimeToLoadAsTimeToPageReady overrides the timeToLoadFromPreviousPage value with the timeToPageReadyFromPreviousPage value
		 * This used to report more accurate page LOAD times on pages that have subsequent AJAX calls. e.g. product page
		 */
		setTimeToLoadAsTimeToPageReady : function() {
			var pm = performanceMonitor;
			pm.timeToLoadFromPreviousPage = pm.timeToPageReadyFromPreviousPage;
			pm.controller.setDomLoadMetrics();
		},


		isReferrerValid:function() {
			var isReferrerValid = false;
			var isTestEnvironment = false;
			var referrer = null;
			var brandUrl = brandConst.BRAND_URL;
			var expressionValue = "^https?://.*(" + brandUrl + "|gap.com|gap.eu|gapcanada.ca)";
			var expressionValueForTestEnvironments = "^https?://.*(" + brandUrl + "|gidgol.com|gidgol.ca|gidgol.co.uk|gidapps.com|gidapps.ca|gidapps.eu)";
			var urlExpression = new RegExp(expressionValue, "i");
			var urlExpressionForTestEnvironments = new RegExp(expressionValueForTestEnvironments, "i");
			if (document.referrer) {
				referrer = document.referrer;
				isReferrerValid = urlExpression.test(referrer);
				if (isReferrerValid == false) {
					// Test to see if the environment is a test environment - using test environment expression.
					isReferrerValid = urlExpressionForTestEnvironments.test(referrer);
					if (isReferrerValid == true) {
						isTestEnvironment = true;
					}
				}
			}
			if (clientBrowser.isIE6up) {
				if (performanceMonitor.previousPageUnloadDate != null) {
					isReferrerValid = true;
				}
			}
			performanceMonitor.referrer = referrer;
			performanceMonitor.expressionValue = expressionValue;
			performanceMonitor.isReferrerValid = isReferrerValid;
			performanceMonitor.isReferrerValid = isTestEnvironment;
			return isReferrerValid;
		},

		setReportingMetrics:function() {
			var pm = performanceMonitor;
			var reporting = pm.reporting;
			var isReferrerValid = pm.isReferrerValid;
			var timeToReadyFromPreviousPageFormatted = null;
			var timeToReadyFromPreviousPage = pm.timeToReadyFromPreviousPage;
			var previousPage = reporting.previousPage;
			var timeToLoadFromPreviousPage = previousPage.timeToLoadFromPreviousPage;

			if (isReferrerValid) {
				if (timeToReadyFromPreviousPage != null) {
					timeToReadyFromPreviousPageFormatted = pm.controller.getFormattedReportingTimeValue(timeToReadyFromPreviousPage);
					reporting.timeToReadyFromPreviousPageFormatted = timeToReadyFromPreviousPageFormatted;
					reporting.timeToReadyFromPreviousPage = timeToReadyFromPreviousPage;
					// s_prop45 = reporting.timeToReadyFromPreviousPageFormatted;

					// Calculate sprop42
					if ((timeToLoadFromPreviousPage != "") || (timeToLoadFromPreviousPage != null)) {
						if ((previousPage.businessId != "") || (previousPage.businessId != null)) {
							previousPage.timeToLoadFromPreviousPageFormatted = previousPage.businessId + ":" + pm.controller.getFormattedReportingTimeValue(timeToLoadFromPreviousPage);
							// s_prop42 = previousPage.timeToLoadFromPreviousPageFormatted;
						}
						else {
							previousPage.timeToLoadFromPreviousPageFormatted = "invalid data";
						}
					}
					else {
						previousPage.timeToLoadFromPreviousPageFormatted = "invalid data";
					}
				}
				else {
					reporting.timeToReadyFromPreviousPageFormatted = "invalid data";
					previousPage.timeToLoadFromPreviousPageFormatted = "invalid data";
					reporting.timeToReadyStatus = "invalid data";
					reporting.timeToLoadStatus = "invalid data";
				}
			}
			else {
				reporting.timeToReadyFromPreviousPageFormatted = "invalid referrer";
				previousPage.timeToLoadFromPreviousPageFormatted = "invalid referrer";
				reporting.timeToReadyStatus = "invalid referrer";
				reporting.timeToLoadStatus = "invalid referrer";
			}


			// sprop42 for G Code
			if (previousPage.timeToLoadFromPreviousPageFormatted != null) {
				if(!(window['reportingService']||{}).isActive) {
					s_prop42 = previousPage.timeToLoadFromPreviousPageFormatted;
				}
			}
			// sprop45 for G Code
			if (reporting.timeToReadyFromPreviousPageFormatted != null) {
				if(!(window['reportingService']||{}).isActive) {
					s_prop45 = reporting.timeToReadyFromPreviousPageFormatted;
				}
			}
		},
		setDomLoadMetrics:function() {
			var pm = performanceMonitor;
			var reporting = pm.reporting;
			var isReferrerValid = pm.isReferrerValid;
			var timeToLoadFromPreviousPageFormatted = null;
			var timeToLoadFromPreviousPage = pm.timeToLoadFromPreviousPage;

			if (isReferrerValid) {
				if (timeToLoadFromPreviousPage != null) {
					timeToLoadFromPreviousPageFormatted = pm.controller.getFormattedReportingTimeValue(timeToLoadFromPreviousPage);
					reporting.timeToLoadFromPreviousPageFormatted = timeToLoadFromPreviousPageFormatted;
					reporting.timeToLoadFromPreviousPage = timeToLoadFromPreviousPage;
				}
				else {
					reporting.timeToLoadStatus = "invalid referrer";
				}
			}
			else {
				reporting.timeToLoadStatus = "invalid referrer";
			}
		},

		/**
		 * getFormattedReportingTimeValue converts milliseconds to the desired reporting time value
		 */
		getFormattedReportingTimeValue : function(ms) {
			var reportingTime = ms;
//			var reportingTime = Math.round(ms * .001); // convert to seconds and round.
			return reportingTime;
		}
	}
}

var performanceMonitorService = new PerformanceMonitorService();
var performanceMonitor = performanceMonitorService;
performanceMonitorService.controller.init.main();
performanceMonitorService.controller.monitors.ecom.siteWide.siteWideMonitor = new performanceMonitorService.constructors.AbstractPagePerformanceMonitor();
Object.extend(performanceMonitorService.controller.monitors.ecom.siteWide.siteWideMonitor, {
	performanceMonitorName: "Site Wide Performance Monitor",
	performanceMonitorClassPath: "performanceMonitorService.controller.monitors.ecom.siteWide.siteWideMonitor",
	constants: {
		PAGE_LOAD_TIME_DELTA_FROM_PREVIOUS_PAGE: "pageLoadTimeDeltaFromPreviousPage",
		DOM_READY_TIME_DELTA_FROM_PREVIOUS_PAGE: "domReadyTimeDeltaFromPreviousPage"
	},
	controller: {
		init: {
			main: function() {
				var controller = performanceMonitorService.controller;
				var monitor = controller.monitors.ecom.siteWide.siteWideMonitor;
				var constants = monitor.constants;

				monitor.model.previousPage.pageLoadTimeDelta
					= controller.getTimeDeltaFromCookieVar(constants.PAGE_LOAD_TIME_DELTA_FROM_PREVIOUS_PAGE);
				monitor.model.previousPage.domReadyTimeDelta
					= controller.getTimeDeltaFromCookieVar(constants.DOM_READY_TIME_DELTA_FROM_PREVIOUS_PAGE);
			}
		},
		handlers: {
			setEventHandlers: function() {
				var monitor = performanceMonitorService.controller.monitors.ecom.siteWide.siteWideMonitor;
				var handlers = monitor.controller.handlers;

				Event.observe(window, "beforeunload", handlers.pageOnUnloadHandler.bind(monitor));
				Event.observe(window, "load",   handlers.pageLoadHandler.bind(monitor));

				processingService.api.addApplicationMethodToRegistry(handlers.domReadyHandler.bind(monitor), "siteWide monitor");
			},
			pageLoadHandler: function() {
				var result = performanceMonitorService.controller.getDeltaTimeResult(this);
				result.model.pageLoadTime = result.start;
				result.model.pageLoadTimeDelta = result.delta;
			},
			domReadyHandler : function() {
				var result = performanceMonitorService.controller.getDeltaTimeResult(this);
				result.model.domReadyTime = result.start;
				result.model.domReadyTimeDelta = result.delta;
			},
			pageOnUnloadHandler: function() {
				var controller = performanceMonitorService.controller;
				var monitor    = performanceMonitorService.controller.monitors.ecom.siteWide.siteWideMonitor;
				var model      = monitor.model;
				var constants  = monitor.constants;

				controller.setCookieVar(constants.PAGE_LOAD_TIME_DELTA_FROM_PREVIOUS_PAGE,
						model.pageLoadTimeDelta);
				controller.setCookieVar(constants.DOM_READY_TIME_DELTA_FROM_PREVIOUS_PAGE,
						model.domReadyTimeDelta);
			}
		}
	},
	model: {
		pageLoadTime     : null,
		pageLoadTimeDelta: null,
		domReadyTime     : null,
		domReadyTimeDelta: null,
		previousPage: {
			pageLoadTimeDelta: null,
			domReadyTimeDelta: null
		}
	}
});
performanceMonitorService.controller.addMonitorToRegistry(performanceMonitorService.controller.monitors.ecom.siteWide.siteWideMonitor);

/***************************************************
  SURVEY OBJECT CODE START
***************************************************/
function PopDispatcher(surveyTypes, surveyDist) {
	this.types = surveyTypes;
	this.dist = surveyDist;
	this.currentBrand = '';
	this.currentProbs = 0;
	this.currentDist = 0;
    this.width = 0;
    this.height = 0;
}

PopDispatcher.prototype.getRandType = function(page, Dist) {
    var total = 0;

    for(var i=0; i < Dist.length; i++ )
        total += Dist[i];

    Probs = [];
    var j, k, l = Dist.length;
    var Q = 1;

    for (j = Dist.length-1 ; j >= 0 ; j-- ) {
        Probs[j] = Q ;
        Q -= Dist[j]/total;
    }

    k = Math.random()%1;
    for (j=0 ; j < Probs.length ; j++)
        if (Probs[j] > k) break;

    return {'rand':k, 'index': j};
}

PopDispatcher.prototype.dispatch = function(page) {
    var Dist = eval( 'this.dist.' + page );
    var objSurvey = Survey.getSurveyFromCookie(window.location.href);

    if( objSurvey == null ) {
        var selected = this.getRandType(page, Dist);
        popBrand = ( this.types[selected.index] == '' ) ? 'None' : this.types[selected.index];
        this.currentBrand = popBrand;
        this.currentIndex = selected.index;
        this.currentDist = Dist[selected.index];
        this.currentThreshHold = selected.rand;

        if( popBrand == 'None' ) return null;
        else return eval(popBrand);
     } else return objSurvey;
}

var popDispatcher = new PopDispatcher(brandConst.SURVEY_BRAND, brandConst.SURVEY_POPUP_DISTRIBUTION);

// Survey object wrapper class
function Survey(name) {
    this.url = '';
    this.name = name;
    this.params = "toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=no,width=320,height=280,top=200,left=200";
    this.width = 320;
    this.height = 280;
    this.cookieExpirationTime = new Date((new Date()).getTime() + brandConst.BIZRATE_COOKIE_EXPIRATION_TIME);
}

Survey.prototype.getSurveyURL = function(page) { return this.url; };
Survey.prototype.getURLParams = function(page) {
    var sessionID = getCookie("JSESSIONID");
    var customerId = getCookie("customerId") || '';
    var unknownShopperId = getCookie("unknownShopperId");
    var shopperID = (customerId != "" ? (customerId.indexOf("null") == -1 ? customerId : unknownShopperId).replace(/\|\|\|/,"") : "");
    var referringPage = escape(location.pathname+location.search);
    var referringPageType = eval('brandConst.PAGE_TYPES.' + page ) ;
    return "?id=27298&Q104534=" + sessionID + "&Q104535=" + shopperID + "&Q104536=" + referringPage + "&Q104533=" + referringPageType;

};

Survey.prototype.byPassPopBlocker = function(mypop, url, page) {
     try { mypop.focus(); }
        catch (e) {
         //   if( page == 'confirm') {
                var popdiv = document.createElement('div');
                popdiv.id = 'surveyDiv';

                popdiv.style.width = this.width + 'px';
                popdiv.style.height = (this.height + 10) + 'px';
                gidLib.setObjCenter(popdiv, this.width, this.height + 10);

                popdiv.innerHTML = '<div style="padding-right: 10px; width: ' + this.width + 'px; text-align: right;" align="right">' +
                        '<a href="#" onclick="$(\'surveyDiv\').style.display = \'none\';">close [x]</a></div>' +
                        '<iframe id="surveyFrame" width="' + this.width + '" height="' + this.height + '" frameborder="0" scrolling="no" src="' + url + '&divPop=true' +
                                   '" />'

				var mainContent = $('mainContent');
				var mainContentContainer = $('mainContentContainer');
				if (mainContentContainer) {
					mainContentContainer.appendChild(popdiv);
				} else if (mainContent) {
					mainContent.appendChild(popdiv);
				}

         //    }
        }
}

Survey.getSurveyFromCookie = function(strPageURL) {
    var objSurvey = null;
    var dateSixMonths = this.cookieExpirationTime;

    var surveyTestForceSurveyType = strPageURL.indexOf("surveyType") > 1 ? getQuerystringParam('surveyType') : '';
    if( surveyTestForceSurveyType == '' ) surveyTestForceSurveyType = getCookieVar("SurveyPersist","surveyType");

    if( surveyTestForceSurveyType != null && surveyTestForceSurveyType != '' ) {
        setCookieVar("SurveyPersist","surveyType",surveyTestForceSurveyType,dateSixMonths);
        if( surveyTestForceSurveyType == 'bizrate' ) objSurvey = BizrateSurvey;
        //if( surveyTestForceSurveyType == 'rockbridge' ) objSurvey = RockbridgeSurvey;
    }

    return objSurvey;
}
// public static popSurvey menthod call, use by all page on page load
Survey.popSurvey = function (page, survey) {
	//kwid suppression & qa testing param added
	if(gidLib.getQuerystringParam("kwid")!="" && gidLib.getQuerystringParam("qa-test")!="true"){ return; }
    var strPageURL = window.location.href;
    var isSurveyTestOn = strPageURL.indexOf("surveytest") != -1 || getCookieVar("SurveyPersist","surveyTest") == "1";

    if( isSurveyTestOn ) { setCookieVar("SurveyPersist","surveyTest",isSurveyTestOn ? '1' : '0',dateSixMonths); }

    if (getCookieVar("SurveyPersist","served") == "" || isSurveyTestOn || page == 'confirm' ) {

        /***************************  QA DEBUG STUFF ***************************************************************/
        if( survey != null )
            objSurvey = survey;
        else {
            objSurvey = this.getSurveyFromCookie(strPageURL);

            if( objSurvey == null )
                objSurvey = popDispatcher.dispatch(page);

        }

        if (isSurveyTestOn)
        	alert(
        		"TEST MODE:" +
                "\n\r\t1)Page type: " + page +
                ".\n\r\t2)Pop type: " + popDispatcher.currentBrand +
        		".\n\r\t3)Pop type distribution: " + popDispatcher.currentDist +
        		".\n\r\t4)Pop type (index, threshold): " + popDispatcher.currentIndex + ", " + popDispatcher.currentThreshHold +
        		".\n\r\t5)Should Pop: " + (( objSurvey == null || objSurvey == '' ) ? 'No' : 'Yes')
            );
         /***************************** DEBUG CODE ENDS *************************************************************/
        if( objSurvey == null || objSurvey == '' ) return;

        var url = objSurvey.getSurveyURL(page);
        if( url == '' ) return;

        url += objSurvey.getURLParams(page);
        var mypop = window.open(url, objSurvey.name, objSurvey.getPopParams());
        objSurvey.byPassPopBlocker(mypop, url, page);

        var dateSixMonths = this.cookieExpirationTime;
        if (strPageURL.indexOf("surveytest") != -1) alert("Survey cookie set to expire in 6 months (" + dateSixMonths + ").");
        setCookieVar("SurveyPersist","served","1",dateSixMonths);
	}
};

// sets/return pop up window parameters such as dimension and scrollbar properties
Survey.prototype.setPopParams = function(params) { this.params = params; };
Survey.prototype.getPopParams = function() { return this.params; }

// bizrate
var BizrateSurvey = new Survey('BizrateSurvey');
BizrateSurvey.url = 'http://eval.bizrate.com/vispop_popup.pl';
BizrateSurvey.getSurveyURL = function(page)  {
    if( page == 'confirm' )
        BizrateSurvey.customPop();
    else return this.url;

    return '';
}
BizrateSurvey.customPop = function() {
	document.write('<script type="text/javascript" src="https://eval.bizrate.com/js/pos_27298.js"></script>');
}
BizrateSurvey.extraSurveyParams = { bfmid: '32961748', cid: '1501050', type: '307497', passin1: 'Q104534', passin2: 'Q108636' };

// rockbridge survey object
//var RockbridgeSurvey = new Survey('RockbridgeSurvey');
//RockbridgeSurvey.url = 'http://survey.confirmit.com/wix/p411310966.aspx';
//RockbridgeSurvey.params = "toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=no,width=330,height=280";
//RockbridgeSurvey.width = 330;
//RockbridgeSurvey.height = 280;
//
//RockbridgeSurvey.getURLParams = function(page) {
//    var sessionID = getCookie("JSESSIONID");
//    var customerId = getCookie("customerId") || '';
//    var unknownShopperId = getCookie("unknownShopperId");
//    var shopperID = (customerId != "" ? (customerId.indexOf("null") == -1 ? customerId : unknownShopperId).replace(/\|\|\|/,"") : "");
//    var referringPageType = eval('brandConst.PAGE_TYPES.' + page ) ;
//    var referringPage = escape(location.pathname+location.search);
//    var paramString = "?Q104534=" + sessionID + "&Q104535=" + shopperID + "&Q104533=" + referringPageType + "&Q104536=" + referringPage;
//
//    paramString += (RockbridgeSurvey.orderID ? "&OrderID=" + RockbridgeSurvey.orderID : '') +
//                (RockbridgeSurvey.brandsVisited ? "&brandsVisited=" + RockbridgeSurvey.brandsVisited : '') +
//                (RockbridgeSurvey.brandsPurchased ? "&brandsPurchased=" + RockbridgeSurvey.brandsPurchased : '') +
//                (RockbridgeSurvey.brand ? "&brand=" + RockbridgeSurvey.brand : '') +
//                (RockbridgeSurvey.market ? "&market=" + RockbridgeSurvey.market : '') +
//                (RockbridgeSurvey.locale ? "&locale=" + RockbridgeSurvey.locale : '') ;
//
//    
//    return paramString;
//    
//};

/***************************************************
  SURVEY OBJECT CODE END
***************************************************/

/*
 * Extend siteNavigation.topNav with brand specific topNav functions
 */
Object.extend(siteNavigation.topNav,
	{
		divisionOver:function(division,isSelected) {
			if (!isSelected) {
				$(division).className = "topnavSprite_" + division + "_over";
				
			}
		},

		divisionOut:function(division,isSelected) {
			if (!isSelected) {
				$(division).className = "topnavSprite_" + division + "_off";
			}
		}
	}
);
/**
 * deletes Cookies for given domain.
 *
 * [domain]   Domain where the cookie is valid
 *              (default: domain of calling document)
 * 
 */

function deleteDomainCookies(domain){
					
	var myArray = ['unknownShopperId','JSESSIONID','customerId','customerIdVerified','mktUniversalPersist','TID','CJID','RANGE',
						'MICROSOFT',
						'PARTNER',
						'TIDBRAND',
						'BRAND_TID_MAP',
						'globalSession',
						'SurveyPersist',
						'locale',
						'omniSession',
						'omniSessionProducts',
						'gid.h',
						'gid.p',
						'mktUniversalSession'];
		for (var i=0; i<myArray.length; i++) {
			
			deleteCookie(myArray[i],"/",domain);
			deleteCookie(myArray[i],"/", "." + domain);
		
		}
}

/**
 * deletes Cookies for given domain.
 *
 * [domain]   Domain where the cookie is valid
 *              (default: domain of calling document)
 * 
 */

function deleteDomainCookies(domain,isCoher){
					
	var myArray = ['unknownShopperId','JSESSIONID','customerId','customerIdVerified','mktUniversalPersist','TID','CJID','RANGE',
						'MICROSOFT',
						'PARTNER',
						'TIDBRAND',
						'BRAND_TID_MAP',
						'globalSession',
						'SurveyPersist',
						'locale',
						'omniSession',
						'omniSessionProducts',
						'gid.h',
						'gid.p',
						'mktUniversalSession'];
	    
		for (var i=0; i<myArray.length; i++) {
			var delCookie = true;
			if(myArray[i] == 'JSESSIONID' && isCoher == 'false') delCookie = false;
			
			if(delCookie)			{
				deleteCookie(myArray[i],"/",'');
				deleteCookie(myArray[i],"/", "." + domain);
			}
			
		}
}
/**
 * Sets a Cookie with the given name and value.
 *
 * name       Name of the cookie
 * value      Value of the cookie
 * [expires]  Expiration date of the cookie (default: end of current session)
 * [path]     Path where the cookie is valid (default: path of calling document)
 * [domain]   Domain where the cookie is valid
 *              (default: domain of calling document)
 * [secure]   Boolean value indicating if the cookie transmission requires a
 *              secure transmission
 */
function setCookie(name, value, expires, path, domain, secure)
{
    document.cookie= name + "=" + escape(value) +
        ((expires) ? "; expires=" + expires.toGMTString() : "") +
        ((path) ? "; path=" + path : "") +
        ((domain) ? "; domain=" + domain : "") +
        ((secure) ? "; secure" : "");
}

/**
 * Gets the value of the specified cookie.
 *
 * name  Name of the desired cookie.
 *
 * Returns a string containing value of specified cookie,
 *   or null if cookie does not exist.
 */
function getCookie(name)
{
    var dc = document.cookie;
    var prefix = name + "=";
    var begin = dc.indexOf("; " + prefix);
    if (begin == -1)
    {
        begin = dc.indexOf(prefix);
        if (begin != 0) return null;
    }
    else
    {
        begin += 2;
    }
    var end = document.cookie.indexOf(";", begin);
    if (end == -1)
    {
        end = dc.length;
    }
    return unescape(dc.substring(begin + prefix.length, end));
}

/**
 * Deletes the specified cookie.
 *
 * name      name of the cookie
 * [path]    path of the cookie (must be same as path used to create cookie)
 * [domain]  domain of the cookie (must be same as domain used to create cookie)
 */
function deleteCookie(name, path, domain)
{
    if (getCookie(name))
    {
    	var mydate = new Date();
		mydate.setTime(mydate.getTime() - 100000);
        document.cookie = name + "=" + 
            ((path) ? "; path=" + path : "") +
            ((domain) ? "; domain=" + domain : "") +
            ( ( mydate ) ? ";expires=" + mydate.toGMTString() : "" );
    }
}
/**********************************************
@title:  ErrorDisplayService.js
@author:  James Erb
@date:    06-11-2007
@rev:     1.2
@desc:    Page-specific error messaging framework for ajax on checkout.
 * Modified:  Andrew Southwick 6/24/07 - moved variables from ErrorManagerStateMonitor to
 * errorDisplayService.stateMonitor and errorDisplayService.constants.
 * @author Andrew Southwick
@assumes: prototype.js 1.5 rel.
@event dependency:  create with onload
********************************/

var ErrorDisplayService = Class.create();
 
ErrorDisplayService.prototype = {


	initialize:function() {
		//if multiple instances required at later date
	},

	hiddenFocusDropDowns:[],
	hiddenPreviewDropDowns:[],
	errorMsgTarget:null,
	
	stateMonitor:{
		errorHasFocus:"false",
		errTextContainsLinks:"false",
		currentErrorCollection:{},
		currentMessageCollection:{},
		currentNamespace:"",
		currentModuleErrorNode:null,
		currentModuleErrorNodeName:null,
		currentModuleMessageNode:null,
		currentModuleMessageNodeName:null,
		currentModuleErrorSummaryNode:null,
		currentModuleErrorSummaryNodeName:null,
		currentModuleMessageSummaryNode:null,
		currentModuleMessageSummaryNodeName:null
	},
	constants:{
		ERROR_STYLE_CLASS:"err",
		MESSAGE_STYLE_CLASS:"messageDisplayService",
		MSG_BBL_ID:"msgBbl",
		MSG_TEXT_ID:"msgTxt",
		MODULE_ERROR_ID_NAME:"moduleError",
		MODULE_MESSAGE_ID_NAME:"moduleMessage",
		LONG_ERROR_MSG_THRESHOLD:100,
		LONG_ERROR_MSG_CLASSNAME:"msgBblWide"
	},
	processErrors:function(errorsCollection, namespace, prefixFieldList, prefixName) {
		var errorId = null;
		var errorMessage = null;
		var targetNode = null;
		var targetNodeName = null;
		var summaryTargetNode = null;
		var summaryTargetNodeName = null;
		var hasSummaryTargetNode = false;
		var moduleErrorNodeName = null;
		var moduleErrorNode = null;
		var moduleErrorSummaryNode = null;
		var moduleErrorSummaryNodeName = null;
		var hasModuleError = false;
		var hasSummaryTargetNode = false;
		var hasModuleErrorSummaryNode = false;
		var moduleErrorBuffer = "<ul>";
		errorDisplayService.stateMonitor.currentNamespace = namespace;
		this.clearErrorStyles();
		for (var errorCounter = 0; errorCounter < errorsCollection.length; errorCounter++) {
			hasSummaryTargetNode = false;
			summaryTargetNodeName = null;
			if (errorsCollection[errorCounter].idRo != null) {
				errorId = errorsCollection[errorCounter].idRo;
			}
			if (errorsCollection[errorCounter].errorMessageRo != null) {
				errorMessage = errorsCollection[errorCounter].errorMessageRo;
			}
			if ((errorId != null) && (errorMessage != null)) {
				errorDisplayService.stateMonitor.currentErrorCollection = errorsCollection;
				try {
					/*
					 * Check to see if this error is a moduleError, moduleMessage, or field 
					 * error and handle accordingly.  All modules have have either field errors, 
					 * module errors, or module messages.  
					 * If it is a module error then apply to buffer and set isModuleError to true. 
					 * The buffer is needed because there could be multiple module errors.
					 * If it is a module message then apply to buffer and set isModuleMessage 
					 * to true. 
					 */
					if (errorId == errorDisplayService.constants.MODULE_ERROR_ID_NAME) {
						 moduleErrorBuffer += "<li>" + errorMessage + "</li>";
						 hasModuleError = true;
					}
					else {
						/*
						 * Assume a field error - since this is the only other type of error display related to 
						 * the errorsRo collection.  Process the field error accordingly.
						 */
						targetNodeName = namespace +  errorId.substring(0,1).toUpperCase() + errorId.substring(1, errorId.length);
						if (prefixFieldList != null && prefixFieldList != undefined && prefixName != null && prefixName != undefined) {
							/* prefixFieldList and prefixName have been passed for processing, so check to see if the targetNodeName
							 * is in the prefixFieldList and then if so pre-pend the prefixName.
							 */
							if (prefixFieldList.indexOf(targetNodeName) != -1) {
								targetNodeName = prefixName + "-" + targetNodeName;
							}
						}
						summaryTargetNodeName = namespace + "Summary" + errorId.substring(0,1).toUpperCase() + errorId.substring(1, errorId.length);
						targetNode = $(targetNodeName);
						summaryTargetNode = $(summaryTargetNodeName);
						if (summaryTargetNode != null) {
							hasSummaryTargetNode = true;
							errorDisplayService.setFieldErrorNodeForDisplay(summaryTargetNode, errorId);
						}
						if (targetNode != null) {
							errorDisplayService.setFieldErrorNodeForDisplay(targetNode, errorId);
						}
						// Original set of properties.
					}
				}
				catch(e) {
					
				}
			}
		}
		if (hasModuleError == true) {
			/*
			 * Update moduleError DOM node with the contents of the moduleErrorBuffer.  Now that
			 * errorsRo collection inspection and processing is complete - moduleErrorBuffer is 
			 * ready to display.
			 */
			moduleErrorBuffer += "</ul>";
			try {
			 	with (errorDisplayService.constants) {
					// Open module module error display.
					moduleErrorNodeName = namespace +  MODULE_ERROR_ID_NAME.substring(0,1).toUpperCase() + MODULE_ERROR_ID_NAME.substring(1, MODULE_ERROR_ID_NAME.length);
					moduleErrorNode = $(moduleErrorNodeName);
					moduleErrorNode.update(moduleErrorBuffer);
					moduleErrorNode.show();
					/* Summary module module error display.  
					 * Not all summary module displays have any module error display.
					 * This is why the conditional check for null on the SummaryNode is present.
					 */
					moduleErrorSummaryNodeName = namespace +  "Summary" + MODULE_ERROR_ID_NAME.substring(0,1).toUpperCase() + MODULE_ERROR_ID_NAME.substring(1, MODULE_ERROR_ID_NAME.length);
					moduleErrorSummaryNode = $(moduleErrorSummaryNodeName);
					if (moduleErrorSummaryNode != null) {
						hasModuleErrorSummaryNode = true;
						moduleErrorSummaryNode.update(moduleErrorBuffer);
						moduleErrorSummaryNode.show();
					}
					
					
					// Assign to stateMonitor so that the clearErrorStyles() method can hide the module errors when needed.
					errorDisplayService.stateMonitor.currentModuleErrorNode = moduleErrorNode;
					errorDisplayService.stateMonitor.currentModuleErrorNodeName = moduleErrorNodeName;
					if (hasModuleErrorSummaryNode == true) {
						errorDisplayService.stateMonitor.currentModuleErrorSummaryNode = moduleErrorSummaryNode;
						errorDisplayService.stateMonitor.currentModuleErrorSummaryNodeName = moduleErrorSummaryNodeName;
					}
			 	}
			}
			catch(e) {
				
			}
		}
  		errorDisplayService.setFieldErrorFocus();
	},
	setFieldErrorFocus:function() {
		var moduleControl = null;
		var isInSummaryMode = false;
		var firstErrorNode = null;
		var firstErrorNodeId = null;
		var focusErrorNode = null;
		var errorNode = null;
		var errorNodeId = null;
		var errorInputNodes = $$('.' + errorDisplayService.constants.ERROR_STYLE_CLASS);
		var currentNamespace = errorDisplayService.stateMonitor.currentNamespace;
		var summaryNodeIdKey = "Summary";
		var validFieldErrorNodes = $w('INPUT SELECT TEXTAREA'); 

		if (errorInputNodes.length > 0) {

			// Find the 1st valid field error
			for (var i=0;i<errorInputNodes.length;i++) {
				if (errorInputNodes[i].tagName && validFieldErrorNodes.include(errorInputNodes[i].tagName) == true) {
					firstErrorNode = errorInputNodes[i];
					firstErrorNodeId = firstErrorNode.getAttribute("id");
					break;
				}
			}

			/* Errors in the half-collapsed billing state removed.  There are no 
			 * field errors now that occur in the summary state
			 * 
			if (checkout.controller.modules[currentNamespace]) {
				moduleControl = checkout.controller.modules[currentNamespace];
				if (moduleControl.isInSummaryMode) {
					isInSummaryMode = moduleControl.isInSummaryMode;
				}	
			}
			if (isInSummaryMode == true) {
				* 
				 * Summary mode is active.  First check the first node and 
				 * then search the errorInputNodes array for another error 
				 * with "Summary" as its ID naming pattern.
				 *
				if (firstErrorNodeId.indexOf(summaryNodeIdKey) != -1) {
					focusErrorNode = firstErrorNode;
				}
				else {
					for (var errorInputCounter = 1;errorInputCounter < errorInputNodes.length; errorInputCounter++) {
						errorNode = errorInputNodes[errorInputCounter];
						errorNodeId = errorNode.getAttribute("id");
						if (errorNodeId.indexOf(summaryNodeIdKey) != -1) {
							focusErrorNode = errorNode;
							break;
						}
					}								
				}
			}
			else {
				* Not in summary mode so set the firstErrorNode as 
				 * the focusErrorNode.
				 *
				focusErrorNode = firstErrorNode;
			}
			*/
			focusErrorNode = firstErrorNode;
		}
		if (focusErrorNode != null) {
			gidLib.setFocus(focusErrorNode);
		}
	},
	processMessages:function(messagesCollection, namespace) {
		var messageId = null;
		var message = null;
		var moduleMessageNodeName = null;
		var moduleMessageNode = null;
		var moduleMessageSummaryNodeName = null;
		var moduleMessageSummaryNode = null;
		var hasModuleMessage = false;
		var hasModuleMessageSummaryNode = false;
		var moduleMessageBuffer = "<ul>";
		errorDisplayService.stateMonitor.currentNamespace = namespace;
		this.clearMessageDisplay();
		for (var messageCounter = 0; messageCounter < messagesCollection.length; messageCounter++) {
			if (messagesCollection[messageCounter].idRo != null) {
				messageId = messagesCollection[messageCounter].idRo;
			}
			if (messagesCollection[messageCounter].messageRo != null) {
				message = messagesCollection[messageCounter].messageRo;
			}
			if ((messageId != null) && (message != null)) {
				errorDisplayService.stateMonitor.currentMessageCollection = messagesCollection;
				try {
					/*
					 * Check to see if this error is a moduleError, moduleMessage, or field 
					 * error and handle accordingly.  All modules have have either field errors, 
					 * module errors, or module messages.  
					 * If it is a module error then apply to buffer and set isModuleError to true. 
					 * The buffer is needed because there could be multiple module errors.
					 * If it is a module message then apply to buffer and set isModuleMessage 
					 * to true. 
					 */
					if (messageId == errorDisplayService.constants.MODULE_MESSAGE_ID_NAME) {
						 moduleMessageBuffer += "<li>" + message + "</li>";
						 hasModuleMessage = true;
					}
				}
				catch(e) {
					
				}
			}
		}
		if (hasModuleMessage == true) {
			/*
			 * Update moduleMessage DOM node with the contents of the moduleMessageBuffer. Now that
			 * messagesRo collection inspection and processing is complete - moduleMessageBuffer is 
			 * ready to display.
			 */
			moduleMessageBuffer += "</ul>"
			try {
			 	with (errorDisplayService.constants) {
					// Open module module message display.
					moduleMessageNodeName = namespace +  MODULE_MESSAGE_ID_NAME.substring(0,1).toUpperCase() + MODULE_MESSAGE_ID_NAME.substring(1, MODULE_MESSAGE_ID_NAME.length);
					moduleMessageNode = $(moduleMessageNodeName);
					moduleMessageNode.update(moduleMessageBuffer);
					moduleMessageNode.show();
					/* Summary module module error display.  
					 * Not all summary module displays have any module message display.
					 * This is why the conditional check for null on the SummaryNode is present.
					 */
					moduleMessageSummaryNodeName = namespace + "Summary" + MODULE_MESSAGE_ID_NAME.substring(0,1).toUpperCase() + MODULE_MESSAGE_ID_NAME.substring(1, MODULE_MESSAGE_ID_NAME.length);
					moduleMessageSummaryNode = $(moduleMessageSummaryNodeName);
					if (moduleMessageSummaryNode != null) {
						hasModuleMessageSummaryNode = true;
						moduleMessageSummaryNode.update(moduleMessageBuffer);
						moduleMessageSummaryNode.show();
					}

					// Assign to stateMonitor so that the clearErrorStyles() method can hide the module errors and messages when needed.
					errorDisplayService.stateMonitor.currentModuleMessageNode = moduleMessageNode;
					errorDisplayService.stateMonitor.currentModuleMessageNodeName = moduleMessageNodeName;
					if (hasModuleMessageSummaryNode == true) {
						errorDisplayService.stateMonitor.currentModuleMessageSummaryNode = moduleMessageSummaryNode;
						errorDisplayService.stateMonitor.currentModuleMessageSummaryNodeName = moduleMessageSummaryNodeName;
					}
			 	}
			}
			catch(e) {
				
			}
		}
	},
	setFieldErrorNodeForDisplay:function(targetNode, errorId) {
		Element.addClassName(targetNode, errorDisplayService.constants.ERROR_STYLE_CLASS);//NB: inputs
		if (errorId != errorDisplayService.constants.MODULE_MESSAGE_ID_NAME && errorId != errorDisplayService.constants.MODULE_ERROR_ID_NAME) {
			Element.addClassName(targetNode.parentNode, errorDisplayService.constants.ERROR_STYLE_CLASS); //NB: parent node access to highlight child label
			targetNode.onmouseover = this.showErrorPreview;
			targetNode.onmouseout = this.hideErrorPreview
			targetNode.onfocus =  this.showErrorMsg;
			targetNode.onblur =  this.closeMsgBbl;
		}
		targetNode.errorId = errorId;
	},
	clearMessageDisplay:function() {
		var currentModuleMessageNode = errorDisplayService.stateMonitor.currentModuleMessageNode;
		var currentModuleMessageSummaryNode = errorDisplayService.stateMonitor.currentModuleMessageSummaryNode;
		
		if (currentModuleMessageNode != null) {
			currentModuleMessageNode.update();
			currentModuleMessageNode.hide();
			errorDisplayService.stateMonitor.currentModuleMessageNode = null; // Reset to null for further processing of new errors collection.
			errorDisplayService.stateMonitor.currentModuleMessageNodeName = null; // NodeName value for debug inspection.
		}
		if (currentModuleMessageSummaryNode != null) {
			currentModuleMessageSummaryNode.update();
			currentModuleMessageSummaryNode.hide();
			errorDisplayService.stateMonitor.currentModuleMessageSummaryNode = null; // Reset to null for further processing of new errors collection.
			errorDisplayService.stateMonitor.currentModuleMessageSummaryNodeName = null; // NodeName value for debug inspection.
		}
	},

	clearErrorStyles:function(){
		this.forceCloseMsgBbl();
		var previousErrorFields = $$('.' + errorDisplayService.constants.ERROR_STYLE_CLASS );
		var currentModuleErrorNode = errorDisplayService.stateMonitor.currentModuleErrorNode;
		var currentModuleErrorSummaryNode = errorDisplayService.stateMonitor.currentModuleErrorSummaryNode;
		var labelErrorNode = null;
		for (var i=0;i<previousErrorFields.length; i++) {
			Element.removeClassName(previousErrorFields[i], errorDisplayService.constants.ERROR_STYLE_CLASS);
			previousErrorFields[i].onfocus = null; //remove onmouseover method too
			previousErrorFields[i].onmouseover = null;
			previousErrorFields[i].onblur = null;
			previousErrorFields[i].onmouseout = null;
			labelErrorNode = previousErrorFields[i].getElementsBySelector(".labelErrorMessage")[0];
			if (labelErrorNode) {
				labelErrorNode.update();
			}
		}
		if (currentModuleErrorNode != null) {
			currentModuleErrorNode.update();
			errorDisplayService.stateMonitor.currentModuleErrorNode = null; // Reset to null for further processing of new errors collection.
			errorDisplayService.stateMonitor.currentModuleErrorNodeName = null; // NodeName value for debug inspection.
		}
		if (currentModuleErrorSummaryNode != null) {
			currentModuleErrorSummaryNode.update();
			errorDisplayService.stateMonitor.currentModuleErrorSummaryNode = null; // Reset to null for further processing of new errors collection.
			errorDisplayService.stateMonitor.currentModuleErrorSummaryNodeName = null; // NodeName value for debug inspection.
		}
		return;
	},
	
	showErrorMsg:function(){
		var msgBbl = $(errorDisplayService.constants.MSG_BBL_ID);
		ErrorDisplayService.prototype.errorMsgTarget = this;
		errorDisplayService.stateMonitor.errTextContainsLinks = false;
		ErrorDisplayService.prototype.populateErrorMsg(this);
		ErrorDisplayService.prototype.setErrorMsgPosition(this);
		msgBbl.style.visibility = "visible";
		msgBbl.style.display = "block";
		this.hiddenFocusDropDowns = gidLib.hideDropDownsUnderElement(msgBbl);
		errorDisplayService.stateMonitor.errorHasFocus = true;
		Event.observe(
			window,
			"resize",
			ErrorDisplayService.prototype.errorMsgResizeHandler
		);
	},
	
	showErrorPreview:function(){
		var msgBbl = $(errorDisplayService.constants.MSG_BBL_ID);
		
		if(errorDisplayService.stateMonitor.errorHasFocus != true){
			errorDisplayService.stateMonitor.errTextContainsLinks = false;
			ErrorDisplayService.prototype.populateErrorMsg(this);
			ErrorDisplayService.prototype.setErrorMsgPosition(this);
			msgBbl.style.visibility = "visible";
			msgBbl.style.display = "block";
			this.hiddenPreviewDropDowns = gidLib.hideDropDownsUnderElement(msgBbl);
		}
	},
	
	hideErrorPreview:function(){
		gidLib.showDropDowns(this.hiddenPreviewDropDowns);
		var msgBbl = $(errorDisplayService.constants.MSG_BBL_ID);
		if(errorDisplayService.stateMonitor.errorHasFocus != true){	
			msgBbl.style.visibility = "hidden";
			msgBbl.style.display = "none";
		}
	},
	
	closeMsgBbl:function() {
		gidLib.showDropDowns(this.hiddenFocusDropDowns);
		var msgBbl = $(errorDisplayService.constants.MSG_BBL_ID);
		if(errorDisplayService.stateMonitor.errTextContainsLinks != true){
        	msgBbl.style.visibility = "hidden";
		}
		errorDisplayService.stateMonitor.errorHasFocus = false;
		Event.stopObserving(
			window,
			"resize",
			ErrorDisplayService.prototype.errorMsgResizeHandler
		);
		return;
	},
	
	forceCloseMsgBbl:function() {
		gidLib.showDropDowns(this.hiddenFocusDropDowns);
		var msgBbl = $(errorDisplayService.constants.MSG_BBL_ID);
		msgBbl.style.visibility = "hidden";
		errorDisplayService.stateMonitor.errorHasFocus = false;
		Event.stopObserving(
			window,
			"resize",
			ErrorDisplayService.prototype.errorMsgResizeHandler
		);
		return;
	},
	
	errorMsgResizeHandler:function() {
		ErrorDisplayService.prototype.setErrorMsgPosition(ErrorDisplayService.prototype.errorMsgTarget);
	},
	
	setErrorMsgPosition:function(target) {
		var msgBbl = $(errorDisplayService.constants.MSG_BBL_ID);
		var targetDimensions = Element.getDimensions(target);
		var msgBblDimensions = Element.getDimensions(msgBbl);
		var targetPosition = Position.cumulativeOffset(target);
		var newX  =  targetPosition[0];
		var newY = targetPosition[1];
		msgBbl.style.left =   (newX - 20) + 'px';
		msgBbl.style.top = (newY - targetDimensions.height/2 - msgBblDimensions.height) + 'px';	
	},
	
	populateErrorMsg:function(node){
		var constants = errorDisplayService.constants;
		var msgBbl = $(constants.MSG_BBL_ID);
		var msgText = $(constants.MSG_TEXT_ID);
		var labelNode = null;
		var labelErrorNode = null;

		/*
		var nid = node.getAttribute('id');
		var originalId = nid;
		var namespace = errorDisplayService.stateMonitor.currentNamespace;
		nid = nid.substring(namespace.length, nid.length);
		nid = nid.substring(0,1).toLowerCase() + nid.substring(1, nid.length);
		*/
		var errorId = node.errorId;
		var newErrorString = "";
		for(i=0;i<errorDisplayService.stateMonitor.currentErrorCollection.length;i++){
			if(errorDisplayService.stateMonitor.currentErrorCollection[i].idRo == errorId){
				newErrorString = errorDisplayService.stateMonitor.currentErrorCollection[i].errorMessageRo;
			}
			msgText.innerHTML = newErrorString;
			if(newErrorString.indexOf("<a") > 0) {
				errorDisplayService.stateMonitor.errTextContainsLinks = true;
			}
			msgBbl.className = (newErrorString && newErrorString.length > constants.LONG_ERROR_MSG_THRESHOLD ? constants.LONG_ERROR_MSG_CLASSNAME  : "");
		}

		// Populate the error message in the label tag for screen readers
		labelNode = $$("label[for="+node.id+"]")[0];
		if (labelNode && newErrorString != "") {
			labelErrorNode = labelNode.getElementsBySelector(".labelErrorMessage")[0]; 
			if (labelErrorNode) {
				labelErrorNode.update(checkout.constants.messages.errors.LABEL_ERROR_MESSAGE_PREFIX + " " + newErrorString);
			}
		}
	}
};

errorDisplayService = new ErrorDisplayService();


 /**********************************************
@title:  reportingService.js
@author:  Andrew Southwick
@date:    02-11-2007
@rev:     1.2
@desc:    Reporting Service - this is the principle JS Controller framework file for reporting and references on all
* other <view type>Manager.js files for runtime (the core view managers and have been seperated into other js files.
@assumes: prototype.js 1.5 rel.
@assumes: commonManager.js,<list others here>
********************************/
 

var ReportingService = Class.create();

ReportingService.prototype = {
    isActive: true,
    initialize:function() {
		//initialize properties here
	},
	api:{},
	model:{
		reportRegistry:[],
		reportsWaiting:[],
		reportsToClean:[],
		reportParameterRegistry:[],
		reportParameterMap:{},
		isPersonalizationDataReady:false,
		isDomReady:false
	},
	controller: {
		init:{},
		reportingManager:{},
		viewManagers:{
			commonViewManager:{},
			framesetViewManager:{},
			errorPageViewManager:{},
			categorySearchViewManager:{},
			categoryViewManager:{},
			siteSearchViewManager:{},
			productLoaderViewManager:{},
			productpageViewManager:{},
			productImageLoaderViewManager:{},
			viewLargerViewManager:{},
			altViewsViewManager:{},
			zoomViewViewManager:{},
			quicklookViewManager:{},
			outfitViewManager:{},
			inlineBagAddViewManager:{},
			shoppingBagViewManager:{},
			firstCheckoutPageViewManager:{},
			checkoutOrderModuleViewManager:{},
			orderConfirmViewManager:{},
			internationalPlaceOrderViewManager:{},
			reviewsWrapperViewManager:{},
			reviewsProfileViewManager:{},
			reviewsVoteViewManager:{},
			swatchEnagementViewManager:{},
			categoryFacetedSearchViewManager:{}
		},
		appManagers:{
			shopBySizeAppManager:{},
			facetedSearchAppManager:{},
			searchAppManager:{},
			checkoutAppManager:{}
		},
		AbstractReportViewManager:null,
		AbstractReportAppManager:null,
		AbstractABTestManager:null,
		AbstractReportParameter:null,
		AbstractPageReportResponseLogEntry:null
	},
	constants:{
		viewTypes:{
			COMMON_VIEW:"common",
			FRAMESET_VIEW:"frameset",
			ERROR_PAGE_VIEW:"errorPage",
			CATEGORY_SEARCH_VIEW:"categorySearch",
			CATEGORY_VIEW:"category",
			SITE_SEARCH_VIEW:"siteSearch",
			PRODUCT_LOADER_VIEW:"productLoader",
			PRODUCT_VIEW:"product",
			PRODUCT_IMAGE_LOADER_VIEW:"productImageLoader",
			VIEW_LARGER_VIEW:"viewLarger",
			ALT_VIEWS_VIEW:"altViews",
			ZOOM_VIEW_VIEW:"zoomView",
			QUICKLOOK_VIEW:"quicklook",
			OUTFIT_VIEW:"outfit",
			INLINE_BAG_ADD_VIEW:"inlineBagAdd",
			SHOPPING_BAG_VIEW:"shoppingBag",
			FIRST_CHECKOUT_PAGE_VIEW:"firstCheckoutPage",
			CHECKOUT_ORDER_MODULE_VIEW:"checkoutOrderModule",
			ORDER_CONFIRM_VIEW:"orderConfirm",
            ERROR_VIEW:"error",
            REVIEWS_WRAPPER_VIEW:"reviewsWrapper",
            REVIEWS_PROFILE_VIEW:"reviewsProfile",
            REVIEWS_VOTE_VIEW:"reviewsVote",
            SWATCH_ENGAGEMENT_VIEW:"swatchEngagement",
            CATEGORY_FACETED_SEARCH_VIEW:"categoryFacetedSearch"
        },
        parameterNames:{
        	VIEW_TYPE_PARAMETER_NAME:"viewType"
        },
		reportingServiceTypes:{
			REPORTING_SERVICE_TYPE_G:"g",
			REPORTING_SERVICE_TYPE_H:"h"
		}
	}
}

var reportingService = new ReportingService();
reportingService.controller.AbstractReportViewManager = Class.create();
reportingService.controller.AbstractReportViewManager.prototype = {
	reportName:null,
	isReadyForTransmission:false,
	isWaitReport:false,
	isWaitComplete:false,
	initialize:function() {
		//initialize properties here
	},
	model: {},
	controller: {
		setReportModel:null,
		setReportTransmissionVars:null,
		setCleanUp:null
	}
}

reportingService.controller.AbstractReportAppManager = Class.create();
reportingService.controller.AbstractReportAppManager.prototype = {
	reportName:null,
	initialize:function() {
		//initialize properties here
	},
	model: {},
	controller: {
		setReportModel:null,
		setReportTransmissionVars:null
	}
}

reportingService.controller.AbstractReportProcessingLog = Class.create();
reportingService.controller.AbstractReportProcessingLog.prototype = {
	reportRegistry:[],
	reportsToClean:[],
	reportsWaiting:[],
	reportParameterRegistry:[],
	reportTime:null,
	initialize:function() {
		//initialize properties here
	}
}

reportingService.controller.AbstractPageReportResponseLogEntry = Class.create();
reportingService.controller.AbstractPageReportResponseLogEntry.prototype = {
	pageReportResponse:null,
	reportTime:null,
	pageReportUrl:null,
	initialize:function() {
		//initialize properties here
	}
}

reportingService.controller.AbstractOmniStub = Class.create();
reportingService.controller.AbstractOmniStub.prototype = {
	strCurrentPageName:null,
	initialize:function() {
		//initialize properties here
	}
}

reportingService.controller.AbstractReportParameter = Class.create();
reportingService.controller.AbstractReportParameter.prototype = {
	parameterName:null,
	parameterValue:null,
	initialize:function() {
		//initialize properties here
	}
}

Object.extend(
	reportingService.controller.init, {
		main:function() {
			processingService.api.addApplicationMethodToRegistry(this.setDomReadyHandler, "reporting service");
			Event.observe(document, "personalizationData:ready", this.personalizationDataReadyHandler);
			// Create instance of OmniStub class for WCD code.  This code
			// will be deprecated over time as WCD cleans it's code to H code
			// standard.
			omni = new reportingService.controller.AbstractOmniStub();
		},
		setDomReadyHandler:function() {
			var isPersonalizationDataReady = reportingService.model.isPersonalizationDataReady;
			var reportingServiceReadyHandler = reportingService.controller.init.reportingServiceReadyHandler;
			if (isPersonalizationDataReady) {
				reportingServiceReadyHandler();
			} else {
				Event.observe(document, "personalizationData:ready", reportingServiceReadyHandler);
			}
		},
		personalizationDataReadyHandler:function() {
			reportingService.model.isPersonalizationDataReady = true;
		},
		reportingServiceReadyHandler:function() {
			var reportingManager = reportingService.controller.reportingManager;
			var categoryOrCheckoutRegExp = /(browse\/search\.do)|(browse\/category(Search)?\.do)|(checkout\/order\.do)/;
			
			// If IE and on the search, category or checkout page, delay the omniture call
			if (clientBrowser.isIE6up && categoryOrCheckoutRegExp.test(window.location)) {
				setTimeout("reportingService.controller.reportingManager.getReportingRequest()",1000);
			} else {
				reportingManager.getReportingRequest();
			}
		}
	}
);

Object.extend(
	reportingService.controller.reportingManager, {
		reportingAccount:null,
		currencyType:null,
		marketSpecificOmnitureUrl:null,
		marketSpecificSecureOmnitureUrl:null,
		registryHasWaitReports:false,
		pageReportResponseLog:[],
		reportProcessingLogs:[],
		pageReportTransmissionCount:0,
		isPageReportActive:true,
		isPageReportSingleTransmissionActive:false,
		inspectReportRegistry:function() {
            if(!window['s']) { return; }
			var reportingManager = reportingService.controller.reportingManager;
			var reportRegistry = reportingService.model.reportRegistry;
			var reportsWaiting = reportingService.model.reportsWaiting;
			var registryIterator = null;
			reportingManager.registryHasWaitReports = false;
			registryIterator = function(viewManager, registryIndex) {
				var viewManagerController = viewManager.controller;
				var reportName =  viewManager.reportName;
				var reportingManager = reportingService.controller.reportingManager;
				if (viewManager.isReadyForTransmission == false) {
					if ((viewManager.isWaitReport == true) && (viewManager.isWaitComplete == false)) {
						reportingManager.registryHasWaitReports = true;
						reportsWaiting.push(viewManager);
					}
					else {
						reportingManager.processReport(viewManager);
					}
				}
				else {
					// Report is ready for transmission.
					reportingManager.processReport(viewManager);
				}
			}

			reportRegistry.each(registryIterator);
			
			if (reportingManager.registryHasWaitReports == true) {
				// logService.info
			}
			else {
				/* No reportsWaiting found in registry inspection so transmit request
				 * immediately.
				 */
				
				// Override variables for AB Testing 
				reportingManager.setABTestingOverrideVariables();
				reportingManager.transmitRequest();
			}
		},
		processReport:function(viewManager) {
			var viewManagerController = viewManager.controller;
			var reportsToClean = reportingService.model.reportsToClean;
			if (viewManagerController) {
				if (viewManagerController.setReportModel && viewManagerController.setReportTransmissionVars) {
					viewManagerController.setReportModel();
					viewManagerController.setReportTransmissionVars();
					viewManager.isReadyForTransmission = true;
				}
				else {
					// logService.error(reportName + " does not have proper interface methods.");
				}
				if (viewManagerController.setCleanUp) {
					reportsToClean.push(viewManager);
				}
			}
		},
		setViewManagerToReportRegistry:function(viewManager) {
			var reportRegistry = reportingService.model.reportRegistry;
			var reportRegistrySize = null;
			var nextReportIndex = null;
			if (reportRegistry == null) {
				reportingService.model.reportRegistry = new Array();
				reportRegistry = reportingService.model.reportRegistry;
			}
			reportRegistry.push(viewManager);
		},
		getReportingRequest:function(viewManagers) {
			var reportingManager = reportingService.controller.reportingManager;
			var reportRegistry = reportingService.model.reportRegistry;
			var viewManager = null;
			if (viewManagers instanceof Array) {
				if (viewManagers) {
					for (var viewManagerCount = 0;viewManagerCount < viewManagers.length;viewManagerCount++) {
						viewManager = viewManagers[viewManagerCount];
						reportingManager.setViewManagerToReportRegistry(viewManager);
					}
				}
			}
			else if (viewManagers instanceof Object) {
				viewManager = viewManagers;
				reportingManager.setViewManagerToReportRegistry(viewManager);
			}
			reportingManager.inspectReportRegistry();
		},
		/**
		 * setABTestingOverrideVariables reads values from a globally scoped object to override values.
		 * 
		 * Modified 8/26/09 Byung -- relaxed the eVar restriction to allow overriding of any value 
		 */
		setABTestingOverrideVariables:function() {
			if (window["ABTestVariables"]) {
				var evars = Object.keys(ABTestVariables);
				evars.each(function(evar) {
					if (evar) s[evar] = ABTestVariables[evar];
				});
			}
		},
		transmitRequest:function() {
			/* Omniture sCode transmission code here.
			 * After transmission call the setCleanUp() method on each viewManager in the reportRegistry.
			 * After setCleanUp clear the reportRegistry, reportsToClean, reportsWaiting, and reportParameterRegistry.
			 */
			var reportingManager = reportingService.controller.reportingManager;
			var reportRegistry = reportingService.model.reportRegistry;
			var reportsToClean = reportingService.model.reportsToClean;
			var reportsWaiting = reportingService.model.reportsWaiting;
			var reportParameterRegistry = reportingService.model.reportParameterRegistry;
			var reportProcessingLogs = reportingService.controller.reportingManager.reportProcessingLogs;
			var reportsToCleanIterator = null;
			var reportProcessingLog = null;
			
			reportingManager.sendAnalyticsEvent();
			
			reportsToCleanIterator = function(viewManager, reportIndex) {
				if (viewManager.controller.setCleanUp) {
					viewManager.controller.setCleanUp();
				}
			}
			
			reportsToClean.each(reportsToCleanIterator);
			reportProcessingLog = new reportingService.controller.AbstractReportProcessingLog();
			reportProcessingLog.reportRegistry = reportRegistry;
			reportProcessingLog.reportsToClean = reportsToClean;
			reportProcessingLog.reportsWaiting = reportsWaiting;
			reportProcessingLog.reportParameterRegistry = reportParameterRegistry;
			reportProcessingLog.reportTime = new Date();
			reportProcessingLogs.push(reportProcessingLog);

			reportingService.model.reportRegistry = new Array();
			reportingService.model.reportsToClean = new Array();
			reportingService.model.reportsWaiting = new Array();
			// reportingService.model.reportParameterRegistry = new Array();
			
		},
		sendAnalyticsEvent:function() {
			var reportingManager = reportingService.controller.reportingManager;
			var pageReportTransmissionCount = reportingManager.pageReportTransmissionCount;
			var isPageReportActive = reportingManager.isPageReportActive;
			var isPageReportSingleTransmissionActive = reportingManager.isPageReportSingleTransmissionActive;
			
			reportingManager.sendAnalyticsEventForOmniReport();
			
			if (isPageReportActive) {
				/* Only transmit a single pageReport transmission per page for initial release,
				 * and subsequent to release check the boolean flag isPageReportSingleTransmissionActive*/
				if (isPageReportSingleTransmissionActive && pageReportTransmissionCount < 1) {
					reportingManager.sendAnalyticsEventForPageReport();
					reportingManager.pageReportTransmissionCount++;
				} else {
					reportingManager.sendAnalyticsEventForPageReport();
					reportingManager.pageReportTransmissionCount++;
				}
			}
		},
		sendAnalyticsEventForOmniReport:function() {
			/* Process Omni request */
			var omniDiv = $('omniSendAnalytics');
		
			if (!omniDiv) {
				omniDiv = document.createElement('div');
				omniDiv.id = "omniSendAnalytics";
				omniDiv.setAttribute('omniIndex', 0)
				document.body.appendChild(omniDiv);
			}
			s_code = s.t();
			var index = parseInt(omniDiv.getAttribute('omniIndex')||0);
			var indexStr = index ? '_' + index : '';
			var omniPixel = window['s_i_' + s.visitorNamespace + indexStr];
			if (omniPixel || s_code) {
				var index = index + 1;
                var content = $('omniSendAnalytics').innerHTML;
				omniDiv.innerHTML = (omniPixel.src || s_code);
				omniDiv.setAttribute('omniIndex', index);
			}
		},
		sendAnalyticsEventForPageReport:function() {
			/* Process pageReport request */
			var reportingManager = reportingService.controller.reportingManager;
			var pageReportUrl = reportingManager.getPageReportUrl();
			
			if (pageReportUrl != null) {
				new Ajax.Request(pageReportUrl, {
						method:"get",
						onSuccess: function(transport) {
							var pageReportResponseLog = reportingManager.pageReportResponseLog;
							var pageReportResponse = transport.responseJSON;
							if (pageReportResponse) {
								var pageReportResponseLogEntry = new reportingService.controller.AbstractPageReportResponseLogEntry();
								pageReportResponseLogEntry.pageReportResponse = pageReportResponse;
								pageReportResponseLogEntry.reportTime = new Date();
								pageReportResponseLogEntry.pageReportUrl = pageReportUrl;
								pageReportResponseLog.push(pageReportResponseLogEntry);
							}
						}
					}
				);
			}
		},
		getPageReportUrl:function() {
			var pageReportUrl = null;
			var reportParameterRegistry = reportingService.model.reportParameterRegistry
			var reportParametersFormattedForRequest = "";
			var reportParameterPrefix = "";
			var pageNameParameter = reportingService.model.reportParameterMap["p1"];
			if (pageNameParameter != null && pageNameParameter != undefined) {
				var pageName = reportingService.model.reportParameterMap["p1"].parameterValue;
				var pageNameEncoded = encodeURIComponent(pageName);
				var reportParameterRegistryIterator = function(reportParameter, reportParameterIndex) {
					if (reportParameter) {
						if (reportParameterIndex > 0) {
							reportParameterPrefix = "&";
						} else {
							reportParameterPrefix = "?";
						}
						var parameterName = reportParameter.parameterName;
						var parameterValue = encodeURIComponent(reportParameter.parameterValue);
						if (reportParameter.parameterName == "pagePath") {
							parameterValue = escape(reportParameter.parameterValue);
						}
						reportParametersFormattedForRequest = reportParametersFormattedForRequest + reportParameterPrefix + parameterName + "=" + parameterValue;
					}
				}
				reportParameterRegistry.each(reportParameterRegistryIterator);
				/* sem */
				var isReferrerAThirdPartySite = personalizationService.controller.isReferrerAThirdPartySite();
				var SEM_PARAMETER = personalizationService.constants.requestParameters.SEM_PARAMETER;
				var semParameter = gidLib.getQuerystringParam(SEM_PARAMETER, true);
				var semParameterValue = null;
				if (semParameter == "true") {
					semParameterValue = encodeURIComponent(semParameter);
				} else if (isReferrerAThirdPartySite){
					semParameterValue = "true";
				}
				if (semParameterValue != null) {
					reportParametersFormattedForRequest = reportParametersFormattedForRequest + "&" + SEM_PARAMETER + "=" + semParameterValue;
				}
				/* prototcol */
				var requestProtocol = null;
				var requestHost = window.location.host;
				if (window.location.protocol == "http:") {
					requestProtocol = "http:";
				} else {
					requestProtocol = "https:";
				}
				pageReportUrl = requestProtocol + "//" + requestHost + "/resources/metrics/pageReport/v1/" + pageNameEncoded + reportParametersFormattedForRequest;
			}
			
			return pageReportUrl;
		},
		setReportParameterToReportParameterRegistry:function(reportParameter) {
			var reportingManager = reportingService.controller.reportingManager;
			var validator = reportingManager.validator;
			var reportParameterRegistry = reportingService.model.reportParameterRegistry;
			var reportParameterMap = reportingService.model.reportParameterMap;
			var isReportParameterValid = validator.isReportParameterValid(reportParameter);
			if (isReportParameterValid) {
				/* Check if the parameter is already created, and if so then update the existing parameter, otherwise create a new parameter */
				var parameterName = reportParameter.parameterName;
				var parameterValue = reportParameter.parameterValue;
				var reportParameterRegistered = reportParameterMap[parameterName];
				if (reportParameterRegistered) {
					/* reportParameter exists, so update value */
					reportParameterRegistered.parameterValue = parameterValue + "";
				} else {
					/* reportParameter does not exist, so register. */
					reportParameterRegistry.push(reportParameter);
					reportParameterMap[reportParameter.parameterName] = reportParameter;
				}
			}
		},
		setReportParameter:function(parameterName, parameterValue) {
			var reportingManager = reportingService.controller.reportingManager;
			var reportParameter = new reportingService.controller.AbstractReportParameter();
			reportParameter.parameterName = parameterName;
			reportParameter.parameterValue = parameterValue;
			reportingManager.setReportParameterToReportParameterRegistry(reportParameter);
			return reportParameter;
		},
		validator:{
			isReportParameterValid:function(reportParameter) {
				var isReportParameterValid = false;
				var reportParameterRegistry = reportingService.model.reportParameterRegistry;
				var reportingManager = reportingService.controller.reportingManager;
				var validator = reportingManager.validator;
				if (reportParameter != null && reportParameter != undefined && reportParameterRegistry != null) {
					var parameterName = reportParameter.parameterName;
					var parameterValue = reportParameter.parameterValue;
					var isParameterNameValid = validator.isStringNotEmpty(parameterName);
					var isParameterValueValid = validator.isStringNotEmpty(parameterValue);
					if (isParameterNameValid && isParameterValueValid) {
						isReportParameterValid = true;
					}
				}
				return isReportParameterValid;
			},
			isStringNotEmpty:function(testValue) {
				var isStringNotEmpty = false;
				if (testValue != null) {
					var testValueString = testValue + "";
					if (testValueString.length > 0) {
						isStringNotEmpty = true;
					}
				}
				return isStringNotEmpty;
			}
		}
	}
);

Object.extend(
	reportingService.api, {
		setReportParameter:function(parameterName, parameterValue) {
			var reportingManager = reportingService.controller.reportingManager;
			var reportParameter = reportingManager.setReportParameter(parameterName, parameterValue);
			return reportParameter;
		},
		setViewTypeReportParameter:function(viewTypeValue) {
            var VIEW_TYPE_PARAMETER_NAME = reportingService.constants.parameterNames.VIEW_TYPE_PARAMETER_NAME;
            var viewTypeReportParameter = reportingService.api.setReportParameter(VIEW_TYPE_PARAMETER_NAME, viewTypeValue);
            return viewTypeReportParameter;
		}
	}
);
reportingService.controller.init.main();

// Initialize the AB Test variables object
if (!window["ABTestVariables"]) var ABTestVariables = {};

reportingService.controller.viewManagers.commonViewManager = new reportingService.controller.AbstractReportViewManager();
reportingService.controller.reportingManager.setViewManagerToReportRegistry(reportingService.controller.viewManagers.commonViewManager);
	
Object.extend(
	reportingService.controller.viewManagers.commonViewManager, {
		reportName:reportingService.constants.viewTypes.COMMON_VIEW,
		model:{
			commonCurrentPageName:null,
			commonCurrentBusinessId:null,
			
			commonAffiliateSiteId:null,
			commonTrackingCode:null,
			commonAffiliateAdId:null,
			commonContentItemLinkReferringBusinessId:null,
			commonContentItemIdAndLink:null,
			commonContentItemId:null,
			commonEmailVersion:null,
			commonDigitalImpactEmailId:null,
			commonEmailCardType:null,
			commonSisterSiteId:null,
			
			commonBrandPrefix:null,
			commonChannelName:null,
			commonChannelNameFormatted:null,
			commonCurrentPath:null,
			commonHierarchy:null,
			commonContentItemIdList:null,
			commonPrevStyle:null,
			commonProductCountInBag:null,
			commonProductBrandCode:null,
			commonProductBrandAbbr:null,
			commonHasTransmittedClinkAndMlink:false,
			commonIsThirdPartyCookieBlocked:false,
			commonVariantId:null,
			commonVariantStyle:null,
			commonHierarchyProduct:null,
			commonUniqueDomainReference:null,
			commonGetContentItemIdListFromDom:false,
			commonOmniLocaleCode:null,
			viewType:null,
			profile:{
				unknownShopperId:null,
				sessionId:null,
				customerId:null,
				isRegistrationCheckActive:null,
				setProfileData:function() {
					this.sessionId = (getCookie("JSESSIONID") + "").replace(":-1","");
					this.unknownShopperId = (getCookie("unknownShopperId") + "").replace(/\|\|\|/,"");
					this.customerId = (getCookie("customerId") + "").replace(/\|\|\|/,""); // convert to String for IE - IE returns 0.
					
					this.sessionId = this.sessionId == "null" ? "" : this.sessionId; // Mozilla cleanup.
					this.sessionId = this.sessionId == "0" ? "" : this.sessionId; // IE cleanup.
					this.unknownShopperId = this.unknownShopperId == "null" ? "" : this.unknownShopperId; // Mozilla cleanup.
					this.unknownShopperId = this.unknownShopperId == "0" ? "" : this.unknownShopperId; // IE cleanup.
					this.customerId = this.customerId == "null" ? "" : this.customerId; // Mozilla cleanup.
					this.customerId = this.customerId == "0" ? "" : this.customerId; // IE cleanup.
				}
			},
			performance:{
				timeToReadyFromPreviousPageFormatted:null,
				timeToLoadFromPreviousPageFormatted:null
			},
			shoppingBag:{
				products:null
			},
			order:{
				zip:null,
				state:null,
				products:null,
				orderId:null,
				duplicateProducts:null,
				cardTypeName:null
			},
			AbstractMerchandising:null,
			setReferringStyle:function (prevStyle) {
				setCookieVar("omniSession","prevStyle",prevStyle);
			},
			getProductsEncoded:function (strProducts) {
				// parse || and ^,^ to encode friendly delims.
				var arrayProducts = strProducts.split("||");
				var strProducts = arrayProducts.join("....");
				var arrayProducts = strProducts.split("^,^");
				var strProducts = arrayProducts.join("--");
				// alert("strProducts = " + strProducts);
				return strProducts;
			},
			getProductsDecoded:function (strProducts) {
				// parse || and ^,^ to encode friendly delims.
				var arrayProducts = strProducts.split("....");
				var strProducts = arrayProducts.join("||");
				var arrayProducts = strProducts.split("--");
				var strProducts = arrayProducts.join("^,^");
				// alert("strProducts = " + strProducts);
				return strProducts;
			},
			getOmniTrackingLocaleCode:function() {
				var model = reportingService.controller.viewManagers.commonViewManager.model;
				var previousLocaleCode = brandConst.PREVIOUS_LOCALE;
					return  previousLocaleCode ;
			
        	}, 
			getHierarchy1:function () {
				var model = reportingService.controller.viewManagers.commonViewManager.model;
				var hierarchy1 = model.commonHierarchy||'';

				if (hierarchy1 == '') {
					hierarchy1 = model.commonCurrentPageName||'';
                    model.commonChannelName = model.commonChannelName||'';
                    model.commonCurrentPageName = model.commonCurrentPageName||'';
                    if (model.commonChannelName.toLowerCase() == "home") {
						hierarchy1 = model.commonCurrentPageName.substring(0, model.commonCurrentPageName.indexOf(":"));
					}
					// Remove view values
					hierarchy1 = hierarchy1.replace(/:(product|outfit|quicklook|null).+/, '');
                    if ((hierarchy1.match(/giftcard/gi)||[]).length > 0) {
                        hierarchy1 = hierarchy1.replace(/:regular/gi, '');
                    }
					model.commonHierarchy = hierarchy1;
				}
				return hierarchy1;
			},
			getShortPageName:function () {
				var pageNameArray = this.getHierarchy1().split(':');
				return pageNameArray[0] + (pageNameArray[1] ? ":" + pageNameArray[1] : '');
			},
			getDuplicateProducts:function (strProducts, strProductBusinessId) {
				var hasDuplicateProducts = false;
				var intFirstDelim, intSecondDelim;
				var intFirstDelim = strProducts.indexOf(strProductBusinessId);
				var intSecondDelim = (strProducts.indexOf(strProductBusinessId, (intFirstDelim + 1)));
				if (intSecondDelim != -1) {
					var hasDuplicateProducts = true;
				}
				return hasDuplicateProducts;
			},
			getOrderConfirmMerchandizing:function (
				strProductBusinessId,
				strProductName,
				strQuantity,
				strPrice,
				strColorDescription,
				strSkuDescription,
				strEvents,
				productBrandAbbr) {

				var commonModel = reportingService.controller.viewManagers.commonViewManager.model;
                var merchandising = new commonModel.AbstractMerchandising();
				if (strProductBusinessId.indexOf("gross") != -1) {
					merchandising.strEventIncrementor = strProductName;
				}
				else {
					// Removed size info - no size info, only color.  Keep for reference in comments.
					// ("evar4=" + strColorDescription + "," + strSkuDescription)
					merchandising.strBrand = productBrandAbbr;
					merchandising.strProductName = strProductBusinessId;
					merchandising.strQuantity = strQuantity;
					merchandising.strPrice = strPrice;
					merchandising.strEventIncrementor = strEvents;
				}
				return merchandising;
			},
			parseMerchandisingVariable:function (strVariable) {
				if (strVariable != null) {
					return strVariable;
				}
				else {
					return "";
				}
			},
			getTid:function() {
				var commonModel = reportingService.controller.viewManagers.commonViewManager.model;
				var commonTrackingCode = commonModel.commonTrackingCode;
				var returnValue = null;
				
				if (commonTrackingCode != null) {
					returnValue = commonTrackingCode;
				}
				else {
					returnValue = gidLib.getCookieVar("omniSession","strTid");
					if (returnValue!='') {
						gidLib.setCookieVar("omniSession","strTid","");
						
					}
					else {
						returnValue = gidLib.getQuerystringParam("tid");
					}
					if (returnValue!='') {
						commonModel.commonTrackingCode = returnValue;	
					}
				}
				// Capture SEO tid if it exists. TD 42257
				if (window["personalizationService"]) {
					var personalizationServiceModel = personalizationService.model;
					if (personalizationServiceModel.isReferrerAThirdPartySite){
						var partnerCookieValue = gidLib.getCookie("PARTNER");
						if (partnerCookieValue != null && partnerCookieValue != undefined 
							&& partnerCookieValue != ""	&& partnerCookieValue.indexOf("|||") != -1) {					
							var partnerCookieValueArray = partnerCookieValue.split("|||");
							returnValue = partnerCookieValueArray[1];
						}
					}
				}
				return returnValue;
			},
            getAffiliateAdId:function() {
            	var tid = gidLib.getQuerystringParam("tid");
				var returnValue = null;
            	if (tid != '') {
					var commonModel = reportingService.controller.viewManagers.commonViewManager.model;
					var commonAffiliateAdId = commonModel.commonAffiliateAdId;
					
					if (commonAffiliateAdId != null) {
						returnValue = commonAffiliateAdId;
					}
					else {
						var adid = getQuerystringParam("adid",true); // preserve the case sensitivity of the encrypted adid value
						returnValue = (tid != '' && adid !='') ? adid : "other channel";
						commonModel.commonAffiliateAdId = returnValue;	
					}
				} else {
					if (window["personalizationService"]) {
						var personalizationServiceModel = personalizationService.model;
						if (personalizationServiceModel.isReferrerAThirdPartySite){
							returnValue = "other channel";
						}
					}
				}
				return returnValue;
            },
            getAffiliateSiteId:function(){
            	var tid = gidLib.getQuerystringParam("tid");
				var returnValue = null;
            	if (tid != '') {
					var commonModel = reportingService.controller.viewManagers.commonViewManager.model;
					var commonAffiliateSiteId = commonModel.commonAffiliateSiteId;
					
					if (commonAffiliateSiteId != null) {
						returnValue = commonAffiliateSiteId;
					}
					else {
						var siteID = getQuerystringParam("siteID",true);
						returnValue = (tid != '' && siteID !='') ? siteID : "other channel";
						commonModel.commonAffiliateSiteId = returnValue;	
					}
				} else {
					if (window["personalizationService"]) {
						var personalizationServiceModel = personalizationService.model;
						if (personalizationServiceModel.isReferrerAThirdPartySite){
							returnValue = "other channel";
						}
					}
				}
				return returnValue;
            },
            getClickThroughProdIdPath:function(pid) {
                var clickThrough = (gidLib.getCookieVar("globalSession","omniClickThrough") + '>' + pid).match(/\d+>\d+/);
                return clickThrough ? clickThrough : 'no cross-sell';
			},
			
            getMerchandisingVariables:function (merchandising) {
				var commonModel = reportingService.controller.viewManagers.commonViewManager.model;
				var strMerchandisingVariables = commonModel.parseMerchandisingVariable(merchandising.strBrand) + ";" +
				commonModel.parseMerchandisingVariable(merchandising.strProductName) + ";" +
				commonModel.parseMerchandisingVariable(merchandising.strQuantity) + ";" +
				commonModel.parseMerchandisingVariable(merchandising.strPrice) + ";" +
				commonModel.parseMerchandisingVariable(merchandising.strEventIncrementor) + ";" +
				commonModel.parseMerchandisingVariable(merchandising.strMerchandisingEvar);
				// alert("strMerchandisingVariables = " + strMerchandisingVariables);
				return strMerchandisingVariables;
			},
            getPreviousStyleId:function () {
				var previousStyleId = getQuerystringParam("refpid");
				if (previousStyleId == "") {
					previousStyleId = "no suggested item";
				}
				return previousStyleId;
			},
			getSProductsFromBag:function(strProducts, reportName) {
				var commonModel = reportingService.controller.viewManagers.commonViewManager.model;
				var order = commonModel.order;
				var strSProducts = "";
				var arrayProducts = strProducts.split("||");

				for (var i=0;i<arrayProducts.length;i++) {
					var strProduct = arrayProducts[i];
					var arrayProductAttributes = strProduct.split("^,^");
					var strProductBusinessId = arrayProductAttributes[0];
					var hasDuplicateProducts = false;
					var isSingleProduct = false;
                    var strOrderConfirmEvents = null;
					var productBrandCode = arrayProductAttributes[1];
					var productBrandSite = gidLib.reporting.getBrandSite(productBrandCode);
					var productBrandAbbr = (productBrandSite ? productBrandSite.brandAbbr : "");
					var backordered = arrayProductAttributes[arrayProductAttributes.length - 1] == "true";
					if (productBrandAbbr == "") {
						productBrandAbbr = commonModel.commonProductBrandAbbr;
					}
                    if (strProductBusinessId.indexOf("gross") == -1) {
						// Do not process the "gross" products.
						var isMultiProduct = commonModel.getDuplicateProducts(strProducts, strProductBusinessId);
						if (!isMultiProduct) {
							isSingleProduct = true;
						}
						if (order.duplicateProducts == null) {
							order.duplicateProducts = "";
						}
						if (order.duplicateProducts.indexOf(strProductBusinessId) == -1) {
							// Business ID has not yet been found, so process it.
							hasDuplicateProducts = commonModel.getDuplicateProducts(strProducts, strProductBusinessId);
							if (hasDuplicateProducts) {
								if (order.duplicateProducts.length == 0) {
									order.duplicateProducts = strProductBusinessId;
								}
								else {
									order.duplicateProducts = order.duplicateProducts + "," + strProductBusinessId;
								}
							}
						}
					}
					/*
					logger.debug("omni.objOrder.strDuplicateProducts = " + omni.objOrder.strDuplicateProducts +
							"\n\rhasDuplicateProducts (>1 style number present for " + strProductBusinessId + ") = " + hasDuplicateProducts +
							"\n\risSingleProduct (1 style number present for " + strProductBusinessId + ") = " + isSingleProduct);
					}
					*/
					if (hasDuplicateProducts || isSingleProduct) {
						strOrderConfirmEvents = "event16=1";
					}

					var strProductName = arrayProductAttributes[2];
					var strQuantity = arrayProductAttributes[3];
					var strPrice = arrayProductAttributes[4];
					var strColorDescription = (arrayProductAttributes[5] ? commonModel.parseVars(arrayProductAttributes[5], ",") : "");
					var strSkuDescription = (arrayProductAttributes[6] ? commonModel.parseVars(arrayProductAttributes[6], ",") : "");
					//Miniq-1476: exposing SkuId for future use and for use by PixelManagementService.
					var strSkuId = arrayProductAttributes[7];
					if (backordered) {
						strOrderConfirmEvents = strOrderConfirmEvents ? strOrderConfirmEvents + "|" : "";
						strOrderConfirmEvents += "event29=" + strPrice + "|event30=" + strQuantity;
						
						if (!commonModel.backordered) {
							commonModel.backordered = backordered;
						}
					}
					
					// Pass evar3 for "inlineBagAdd" but not for "shoppingBagView".
					switch (reportName) {
						case "inlineBagAdd":
							var inlineBagAddViewManager = reportingService.controller.viewManagers.inlineBagAddViewManager;
							var inlineBagAddModel = inlineBagAddViewManager.model;
							var merchandising = new commonModel.AbstractMerchandising();
							var activeSPageName = inlineBagAddModel.activeSPageName;
							merchandising.strBrand = productBrandAbbr;
							merchandising.strProductName = strProductBusinessId;
							merchandising.strQuantity = strQuantity;
							merchandising.strPrice = strPrice;
							merchandising.strMerchandisingEvar = "evar3=" + activeSPageName;
							break;
						case "quicklook":
							var merchandising = new commonModel.AbstractMerchandising();
							merchandising.strBrand = productBrandAbbr;
							merchandising.strProductName = strProductBusinessId;
							merchandising.strEventIncrementor = "event8=1",
							merchandising.strMerchandisingEvar = "evar2=" + commonModel.commonHierarchy;
							break;
						case "outfit":
							var merchandising = new commonModel.AbstractMerchandising();
							merchandising.strBrand = productBrandAbbr;
							merchandising.strProductName = strProductBusinessId;
							merchandising.strEventIncrementor = "event1=1",
							merchandising.strMerchandisingEvar = "evar2=" + commonModel.commonHierarchy;
							break;
						case "orderConfirm":
							var merchandising = commonModel.getOrderConfirmMerchandizing(
								strProductBusinessId,
								strProductName,
								strQuantity,
								strPrice,
								strColorDescription,
								strSkuDescription,
								strOrderConfirmEvents,
								productBrandAbbr);
							break;
						case "checkoutOrderModule":
							var merchandising = commonModel.getOrderConfirmMerchandizing(
								strProductBusinessId,
								strProductName,
								strQuantity,
								strPrice,
								strColorDescription,
								strSkuDescription,
								null,
								productBrandAbbr);
							break;
						default:
							var merchandising = new commonModel.AbstractMerchandising();
							merchandising.strBrand = productBrandAbbr;
							merchandising.strProductName = strProductBusinessId;
							merchandising.strQuantity = strQuantity;
							merchandising.strPrice = strPrice;
							break;
					}
					if (strSProducts == "") {
						// alert("expected path " + objMerchandising);
						strSProducts = commonModel.getMerchandisingVariables(merchandising);
					}
					else {
						strSProducts = strSProducts + "," + commonModel.getMerchandisingVariables(merchandising);
					}
					// alert("Omni.prototype.getSProductsInlineBag.strSProducts = " + strSProducts);
				}
				return strSProducts;
			},
			parseVars:function (strVariable, strDelimeter) {
				if (strVariable != "") {
					if (strVariable.indexOf(strDelimeter) > -1) {
						var arrayParsedString = strVariable.split(strDelimeter);
						strVariable = arrayParsedString.join(" ");
					}
					return strVariable;
				}
				return "";
			},
            getSearchSelectedRefinementsFixedOrder:function() {
                var refinements = (window['objSearch']||{}).strSelectedRefinementsFixedOrder||'no refinements';
                return (this.getSearchSelectedRefinementsFixedOrder = function() { return refinements; })();
            },
			getSearchSortByDesc:function() {
                var sortBy = (window['objSearch']||{}).strSortByDesc||'no sort by';
                return (this.getSearchSortByDesc = function() { return sortBy; })();
            },
			getContentItemIdList:function() {
            	var gidGetContentItemIdListFromDomParam = getQuerystringParam("gidGetContentItemIdListFromDom");
            	var commonGetContentItemIdListFromDom = reportingService.controller.viewManagers.commonViewManager.model.commonGetContentItemIdListFromDom;
            	if (gidGetContentItemIdListFromDomParam == "true" || commonGetContentItemIdListFromDom) {
    				var contentItemIdArray = gidLib.reporting.getContentItemIds();
    				var contentDelimited  = contentItemIdArray.join(",");
            	} else {
            		contentDelimited = reportingService.controller.viewManagers.commonViewManager.model.contentItemIdListReport;
            	}
				return contentDelimited;
			},
			getReferringStyleId:function () {
				var previousStyleId = getQuerystringParam("refpid");
				if (previousStyleId == "") {
					previousStyleId = "no suggested item";
				}
				return previousStyleId;
			},
			getSisterSiteId:function() {
				var sisterSiteId = "";
				try {
					sisterSiteId = parent.getQuerystringParam("ssiteID").toLowerCase();
				} catch(e) {
					/* failure to get the any info from the parent.
					 *  -- where the parent is a secure URL and the page is insecure
					 *  -- where the parent is a different domain
					 */
				}
				return sisterSiteId;
			},
			getCurrentPathFormatted:function(currentPath) {
				currentPathFormatted = null;
				if (currentPath != null) {
					currentPathFormatted = currentPath.replace("/","");
				}
				return currentPathFormatted;
			},
			getChannelNameFormatted:function(channelName, brandPrefix) {
				var channelNameFormatted = brandPrefix ||'';
				if ((channelName != null) && channelName != '') {
					channelNameFormatted +=  ":" + channelName;
				}
				return channelNameFormatted;
			},
			setProductCountInBag:function (strProductCountInBag) {
				var commonModel = reportingService.controller.viewManagers.commonViewManager.model;
				if ((strProductCountInBag != null) && (strProductCountInBag != "")) {
					setCookieVar("omniSession","strProductCountInBag",strProductCountInBag);
					commonModel.productCountInBag = getCookieVar("omniSession","strProductCountInBag");
					// alert("omni.strProductCountInBag = " + omni.strProductCountInBag);
				}
			},
			getProductCountInBag:function() {
				var commonModel = reportingService.controller.viewManagers.commonViewManager.model;
				return commonModel.productCountInBag;
			},
// getEntrySite:function() {
// var returnValue = "";
// if (window["gidBrandSiteConstruct"] &&
// gidBrandSiteConstruct.hasGIDBrandSites) {
// var visitedSites = gidBrandSiteConstruct.visitedSites;
// var entrySite = (visitedSites.split(","))[0];
// if (entrySite && entrySite != "") {
// var brandSite = gidBrandSiteConstruct.gidBrandSites[Number(entrySite)];
// if (brandSite) returnValue = brandSite.brandAbbr;
// }
// }
// return returnValue;
// },
			getEntrySite:function() {
				var returnValue = "";
				if (window["personalizationService"]) {
					if (window["gidBrandSiteConstruct"]) {
						if (gidBrandSiteConstruct.hasGIDBrandSites) {
							var personalizationInfo = personalizationService.model.personalizationData.personalizationInfoV1;
							var visitedSites = personalizationInfo.userContext.clickStream.sitesVisited;
							var entrySite = (visitedSites.split(","))[0];
							if (entrySite && entrySite != "") {
								var brandSite = gidBrandSiteConstruct.gidBrandSites[Number(entrySite)];
								if (brandSite) returnValue = brandSite.brandAbbr;
							}
						}
					}
				}
				return returnValue;
			},
            /**
			 * checkFirstVisit returns the brand abbreviation on the first visit to the site within a session.
			 * @return the brand abbreviation if the conditions are met.
			 * 
			 * @author Byung Kim
			 * @date 12/06/2007
			 */
			checkFirstVisit:function() {
				var returnValue = "";
				var cookieVal = gidLib.getCookieVar("omniSession","firstVisit");
				if (cookieVal != "true" && window["gidBrandSiteConstruct"]) {
					if (window["gidBrandSiteConstruct"] && gidBrandSiteConstruct.hasGIDBrandSites) {
						var brandSite = gidBrandSiteConstruct.gidBrandSites[gidBrandSiteConstruct.currentBrandCode];
						if (brandSite) {
							returnValue = brandSite.brandAbbr;
							gidLib.setCookieVar("omniSession","firstVisit","true");
						}
					}
				}
				return returnValue;
			},
			setMarketingLinkModelValues:function() {
				var commonModel = reportingService.controller.viewManagers.commonViewManager.model;
				if (!commonModel.commonHasTransmittedClinkAndMlink) {
					var strMarketingLink = getQuerystringParam("mlink");
					var strBusinessId = "";
					var strContentItemId = "";
					var strLinkId = "";
					var isLinkValid = false;
					var isBusinessIdValid = false;
					var isContentItemIdValid = false;
					var hasLinkId = false;
					var isLinkIdValid = false;
					/*
					s_eVar5 = "";
					s_eVar6 = "";
			        s_eVar7 = "";         // TD #15368 moved *Var7 from sCode.js
					*/
					
					if (strMarketingLink.indexOf(",") != -1) {
						// Process Business Id.
						var intFirstDelim = strMarketingLink.indexOf(",");
						var strBusinessId = strMarketingLink.substring(0, intFirstDelim);
						if (strBusinessId != "") {
							var isBusinessIdValid = true;
						}
						if (isBusinessIdValid) {
							// Process Content Item Id.
							if (strMarketingLink.indexOf(",", intFirstDelim + 1) != -1) {
								var intSecondDelim = strMarketingLink.indexOf(",", intFirstDelim + 1);
							}
							else {
								var intSecondDelim = strMarketingLink.length;
							}
							var strContentItemId = strMarketingLink.substring(intFirstDelim + 1, intSecondDelim);
							/*
							alert("strContentItemId = " + strContentItemId +
								"\n\rintFirstDelim = " + intFirstDelim + 
								"\n\rintSecondDelim = " + intSecondDelim);
							*/
							if (strContentItemId != "") {
								var isContentItemIdValid = true;
							}
							if (isContentItemIdValid) {
								// Process Link Id.
								if ((intSecondDelim != -1) && ((intSecondDelim + 1) < strMarketingLink.length)) {
									var hasLinkId = true;
									var strLinkId = strMarketingLink.substring(intSecondDelim + 1,strMarketingLink.length);
									if (strLinkId != "") {
										var isLinkIdValid = true;
									}
								}
							}
						}
					}
					if ((isBusinessIdValid) && (isContentItemIdValid)) {
						var isLinkValid = true;
						commonModel.commonHasTransmittedClinkAndMlink = (reportingService.model.reportRegistry.length != 0 || this.isWaitReport) ? false : true ; // TD 24180: clink and mlink vars must fire on product page
						commonModel.commonContentItemLinkReferringBusinessId = strBusinessId;
						if (isLinkIdValid) {
							// G TO H: s_eVar6 = strContentItemId + "," + strLinkId;
							commonModel.commonContentItemIdAndLink = strContentItemId + "," + strLinkId;
						}
						else {
							// G TO H: s_eVar6 = strContentItemId;
							commonModel.commonContentItemIdAndLink = strContentItemId;
						}
			            // G TO H: s_eVar7 = getQuerystringParam("clink"); // TD #15368 moved *Var7 from sCode.js
			            commonModel.commonContentItemId = getQuerystringParam("clink");
					}
			        // TD #15368 moved *Var7 from sCode.js
				}
				else {
					// Clear model values to avoid duplicate transmission.
					commonModel.commonContentItemLinkReferringBusinessId = null;
					commonModel.commonContentItemIdAndLink = null;
					commonModel.commonContentItemId = null;
				}
			},
			getCommonIsThirdPartyCookieBlocked:function() {
				var commonModel = reportingService.controller.viewManagers.commonViewManager.model;
				var returnValue = null;
				if (commonModel.commonIsThirdPartyCookieBlocked == true) {
					returnValue = commonModel.commonIsThirdPartyCookieBlocked;
				}
				return returnValue;
			},
			setPidTrackRevenue : function () {
				var strTid = getQuerystringParam("tid");
				var strPid = getQuerystringParam("pid");
				if ((strTid != "") && (strPid != "")) {
					return strPid;
				}
				else {
					return "";
				}
			},
			getURLParam : function () {
				var commonDomainRedirect = getQuerystringParam("redirect");
				if (commonDomainRedirect != "") {
					if (commonDomainRedirect == "true"){ 
						return "redirect";
					} else {
						return "";
					}
				} else {
					return "";
				}
			}
        },
		controller:{
            setReportModel:function(strVariantId) {
                var model = reportingService.controller.viewManagers.commonViewManager.model;
                var searchAppManager = reportingService.controller.appManagers.searchAppManager;
                var timeToReadyFromPreviousPageFormatted = null;
                var timeToLoadFromPreviousPageFormatted = null;
                if (window['performanceMonitor'] != undefined) {
                	timeToReadyFromPreviousPageFormatted = performanceMonitor.reporting.timeToReadyFromPreviousPageFormatted;
                	timeToLoadFromPreviousPageFormatted = performanceMonitor.reporting.previousPage.timeToLoadFromPreviousPageFormatted;
                }
                // capture any variant info
                var product = (strVariantId) ? quickLook.objP : null;
            	model.commonVariantId = (strVariantId) ? strVariantId : (product) ? product.strDefaultVariantId : '';
                if (product) {
	                var variant = product.arrayVariantStyles[model.commonVariantId];
	            } 
                var variantStyle = (variant && variant.strVariantName != '') ? (":" + variant.strVariantName.toLowerCase()) : '';
                var currentPath = model.getCurrentPathFormatted(model.commonCurrentPath)||'';
                if (currentPath.toLowerCase() == 'info' || document.body.className == 'popup') {
                	var hier = model.commonBrandPrefix + ":" + currentPath;
                	if (document.body.className == 'popup') {
                		hier += ":popup";
                	}
                	if (getQuerystringParam("cid") != '') {
                		hier += ":content:cid=" + getQuerystringParam("cid");
                	}
                	model.commonHierarchy = hier;
                	model.commonCurrentPageName = hier + variantStyle;
                } else {
                	model.commonHierarchy = model.getHierarchy1();
                }
		        model.commonHierarchyProduct = (product) ? model.commonHierarchy + variantStyle + '|' + product.productId : '';
            	model.commonVariantStyle = variantStyle;
            	
            	if (window["personalizationService"]) {
	            	var totalItemCount = personalizationService.model.personalizationData.personalizationInfoV1.shoppingBag.totalItemCount;
	            	model.setProductCountInBag(totalItemCount);
            	}

                //Refinements
                /*
                if (omni.strViewType == "categorySearch" || omni.strViewType == "siteSearch" || omni.strViewType == "product" || omni.strViewType == "quicklook" || omni.strViewType == "outfitLoader" || omni.strViewType == "outfit") {
					if (omni.strCurrentBusinessId && (omni.strCurrentBusinessId != "") && (searchAppManager.getCurrentBusinessIdCookie() != omni.strCurrentBusinessId)) {
						if (searchAppManager.getRefinementsFixedOrderCookie() != "") {
							searchAppManager.setRefinementsFixedOrderCookie(false);
						}
						if (searchAppManager.getSortByDescCookie() != "") {
							searchAppManager.setSortByDescCookie(false);
						}
					}
				}*/
	
                model.commonContentItemIdList = model.getContentItemIdList();
                model.commonPreviousStyle = model.getPreviousStyleId();
                model.commonSisterSiteId = model.getSisterSiteId();
                model.profile.setProfileData();
                model.setMarketingLinkModelValues();
                model.commonChannelNameFormatted = model.getChannelNameFormatted(model.commonChannelName, model.commonBrandPrefix);
                model.commonCurrentPath = currentPath;
                model.commonEntrySite = model.getEntrySite();
                model.commonFirstVisit = model.checkFirstVisit();
                model.performance.timeToReadyFromPreviousPageFormatted = timeToReadyFromPreviousPageFormatted;
                model.performance.timeToLoadFromPreviousPageFormatted = timeToLoadFromPreviousPageFormatted;
				model.commonAffiliateSiteId = model.getAffiliateSiteId();
				model.commonTrackingCode = model.getTid();
				model.commonAffiliateAdId = model.getAffiliateAdId();
				model.commonEmailVersion = gidLib.getQuerystringParam("ev");
				model.commonDigitalImpactEmailId = gidLib.getQuerystringParam("di");
				model.commonEmailCardType = gidLib.getQuerystringParam("cd");
				model.commonIsThirdPartyCookieBlocked = model.getCommonIsThirdPartyCookieBlocked();
				model.commonPidTrackRevenue = model.setPidTrackRevenue();
				model.commonUniqueDomainReference = model.getURLParam();
				model.commonOmniLocaleCode = model.getOmniTrackingLocaleCode();
				model.viewType = reportingService.constants.viewTypes.COMMON_VIEW;
			},
			setReportTransmissionVars:function() {
                var model = reportingService.controller.viewManagers.commonViewManager.model;
                s.pageName = model.commonCurrentPageName + model.commonVariantStyle;
                s.campaign = model.commonTrackingCode;
				s.channel = model.commonChannelNameFormatted;
				s.hier1 = model.commonHierarchy + model.commonVariantStyle;
				s.prevStyle = model.commonPreviousStyle;
				
								
				s.prop8 = model.profile.unknownShopperId;
				s.prop9 = model.profile.sessionId;
				s.prop10 = model.profile.customerId;
				s.prop11 = model.commonContentItemIdList;
				s.prop32 = model.commonBrandPrefix;
				s.prop33 = model.commonCurrentPath;
				s.prop35 = model.commonFirstVisit;
				s.prop42 = model.performance.timeToLoadFromPreviousPageFormatted
				s.prop45 = model.performance.timeToReadyFromPreviousPageFormatted;
				
				s.eVar1 = model.commonAffiliateSiteId;
				s.eVar5 = model.commonContentItemLinkReferringBusinessId;
				s.eVar6 = model.commonContentItemIdAndLink;
				s.eVar7 = model.commonContentItemId;
				s.eVar10 = model.commonEmailCardType;
				s.eVar11 = model.commonEmailVersion;
				s.eVar13 = model.commonPidTrackRevenue;
				s.eVar16 = ''; //MiniQ-1562: removal of evar16
				s.eVar18 = model.commonTrackingCode;
				s.eVar22 = model.profile.unknownShopperId;
				s.eVar25 = model.commonSisterSiteId;
				s.eVar35 = model.commonEntrySite;
				s.eVar39 = model.commonHierarchyProduct;
				s.eVar41 = model.commonIsThirdPartyCookieBlocked;
				s.eVar43 = model.commonDigitalImpactEmailId; // replaces prop7 per Trac #578
				s.eVar48 = model.commonAffiliateAdId;
				s.eVar49 = model.commonOmniLocaleCode;
				s.eVar50 = ''; //MiniQ-1562: removal of evar50
				
				/* Set pageReport metrics */
				var p1 = reportingService.api.setReportParameter("p1", model.commonCurrentPageName);
				var p2 = reportingService.api.setReportParameter("p2", window.location.href);
				var p3 = reportingService.api.setReportParameter("p3", model.commonBrandPrefix);
				var p4 = reportingService.api.setReportParameter("p4", model.commonChannelNameFormatted);
				var p5 = reportingService.api.setReportParameter("p5", model.commonEntrySite);
				var p6 = reportingService.api.setReportParameter("p6", model.commonFirstVisit);
				var p7 = reportingService.api.setReportParameter("p7", model.commonHierarchy + model.commonVariantStyle);
				var p8 = reportingService.api.setReportParameter("p8", model.commonHierarchyProduct);
				var p9 = reportingService.api.setReportParameter("p9", model.commonSisterSiteId);
				var p10 = reportingService.api.setReportParameter("p10", model.commonTrackingCode);
				var p11 = reportingService.api.setReportParameter("p11", model.performance.timeToLoadFromPreviousPageFormatted);
				var p12 = reportingService.api.setReportParameter("p12", model.performance.timeToReadyFromPreviousPageFormatted);
				var p13 = reportingService.api.setReportParameter("p13", model.profile.unknownShopperId);
				var p14 = reportingService.api.setReportParameter("p14", model.profile.sessionId);
				var p15 = reportingService.api.setReportParameter("p15", model.profile.customerId);
				var p16 = reportingService.api.setReportParameter("p16", model.commonCurrentPath);
				var p17 = reportingService.api.setReportParameter("p17", model.commonContentItemIdAndLink);
				var p18 = reportingService.api.setReportParameter("p18", model.commonContentItemIdList);
				var p19 = reportingService.api.setReportParameter("p19", model.commonContentItemLinkReferringBusinessId);
				var p20 = reportingService.api.setReportParameter("p20", model.commonDigitalImpactEmailId);
				var p21 = reportingService.api.setReportParameter("p21", model.commonEmailCardType);
				var p22 = reportingService.api.setReportParameter("p22", model.commonIsThirdPartyCookieBlocked);
				var p23 = reportingService.api.setReportParameter("p23", model.commonPidTrackRevenue);
				var p24 = reportingService.api.setReportParameter("p24", model.commonPreviousStyle);
				var p25 = reportingService.api.setReportParameter("p25", model.commonUniqueDomainReference);

				var viewTypeReportParameter = reportingService.api.setViewTypeReportParameter(model.viewType);
				var pagePath = reportingService.api.setReportParameter("pagePath", window.location.pathname);
				
			},
			setCleanUp:function() {
				var reportingManager = reportingService.controller.reportingManager;
				
				s.eVar5 = "";
				s.eVar6 = "";
		        s.eVar7 = ""; 
		        /* p12 should only transmit once on a given page for processing.
		         * Set to default 0 after initial transmission. */
		        var p12 = reportingService.api.setReportParameter("p12", "0");
			},
			getReportRequest:function() {
				var reportingManager = reportingService.controller.reportingManager;
				var commonViewManager = reportingService.controller.viewManagers.commonViewManager;
				commonViewManager.isWaitComplete = true;
				reportingManager.getReportingRequest(commonViewManager);
			}
		}	
	}
);






reportingService.controller.viewManagers.commonViewManager.model.AbstractMerchandising = Class.create();
reportingService.controller.viewManagers.commonViewManager.model.AbstractMerchandising.prototype = {
	strBrand:null,
	strProductName:null,
	strQuantity:null,
	strPrice:null,
	strEventIncrementor:null,
	strMerchandisingEvar:null,
	initialize:function() {
		//initialize properties here
	}
}







reportingService.controller.viewManagers.inlineBagAddViewManager = new reportingService.controller.AbstractReportViewManager();

Object.extend(
	reportingService.controller.viewManagers.inlineBagAddViewManager, {
		reportName:reportingService.constants.viewTypes.INLINE_BAG_ADD_VIEW,
		isWaitReport:true,
		isWaitComplete:false,
		model:Object.extend(gidLib.clone(reportingService.controller.viewManagers.commonViewManager.model),{
			inlineShoppingBag:{
				products:null
			},
			inlineBagAddPageName:null,
			activeSPageName:null,
			reportName:reportingService.constants.viewTypes.INLINE_BAG_ADD_VIEW,
			getInlineBagPageName:function () {
				var activePageNameInSObject = s.pageName;
				var pageName = activePageNameInSObject + ":Inline Shopping Bag";
				return pageName;
			}
		}),
		controller:{
			setReportModel:function() {
				var commonModel = reportingService.controller.viewManagers.commonViewManager.model;
				var inlineBagAddViewManager = reportingService.controller.viewManagers.inlineBagAddViewManager;
				var inlineBagAddModel = inlineBagAddViewManager.model;
				var products = inlineBagAddModel.inlineShoppingBag.products;
				var reportName = inlineBagAddViewManager.reportName;
				
				inlineBagAddModel.activeSPageName = s.pageName;
				inlineBagAddModel.originalSProducts = s.products;
				inlineBagAddModel.originalSEvent = s.events;
				inlineBagAddModel.inlineBagAddPageName = inlineBagAddModel.getInlineBagPageName();
				// inlineBagAddModel.currentActivePageName
				// s_pageName = omni.getInlineBagPageName();
				
				//hack to override pagetype value for inlineBagAdd
				// s_prop33 = "inlineBagAdd";
				// s_prop45 = "";
				
				/*
				if (omni.objDebug.isDebugEnabled && (omni.objDebug.isInlineBagAddDebugEnabled || omni.objDebug.isActivateAllDebugEnabled)) {
					alert("omni.objInlineShoppingBag.strProducts = " + omni.objInlineShoppingBag.strProducts);
				}
				*/ 
				// s_products = omni.getSProductsFromBag(omni.objInlineShoppingBag.strProducts);
				inlineBagAddModel.inlineShoppingBag.sProducts = commonModel.getSProductsFromBag(products, reportName);
				// s_products = omni.getSProductsFromBag(omni.objInlineShoppingBag.strProducts);
				if (commonModel.getProductCountInBag() == "1") {
					if (getCookieVar("omniSession","hasOpenBag") == "true") {
						// s_events = "scAdd";
						inlineBagAddModel.events = "scAdd";
					}
					else {
						// s_events="scAdd,scOpen";
						inlineBagAddModel.events = "scAdd,scOpen";
						setCookieVar("omniSession","hasOpenBag","true");
					}
				}
				else {
					// s_events="scAdd";
					inlineBagAddModel.events = "scAdd";
				}
				inlineBagAddModel.viewType = reportingService.constants.viewTypes.INLINE_BAG_ADD_VIEW;
				/*
				if (omni.objDebug.isDebugEnabled && (omni.objDebug.isInlineBagAddDebugEnabled || omni.objDebug.isActivateAllDebugEnabled)) {
					omni.objDebug.displayDebugMsg("BEFORE send: s_pageName = " + s_pageName + "\r\ns_products = " + s_products + "\r\ns_events = " + s_events)
				}
				
				if (omni.objCurrentServer != null) {
					if (omni.objCurrentServer.isActiveReportingServer) {
						omni.sendAnalyticsEvent();
					}
				}
				*/
				
				// H code moved following clean up / reset code to setCleanUp() method.
				// reset values to original.
				/*
				s_pageName = omni.strCurrentPageName;
				s_products = strOriginalSProducts;
				s_events=strOriginalSEvent;
				if (omni.objDebug.isDebugEnabled && (omni.objDebug.isInlineBagAddDebugEnabled || omni.objDebug.isActivateAllDebugEnabled)) {
					omni.objDebug.displayDebugMsg("AFTER send: s_pageName = " + s_pageName + "\n\rs_products = " + s_products + "\n\rs_events = " + s_events);
				}
				*/
			},
			setReportTransmissionVars:function() {
				var reportingManager = reportingService.controller.reportingManager;
				var inlineBagAddModel = reportingService.controller.viewManagers.inlineBagAddViewManager.model;
				s.pageName = inlineBagAddModel.inlineBagAddPageName;
				s.events = inlineBagAddModel.events;
				s.products = inlineBagAddModel.inlineShoppingBag.sProducts;
				// Do not transmit contentItemIdList (s.prop11) for inline bag add.
				s.prop11 = "";
				s.prop33 = inlineBagAddModel.reportName;
				s.prop45 = "";
				
				// Silence eVars not required for adding to bag to match current G code transmission.
				s.eVar4 = "";
				s.eVar12 = "";
				s.eVar15 = "";
				s.eVar19 = "";
				s.eVar23 = "";
				s.eVar31 = "";
				s.eVar39 = "";
				s.eVar44 = "";
				
				
				/* Set pageReport metrics */
				var p1 = reportingService.api.setReportParameter("p1", inlineBagAddModel.inlineBagAddPageName);
				var p12 = reportingService.api.setReportParameter("p12", "2000");
				var p26 = reportingService.api.setReportParameter("p26", inlineBagAddModel.events);
				var p27 = reportingService.api.setReportParameter("p27", inlineBagAddModel.inlineShoppingBag.sProducts);
				
				var viewTypeReportParameter = reportingService.api.setViewTypeReportParameter(inlineBagAddModel.viewType);
			},
			getReportRequest:function(strProductCountInBag, strOmniInlineBagJS) {
				var reportingManager = reportingService.controller.reportingManager;
				var commonViewManager = reportingService.controller.viewManagers.commonViewManager;
				var inlineBagAddViewManager = reportingService.controller.viewManagers.inlineBagAddViewManager;
				var commonModel = reportingService.controller.viewManagers.commonViewManager.model;
				var inlineBagAddModel = inlineBagAddViewManager.model;
				var viewManagers = new Array();
				
				inlineBagAddModel.inlineShoppingBag.products = strOmniInlineBagJS;
				commonModel.setProductCountInBag(strProductCountInBag);
				inlineBagAddViewManager.isWaitComplete = true;
				
				viewManagers.push(commonViewManager);
				viewManagers.push(inlineBagAddViewManager);
				reportingManager.getReportingRequest(inlineBagAddViewManager);
			},
            setCleanUp:function() {
				var reportingManager = reportingService.controller.reportingManager;
            	var inlineBagAddModel = reportingService.controller.viewManagers.inlineBagAddViewManager.model;
            	var commonViewManagerModel = reportingService.controller.viewManagers.commonViewManager.model;
				// s_pageName = omni.strCurrentPageName;
				// No H code translation needed because currentPageName is persisted in the commonModel.
				// s_products = strOriginalSProducts;
				s.products = inlineBagAddModel.originalSProducts;
				// s_events=strOriginalSEvent;
				s.events = inlineBagAddModel.originalSEvent;
				/*
				if (omni.objDebug.isDebugEnabled && (omni.objDebug.isInlineBagAddDebugEnabled || omni.objDebug.isActivateAllDebugEnabled)) {
					omni.objDebug.displayDebugMsg("AFTER send: s_pageName = " + s_pageName + "\n\rs_products = " + s_products + "\n\rs_events = " + s_events);
				}
				*/
				var p1 = reportingService.api.setReportParameter("p1", commonViewManagerModel.commonCurrentPageName);
				var p26 = reportingService.api.setReportParameter("p26", inlineBagAddModel.originalSEvent);
				var p27 = reportingService.api.setReportParameter("p27", inlineBagAddModel.originalSProducts);
			}
		}
	}
);

performanceMonitorService.controller.monitors.ecom.browse.productMonitor = new performanceMonitorService.constructors.AbstractPagePerformanceMonitor();
Object.extend(performanceMonitorService.controller.monitors.ecom.browse.productMonitor, {
		performanceMonitorName: "Product Page Performance Monitor",
		performanceMonitorClassPath: "performanceMonitorService.controller.monitors.ecom.browse.productMonitor",
		constants: {
			productPage: {
				READY_TIME_DELTA_FROM_PREVIOUS_PAGE: "productPageReadyTimeDeltaFromPreviousPage",
				DATA_REQUEST_TIME_DELTA_FROM_PREVIOUS_PAGE: "productPageDataRequestTimeDeltaFromPreviousPage",
				DATA_LOAD_TIME_DELTA_FROM_PREVIOUS_PAGE: "productPageDataLoadTimeDeltaFromPreviousPage",
				REVIEWS_REQUEST_TIME_DELTA_FROM_PREVIOUS_PAGE: "productPageReviewsRequestTimeDeltaFromPreviousPage",
				REVIEWS_LOAD_TIME_DELTA_FROM_PREVIOUS_PAGE: "productPageReviewsLoadTimeDeltaFromPreviousPage",
				PRODUCT_BUSINESS_ID: "productPageBusinessId"
			}
		},
		controller:{
			init: {
				main: function() {
					var controller = performanceMonitorService.controller;
					var productMonitor = controller.monitors.ecom.browse.productMonitor;
					var constants = productMonitor.constants.productPage;

					productMonitor.model.previousProductPage.productPageReadyTimeDeltaFromPreviousPage
						= controller.getTimeDeltaFromCookieVar(constants.READY_TIME_DELTA_FROM_PREVIOUS_PAGE);

					productMonitor.model.previousProductPage.productPageDataRequestTimeDeltaFromPreviousPage
						= controller.getTimeDeltaFromCookieVar(constants.DATA_REQUEST_TIME_DELTA_FROM_PREVIOUS_PAGE);

					productMonitor.model.previousProductPage.productPageDataLoadTimeDeltaFromPreviousPage
						= controller.getTimeDeltaFromCookieVar(constants.DATA_LOAD_TIME_DELTA_FROM_PREVIOUS_PAGE);

					productMonitor.model.previousProductPage.productPageReviewsRequestTimeDeltaFromPreviousPage
						= controller.getTimeDeltaFromCookieVar(constants.REVIEWS_REQUEST_TIME_DELTA_FROM_PREVIOUS_PAGE);

					productMonitor.model.previousProductPage.productPageReviewsLoadTimeDeltaFromPreviousPage
						= controller.getTimeDeltaFromCookieVar(constants.REVIEWS_LOAD_TIME_DELTA_FROM_PREVIOUS_PAGE);

					productMonitor.model.previousProductPage.productPageBusinessId
						= controller.getTimeDeltaFromCookieVar(constants.PRODUCT_BUSINESS_ID);
				}
			},
			handlers: {
				setEventHandlers: function() {
					var productMonitor = performanceMonitorService.controller.monitors.ecom.browse.productMonitor;
					var handlers = productMonitor.controller.handlers;

					Event.observe(window, 'beforeunload', handlers.pageOnUnloadHandler.bind(productMonitor));

					Event.observe(document, "productPage:ready",          handlers.productPageReadyHandler.bind(productMonitor));
					Event.observe(document, "productPage:dataRequest",    handlers.productPageDataRequestHandler.bind(productMonitor));
					Event.observe(document, "productPage:dataLoad",       handlers.productPageDataLoadHandler.bind(productMonitor));
					Event.observe(document, "productPage:reviewsRequest", handlers.productReviewsPageRequestHandler.bind(productMonitor));
					Event.observe(document, "productPage:reviewsLoad",    handlers.productReviewsPageLoadHandler.bind(productMonitor));
				},
				productPageReadyHandler: function() {
					var result = performanceMonitorService.controller.getDeltaTimeResult(this);
					result.model.productPageReadyTime = result.start;
					result.model.productPageReadyTimeDeltaFromPreviousPage = result.delta;

					var productPageBusinessId = productPage.objP.strProductId;
					result.model.productPageBusinessId = productPageBusinessId;
					result.model.previousPageUnloadTime = performanceMonitorService.previousPageUnloadTime;
				},
				productPageDataRequestHandler: function() {
					var result = performanceMonitorService.controller.getDeltaTimeResult(this);
					result.model.productPageDataRequestTime = result.start;
					result.model.productPageDataRequestTimeDeltaFromPreviousPage = result.delta;
				},
				productPageDataLoadHandler: function() {
					var result = performanceMonitorService.controller.getDeltaTimeResult(this);
					result.model.productPageDataLoadTime = result.start;
					result.model.productPageDataLoadTimeDeltaFromPreviousPage = result.delta;
				},
				productReviewsPageRequestHandler: function() {
					var result = performanceMonitorService.controller.getDeltaTimeResult(this);
					result.model.productPageReviewsRequestTime = result.start;
					result.model.productPageReviewsRequestTimeDeltaFromPreviousPage = result.delta;
				},
				productReviewsPageLoadHandler: function() {
					var result = performanceMonitorService.controller.getDeltaTimeResult(this);
					result.model.productPageReviewsLoadTime = result.start;
					result.model.productPageReviewsLoadTimeDeltaFromPreviousPage = result.delta;
				},
				pageOnUnloadHandler: function() {
					var productMonitor = performanceMonitorService.controller.monitors.ecom.browse.productMonitor;
					var controller     = performanceMonitorService.controller;
					var model          = productMonitor.model;
					var constants      = productMonitor.constants.productPage;

					controller.setCookieVar(constants.READY_TIME_DELTA_FROM_PREVIOUS_PAGE,
							model.productPageReadyTimeDeltaFromPreviousPage);

					controller.setCookieVar(constants.DATA_REQUEST_TIME_DELTA_FROM_PREVIOUS_PAGE,
							model.productPageDataRequestTimeDeltaFromPreviousPage);

					controller.setCookieVar(constants.DATA_LOAD_TIME_DELTA_FROM_PREVIOUS_PAGE,
							model.productPageDataLoadTimeDeltaFromPreviousPage);

					controller.setCookieVar(constants.REVIEWS_REQUEST_TIME_DELTA_FROM_PREVIOUS_PAGE,
							model.productPageReviewsRequestTimeDeltaFromPreviousPage);

					controller.setCookieVar(constants.REVIEWS_LOAD_TIME_DELTA_FROM_PREVIOUS_PAGE,
							model.productPageReviewsLoadTimeDeltaFromPreviousPage);

					controller.setCookieVar(constants.PRODUCT_BUSINESS_ID,
							model.productPageBusinessId);

				}
			}
		},
		model: {
			productPageReadyTime: null,
			productPageDataRequestTime: null,
			productPageDataLoadTime: null,
			productPageReviewsRequestTime: null,
			productPageReviewsLoadTime: null,

			previousPageUnloadTime: null,

			productPageBusinessId: null,

			productPageReadyTimeDeltaFromPreviousPage: null,
			productPageDataRequestTimeDeltaFromPreviousPage: null,
			productPageDataLoadTimeDeltaFromPreviousPage: null,
			productPageReviewsRequestTimeDeltaFromPreviousPage: null,
			productPageReviewsLoadTimeDeltaFromPreviousPage: null,

			previousProductPage:{
				productPageBusinessId: null,

				productPageReadyTimeDeltaFromPreviousPage         : null,
				productPageDataRequestTimeDeltaFromPreviousPage   : null,
				productPageDataLoadTimeDeltaFromPreviousPage      : null,
				productPageReviewsRequestTimeDeltaFromPreviousPage: null,
				productPageReviewsLoadTimeDeltaFromPreviousPage   : null
			}
		}
	}
);

function GIDProducts(arrayProducts, hasSelectedProduct, strSelectedProductId) {
    this.hasSelectedProduct = hasSelectedProduct;
    this.strSelectedProductId = strSelectedProductId;
    this.arrayProducts = (!arrayProducts ? [] : arrayProducts); // array.
}

GIDProducts.prototype.addProduct = function (objProducts, objP, hasSelectedProduct, strSelectedProductId) {
    if (objProducts) {
        if (objP && (objP.strProductId.length > 1)) {
            if (objProducts.arrayProducts[objP.strProductId]) {
                if (!objProducts.arrayProducts[objP.strProductId].arrayVariantStyles[objP.strDefaultVariantId].isLoaded) {
                    objProducts.arrayProducts[objP.strProductId].arrayVariantStyles[objP.strDefaultVariantId] = objP.arrayVariantStyles[objP.strDefaultVariantId];
                    objProducts.arrayProducts[objP.strProductId].arrayVariantStyles[objP.strDefaultVariantId].isLoaded = true;
                }
            } else {
                objProducts.arrayProducts[objP.strProductId] = objP;
                objProducts.arrayProducts[objP.strProductId].arrayVariantStyles[objP.strDefaultVariantId].isLoaded = true;
                if (hasSelectedProduct && strSelectedProductId.length > 1) {
                    objProducts.setSelectedProduct(objProducts, hasSelectedProduct, strSelectedProductId);
                }
            }
        }
    }
}


GIDProducts.prototype.removeProduct = function (objProducts, objP, hasSelectedProduct, strSelectedProductId) {
    if (objProducts) {
        if ((objP) && (objP.strProductId.length > 1)) {
            if (objProducts.arrayProducts[objP.strProductId]) {
                delete objProducts.arrayProducts[objP.strProductId];
                if (hasSelectedProduct && strSelectedProductId.length > 1) {
                    objProducts.setSelectedProduct(objProducts, hasSelectedProduct, strSelectedProductId);
                }
            }
        }
    }
}

GIDProducts.prototype.setSelectedProduct = function (objProducts, hasSelectedProduct, strSelectedProductId) {
    if (objProducts) {
        objProducts.hasSelectedProduct = hasSelectedProduct;
        objProducts.strSelectedProductId = strSelectedProductId;
    }
}



function GIDProduct() {}

GIDProduct.prototype.toString = function (obj) {
    var strProps = "";
    for (var prop in obj) {
        if (!isPrototypeSafe(prop, obj)) continue;
        if (strProps == "") {
            if (typeof obj[prop] == "object") {
                var strProps = "<li>Object: <ol><b>" + prop + "</b><br>" + this.toStringParseConstructor(obj[prop].constructor) + "<ol> " + this.toString(obj[prop]) + "</ol></ol></li><br>";
            }	else {
                var strProps = "<li>" + prop + " = " + obj[prop] + "<br></li>";

            }
        }
        else {
            if (typeof obj[prop] == "object") {
                var isSubObjectProperty = true;
                var strProps = strProps + "<li>Object: <ol><b>" + prop + "</b><br>" + this.toStringParseConstructor(obj[prop].constructor) + "<ol> " + this.toString(obj[prop]) + "</ol></ol></li><br>";
            }	else {
                if (isSubObjectProperty) {

                }
                var strProps = strProps + "<li>" + prop + " = " + obj[prop] + "<br></li>";
            }

        }
    }
    var strProps = "<ol>" + strProps + "</ol>";
    return strProps;
}

GIDProduct.prototype.toStringParseConstructor = function (objConstructor) {
    var strConstructor = new String(objConstructor);
    var strX = strConstructor.indexOf(" {");
    var strConstructorName = strConstructor.substring(0, strX);
    var strX = 9;
    var strY = strConstructorName.indexOf("(");
    var strConstructorName = '<b>' + strConstructor.substring(strX, strY) + '</b>';
    return strConstructorName;
}

GIDProducts.prototype.toString = GIDProduct.prototype.toString;
GIDProducts.prototype.toStringParseConstructor = GIDProduct.prototype.toStringParseConstructor;

function GIDCategory (
    strCatalogItemId,
    strSetupCategoryId,
    arrayProducts) {

    this.strCatalogItemId = strCatalogItemId;
    this.strSetupCategoryId = strSetupCategoryId;
    this.arrayProducts = arrayProducts;
}

function GIDCategoryProduct(
    strCatalogItemId,
    strProductId,
    strSizeCategoryId,
    strVariantId) {

    this.strCatalogItemId = strCatalogItemId;
    this.strProductId = strProductId;
    this.strSizeCategoryId = strSizeCategoryId;
    this.strVariantId = strVariantId;
}

GIDCategory.prototype.toString = GIDProduct.prototype.toString;

function GIDOutfit(
    strOutfitName,
    strEditorialText,
    strSecondaryEditorialText,
    strOutfitId,
    strOutfitType,
    strOutfitSubType,
    isPhotoOutfit,
    arrayProducts,
    arrayItemType,
    arraySizeSelections,
    outfitPhotoImage) {

    this.strOutfitName = strOutfitName;
    this.strEditorialText = strEditorialText;
    this.strSecondaryEditorialText = strSecondaryEditorialText;
    this.strOutfitId = strOutfitId;
    this.strOutfitType = strOutfitType;
    this.strOutfitSubType = strOutfitSubType;
    this.isPhotoOutfit = eval(isPhotoOutfit);
    this.arrayProducts = (!arrayProducts ? new Array() : arrayProducts);
    this.arrayItemType = (!arrayItemType ? new Array() : arrayItemType);
    this.arraySizeSelections = (!arraySizeSelections ? new Array() : arraySizeSelections);
    this.outfitPhotoImage = outfitPhotoImage;
}

GIDOutfit.prototype.toString = GIDProduct.prototype.toString;

GIDProduct.prototype.InfoTab = function (
    strInfoTabId,
    strInfoTabName,
    intInfoTabSortOrder,
    hasInfoTabInfoBlocks,
    strInfoTabDescription,
    arrayInfoTabInfoBlocks) {

    this.strInfoTabId = strInfoTabId;
    this.strInfoTabName = strInfoTabName;
    this.intInfoTabSortOrder = intInfoTabSortOrder;
    this.hasInfoTabInfoBlocks = hasInfoTabInfoBlocks;
    this.strInfoTabDescription = strInfoTabDescription;
    this.arrayInfoTabInfoBlocks = arrayInfoTabInfoBlocks;
}

GIDProduct.prototype.InfoTabInfoBlock = function (
    strInfoBlockId,
    intSortOrder,
    strDisplayText,
    hasLink,
    isExternal,
    strExternalLink,
    isSizeChart,
    isPopUp,
    intPopUpHeight,
    intPopUpWidth,
    strTemplateAction,
    intBusinessId) {

    this.strInfoBlockId = strInfoBlockId;
    this.intSortOrder = intSortOrder;
    this.strDisplayText = strDisplayText;
    this.hasLink = hasLink;
    this.isExternal = isExternal;
    this.strExternalLink = strExternalLink;
    this.isSizeChart = isSizeChart;
    this.isPopUp = isPopUp;
    this.intPopUpHeight = intPopUpHeight;
    this.intPopUpWidth = intPopUpWidth;
    this.strTemplateAction = strTemplateAction;
    this.intBusinessId = intBusinessId;
}

GIDProduct.prototype.setArrayInfoTabInfoBlocks = function (objInfoTab, strInfoTabInfoBlocks) {
    with (objInfoTab) {
        var arrayTempInfoTabInfoBlocks = strInfoTabInfoBlocks.split("||");
        arrayInfoTabInfoBlocks = new Array();
        for (var intI = 0; intI < arrayTempInfoTabInfoBlocks.length; intI++) {
            var arrayInfoBlock = arrayTempInfoTabInfoBlocks[intI].split("^,^");
            var strInfoBlockId = arrayInfoBlock[0];
            var intSortOrder = eval(arrayInfoBlock[1]);
            var strDisplayText = arrayInfoBlock[2];
            var hasLink = eval(arrayInfoBlock[3]);
            var isExternal = eval(arrayInfoBlock[4]);
            var strExternalLink = arrayInfoBlock[5];
            var isSizeChart = eval(arrayInfoBlock[6]);
            var isPopUp = eval(arrayInfoBlock[7]);
            var intPopUpHeight = arrayInfoBlock[8];
            var intPopUpWidth = arrayInfoBlock[9];
            var strTemplateAction = arrayInfoBlock[10];
            var intBusinessId = arrayInfoBlock[11];
            arrayInfoTabInfoBlocks[intI] = new GIDProduct.prototype.InfoTabInfoBlock(
                strInfoBlockId,
                intSortOrder,
                strDisplayText,
                hasLink,
                isExternal,
                strExternalLink,
                isSizeChart,
                isPopUp,
                intPopUpHeight,
                intPopUpWidth,
                strTemplateAction,
                intBusinessId);
        }
    }
}

function ProductStyle(
    brandCode,
    strProductId,
    strVendorId,
    strCatalogItemId,
    strProductType,
    hasFitAttributeOverlayImages,
    hasAlternateImage,
    hasMarketingFlag,
    hasMarketingCallOut,
    strMupMessage,
    strGIDPromoMessage,
    sizeChartId,
    hasZoomEnabled,
    strProductPriceRange,
    isInStock,
    isOnSale,
    isOnClearence,
    isGiftCard,
    productClassTypId,
    hasCrossSell,
    hasSplitVariants,
    hasMergeVariants,
    isImported,
    intMaxOrderQuantity,
    intMaxQuantity,
    strProductStyleName,
    strStyleColorDisplayName,
    strAllowableReturnCode,
    strCareInstructionText,
    strFlammableWarningText,
    isHazardousMaterial,
    isNonGiftWrap,
    isWaterResistant,
    intBestSellingScore,
    intNewnessScore,
    strTaxExemptCode,
    strDefaultVariantId,
    strVendorName,
    intlShip,
    objProductStyleImages,
    arrayInfoTabs,
    arrayVariantStyles,
    objCrossSellInfo,
    arrayFabricContent,
    objMarketingFlag,
    objMarketingCallOut) {

    this.brandCode = brandCode;
    this.strProductId = strProductId;
    this.strVendorId = strVendorId;
    this.strCatalogItemId = strCatalogItemId;
    this.strProductType = strProductType + "";
    this.hasFitAttributeOverlayImages = eval(hasFitAttributeOverlayImages);
    this.hasAlternateImage = eval(hasAlternateImage);
    this.hasMarketingFlag = eval(hasMarketingFlag);
    this.hasMarketingCallOut = eval(hasMarketingCallOut);
    this.strMupMessage = strMupMessage;
    this.strGIDPromoMessage = strGIDPromoMessage;
    this.sizeChartId = sizeChartId;
    this.hasZoomEnabled = eval(hasZoomEnabled);
    this.strProductPriceRange = strProductPriceRange;
    this.isInStock = eval(isInStock);
    this.isOnSale = eval(isOnSale);
    this.isOnClearence = eval(isOnClearence);
    this.isGiftCard = eval(isGiftCard);
    this.productClassTypId = productClassTypId;
    this.hasCrossSell = eval(hasCrossSell);
    this.hasSplitVariants = eval(hasSplitVariants);
    this.hasMergeVariants = eval(hasMergeVariants);
    this.isImported = eval(isImported);
    this.intMaxOrderQuantity = intMaxOrderQuantity;
    this.intMaxQuantity = intMaxQuantity;
    this.strProductStyleName = strProductStyleName;
    this.strStyleColorDisplayName = strStyleColorDisplayName;
    this.strAllowableReturnCode = strAllowableReturnCode;
    this.strCareInstructionText = strCareInstructionText;
    this.strFlammableWarningText = strFlammableWarningText;
    this.isHazardousMaterial = eval(isHazardousMaterial);
    this.isNonGiftWrap = eval(isNonGiftWrap);
    this.isWaterResistant = eval(isWaterResistant);
    this.intBestSellingScore = intBestSellingScore;
    this.intNewnessScore = intNewnessScore;
    this.strTaxExemptCode = strTaxExemptCode;
    this.strDefaultVariantId = strDefaultVariantId;
    this.strVendorName = strVendorName;
    this.objProductStyleImages = (!objProductStyleImages ? {} : objProductStyleImages);
    this.arrayInfoTabs = (!arrayInfoTabs ? [] : arrayInfoTabs);
    this.arrayVariantStyles = (!arrayVariantStyles ? {} : arrayVariantStyles);
    this.objCrossSellInfo = objCrossSellInfo;
    this.arrayFabricContent = (!arrayFabricContent ? [] : arrayFabricContent);
    this.objMarketingFlag = objMarketingFlag;
    this.objMarketingCallOut = objMarketingCallOut;
    this.isIntlShip = intlShip;
}

ProductStyle.prototype = new GIDProduct();

ProductStyle.prototype.setArrayProductStyleImages = function (arrayImages, strProductStyleImages) {
    var arrayTempStyleImages = strProductStyleImages.split("||");
    for (var intI = 0; intI < arrayTempStyleImages.length; intI++) {
        var arrayImage = arrayTempStyleImages[intI].split("^,^");
        var strImagePath = arrayImage[0];
        var strThumbImagePath = arrayImage[1];
        var strZoomImagePath = arrayImage[2];
        var strDisplayText = arrayImage[3];
        var intMediaType = arrayImage[4];
        var intWidth = arrayImage[5];
        var intHeight = arrayImage[6];
        arrayImages[intI] = new ProductStyle.prototype.styleImage(strImagePath, strThumbImagePath, strZoomImagePath, strDisplayText, intMediaType, intWidth, intHeight);
    }
}

ProductStyle.prototype.setArrayVariantStyleColorImages = function (objProductStyleColor, strStyleColorImages) {
    with (objProductStyleColor) {
        var arrayTempVariantStyleColorImages = strStyleColorImages.split("||");
        for (var intI = 0; intI < arrayTempVariantStyleColorImages.length; intI++) {
            var arrayStyleColorImage = arrayTempVariantStyleColorImages[intI].split("^,^");
            var strImageType = arrayStyleColorImage[0];
            var strImagePath = arrayStyleColorImage[1];
            var strThumbImagePath = arrayStyleColorImage[2];
            var strZoomImagePath = arrayStyleColorImage[3];
            arrayVariantStyleColorImages[strImageType] = new ProductStyle.prototype.StyleColorImage(strImageType, strImagePath, strThumbImagePath, strZoomImagePath);
        }
    }
}

ProductStyle.prototype.StyleColor = function (
    strColorCodeId,
    strColorName,
    isOnSale,
    isOnClearance,
    isInStock,
    hasLargerImage,
    hasRotationalImages,
    intSortOrder,
    strRegularPrice,
    strSalePrice,
    strPromoPrice,
    strPartialMupMessage,
    strVendorStyleNumber,
    strVendorProductId,
    strProductStyleColorSkus,
    arrayVariantStyleColorImages,
    objMarketingFlag) {

    var styleColorVarUndefined;

    this.strColorCodeId = strColorCodeId;
    this.strColorName = strColorName;
    this.isOnSale = isOnSale;
    this.isOnClearance = isOnClearance;
    this.isInStock = isInStock;
    this.hasLargerImage = hasLargerImage;
    this.hasRotationalImages = hasRotationalImages;
    this.intSortOrder = intSortOrder;
    this.strRegularPrice = (strRegularPrice != null ? strRegularPrice : styleColorVarUndefined);
    this.strSalePrice = (strSalePrice != null ? strSalePrice : styleColorVarUndefined);
    this.strPromoPrice = (strPromoPrice != null ? strPromoPrice : styleColorVarUndefined);
    this.strPartialMupMessage = strPartialMupMessage;
    this.strVendorStyleNumber = strVendorStyleNumber;
    this.strVendorProductId = strVendorProductId;
    this.strProductStyleColorSkus = strProductStyleColorSkus;
    this.arrayVariantStyleColorImages = (!arrayVariantStyleColorImages ? new Array() : arrayVariantStyleColorImages); // array.
    this.objMarketingFlag = objMarketingFlag;
}

GIDProduct.prototype.SizeInfo = function (
    intSizeDimensionsCount,
    objSizeDimension1) {

    this.intSizeDimensionsCount = intSizeDimensionsCount;
    this.objSizeDimension1 = objSizeDimension1;
}

GIDProduct.prototype.SizeInfoSummary = function (
    intSizeDimensionsCount,
    intSizeCategoryId,
    strSizeDisplayName,
    strSizeDimension1Name,
    strSizeDimension2Name,
    strSizeDimension1Id,
    strSizeDimension2Id,
    strSizeDimension1ListOptions,
    strSizeDimension2ListOptions,
    hasSizeDimension1Default,
    hasSizeDimension2Default,
    strDefaultSizeDimension1SizeCodeId,
    strDefaultSizeDimension2SizeCodeId) {

    this.intSizeDimensionsCount = intSizeDimensionsCount;
    this.intSizeCategoryId = intSizeCategoryId;
    this.strSizeDisplayName = strSizeDisplayName;
    if (strSizeDimension1ListOptions != null) this.strSizeDimension1ListOptions = strSizeDimension1ListOptions;
    if (strSizeDimension2ListOptions != null) this.strSizeDimension2ListOptions = strSizeDimension2ListOptions;
    if (strSizeDimension1Name != null) this.strSizeDimension1Name = strSizeDimension1Name;
    if (strSizeDimension2Name != null) this.strSizeDimension2Name = strSizeDimension2Name;
    if (strSizeDimension1Id != null) this.strSizeDimension1Id = strSizeDimension1Id;
    if (strSizeDimension2Id != null) this.strSizeDimension2Id = strSizeDimension2Id;
    this.hasSizeDimension1Default = hasSizeDimension1Default;
    this.hasSizeDimension2Default = hasSizeDimension2Default;
    this.strDefaultSizeDimension1SizeCodeId = strDefaultSizeDimension1SizeCodeId;
    this.strDefaultSizeDimension2SizeCodeId = strDefaultSizeDimension2SizeCodeId;
}

ProductStyle.prototype.ProductStyleImages = function (arrayAlternateViewImages,arrayWaysToWearImages) {
    this.arrayAlternateViewImages = [];
    this.arrayWaysToWearImages = [];
    this.arrayZoomViewImages = [];
}

GIDProduct.prototype.styleImage = function (strImagePath, strThumbImagePath, strZoomImagePath, strImageDisplayText, intMediaType, intWidth, intHeight) {
    this.strImagePath = strImagePath;
    this.strThumbImagePath = strThumbImagePath;
    this.strZoomImagePath = strZoomImagePath;
    this.strImageDisplayText = strImageDisplayText;
    this.intMediaType = intMediaType;
    this.intWidth = intWidth;
    this.intHeight = intHeight;
}

GIDProduct.prototype.outfitPhotoImage = function (strImagePath, strImageDisplayText) {
    this.strImagePath = strImagePath;
    this.strImageDisplayText = strImageDisplayText;
}

GIDProduct.prototype.fabricType = function (intId, strName, strPercent) {
    this.intId = intId;
    this.strName = strName;
    this.strPercent = strPercent;
}

ProductStyle.prototype.StyleColorImage = function (
    strImageType,
    strImagePath,
    strThumbImagePath,
    strZoomImagePath) {

    this.strImageType = strImageType;
    this.strImagePath = strImagePath;
    if (strThumbImagePath) this.strThumbImagePath = strThumbImagePath;
    if (strZoomImagePath) this.strZoomImagePath = strZoomImagePath;
}

GIDProduct.prototype.VariantStyle = function (
    isLoaded,
    strCatalogItemId,
    strVariantId,
    strVariantName,
    arrayVariantStyleColors,
    arrayVariantSkus,
    objStyleSizeInfo) {

    this.isLoaded = eval(isLoaded);
    this.strCatalogItemId = strCatalogItemId;
    this.strVariantId = strVariantId;
    this.strVariantName = strVariantName;
    this.arrayVariantStyleColors = (!arrayVariantStyleColors ? new Array() : arrayVariantStyleColors); // array.
    this.arrayVariantSkus = (!arrayVariantSkus ? {} : arrayVariantSkus); // array.
    this.objStyleSizeInfo = objStyleSizeInfo;
}

GIDProduct.prototype.VariantSku = function (
    strSkuId,
    strSizeDim1Id,
    strSizeDim2Id,
    isLowInventory,
    isOnOrder,
    strOnOrderDate,
    strUPCCode,
    strAllowableReturnCode,
    strCurrentPrice,
    strRegularPrice,
    objMarketingFlag) {

    this.strSkuId = strSkuId;
    this.strSizeDim1Id = strSizeDim1Id;
    this.strSizeDim2Id = strSizeDim2Id;
    this.isLowInventory = eval(isLowInventory);
    this.isOnOrder = eval(isOnOrder);
    this.strOnOrderDate = strOnOrderDate;
    this.strUPCCode = strUPCCode;
    this.strAllowableReturnCode = strAllowableReturnCode;
    this.strCurrentPrice = strCurrentPrice;
    this.strRegularPrice = strRegularPrice;
    this.objMarketingFlag = objMarketingFlag;
}

GIDProduct.prototype.setProductVariantStyles = function(arrayVariantStyles,strVariantStyles) {
    strVariantStyles.split('||').
        invoke('split','^,^').inject(arrayVariantStyles,
            function(obj, value) {
                obj[Number(value[0])] = new GIDProduct.prototype.VariantStyle(false,
                    value[1],value[2],value[3]);
                return obj;

            }
        );
}

GIDProduct.prototype.setProductVariantSkus = function (objVariantStyle) {
    with(objVariantStyle) {
        for (var intI in arrayVariantStyleColors) {
            if (!isPrototypeSafe(intI, arrayVariantStyleColors)) continue;
            var arrayTempSkus = arrayVariantStyleColors[intI].strProductStyleColorSkus.split("||");
            for (var intJ in arrayTempSkus) {
                if (!isPrototypeSafe(intJ, arrayTempSkus)) continue;
                var arrayTempSku = arrayTempSkus[intJ].split("^,^");
                var strSkuId = arrayTempSku[0];
                var strSizeDim1Id = arrayTempSku[1];
                var strSizeDim2Id = arrayTempSku[2];
                var isLowInventory = arrayTempSku[3];
                var isOnOrder = arrayTempSku[4];
                var strOnOrderDate = arrayTempSku[5];
                var strUPCCode = arrayTempSku[6];
                var isMailReturn = arrayTempSku[7];
                var strCurrentPrice = arrayTempSku[8];
                var strRegularPrice = arrayTempSku[9];
                if (arrayTempSku.length > 10) {
                    var objMarketingFlag = new GIDProduct.prototype.marketingFlag(arrayTempSku[10],arrayTempSku[11],arrayTempSku[12],arrayTempSku[13])
                    var strMarketingFlagAttribs = arrayTempSku[14];
                    if (strMarketingFlagAttribs) {
                            var arrayAttrib = strMarketingFlagAttribs.split("|,|");
                            var objAttribs = new GIDProduct.prototype.marketingFlagAttributes(
                                arrayAttrib[0],
                                arrayAttrib[1],
                                arrayAttrib[2],
                                arrayAttrib[3],
                                arrayAttrib[4],
                                arrayAttrib[5],
                                arrayAttrib[6]);

                    }
                } else {
                    var objMarketingFlag = undefined;
                }
                if (strSizeDim2Id) {
                    var strIndex1 = arrayVariantStyleColors[intI].strColorCodeId + "_" + strSizeDim1Id + "_";
                    var strIndex2 = arrayVariantStyleColors[intI].strColorCodeId + "__" + strSizeDim2Id;
                    arrayVariantSkus[strIndex1] = (arrayVariantSkus[strIndex1] ? arrayVariantSkus[strIndex1] + strSizeDim2Id : strSizeDim2Id) + ",";
                    arrayVariantSkus[strIndex2] = (arrayVariantSkus[strIndex2] ? arrayVariantSkus[strIndex2] + strSizeDim1Id : strSizeDim1Id) + ",";
                }
                var strMap = arrayVariantStyleColors[intI].strColorCodeId + "_" + strSizeDim1Id + "_" + strSizeDim2Id;
                arrayVariantSkus[strMap] = new GIDProduct.prototype.VariantSku(
                    strSkuId,
                    strSizeDim1Id,
                    strSizeDim2Id,
                    isLowInventory,
                    isOnOrder,
                    strOnOrderDate,
                    strUPCCode,
                    isMailReturn,
                    strCurrentPrice,
                    strRegularPrice,
                    objMarketingFlag);
            }
        }
    }
}

GIDProduct.prototype.setFabricContent = function(arrayFabricContent, strFabricContent) {
        var arrayFabrics = strFabricContent.split("||");
        for (var i in arrayFabrics) {
            if (!isPrototypeSafe(i, arrayFabrics)) continue;
            var arrayVals = arrayFabrics[i].split("^,^");
            var fabricObj = new GIDProduct.prototype.fabricType(arrayVals[0],arrayVals[1],arrayVals[2]);
            arrayFabricContent.push(fabricObj);
        }
}

GIDProduct.prototype.marketingFlag = function(
    isImageType,
    intMarketingFlagId,
    strMarketingFlagName,
    intMarketingFlagTypeId,
    objMarketingFlagAttributes) {

    this.isImageType = eval(isImageType);
    this.intMarketingFlagId = intMarketingFlagId;
    this.strMarketingFlagName = strMarketingFlagName;
    this.intMarketingFlagTypeId = intMarketingFlagTypeId;
    this.objMarketingFlagAttributes = objMarketingFlagAttributes;
}

GIDProduct.prototype.marketingFlagAttributes = function(
    strAltTagText,
    intCatalogContentId,
    intContentTypeId,
    intMediaTypeId,
    strPath,
    intHeight,
    intWidth) {

    this.strAltTagText = strAltTagText;
    this.intCatalogContentId = intCatalogContentId;
    this.intContentTypeId = intContentTypeId;
    this.intMediaTypeId = intMediaTypeId;
    this.strPath = strPath;
    this.intHeight = intHeight;
    this.intWidth = intWidth;
}

GIDProduct.prototype.crossSellInfo = function(
    intTypeId,
    isPhotoOutfit,
    isNonPhotoOutfit,
    hasAssociatedItems,
    isProductSet,
    strCopy,
    strLabelHeader,
    strLabelName,
    strImageSrc,
    strImageZoomSrc,
    strLink,
    arrayProducts) {

    this.intTypeId = intTypeId;
    this.isPhotoOutfit = isPhotoOutfit;
    this.isNonPhotoOutfit = isNonPhotoOutfit;
    this.hasAssociatedItems = hasAssociatedItems;
    this.isProductSet = isProductSet;
    this.strCopy = strCopy;
    this.strLabelHeader = strLabelHeader;
    this.strLabelName = strLabelName;
    this.strImageSrc = strImageSrc;
    this.strImageZoomSrc = strImageZoomSrc;
    this.strLink = strLink;
    this.arrayProducts = (!arrayProducts ? new Array() : arrayProducts);
}

GIDProduct.prototype.crossSellItem = function(
    intCatalogItemId,
    strName,
    strVendorName,
    strImgSrc,
    strPrice,
    strProductLink,
    isOutfitItem,
    mups,
    brandCode){

    this.intCatalogItemId = intCatalogItemId;
    this.strName = strName;
    this.strVendorName = strVendorName;
    this.strImgSrc = strImgSrc;
    this.strPrice = strPrice;
    this.strProductLink = strProductLink;
    this.isOutfitItem = isOutfitItem;
    this.mups = mups;
    this.brandCode = brandCode;
}

var objGIDPageViewAdapter = {
    initialized: false,
    initializeGidProducts : function () {
        if(objGIDPageViewAdapter.initialized) return;

        objGIDPageViewAdapter.objGIDProducts = new GIDProducts();
        objGIDPageViewAdapter.initialized = true;
    }
};


objGIDPageViewAdapter.initializeGidProducts();


/**
 * Extends brandConst with the CopyBox and resources obj
 * @requires brandConst Defined in brandConst.js
 * @author yoshi chen
 */

Object.extend(brandConst, {
	CopyBox : {
		GROUP_NAME : {Overview : resourceBundleValues.product.groupNameOverview, Tips : resourceBundleValues.product.groupNameTips, Fabric : resourceBundleValues.product.groupNameFabric, Fit : resourceBundleValues.product.groupNameFit},
		GROUP_NAME_DEFAULT : resourceBundleValues.product.groupNameDefault
	},
	/**
 	* resources - Define asset path for Quicklook and Buttons
 	* This is a method of class brandConst
 	* Modified:  Keo 12/04/07 - Added Comments
 	* @author Unkown
 	*/
	resources : { ASSET_PATH : '/assets/common/quicklook/'+brandConst.BRAND_LOCALE+'/', BTN_PATH :  '/assets/common/buttons/'+brandConst.BRAND_LOCALE+'/',
		ADD_TO_BAG_CLASS : 'button_add_to_bag'},

	BRAND_PRODUCT_IMG_MAP : brandConst.BRAND_PRODUCT_IMG_MAP,
	
    INTCOLORSWATCHWIDTH : {'1':18,'2':18,'3':18,'4':35,'10':18,'7':18,'8':18,'9':18,'20':18,'21':18},
    OUTFIT_SMODULE_WIDTH: {'1':58,'2':58,'3':58,'4':58,'10':77,'7':58,'8':58,'9':58,'20':58,'21':58},
    OUTFIT_SMODULE_HEIGHT: {'1':77,'2':77,'3':77,'4':58,'10':77,'7':77,'8':77,'9':77,'20':77,'21':77},
    productClassTypeId : {'APPAREL':1,'SHOES':2,'HANDBAG':3}
});

/**
 * GID - Root Level of js object
 * @constructor
 * @author Unknown
 * @date 12/04/2007
 */
var GID = {};
GID.Browse = {
	Errors : {isProductDataError:false, isAddToBagError:false},
	Pops : {}
};
/**
 * Base Class for QuickLook, ProductPage, as well as Outfit and ShoppingBagEdit
 * @constructor
 * @author yoshi chen
 */
// prototype for GidBase
GID.Browse.Base = {

	strAddToBagClass: brandConst.resources.ADD_TO_BAG_CLASS,

	objCookieData: {color: -1, s1: -1, s2: -1, qty: -1},
	strDefaultStyleColorId: -1,

	intTab: 0,
	activeColor: -1,
	activeSizeDimension1: -1,
	activeSizeDimension2: -1,
	selectedColor: -1,
	selectedSizeDimension1: -1,
	selectedSizeDimension2: -1,
	strSelectedQty: 1,
	intTabOverviewIndex: -1,
	intDataLoadCounter: 0,
	dataLoaderAction: '',
	selectedColorName: '',
	selectedSizeDimension1Name: '',
	selectedSizeDimension2Name: '',
	strDisplayType: '',
	strSBSUrlSuffix: '&actFltr=true',

	arrayAllSizeDimension1: [],
	arrayAllSizeDimension2: [],
	arrayAddtoBagErrors: [],

//	isLoaded: false,
	isColorError: false,
	isSizeDimension1Error: false,
	isSizeDimension2Error: false,
	isBagError: false,
	hasStyleColorMarketingFlag: false,
	hasSkuMarketingFlag: false,
	isColorTextWrap: false,
	hasInlineBagErrors: false,
	isFromSBS: (getQuerystringParam('actfltr') == 'true'),
	enableDrag: true,
    hiddenDropDowns: [],

	THUMB_WIDTH : brandConst.PRODUCT_THUMB_WIDTH,
	THUMB_HEIGHT : brandConst.PRODUCT_THUMB_HEIGHT,

	// common base templates for all components
	templates : {
		BAG_ERROR : new Template('<div id="bagErrorLeft"></div>' +
							'<div id="bagErrorMiddle">#{msg}</div>' +
							'<div id="bagErrorRight"></div>'),
		COLOR_SWATCH : new Template(
					'<label for="colorSwatch_#{index}#{id}" class="cssHide2" >#{label} #{altText}</label><input type="image" id="colorSwatch_#{index}#{id}" onclick="#{app}.setColor(#{index});" onfocus="#{app}.swatchOver(\'color\', #{index});" onMouseOver="#{app}.swatchOver(\'color\',#{index});" onmouseout="#{app}.swatchOut(\'color\',#{index});" src="#{path}" width="#{swatchSize}" height="#{swatchSize}" alt="#{altText}"/>'
		),

		DIM_SWATCH : new Template(
			'<label for="size#{type}Swatch_#{index}#{id}" class="cssHide2" >#{label} #{size}</label><button type="button" id="size#{type}Swatch_#{index}#{id}" onfocus="#{app}.swatchOver(\'size#{type}\',#{index});" onmouseover="#{app}.swatchOver(\'size#{type}\',#{index});" onblur="#{app}.swatchOut(\'size#{type}\',#{index});" onmouseout="#{app}.swatchOut(\'size#{type}\',#{index});" onClick="#{app}.setSizeDimension#{type}(#{index});#{app}.swatchOut(\'size#{type}\',#{index});">#{size}</button>'
		),

		BR_CLEAR : '<br style="clear:both;"/>',
		DIV_CLEAR : '<div style="clear:both;"></div>',
		MUP_SEPERATOR : '<div style="clear:both;"></div><hr class="mupSeperator" />',

		BULLET : new Template('<li>#{content}</li>'),
		PERCENT : new Template('#{percent}% #{name}#{divider}'),

		INFO_BLOCK_NEW : new Template('<div class="copyBlock">#{name}</div><ul>#{content}</ul>'),
		INFO_BLOCK_OLD  : new Template('<ul>#{content}</ul>'),

		VARIANT_BTNS : new Template(
				'<li><img onclick="#{onclick}" class="productpage-sprites sprite-varianttab_#{name}_#{state}" src="/assets/common/clear.gif" alt="#{name}" border="0" style="#{style}"/></li>' ),

		VARIANT_BTNS_SITEWIDE : new Template(
				'<li onclick="#{onclick}" class="varianttab #{state}" >#{name}</li>' ),

		MORE_VIEWS : new Template('<img id="moreViewsBtn" class="quicklook-sprites sprite-button_more_views" src="/assets/common/clear.gif" border="0" onclick="#{app}.openProductImages();" style="margin-bottom:1px;">'),
		MORE_VIEWS_SITEWIDE : new Template('<img id="moreViewsBtn" src="/assets/common/clear.gif" border="0" onclick="#{app}.openProductImages();" style="margin-bottom:1px;">'),

		VARIANT : new Template(
						'<a href="javascript:#{app}.loadVariantProduct(#{catalogItemId},#{variantId});">#{name}</a>&nbsp;'),

		OVERVIEW_TEMP : new Template(
						'#{content}<p class="productId">\##{id}</p><p class="productDetail"><a href="javascript:#{app}.goProductPage();">#{link}</a></p>'),

		MARKETING_FLAG : new Template('<span class="productMarketingFlag">#{name}</span>'),
		TEXT_COLOR : new Template('<div class="swatchLabelName"><span class="swatchLabel">#{label}</span> <span style="font-weight: normal">#{colorLabel}:</span> #{name}</div>'),
		SIZE_LABEL_TEXT : new Template('<span class="swatchLabel">#{label}</span> #{dispName}: <span class="swatchLabelName">#{name}</span>'),

		INLINE_BAG_URL : new Template('/buy/inlineShoppingBagAdd.do?skuid=#{sku}&quantity#{sku}=#{qty}&sfl#{sku}=false&cid#{sku}=#{cid}'),

		PRODUCT_DATA_URL : new Template(
				brandConst.CATALOG_2_ACTIVE == 'true' ? '/browse/' + brandConst.PRODUCTDATA_2_ACTION + '?pid=#{pid}&vid=#{vid}&scid=#{scid}#{type}&actFltr=#{actFltr}&locale=#{localeCode}' :
                        '/browse/productData.do?pid=#{pid}&vid=#{vid}&scid=#{scid}#{type}&actFltr=#{actFltr}&locale=#{localeCode}'),

		LINE_ITEM : new Template(
			'	<li id="lineItem#{count}" class="clearfix lineItem">' +
			'		<div class="badgeContent"><img class="badge" src="#{badgeSrc}" alt="#{badgeAlt}"></img></div>' +
			'		<div class="lineItemDetails">' +
			'			<div class="imageContent">#{imageContent}</div>' +
			'			<div class="infoContent">' +
			'				<ul>' +
			'					<li class="styleDescription"><a href="#{productNameHref}" class="productName"><span class="styleDescriptionSpan">#{styleDescription}</span></a></li>' +
			'					<li class="sku">##{sku}</li>' +
			'				</ul>' +
			'			</div>' +
			'			<div class="infoContent2">' +
			'				<ul>' +
			'					<li class="colorDescription">' +
			'						<div class="label">#{labelColor}</div>' +
			'						<div class="colorDescriptionSpan productDetail">#{color}</div>' +
			'					</li>' +
			'					<li class="skuDescription">' +
			'						<div class="label">#{labelSize}</div>' +
			'						<div class="skuDescriptionSpan productDetail"/>#{size}</div>' +
			'					</li>' +
			'					<li>' +
			'					   <div class="label">#{labelPrice}</div>' +
			'						<div class="hasSellPrice">' +
			'							<div><span class="reg-price-strike">#{listPrice}</span></div>' +
			'							<div class="salePrice">#{discountedPrice}</div>' +
			'							<div class="sellPrice">#{sellPrice}</div>' +
			'						</div>' +
			'					</li>' +
			'					<li class="quantityAndItemSubtotal">' +
			'						<div class="label">#{labelQuantity}</div>' +
			'						<div class="quantity">#{quantity}</div>' +
			'						<div class="itemSubtotal">' +
			'							<span class="itemSubtotalSpan productDetail">#{subTotal}</span>' +
			'						</div>' +
			'					</li>' +
			'					#{inventoryStatusMessage}' +
			'				</ul>' +
			'			</div>' +
			'		</div>' +
			'	</li>'
		),

		TEXTREADERMESSAGE : new Template(resourceBundleValues.strinlineBagScreenTextReaderMessage)

	},

	resources : gidLib.clone(brandConst.resources),

	commonDomObjMap : {
	   objSoldOutMsg : "productSoldOutMsg",
	   objOnOrderMsg : "productOnOrderMsg",
	   objColorErrorMsg : "productColorError",
	   objSizeDimension1ErrorMsg : "productSizeDimension1Error",
	   objSizeDimension2ErrorMsg : "productSizeDimension2Error",
	   objBagErrorMsg : "productBagError"
	},

/**
 * set swatch color on mouse click
 * @param {integer} i index for the color in color array
 * @author yoshi
 */
	setColor : function(i) {
		var imgs = this.objV.arrayVariantStyleColors[i].styleColorImagesMap;
		var swatch = imgs['S'];
		if (i != this.selectedColor && swatch) {
			this.productThumbs.setColor(i, swatch);
			this.setColorSwatches();
			this.setSizeDimensionSwatches(1, this.size1Swatches);
			this.setSizeDimensionSwatches(2, this.size2Swatches);
			this.updateDataLabels();
			if (this.isColorError) {
				this.isColorError = false;
				this.objColorErrorMsg.style.visibility = 'hidden';
				this.setBagError();
			}
			this.clearAddToBagError();
		}
	},

/**
 * set dimension 1 for product, such as waist size
 * @param {integer} i index in sku array
 * @author yoshi
 */
	setSizeDimension1 : function(i) {
		if (i != this.selectedSizeDimension1) {
			this.selectedSizeDimension1 = i;
			this.activeSizeDimension1 = i;
			this.selectedSizeDimension1Name = (this.arrayAllSizeDimension1[i] ? this.arrayAllSizeDimension1[i].strName : "");

			this.setColorSwatches();
			this.setSizeDimensionSwatches(1, this.size1Swatches);
			this.setSizeDimensionSwatches(2, this.size2Swatches);
			this.updateDataLabels();
			if (this.isSizeDimension1Error) {
				this.isSizeDimension1Error = false;
				setObjVisibility(this.objSizeDimension1ErrorMsg,"hidden");
				this.setBagError();
			}
			this.clearAddToBagError();
		}
	},

/**
 * set dimension 2 for product, such as length
 * @param {integer} i index in sku array
 * @author yoshi
 */
	setSizeDimension2 : function(i) {
		if (i != this.objP.selectedSizeDimension2) {
			this.selectedSizeDimension2 = i;
			this.activeSizeDimension2 = i;
			this.selectedSizeDimension2Name = (this.arrayAllSizeDimension2[i] ? this.arrayAllSizeDimension2[i].strName : "");

			this.setColorSwatches();
			this.setSizeDimensionSwatches(1, this.size1Swatches);
			this.setSizeDimensionSwatches(2, this.size2Swatches);
			this.updateDataLabels();
			if (this.isSizeDimension2Error) {
				this.isSizeDimension2Error = false;
				setObjVisibility(this.objSizeDimension2ErrorMsg,"hidden");
				this.setBagError();
			}
			this.clearAddToBagError();
		}
	},

/**
 * sets the width of dimension btn to be the same, btn can contain text such as S, M, L, XL, XXL, this function
 * sets all width to be the max width of all btns
 * @param {integer} dim dimension
 * @param {object} ele the btn
 * @param {integer} minWidth the minimum width
 * @author yoshi
 */

	setCommonDimensionSizeWidth : function(dim, ele, minWidth) {
		var sizeInfo = this.objV.objStyleSizeInfo;
		if(sizeInfo.intSizeDimensionsCount < 2 && dim == 2 ) { return; }
		var sizeDim = this['size' + dim + 'Buttons'], maxDimWidth = sizeDim.invoke('getDimensions').pluck('width').max() + 2;
		if( sizeInfo.intSizeDimensionsCount == 1 ) { ele.style.width = '100%'; }
		sizeDim.invoke('setStyle', {width: Math.max(maxDimWidth, minWidth||maxDimWidth)+1  + 'px'});
	},

/**
 * updates color, and size info when mouse over, checks for availibility as well
 * @param {string} type type of btn, 'color', 'size1', 'size2'
 * @param {integer} index btn index
 * @author yoshi
 */
	swatchOver : function(type,index) {
		switch(type) {
			case "color":
				if (index != this.selectedColor) {
					this.productThumbs.swatchOver(index);
					this.setSizeDimensionSwatches(1, this.size1Swatches);
					this.setSizeDimensionSwatches(2, this.size2Swatches);
				}
				break;
			case "size1":
				if (index != this.selectedSizeDimension1) {
					this.activeSizeDimension1 = index;
					this.setColorSwatches();
					this.setSizeDimensionSwatches(2, this.size2Swatches);
				}
				break;
			case "size2":
				if (index != this.selectedSizeDimension2) {
					this.activeSizeDimension2 = index;
					this.setColorSwatches();
					this.setSizeDimensionSwatches(1, this.size1Swatches);
				}
				break;
		}

		var objImg = this[type + 'Buttons'][index];
		if (objImg.className != "soldOut" && objImg.className != "selectedSoldOut") {
			objImg.className = "hover";
		}

		this.updateDataLabels(type,index);
		this.showInventoryStatusWindow(false);
	},

/**
 * undo everything on mouse out
 * @param {string} type btn type
 * @param {integer} index btn index
 * @see swatchOver
 * @author yoshi
 */
	swatchOut : function(type,index) {
		var doMouseOut = false;
		switch(type) {
			case "color":
				if (index == this.activeColor) {
					this.productThumbs.swatchOut(index);
					doMouseOut = true;
				}
				break;
			case "size1":
				if (index == this.activeSizeDimension1) {
					doMouseOut = true;
				}
				break;
			case "size2":
				if (index == this.activeSizeDimension2) {
					doMouseOut = true;
				}
				break;
		}
		if (doMouseOut) {
			this.activeColor = this.selectedColor;
			this.activeSizeDimension1 = this.selectedSizeDimension1;
			this.activeSizeDimension2 = this.selectedSizeDimension2;
			this.setColorSwatches();
			this.setSizeDimensionSwatches(1, this.size1Swatches);
			this.setSizeDimensionSwatches(2, this.size2Swatches);
			this.setOnOrderMsgStatus("");
		}
		this.updateDataLabels();
	},

/**
 * updates the color name of the product
 * @author yoshi
 */
	setTextColor : function() {
		this.objTextColor.innerHTML =
				this.getTextColor(this.objV.arrayVariantStyleColors[this.activeColor].strColorName);
	},

/**
 * updates product dimension1 text
 * @author yoshi
 */
	setTextSizeDimension1 : function() {
		var strVariant = this.objV.strVariantName, str = '';
		if (this.objV.objStyleSizeInfo.intSizeDimensionsCount >= 1) {
			if (this.activeSizeDimension1 != -1) {
				str = this.arrayAllSizeDimension1[this.activeSizeDimension1].strName;
			}
			if (this.strRegularVariant != strVariant.toLowerCase()) str += " " + strVariant;
			this.objTextSizeDimension1.innerHTML = this.getSizeDimension1Text(str);
		}
	},

/**
 * updates product dimension2 div text
 * @author yoshi
 */
	setTextSizeDimension2 : function() {
		var strVariant = this.objV.strVariantName, str = "", objSid;
		if (this.objV.objStyleSizeInfo.intSizeDimensionsCount == 2) {
			if (this.activeSizeDimension2 != -1) {
				objSid = this.objV.arrayVariantStyleColors[this.activeColor];
				str = this.arrayAllSizeDimension2[this.activeSizeDimension2].strName;
			}
			//if (this.strRegularVariant != strVariant.toLowerCase()) str += " " + strVariant;
			this.objTextSizeDimension2.innerHTML = this.getSizeDimension2Text(str);
		}
	},

/**
 * sets the appropriate css class, i.e. soldOut, selected, etc, for color swatches
 * @author yoshi
 */
	setColorSwatches : function() {
		var hlpContext = this;
		/**
		 * sets appropirate btn states base on user selected color/size combo
		 * @param ele
		 * @param index
		 */
		function setColorSwatchesHelper(ele, index) {
			var isSizeAvailable, isOnOrder, isLowInventory, classState;
			var isSoldOut = false, isSelected = false;
			with(hlpContext) {
				var labelEle = $(ele).previous(0);	
				var labelEleContent = $(ele).readAttribute('alt');
				isSizeAvailable = isSkuInStock(index, activeSizeDimension1, activeSizeDimension2);
				isOnOrder = isSkuOnOrder(index, activeSizeDimension1, activeSizeDimension2);
				isLowInventory = isSkuLowInventory(index, activeSizeDimension1, activeSizeDimension2);
				isSelected = index == selectedColor;
			}
			isSoldOut = !(isSizeAvailable || isOnOrder || isLowInventory);
			classState = isSoldOut ? (isSelected ? "selectedSoldOut" : "soldOut") : (isSelected ? "selected" : "normal");

			ele.className = classState;

			if(isSoldOut){
				labelEle.update(labelEleContent + ' currently not available');
			}
			else if (isOnOrder) {
				var size1String, size2String
				with(hlpContext) {
					size1String = arrayAllSizeDimension1[activeSizeDimension1].strName;
					size2String = size2String = ((arrayAllSizeDimension2.length > 0) ? ' and ' + arrayAllSizeDimension2[activeSizeDimension2].strName : '');
				}
			    labelEle.update(labelEleContent + ' On order for size ' + size1String + size2String + '. Estimated ship date: ' + isOnOrder); 
			}
			else labelEle.update(labelEleContent);
		}
		this.colorButtons.each(setColorSwatchesHelper);
	},

/**
 * sets the appropriate css class, i.e. soldOut, selected, for size btns
 * @param {integer} dim size dimension
 * @param {object} e btn
 * @author yoshi
 */
	setSizeDimensionSwatches : function(dim, e) {
		if(dim == 2 && this.objV.objStyleSizeInfo.intSizeDimensionsCount < 2 ) return;
		this.setSizeDimension = dim;

		var hlpContext = this;
		/**
		 * sets btn states base on user selected color/size combo
		 * @param ele
		 * @param index
		 */
		function setSizeDimensionSwatchesHelper(ele, i) {
			with(hlpContext) {
				var labelEle = $(ele).previous(0);	
				var labelEleContent = $(ele).innerHTML;
				var activeDim = setSizeDimension == 1 ? activeSizeDimension2 : activeSizeDimension1;
				var selectedDim = setSizeDimension == 1 ? selectedSizeDimension1 : selectedSizeDimension2;
				var size1 = setSizeDimension == 1 ? i : activeDim;
				var size2 = setSizeDimension == 1 ? activeDim : i;
				var isSizeAvailable = isSkuInStock(activeColor, size1, size2);
				var isOnOrder = isSkuOnOrder(activeColor, size1, size2);
				var isLowInventory = isSkuLowInventory(activeColor,size1, size2);
				var skuReturnType = getReturnCode(activeColor,size1, size2);
				var classState = 'normal';
				if (isSizeAvailable || isOnOrder || isLowInventory) {
					classState = (i == selectedDim ? "selected" : "normal");
					if (isOnOrder) {
						labelEle.update('Size ' + labelEleContent + ' on order. Estimated ship date: ' + isOnOrder);
					}
					else labelEle.update('Select size ' + labelEleContent);
						
				} else {
					classState = (i == selectedDim ? "selectedSoldOut" : "soldOut");
					labelEle.update('Size ' + labelEleContent + ' currently not available');
				}
				ele.className = classState;
				
			}
		}
		this['size' + dim + 'Buttons'].each(setSizeDimensionSwatchesHelper);
	},

/**
 * checks sku color combo for availibility of product
 * @author yoshi
 */
	setSoldOut : function(type,index) {
		var isInStock = this.isSkuInStock(this.activeColor,this.activeSizeDimension1,this.activeSizeDimension2);
		var isOnOrder = this.isSkuOnOrder(this.activeColor,this.activeSizeDimension1,this.activeSizeDimension2);
		var isLowInventory = this.isSkuLowInventory(this.activeColor,this.activeSizeDimension1,this.activeSizeDimension2);

		if (this.activeSizeDimension1 == -1 || (this.objV.objStyleSizeInfo.intSizeDimensionsCount == 2 && this.activeSizeDimension2 == -1)) {
			this.showInventoryStatusWindow(false);
				this.setSoldOutMsgStatus("");
				this.setOnOrderMsgStatus("");
		} else {
			if (isOnOrder) {
				this.objInventoryStatusWindow.className = "quickLookOnOrderBkg";
				this.objInventoryStatusWindow.innerHTML = this.getOnOrderWindow(isOnOrder);
				this.showInventoryStatusWindow(true);
				this.setSoldOutMsgStatus("");
				if (type && this[type + 'Buttons'] && this[type + 'Buttons'][index]) {
					this.initializeOnOrderMsg(this[type + 'Buttons'][index]);
					this.setOnOrderMsgStatus(this.getOnOrderBanner());
				}

			} else if (isLowInventory) {
				this.objInventoryStatusWindow.className = "quickLookLowInventoryBkg";
				this.objInventoryStatusWindow.innerHTML = this.getLowInventoryWindow();
				this.showInventoryStatusWindow(true);
				this.setSoldOutMsgStatus("");
				this.setOnOrderMsgStatus("");
			} else if (isInStock){
				this.showInventoryStatusWindow(false);
				this.setSoldOutMsgStatus("");
				this.setOnOrderMsgStatus("");
			} else {
				this.showInventoryStatusWindow(false);
				this.initializeSoldOutMsg();
				this.setSoldOutMsgStatus(this.getSoldOutBanner());
				this.setOnOrderMsgStatus("");
			}
		}
	},

/**
 * shows the inventory status div
 * @param {boolean} doShow
 * @author yoshi
 */
	showInventoryStatusWindow : function(doShow){
		this.objInventoryStatusWindow.style.display = (doShow ? "block" : "none");
	},

/**
 * sets the sold out msg and show it
 * @param {string} msg
 * @author yoshi
 */
	setSoldOutMsgStatus : function(msg) {
		this.objSoldOutMsg.innerHTML = msg;
		this.objSoldOutMsg.setStyle({visibility : msg != '' ? 'visible' : 'hidden'});
	},

	setOnOrderMsgStatus : function(msg) {
		this.objOnOrderMsg.innerHTML = msg;
		this.objOnOrderMsg.setStyle({visibility : msg != '' ? 'visible' : 'hidden'});
	},

/**
 * checks if current sku color combo is availible, then sets add to bag btn state accordingly
 * @author yoshi
 */
	setInventoryStatus : function() {
		if (!this.objP.isIntlShip) {
			this.setAddtoBagState(true, true);
			return;
		}
		var isInStock = this.isSkuInStock(this.selectedColor,this.selectedSizeDimension1,this.selectedSizeDimension2);
		var isOnOrder = this.isSkuOnOrder(this.selectedColor,this.selectedSizeDimension1,this.selectedSizeDimension2);
		var isLowInventory = this.isSkuLowInventory(this.selectedColor,this.selectedSizeDimension1,this.selectedSizeDimension2);

		if (this.selectedSizeDimension1 == -1 || (this.objV.objStyleSizeInfo.intSizeDimensionsCount == 2 && this.selectedSizeDimension2 == -1)) {
			this.setAddtoBagState(true);
			this.objAddtoBag.alt = this.strAddToBagAltText;
		} else {
			if (isOnOrder || isLowInventory || isInStock){
				this.setAddtoBagState(true);
				this.objAddtoBag.alt = this.strAddToBagAltTextActive;
			} else {
				this.setAddtoBagState(false);
				this.objAddtoBag.alt = this.strAddToBagAltText;
			}
		}
	},

/**
 * generate html for the overview tab on quickLook and outfit, as well as product details on product page
 * @author yoshi
 */
    getOverViewTabHTML : function() {
        var content = {};
        var romanceCopy = '';
        var romanceCopyTab = this.objP.arrayInfoTabs[0];
        if(romanceCopyTab) {
        	romanceCopy = romanceCopyTab.strInfoTabDescription;
        }
        var copyContent = [];
        var copyName = brandConst.CopyBox.GROUP_NAME;
        var infoBlockNew = this.templates.INFO_BLOCK_NEW, infoBlockOld = this.templates.INFO_BLOCK_OLD, copyNameDefault = brandConst.CopyBox.GROUP_NAME_DEFAULT;
        var hlpContext = this;

        /**
         * helper function for get getOverViewTabHTML, gets the tab html
         * @param {object} obj accumulator obj
         * @param {object} tab current tab
         * @see getOverViewTabHtML
         * @author yoshi
         */
        function getGroupName(str) {
            if(str.match(/overview/i)) return 'Overview';
            if(str.match(/details/i)) return 'Details';
            if(str.match(/fabric/i)) return 'Fabric';
            if(str.match(/fit/i)) return 'Fit';
            if(str.match(/tips/i)) return 'Tips';
            if(str.match(/care/i)) return 'Care';
        }

		function getTabContentHelper(obj, tab) {
			obj[getGroupName(tab.strInfoTabName)] = hlpContext.getTabContent(tab);
			return obj;
		}

		function getOverviewTabHelper(str, value) {
			if( content[value] != '' ) {
				str.push(infoBlockNew.evaluate({name : copyName[value.replace(/\s/g, '')] ? copyName[value] : copyNameDefault, content : content[value]}));
			}
			return str;
		}

		// set 'Fabric' so it appears at the top of the list.
		content['Fabric'] = (content['Fabric']||'') ;
		//AT specific, put 'Fit' as the 2nd of the list
		if (this.brandCode == '10'){
			content['Fit'] = (content['Fit']||'') ;
		}
		// populate content with default data
		content = this.objP.arrayInfoTabs.inject(content, getTabContentHelper);
		// modify the 'Fabric' content to add fabric & care details.
		content['Fabric'] = this.getFabricCare(content['Fabric']||'');
		// Suppress the output of Details, Tips, and Additional Care
		content['Details']= '';
		content['Tips'] = '';
		content['Care']= '';

		if(this.type == 'productPage') {
			if( romanceCopy != '') {
				replaceHTML($('quickLookProductDescription'), '<p class="description">'+ romanceCopy + '</p>').style.marginTop = '8px';
			}
			if(this.objP.objCrossSellInfo && this.objP.objCrossSellInfo.arrayProducts.length > 0) {
				replaceHTML($('crossSellTabWindow'), this.getCrossSellTab());
				this.objCrossSellTab.style.display = "block";
			}
		} else {
			copyContent.push(romanceCopy != '' ? '<p class="description">'+ romanceCopy + '</p>' : '') ;
		}

		Object.keys(content).inject(copyContent, getOverviewTabHelper);
		return copyContent.join('');
	},

	PopWindowObjMap : {productImages:'objProductImagesWindow', productInfo:'objInfoPopUp', sizeChart:'objSizeChartWindow'},

/**
 * this handles pop window
 * @param {string} id name of the child window
 * @param {string} link the url
 * @param {string} params child window params
 * @author yoshi
 */
	popWindow : function(id, link, params) {
		if (this.isLoaded) {
			Object.keys(this.PopWindowObjMap).each(function(name) {
				var childWindow = GID.Browse.Pops[name];
				if(childWindow && !childWindow.window.closed) { childWindow.window.close();}
			});

			var newWin = window.open(link, id, params);
			GID.Browse.Pops[id] = {window: newWin, app : this.type};
			newWin.focus();
		}
	},

/**
 * opens view larger window
 * @author yoshi
 */
	openProductImages : function() {
		var l = (window.screenLeft + 5) || (window.screenX + 5);
		var t = 20;
		this.popWindow('productImages', '/browse/productImages.do', "toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes,width="+this.MOREVIEWSPOPUPWIDTH+",height=768,left="+ l +",top="+ t );
	},

/**
 * opens size char window
 * @author yoshi
 */
	openSizeChart : function() {
		if (!this.objP.sizeChartId || this.objP.sizeChartId == "") { this.objP.sizeChartId = this.DEFAULTSIZECHARTID; }
		this.popWindow('sizeChart', (this.brandCode != gidBrandSiteConstruct.currentBrandCode ? this.brandSite.unsecureUrl : '') + "/browse/sizeChart.do?cid="+ this.objP.sizeChartId, "toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=no,width="+this.SIZECHARTWINDOWWIDTH+",height="+this.SIZECHARTWINDOWHEIGHT);
	},

/**
 * opens a general pop
 * @param {string} strLink  the url
 * @param {integer} w width
 * @param {integer} h height
 */
	openInfoPopUp : function(strLink,w,h) {
		this.popWindow('productInfo', strLink, "toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes,width="+w+",height="+h);
	},

/**
 * initialize the err divs
 * @author yoshi
 */
	initializeFlagErrors : function() {
		var colorPos = Position.cumulativeOffset(this.objTextColor), dimCount = this.objV.objStyleSizeInfo.intSizeDimensionsCount;
		var colorErrMsgDim = this.objColorErrorMsg.getDimensions();
		var dim1Pos = Position.cumulativeOffset(this.objTextSizeDimension1), dim1ErrMsgDim = this.objSizeDimension1ErrorMsg.getDimensions();

		this.objColorErrorMsg.setStyle({left:colorPos[0] - colorErrMsgDim['width'] + 'px',
			top:colorPos[1] + 'px',
			display:'block'});

		this.objSizeDimension1ErrorMsg.setStyle({left:dim1Pos[0]-dim1ErrMsgDim['width'] + 'px',
			top: dim1Pos[1] + 'px',
			display:'block'});

		if(dimCount == 2 ) {
			var dim2Pos = Position.cumulativeOffset(this.objTextSizeDimension2), dim2ErrMsgDim = this.objSizeDimension2ErrorMsg.getDimensions();
			this.objSizeDimension2ErrorMsg.setStyle({left:dim2Pos[0]-dim2ErrMsgDim['width'] + 'px',
				top: dim2Pos[1] + 'px',
				display:'block'});
		}
	},

/**
 * check the see if current sku color combo is availible, validate user input
 * @author yoshi
 */
	checkErrors : function() {
		if (this.intTab == 1 && this.isQuickLookOpen) this.setTab(0); //switches to swatches when displaying errors for quicklook.
		this.initializeFlagErrors();
		var isColor = this.isColorError = this.selectedColor == -1;
		var dimCount = this.objV.objStyleSizeInfo.intSizeDimensionsCount;
		var isDim1Err = this['selectedSizeDimension1'] == -1;

		if (isColor) {
			$('productColorErrorMain').addClassName("productErrorMain");
			$('productColorErrorArrow').addClassName("productErrorArrow");
		}
		this.objColorErrorMsg['style']['visisbility'] = isColor ? 'visible' : 'hidden';
		
		this.isSizeDimension1Error = isDim1Err;

		if (isDim1Err) {
			$('productSizeDimension1ErrorMain').addClassName("productErrorMain");
			$('productSizeDimension1ErrorArrow').addClassName("productErrorArrow");
		}

		this.objSizeDimension1ErrorMsg['style']['visibility'] = isDim1Err ? 'visible' : 'hidden';
		
		if (dimCount == 2) {
			this.isSizeDimension2Error = isDim2Err;
			var isDim2Err = this.selectedSizeDimension2 == -1;

			if (isDim2Err) {
				$('productSizeDimension2ErrorMain').addClassName("productErrorMain");
				$('productSizeDimension2ErrorArrow').addClassName("productErrorArrow");
			}
			
			this.objSizeDimension2ErrorMsg['style']['visibility'] = isDim2Err ? 'visible' : 'hidden';
		}
		this.setBagError();
	},

/**
 * calculates the div position and shows err msgs
 * @author yoshi
 */
	setBagError : function() {
		this.objBagErrorMsg.innerHTML = this.getBagError();
		var dx = $("bagErrorLeft").offsetWidth + $("bagErrorMiddle").offsetWidth + $("bagErrorRight").offsetWidth;
		var pos = Position.cumulativeOffset(this.objAddtoBag);
		var dim = this.objAddtoBag.getDimensions();
		this.objBagErrorMsg.setStyle({left:pos[0]-dx+dim['width'] + 'px', top:pos[1]+dim['height'] + 'px'});

		var showErrMsg = this.isColorError || this.isSizeDimension1Error || this.isSizeDimension2Error;
		this.objBagErrorMsg['style']['visibility'] = showErrMsg ? 'visible' : 'hidden';
		this.objAddtoBag.alt=resourceBundleValues.product.colorAndSizeSelectError;
	},

/**
 * handles when user clicks add to bag btn, fires ajax call to server.
 * ajax returns html to form dropdown bag after eval
 * @author yoshi
 */
	addToBag : function() {
		if (objInlineBag.isOpen || objInlineBag.isAnimating) {
			setTimeout(this.addToBag.bind(this), 250);
		} else {
			this.checkErrors();
			if (!this.isColorError &&
				!this.isSizeDimension1Error &&
				!this.isSizeDimension2Error &&
				(this.isSkuInStock(this.selectedColor,this.selectedSizeDimension1,this.selectedSizeDimension2) || this.isSkuOnOrder(this.selectedColor,this.selectedSizeDimension1,this.selectedSizeDimension2) || this.isSkuLowInventory(this.selectedColor,this.selectedSizeDimension1,this.selectedSizeDimension2))) {

				document.fire("addToBag:request");

				if (this.objV.objStyleSizeInfo.intSizeDimensionsCount >= 1) {
					var strColorId = this.objV.arrayVariantStyleColors[this.selectedColor].strColorCodeId;
					var strSize1Id = this.arrayAllSizeDimension1[this.selectedSizeDimension1].strId;
					var strSize2Id = "";
				}
				if (this.objV.objStyleSizeInfo.intSizeDimensionsCount == 2) {
					strSize2Id = this.arrayAllSizeDimension2[this.selectedSizeDimension2].strId;
				}
				var strSkuID = this.objV.arrayVariantSkus[strColorId + "_" + strSize1Id + "_" + strSize2Id].strSkuId;

				var inlineBagUrl = this.templates.INLINE_BAG_URL.evaluate({sku:strSkuID, qty:this.strSelectedQty, cid:this.strDefaultCategoryId});
				if (this.hasInlineBagErrors && this.arrayAddtoBagErrors.get(this.objP.strCatalogItemId) &&
                    this.arrayAddtoBagErrors.get(this.objP.strCatalogItemId) == 1) {
					inlineBagUrl += "&addOnOrderItem=true";
					this.hasInlineBagErrors = false;
				}
				objInlineBag.doOpenBag = true;

				// --------------------------------------
				// AJAX INLINE BAG DATA LOADER
				new Ajax.Request(
					inlineBagUrl,
					{
						method: 'get',
						onComplete: this.parseInlineBagAjaxResponse.bind(this),
						onException: function(e) { if(console) {console.log(e.message); } throw e; }
					}
				);
				// ---------------------------------------
				this.setAddtoBagState(true, true);
			}
		}
	},

/**
 * loads the inline bag html, check for errs, then display it
 * @author yoshi
 */
	loadInlineBag : function() {
        var errors = GID.Buy.Errors.addToBagErrors||[];
        var hasErrs = errors.size() > 0;
        var inlineBagData = GID.Buy.AddToBagData;

        if(hasErrs) {
            this.doAddToBagErrors(($('pageError')||{}).innerHTML, errors);
        }

             if(!hasErrs && inlineBagData){
				var data = objInlineBag.model.data;

				// Create the line item markup
				var lineItems = data.inlineBagItems;
				var lineItemMarkup = '';

				lineItems.each(function(lineItem, count) {

					var imageContent = '';
					if (lineItem.imageURI) {
						var productImageClass = "productThumbnail";
                        if (lineItem.brandCode == '10') {
							productImageClass = "athletaProductThumbnail";
                        }
						imageContent = '<a href="' + lineItem.productLink.unescapeHTML() + '"><img src="/' + lineItem.imageURI + '" class="' + productImageClass + '" alt="' + lineItem.styleDescription + '" /></a>';
					}

					var productNameHref = '';
					var styleDescription = '';

					if (lineItem.styleDescription) {
						productNameHref = lineItem.productLink.unescapeHTML();
						styleDescription = lineItem.styleDescription.unescapeHTML();
					}

					var color = '';
					if (lineItem.colorDescription) {
						color = lineItem.colorDescription;
					}

					var size = '';
					if (lineItem.skuDescription) {
						size = lineItem.skuDescription;
					}

					var listPrice = '';
					var discountedPrice = '';
					var sellPrice = '';
	 				if (lineItem.sellPrice) {
						if (lineItem.listPrice != lineItem.promoDiscountedPrice) {
							listPrice = lineItem.listPrice;
							discountedPrice = lineItem.promoDiscountedPrice;

						} else {
							sellPrice = lineItem.sellPrice;
						}
					}

	 				var inventoryStatusMessageString = '';
	 				if (lineItem.inventoryLevel && lineItem.inventoryLevel == 4) {
	 					var inlineOnOrderMessage = lineItem.onOrderMessage;
		 				var inlineOnOrderDate = '';
	 					if (lineItem.estimatedShipDate) {
	 						inlineOnOrderDate = lineItem.estimatedShipDate.replace(/\s/g,"&nbsp;");
	 					}
	 					inventoryStatusMessageString =
	 						'<li class="inlineInventoryStatus">' + inlineOnOrderMessage +
	 						' <div class="inlineInventoryStatusDate">' + inlineOnOrderDate + '</div>' +
	 						'</li>';
	 				}

					var lineItemContent = {
						count:			   count,
						badgeSrc:		   lineItem.brandBadgeSrc,
						badgeAlt:		   lineItem.brandBadgeAlt,
						imageContent:	   imageContent,
						sku:			   lineItem.sku,
						productNameHref:   productNameHref,
						styleDescription:  styleDescription,
						color:			   color,
						size:			   size.unescapeHTML(),
						quantity:		   lineItem.quantity,
						listPrice:		   listPrice,
						discountedPrice:   discountedPrice,
						sellPrice:		   sellPrice,
						subTotal:		   lineItem.itemSubtotal,
						labelColor:		   inlineBag.labelColor,
						labelSize:		   inlineBag.labelSize,
						labelQuantity:     inlineBag.labelQuantity,
						labelPrice:        inlineBag.labelPrice,
						inventoryStatusMessage: inventoryStatusMessageString
					};

					lineItemMarkup += GID.Browse.Base.templates.LINE_ITEM.evaluate(lineItemContent);

					//set message for ScreenReader
					message = GID.Browse.Base.templates.TEXTREADERMESSAGE.evaluate(lineItemContent);
					inlineBag.setInLineBagMessage(message.unescapeHTML());


					$('lineItems').update(lineItemMarkup);
				});

				var itemCount = data.itemCount;
				if (itemCount > 1) {
					itemCount += ' ' +inlineBag.itemInPlural + ' ' +inlineBag.lineItemBag; //" items in bag";
				} else {
					itemCount += ' ' +inlineBag.itemInSingular + ' ' +inlineBag.lineItemBag; //" item in bag";
				}
				$('inlineBagSummaryItemCount').update(itemCount);

				$('inlineBagSummarySubPriceLabel').update(data.subTotal);

				objInlineBag.setInlineShoppingBagData(
						inlineBagData.strInlineBagTextClosed + inlineBagData.strBagTextClosedSuffix,
						null,
						inlineBagData.strInlineBagTextOpen);

				this.doAddToBagSuccess(hasErrs);


				// If there is a promo or crosssell
				var inlineBagMarketingContainer = $('gidInlineBagMarketingContainer');
				if(data.addToBagData && data.addToBagData.hasPromotionOrCrossSell == "true") {
					objInlineBag.setInlineShoppingBagMarketing(null);
					inlineBagMarketingContainer.setStyle({display:"none"});
				} else if (inlineBagMarketingContainer && !inlineBagMarketingContainer.empty()){
					objInlineBag.setInlineShoppingBagMarketing(null);
					inlineBagMarketingContainer.setStyle({display:"block"});
				}

                // (Re)Hide all the potential promo types first
				$('gidProductUpsellMessage').hide();
				$('mupUpsellMessage').hide();
				$('gwpPwpSelectionMessage').hide();
				$('selImageLink').hide();
				$('staticPromoTxt').hide();
                $('promoSelLink').hide();

				if (data.hasPromo == "true") {
					var promoType = data.promoType;

					if (promoType == "gidProductUpsellMessage") {
						$('gidProductUpsellMessage').show();

						$('productUpsellMsg').update(data.prodUpSellMsg.unescapeHTML());
						var link = $('productUpsellLink');
						link.onclick = data.promoURI
						link.update(data.promoLinkText);

					} else if (promoType == "mupUpsellMessage") {
						$('mupUpsellMessage').show();
						$('mupUpSellMsg').update(data.upSellMsg);
						$('mupUpSellLink').update(data.upSellLink.unescapeHTML());

					} else if (promoType == "gwpPwpSelectionMessage") {
						$('gwpPwpSelectionMessage').show();

						if (data.selImageURI) {
							$('selImageLink').show();
							$('selImageLink').src = data.selImageURI;
						}

						$('gwpPwpMsg').update(data.gwpPwpMsg);

						var link = $('promoLink');
						link.onclick = data.promoURI;
						link.update(data.promoLinkText);

						if (data.isAutoAdded == "true") {
							$('staticPromoTxt').show();
						} else {
							var selLink = $('promoSelLink');
							selLink.show();
							selLink.update(data.selLink.unescapeHTML());
						}
					}
				} else {
					$('inlineBagPromo').hide();
				}

				if (objInlineBag.model.data.hasCrossSell == "true") {

					Event.observe('inlineBagCrossSell', 'click', function(){window.location.href=objInlineBag.model.data.crossSellURI;});

					if (objInlineBag.model.data.crossSellImagePath) {
						$('crossSellImg').src = objInlineBag.model.data.crossSellImagePath;
					} else {
						$('crossSellImg').hide();
					}

					$('crossSellOutfitMsg').update(objInlineBag.model.data.crossSellOutfitMsg);

					$('inlineBagCrossSell').show();

				} else {
					$('inlineBagCrossSell').hide();
				}
			}

	},

/**
 * parses the add to bag ajax response, then calls loadInlineBag for user feedback
 * @param {object} transport
 * @see loadInlineBag
 * @author yoshi
 */
	parseInlineBagAjaxResponse : function(transport) {
		var response = null;

		response = transport.responseText.replace(/(\n|\r)/g,"");
		try {
			var responseJSON = response.evalJSON();
			objInlineBag.model.data = responseJSON.inlineBagModelData;
			this.updateForInlineBagAjaxResponse();
			setTimeout(this.loadInlineBag.bind(this), 100);
		} catch(e) {
			window.location.href="/";
		}


	},

/**
 * Update javascript data structures and omniture based upon the
 * AJAX response
 * @author Aaron
 */
	updateForInlineBagAjaxResponse : function() {
		if(!(reportingService||{}).isActive){
			if (omni.objDebug.isDebugEnabled && (omni.objDebug.isInlineBagAddDebugEnabled || omni.objDebug.isActivateAllDebugEnabled)) {
				alert("InlineBagAdd Variables:\n\r" +
						"s_pageName = " + (window["s_pageName"] ? s_pageName : "") + "\n\r " +
						"s_products = " + (window["s_products"] ? s_products : "") + "\n\r " +
						"s_eVar5 = " + (window["s_eVar5"] ? s_eVar5 : "") + "\n\r" +
						"s_eVar6 = " + (window["s_eVar6"] ? s_eVar6 : "") + "\n\r" +
						"s_eVar8 = " + (window["s_eVar8"] ? s_eVar8 : "") + "\n\r" +
						"s_eVar13 = " + (window["s_eVar13"] ? s_eVar13 : "") + "\n\r" +
						"s_prop8 = " + (window["s_prop8"] ? s_prop8 : "") + "\n\r" +
						"s_prop9 = " + (window["s_prop9"] ? s_prop9 : "") + "\n\r" +
						"s_prop10 = " + (window["s_prop10"] ? s_prop10 : "") + "\n\r" +
						"s_hier1 = " + (window["s_hier1"] ? s_hier1 : "") + "\n\r" +
						"s_channel = " + (window["s_channel"] ? s_channel : "") + "\n\r" +
						"s_events = " + (window["s_events"] ? s_events : "") + "\n\r");
			}
		}
		//if(!GID.Buy) {GID.Buy = {Errors : {}, AddToBagData : {}}; }

		{GID.Buy = {Errors : {}, AddToBagData : {}}; }

		var data = objInlineBag.model.data;

		// Omni related code to track add event.
		if (data.omniInlineBagJS) {
			if(!(reportingService||{}).isActive){
				omni.setAddToBag(data.omniAddToBag, data.omniInlineBagJS);
			}
			else {
				reportingService.controller.viewManagers.inlineBagAddViewManager.controller.getReportRequest(data.omniAddToBag, data.omniInlineBagJS);
			}
		}

		/* line item errors */
		if (data.errorAddToBag) {
            var pageErr = this.objInlineBagError;
            if(pageErr) {
                pageErr.update(data.errorAddToBag.errorMsg);
                pageErr.style.display = 'block';
            }

			GID.Buy.Errors.addToBagErrors = new Hash();

			if (data.errorAddToBag.errors != null) {
				data.errorAddToBag.errors.each(function(newError) {
					GID.Buy.Errors.addToBagErrors.set(newError.key, newError.value);
				});

			}

			var quantity = data.itemCount;

			var inlineBagData = GID.Buy.AddToBagData;

			if (quantity == 1) {
				inlineBagData.strInlineBagTextClosed = quantity + ' '+inlineBag.itemInSingular+'  ';
				inlineBagData.strInlineBagTextOpen = inlineBag.addedTo + ' <a href="/buy/shopping_bag.do">'+inlineBag.yourBag+'</a>';
			} else {
				inlineBagData.strInlineBagTextClosed = quantity + ' '+inlineBag.itemInPlural+'  ';
				inlineBagData.strInlineBagTextOpen = inlineBag.addedTo + ' <a href="/buy/shopping_bag.do">'+inlineBag.yourBag+'</a>';
			}
			inlineBagData.strBagTextClosedSuffix = '<a href="/buy/shopping_bag.do">'+inlineBag.yourBag+'</a>';

		} else {
			var quantity = data.addToBagData.inlineBagQty;

			var inlineBagData = GID.Buy.AddToBagData;

			if (quantity == 1) {
				inlineBagData.strInlineBagTextClosed = quantity + ' '+inlineBag.itemInSingular+'  ';
				inlineBagData.strInlineBagTextOpen = inlineBag.addedTo + ' <a href="/buy/shopping_bag.do">'+inlineBag.yourBag+'</a>';
			} else {
				inlineBagData.strInlineBagTextClosed = quantity + ' '+inlineBag.itemInPlural+'  ';
				inlineBagData.strInlineBagTextOpen = inlineBag.addedTo + ' <a href="/buy/shopping_bag.do">'+inlineBag.yourBag+'</a>';
			}

			inlineBagData.hasPromotionOrCrossSell = data.addToBagData.hasPromotionOrCrossSell;
			inlineBagData.strBagTextClosedSuffix = '<a href="/buy/shopping_bag.do">'+inlineBag.yourBag+'</a>';
		}

		document.fire("addToBag:load");
	},

/**
 * checks if sku is in stock for given color and size selection
 * @param {integer} color
 * @param {integer} size1
 * @param {integer} size2
 * @param {integer} n
 * @author yoshi
 */
	isSkuInStock : function(color,size1,size2,n) {
		var isInStock = true, isColorInStock = false;
		var objItem = this.getVariantSku(color,size1,size2,n);
		if (n && this.arrayProducts && this.arrayProducts[n]) {
			isColorInStock = (this.arrayProducts[n].objV.arrayVariantStyleColors[color] ? this.arrayProducts[n].objV.arrayVariantStyleColors[color].isInStock : false);
		} else {
			isColorInStock = (this.objV.arrayVariantStyleColors[color] ? this.objV.arrayVariantStyleColors[color].isInStock : false);
		}
		if ((!objItem && (size1 != -1 || size2 != -1)) || !isColorInStock) {
			isInStock = false;
		}
		return isInStock;
	},

/**
 * checks if current product selections is on order
 * @param color
 * @param size1
 * @param size2
 * @param n
 * @author yoshi
 */
	isSkuOnOrder : function(color,size1,size2,n) {
		var isOnOrder = undefined;
		var objSku = this.getVariantSku(color,size1,size2,n);
		if (objSku && objSku.isOnOrder) isOnOrder = objSku.strOnOrderDate;
		return isOnOrder;
	},

/**
 * checks if current product selection is low inventory
 * @param color
 * @param size1
 * @param size2
 * @param n
 * @author yoshi
 */
	isSkuLowInventory : function(color,size1,size2,n) {
		var isLowInventory = false;
		var objSku = this.getVariantSku(color,size1,size2,n);
		if (objSku) isLowInventory = objSku.isLowInventory;
		return isLowInventory;
	},
/**
 * checks if current product selection has return limitations
 * @param color
 * @param size1
 * @param size2
 * @param n
 * @author aliebling
 */
	getReturnCode : function(color,size1,size2,n) {
		var strReturnCode = "";
		var objSku = this.getVariantSku(color,size1,size2,n);
		if (objSku) strReturnCode = objSku.strAllowableReturnCode;
		return strReturnCode;
	},
/**
 * automatically lauch quickLook
 * @param strProductId
 */
	autoLoadProduct : function(strProductId) {
		if (objGIDPageViewAdapter.objGIDProducts.arrayProducts[strProductId] != null)  {
			this.launchQuickLook();
		} else {
			setTimeout("quickLook.autoLoadProduct("+strProductId+")",100);
		}
	},

/**
 * updates html for selected product attributes
 * @author yoshi
 */
	setSelectionConfirmText : function() {
		var objSid = this.objV.arrayVariantStyleColors[this.selectedColor];
		var strColorName = objSid.strColorName;
		var strSizeDimension1 = "", strSizeDimension2 = "", strVariantName = this.objV.strVariantName;

		if (this.selectedSizeDimension1 != -1) {
			strSizeDimension1 = this.arrayAllSizeDimension1[this.selectedSizeDimension1].strName;
		}
		if (this.selectedSizeDimension2 != -1) {
			strSizeDimension2 = this.arrayAllSizeDimension2[this.selectedSizeDimension2].strName;
		}
		this.objSelectedConfirmText.innerHTML = this.getSelectionConfirmText(strColorName,strSizeDimension1,strSizeDimension2,strVariantName);
	},

/**
 * get all the variants skus for current product
 * @param color
 * @param size1
 * @param size2
 * @param n
 * @author yoshi
 */
	getVariantSku : function(color,size1,size2,n) {
		var product, strColorId, strSize1Id, strSize2Id, strMap, objSku;

		if(n && this.arrayProducts[n]) {
			product = this.arrayProducts[n];
			strColorId = (product.objV.arrayVariantStyleColors[color] ? product.objV.arrayVariantStyleColors[color].strColorCodeId : "");
			strSize1Id = (size1 != -1 ? product.arrayAllSizeDimension1[size1].strId : "");
			strSize2Id = (size2 != -1 ? product.arrayAllSizeDimension2[size2].strId : "");
			strMap = strColorId + "_" + strSize1Id + "_" + strSize2Id;
			objSku = product.objV.arrayVariantSkus[strMap];
		} else {
			strColorId = (this.objV.arrayVariantStyleColors[color] ? this.objV.arrayVariantStyleColors[color].strColorCodeId : "");
			strSize1Id = (size1 != -1 ? this.arrayAllSizeDimension1[size1].strId : "");
			strSize2Id = (size2 != -1 ? this.arrayAllSizeDimension2[size2].strId : "");
			strMap = strColorId + "_" + strSize1Id + "_" + strSize2Id;
			objSku = this.objV.arrayVariantSkus[strMap];
		}
		return objSku;
	},

	/**
	 * setter for price text
	 * @authoer yoshi
	 */
	setPriceText : function() {
		var strPrice, strSalePrice;
		if (this.objP.isGiftCard) {
			var objSku = this.getVariantSku(this.selectedColor,this.selectedSizeDimension1,this.selectedSizeDimension2);
			if (objSku && objSku.strRegularPrice) {
				strPrice = objSku.strRegularPrice;
				strSalePrice = (this.objV.arrayVariantStyleColors[this.selectedColor].isOnSale ? objSku.strCurrentPrice : "" );
				this.objPriceText.innerHTML = this.getPriceText(strPrice,strSalePrice);
			}
		} else {
			var objSid = this.objV.arrayVariantStyleColors[this.selectedColor];
			strPrice = objSid.strRegularPrice;
			strSalePrice = (objSid.isOnSale ? objSid.strSalePrice : "" );
			this.objPriceText.innerHTML = this.getPriceText(strPrice,strSalePrice);
		}
	},

	/**
	 * check to see if theres MUP message attached to the current product, then sets the style accordingly
	 * @author yoshi
	 */
	initializePromotions : function() {
		if (this.objP.strMupMessage && this.objP.strMupMessage.length > 0) {
			this.objStyleMupMessage.innerHTML = this.objP.strMupMessage;
			this.objStyleMupMessage.style.display = "block";
		}
	},

	/**
	 * initialize sold out msg div css position
	 * @author yoshi
	 */
	initializeSoldOutMsg : function(target) {
		var pos = Position.cumulativeOffset(this.p1ImageHolder);
		this.objSoldOutMsg.setStyle({
			left : pos[0]+this.SOLDOUTMSGDX + 'px',
			top: pos[1]+this.SOLDOUTMSGDY + 'px' });
	},

	initializeOnOrderMsg : function(target) {
		if (target) {
			var pos = Position.cumulativeOffset(target);
			this.objOnOrderMsg.setStyle({
				left : pos[0] + brandProperties.ONORDERMSGDX + target.offsetWidth/2+ 'px',
				top: pos[1] + brandProperties.ONORDERMSGDY + 'px' });
		}
	},

	/**
	 * poputlates the select qty dropdown box
	 * @author yoshi
	 */
	setQtyDropDown : function() {
		this.objQtyDropDown.options.length = 0;
		var maxQty = Math.max(this.objP.intMaxQuantity, this.strSelectedQty);
		for ( var i=1;i<= maxQty;i++) {
			this.objQtyDropDown.options[this.objQtyDropDown.length] = new Option(i,i);
		}
		this.objQtyDropDown.selectedIndex = Number(this.strSelectedQty)-1;
	},

	/**
	 * save select qty value from dropdown
	 * @author yoshi
	 */
	setSelectedQty : function() {
		this.strSelectedQty = this.objQtyDropDown.options[this.objQtyDropDown.selectedIndex].value;
	},

	/**
	 * Constructs the productDataUrl for a given request.
	 * @author Andrew Southwick
	 */
	getProductDataUrl:function(strProductId, strVariantId, strStyleColorId, currentLocale) {
		var productDataUrl =
			this.templates.PRODUCT_DATA_URL.evaluate({pid:strProductId||'', vid:strVariantId||'',
				scid:strStyleColorId||'', type:this.strDisplayType ? '&displayType=' + this.strDisplayType : '',
						actFltr:this.isFromSBS, localeCode:currentLocale});
		if (brandConst.CATALOG_2_ACTIVE == 'true') {
			var productStyleSourceFlag = getQuerystringParam("productStyleSourceFlag");
			if (productStyleSourceFlag != "") {
				productDataUrl = productDataUrl + "&productStyleSourceFlag=" + productStyleSourceFlag;
			}
		}
		var globalShippingCurrencyCode = "";
		var globalShippingCountryCode = "";
		if (personalizationService.model.personalizationData["personalizationInfoV1"]) {
			var userContext = personalizationService.model.personalizationData.personalizationInfoV1.userContext;
			if (userContext["globalShippingCurrencyCode"]) {
				globalShippingCurrencyCode = userContext.globalShippingCurrencyCode;
			}
			if (userContext["globalShippingCountryCode"]) {
				globalShippingCountryCode = userContext.globalShippingCountryCode;
			}
		}
		productDataUrl = productDataUrl
					+ "&internationalShippingCurrencyCode=" + globalShippingCurrencyCode
					+ "&internationalShippingCountryCode=" + globalShippingCountryCode;
		if (globalShippingCurrencyCode != "") {
			productDataUrl = productDataUrl + "&globalShippingCurrencyCode=" + globalShippingCurrencyCode;
		}
		if (globalShippingCountryCode != "") {
			productDataUrl = productDataUrl + "&globalShippingCountryCode=" + globalShippingCountryCode;
		}
		return productDataUrl;
	},

	/**
	 * fires ajax request to productData.do. not using the default callback function because the return script could contain
	 * a <scriptHolder> tag, which replaces the <script> tag to get around NIS
	 *
	 * depends on whether the current product is from the current brand, instead of an ajax request, it creates dynamic script
	 * tag to load in the data for cross domain data fetch, such as BR requesting ON product info
	 *
	 * @param strProductId
	 * @param dataLoaderAction
	 * @param strVariantId
	 * @param strStyleColorId
	 * @param productBrandCode
	 */
	loadProductData : function(strProductId,dataLoaderAction,strVariantId,strStyleColorId,productBrandCode) {
		if( strProductId == 0 ) return;
		this.productId = strProductId; this.variantId = strVariantId;
		this.dataLoaderAction = dataLoaderAction;
		var currentLocale = personalizationService.model.personalizationData.personalizationInfoV1.userContext.localeCode;
		var productDataUrl = this.getProductDataUrl(strProductId, strVariantId, strStyleColorId, currentLocale);
		if (!productBrandCode || productBrandCode == gidBrandSiteConstruct.currentBrandCode ) {

			document.fire("productPage:dataRequest");

			new Ajax.Request(
				productDataUrl,
				{
					method: 'get',
					onComplete: this.parseProductDataAjaxResponse.bind(this),
                    onException: function(e) { if(console) {console.log(e.message); } throw e; }
				}
			);
		} else {
			var gidBrand = gidBrandSiteConstruct.gidBrandSites[parseInt(productBrandCode)];
			if (gidBrand) {
				var args = {strProductId:strProductId};
				gidLib.loadScript({
					callerObject:this,
					src:gidBrand.unsecureUrl + productDataUrl,
					timeout:{
						handler:this.loadScriptTimeoutHandler.bind(this),
						args:args,
						timeDelay:20
					},
					callback:{
						handler:this.loadScriptCallbackHandler.bind(this),
						args:args,
						timeDelay:0.1
					}
				});
			}
		}
	},

	/**
	 * err handler when cross domain product load times out
	 * @param args
	 */
	loadScriptTimeoutHandler:function(args) {
		var timedOut = false;
		if (!objGIDPageViewAdapter.objGIDProducts.arrayProducts[args.strProductId]) {
			alert("foreign url failed to load");
			timedOut = true;
		}
		return timedOut;
	},

	/**
	 * call back function for successful cross domain data fetch
	 * @param args
	 * @param periodicalExecuterRef
	 */
	loadScriptCallbackHandler:function(args,periodicalExecuterRef) {
		var loadSuccess = false;
		if (objGIDPageViewAdapter.objGIDProducts.arrayProducts[args.strProductId]) {
			this.dataLoaderCall();
			periodicalExecuterRef.stop();
			loadSuccess = true;
		}
		return loadSuccess;
	},

	/**
	 * initialize data using the correct data loader action
	 * initial - product page
	 * auto - quicklook
	 * variant - variant
	 * next - outfit?
	 */
	dataLoaderCall : function() {
		switch(this.dataLoaderAction) {
				case "initial":
					this.initializeProduct(this.productId);
					break;
				case "auto":
					this.autoLoadProduct(this.productId);
					break;
				case "next":
					this.loadNextProduct();
					break;
				case "variant":
					this.loadVariantProduct(this.productId, this.variantId);
					break;
		}
	},

	/**
	 * call back function for product data ajax request
	 * @param transport
	 */
	parseProductDataAjaxResponse : function(transport) {
		document.fire("productPage:dataLoad");

		(function addTestAutomationElement() {
            var dataLoadedIndicator = $('product_data_loaded');
            var documentBody = document.body;
            if(dataLoadedIndicator) {
            	dataLoadedIndicator.remove();
            }
            if(!documentBody.insert) {
            	documentBody = Element.extend(documentBody);
            }
            documentBody.insert('<span id="product_data_loaded"/>');
		})();

		var response = transport.responseText;
		eval(response);

		if(GID.Browse.Errors.isProductDataError) {
			this.productDataError();
		}

		this.dataLoaderCall();

		document.fire("productPage:ready");
	},
	/**
 	* loadVariantProduct - Handles the loading of Variant per product variant types:petite, regular, tall
 	* This is a method of class ViewLarger
 	* @param {string} strProductId Product ID
 	* @param {string} strVariantId Variant Type
 	* Modified:  Keo 12/04/07 - Added Comments
 	* @author Unkown
 	*/
	loadVariantProduct : function(strProductId,strVariantId) {
		this.p1Image.src = '/assets/common/navigation/en/indicator-large.gif';
		if (objGIDPageViewAdapter.objGIDProducts.arrayProducts[strProductId] == null)  {
			if (strVariantId) {
				this.loadProductData(strProductId,"variant",strVariantId);
			} else {
				this.loadProductData(strProductId,"variant");
			}
		} else if (strVariantId && !objGIDPageViewAdapter.objGIDProducts.arrayProducts[strProductId].arrayVariantStyles[strVariantId].isLoaded) {
				this.loadProductData(strProductId,"variant",strVariantId);
		} else {
			this.initializeVariant(strProductId,strVariantId);
		}
	},

	/**
	 * data loader for quicklook related components
	 */
	loadNextProduct : function() {
		var strProductId = this.objP.arrayVariantProducts[this.intDataLoadCounter].strCatalogItemID;
		if (objGIDPageViewAdapter.objGIDProducts.arrayProducts[strProductId] != null)  {
			this.intDataLoadCounter++;
			this.postLoadData();
		} else {
			setTimeout("quickLook.loadNextProduct()",100);
		}
	},

	/**
	 * sets the css for the corresponding marketing flags
	 */
	initializeMarketingFlags : function() {
		var objP = this.objP, objV = this.objV;
		if (objP.hasMarketingFlag && objP.objMarketingFlag) {
			this.objStyleMarketingFlag.innerHTML = this.getMarketingFlag(objP.objMarketingFlag);
			this.objStyleMarketingFlag.style.display = "block";
		} else {
			this.objStyleMarketingFlag.style.display = "none";
		}
		if (objP.hasMarketingCallOut && objP.objMarketingCallOut) {
			this.objStyleMarketingCallOut.innerHTML = this.getMarketingFlag(objP.objMarketingCallOut);
			this.objStyleMarketingCallOut.style.display = "block";
		} else {
			this.objStyleMarketingCallOut.style.display = "none";
		}
		this.hasStyleColorMarketingFlag = false;
		this.objStyleColorMarketingFlag.style.display = "none";
		var variantColorLen = this.objV.arrayVariantStyleColors.length;
		var variantStyleColors = this.objV.arrayVariantStyleColors;

		for (var i=0;i<variantColorLen;i++) {
			if (variantStyleColors[i].objMarketingFlag != undefined) {
				this.hasStyleColorMarketingFlag = true;
				this.objStyleColorMarketingFlag.style.display = "block";
			}
		}
		this.hasSkuMarketingFlag = false;
		this.objSkuMarketingFlag.style.display = "none";
		var obj = this;
		Object.values(objV.arrayVariantSkus).each(function(sku) {
			if(sku && sku.objMarketingFlag) {
				obj.hasSkuMarketingFlag = true;
				obj.objSkuMarketingFlag.setStyle({display:'block',
					width: (objV.objStyleSizeInfo.intSizeDimensionsCount >= 1 ? obj.INT2SIZEMAXWIDTH : obj.INT1SIZEMAXWIDTH) + "px"});
			}
		});
	},

	updateMarketingFlags : function() {
		var activeColor = this.objV.arrayVariantStyleColors[this.activeColor], marketingFlag = activeColor.objMarketingFlag;
		if (this.hasStyleColorMarketingFlag) {
			this.objStyleColorMarketingFlag.innerHTML = marketingFlag ? this.getMarketingFlag(marketingFlag) : '';
		}
		if (this.hasSkuMarketingFlag && marketingFlag != undefined) {
			var strKey = activeColor.strColorCodeId + "_", dimCount = this.objV.objStyleSizeInfo.intSizeDimensionsCount;
			var str = "";
			if ( dimCount >= 1 && this.activeSizeDimension1 != -1) {
				strKey += this.arrayAllSizeDimension1[this.activeSizeDimension1].strId;
			}
			strKey += "_";
			if (dimCount == 2 && this.activeSizeDimension2 != -1) {
				strKey += this.arrayAllSizeDimension2[this.activeSizeDimension2].strId;
			}
			if (this.objV.arrayVariantSkus[strKey] && this.objV.arrayVariantSkus[strKey].objMarketingFlag) {
				str = this.getMarketingFlag(this.objV.arrayVariantSkus[strKey].objMarketingFlag);
			}
			this.objSkuMarketingFlag = replace(this.objSkuMarketingFlag,str);
		} else {
			this.objSkuMarketingFlag.innerHTML = '';
		}
	},
	/**
	 * setReturnDivs - Checks for for the allowed return type and displays the appropriate div.
	 * @author aliebling 05/12/2009
	 */
	setReturnDivs : function() {
		// Determine the divs we are working with
        var returnDivStyle = this.type == 'quickLook' ?  'block' : 'inline';
		var objMailOnlyDiv;
		if (this.isQuickLookOpen){
			objMailOnlyDiv = $('productMailOnlyReturn2');
		} else {
			objMailOnlyDiv = $('productMailOnlyReturn');
		}

		var objIntlMailOnlyDiv;
		if (this.isQuickLookOpen){
			objIntlMailOnlyDiv = $('productIntlMailOnlyReturn2');
		} else {
			objIntlMailOnlyDiv = $('productIntlMailOnlyReturn');
		}

		var objNonreturnableDiv;
		if (this.isQuickLookOpen){
			objNonreturnableDiv = $('productNonreturnable2');
		} else {
			objNonreturnableDiv = $('productNonreturnable');
		}

		var objFreeReturnsDiv;
		if (this.isQuickLookOpen){
			objFreeReturnsDiv = $('productFreeReturn2');
		} else {
			objFreeReturnsDiv = $('productFreeReturn');
		}

		var objNotIntlShipDiv;
		if (this.isQuickLookOpen){
			objNotIntlShipDiv = $('productNotIntlShip2');
		} else {
			objNotIntlShipDiv = $('productNotIntlShip');
		}

		// Reset all divs to display: none
		if (objMailOnlyDiv) objMailOnlyDiv.setStyle({display: 'none'});
		if (objIntlMailOnlyDiv) objIntlMailOnlyDiv.setStyle({display: 'none'});
		if (objNonreturnableDiv) objNonreturnableDiv.setStyle({display: 'none'});
		if (objFreeReturnsDiv) objFreeReturnsDiv.setStyle({display: 'none'});
		if (objNotIntlShipDiv) objNotIntlShipDiv.setStyle({display: 'none'});

		// Prepare for multi objects by retrieving the sku specific returnType
		multi = this.objP.strAllowableReturnCode == "Multi";
		var skuReturnType;
		if(multi){
			with(this) {
				skuReturnType = getReturnCode(activeColor,activeSizeDimension1, activeSizeDimension2);
			}
		}

		if (brandConst.INTERNATIONAL_SHIPPING_COUNTRY_CODE && brandConst.INTERNATIONAL_SHIPPING_COUNTRY_CODE != "" && brandConst.INTERNATIONAL_SHIPPING_COUNTRY_CODE != "us" ) {
			// Set divs for Intl Ship: Priority: product excluded, not returnable, default: mail only
			if (!this.objP.isIntlShip) {
				if (objNotIntlShipDiv) objNotIntlShipDiv.setStyle({display: returnDivStyle });
			} else if (this.objP.strAllowableReturnCode && this.objP.strAllowableReturnCode != ""
				&& this.objP.strAllowableReturnCode == "N" || (multi && skuReturnType == "N")){
				if (objNonreturnableDiv) objNonreturnableDiv.setStyle({display: returnDivStyle });
			} else {
				if (objIntlMailOnlyDiv) objIntlMailOnlyDiv.setStyle({display: returnDivStyle });
			}
		} else {
			if(this.objP.strAllowableReturnCode && this.objP.strAllowableReturnCode != ""){
				if(this.objP.strAllowableReturnCode == "M" ||
						(multi && skuReturnType == "M")
				){
					if (objMailOnlyDiv) objMailOnlyDiv.setStyle({display: returnDivStyle });
				} else if(this.objP.strAllowableReturnCode == "N" ||
						(multi && skuReturnType == "N")
				){
					if (objNonreturnableDiv) objNonreturnableDiv.setStyle({display: returnDivStyle });
				} else if(this.objP.strAllowableReturnCode == "F" ||
						(multi && skuReturnType == "F")
				){
					if (objFreeReturnsDiv) objFreeReturnsDiv.setStyle({display: returnDivStyle });
				}
			}
		}
	},
	/**
	 * setter for orgin copy in the fabric details bullet
	 */
	setProductOriginCopy : function() {
		this.objProductOriginCopy.innerHTML = this.getProductOrigin();
	},

	/**
	 * setProductVendorInfo sets the appropriate vendor information.  Piperlime products display the vendorProductId.
	 *
	 * Modified 7/28/2009 Krishna Rangavajhala PLA-211 vendor product id is always expected to be available. Therefore, cleaned up displaying vendorstylenumber otherwise.
	 * @author Byung 4/21/09
	 */
	setProductVendorInfo : function() {
		var element = $(this.objProductVendorInfo);
		var id = "";
		if (element) {
	    	if (this.brandCode == 4) {
	    		var activeColor = this.objV.arrayVariantStyleColors[this.activeColor];
	    		if (activeColor) id = activeColor.strVendorProductId;
			} else {
				id = this.objP.strVendorId;
			}
		}
		if (id != "") {
			element.update(brandProperties.VENDORIDSYMBOL + id);
			element.show();
		}
	},

	/**
	 * after initialize color, colorcylinghelper is called to run through all the colors to find the next availible color
	 * size combo.  if the user selected color is not availible for the user selected size, the next availible color will be
	 * chosen.
	 */
	initializeColorCycling : function() {
		var isInStock = this.isSkuInStock(this.selectedColor,this.selectedSizeDimension1,this.selectedSizeDimension2);
		var isOnOrder = this.isSkuOnOrder(this.selectedColor,this.selectedSizeDimension1,this.selectedSizeDimension2);
		var isLowInventory = this.isSkuLowInventory(this.selectedColor,this.selectedSizeDimension1,this.selectedSizeDimension2);
		this.objV.arrayVariantStyleColors.selectedIndex = -1;

		/**
		 * helper function for initialize color cycling
		 */
		var hlpContext = this;
		function colorCyclingHelper() {
			with(hlpContext)  {
				var len = objV.arrayVariantStyleColors.length, i=selectedColor+1, isInStock,isOnOrder, isLowInventory;
				if(len == 1) {return 0;}
				if(i >= len ) { i = 0; }
				while(i != selectedColor) {
					isInStock = isSkuInStock(i, selectedSizeDimension1, selectedSizeDimension2);
					isOnOrder = isSkuOnOrder(i, selectedSizeDimension1, selectedSizeDimension2);
					isLowInventory = isSkuLowInventory(i, selectedSizeDimension1, selectedSizeDimension2);

					if(isInStock||isOnOrder||isLowInventory) { break;}
					i++;
					if(i == len) { i = 0; }
				}

				return i;
			}
		}
		if(!isInStock && !isOnOrder && !isLowInventory) {
			this.selectedColor = this.activeColor = colorCyclingHelper();
			this.selectedColorName = this.objV.arrayVariantStyleColors[this.selectedColor].strColorName;
		}
	},

	/**
	 * goto an outfit page when crosssell outfit img is clicked. store a cookie to keep track of the clickthrough product
	 * for omniture
	 */
	goOutfit : function() {
		var strLink = this.objP.objCrossSellInfo.strLink;
		if (this.isFromSBS) strLink += this.strSBSUrlSuffix;
		var strCookieVal = this.objP.strProductId + "," +
							this.objV.strVariantId + "," +
							this.selectedColor + "," +
							this.selectedSizeDimension1 + "," +
							this.selectedSizeDimension2 + "," +
							this.strSelectedQty;
		setCookieVar("globalSession","selectionData",strCookieVal);
		setCookieVar('globalSession', 'omniClickThrough', this.objP.strProductId);	  //project selfesteem-story #29
		window.location.href = strLink;
	},

	/**
	 * if any of the crossell product is clicked, it sets a cookie to keep track of clickthrough product
	 * @param id
	 */
	goCrossSell : function(id) {
		var crossSellProduct = objGIDPageViewAdapter.objGIDProducts.arrayProducts[id]||{};
		if(!crossSellProduct.crossSellFired) {
			setCookieVar('globalSession', 'omniClickThrough', this.crossSellId);
			crossSellProduct.crossSellFired = true;
		}
	},

	/**
	 * initialize the default size selection, if there is one
	 */
	initializeSkuSelection : function() {
		var obj = this;
		var skus = this.objV.arrayVariantSkus;
		var defaultVariant =
				Object.keys(skus).find(
						function(sku){return sku && skus[sku].strSkuId == obj.strDefaultSkuId;}
						);

		if(defaultVariant) {
			defaultVariant = defaultVariant.split('_');
		   this.selectedColor = this.objV.arrayVariantStyleColors.findIndex(
				function(color) {
					return color.strColorCodeId == defaultVariant[0];
				}
			)||0;

		   this.selectedSizeDimension1 = this.arrayAllSizeDimension1.findIndex(
				function(size1) {
					return size1.strId == defaultVariant[1];
				}
			)||0;

			if(this.objV.objStyleSizeInfo.intSizeDimensionsCount == 2) {
				this.selectedSizeDimension2 = this.arrayAllSizeDimension2.findIndex(
					function(size2) {
						return size2.strId == defaultVariant[2];
					}
				)||0;
			}
		}
		this.activeColor = this.selectedColor;
		this.activeSizeDimension1 = this.selectedSizeDimension1;
		this.activeSizeDimension2 = this.selectedSizeDimension2;
	},

	/**
	 * close all the pops when closing quicklook or leaving productpage
	 */
	closeRelatedWindows : function() {
		if (this.objProductImagesWindow && !this.objProductImagesWindow.closed) this.objProductImagesWindow.close();
		if (this.objSizeChartWindow && !this.objSizeChartWindow.closed) this.objSizeChartWindow.close();
		if (this.objInfoPopUp && !this.objInfoPopUp.closed) this.objInfoPopUp.close();
	},

	/**
	 * if ur coming from Shop by size this will initialize the corret user selected size dimension and makes it default selection
	 * @param cat
	 * @param dim
	 */
	getUserSizeSelection : function(cat,dim) {
		var strDimVal = '';
		if (this.isFromSBS) {
			var objUserSizeSelections = this.objUserSizeSelections;
			var strKey = cat + "," + dim;
			var arrayAllSizeDim = dim == '1' ? this.arrayAllSizeDimension1 : this.arrayAllSizeDimension2;

			if (objUserSizeSelections && objUserSizeSelections.arraySizeCategories[strKey]) {
				strDimVal = objUserSizeSelections.arraySizeCategories[strKey].strSizeDimensionValueId;
				if (objUserSizeSelections.arraySizeCategories[strKey].strSizeDimensionAlphaSize != "") {
					var strEquivalentSizes = ","+objUserSizeSelections.arraySizeCategories[strKey].strSizeDimensionAlphaSize.replace(/\s/g,"") + ",";
					arrayAllSizeDim.inject(strDimVal,
								function(str, value, index) {
									if (strEquivalentSizes.indexOf("," + value.strName + ",") != -1) {
										str += "," + value.strId;
									}
									return str;
								}
							)
				}
				strDimVal += ",";
			}
		}
		return strDimVal;
	},

	setAddtoBagState : function(bln, isDisabled) {
		this.getAddtoBag(bln, isDisabled);
	},

	/**
	 * update text info base on user selection
	 */
	updateDataLabels : function(type,index) {
		this.setTextSizeDimension1();
		this.setTextSizeDimension2();
		this.setTextColor();
		this.setSelectionConfirmText();
		this.setPriceText();
		this.setSoldOut(type, index);
		this.setInventoryStatus();
		this.updateMarketingFlags();
		this.setReturnDivs();
		this.setProductVendorInfo();
	},

	/**
	 * err handler when add to bag ajax request comes back with errors
	 * @param strPageError
	 * @param arrayLineItemErrors
	 */
	doAddToBagErrors : function(strPageError,arrayLineItemErrors) {
		this.setAddToBagError(strPageError);
		if (arrayLineItemErrors.size() > 0) {
			this.hasInlineBagErrors = true;
			this.arrayAddtoBagErrors = arrayLineItemErrors;
		}
		this.setAddtoBagState(true);
	},

	/**
	 * sets css when add to back err is detected
	 * @param strPageError
	 */
	setAddToBagError : function(strPageError) {
        if(!strPageError) return;
		this.objInlineBagError.innerHTML = strPageError;
		if (this.objInlineBagError.style) this.objInlineBagError.style.display = "block";
	},

	/**
	 * hides the err div
	 */
	clearAddToBagError : function() {
		if (this.objInlineBagError) {
			this.objInlineBagError.innerHTML = "";
			if (this.objInlineBagError.style) this.objInlineBagError.style.display = "none";
		}
	},

	/**
	 * sets css for GID promo msg accordingly
	 */
	setGIDPromoMessage : function() {
		if (this.objP.strGIDPromoMessage && this.objP.strGIDPromoMessage != "") {
			this.objGIDPromoMessage = replaceHTML(this.objGIDPromoMessage, this.objP.strGIDPromoMessage);
			this.objGIDPromoMessage.style.display = "block";
		} else {
			this.objGIDPromoMessage.style.display = "none";
		}
	},

	/**
	 * success handler when add to bag ajax request is done
	 */
	doAddToBagSuccess : function(hasErr) {
		this.setAddtoBagState(true);
        if(!hasErr) {
            this.clearAddToBagError();
            this.arrayAddtoBagErrors._objects = {};
        }
        window.scrollTo(0,0);
		if (objInlineBag.doOpenBag) objInlineBag.openInlineBag();
		if (this.isQuickLookOpen) this.closeQuickLook();
	},

	/**
	 * if ajax request failed, displays general no results page
	 */
	productDataError : function() {
		window.location.replace("/browse/GeneralNoResults.do");
	},

	/**
	 * initialize/cache dom reference to all interactive buttons for performance reasons
	 */
	setSwatchButtons : function() {
		this.size1Buttons = Element.getElementsBySelector(this.size1Swatches ,
			'button');
		this.size2Buttons = Element.getElementsBySelector(this.size2Swatches ,
			'button');
		this.colorButtons = Element.getElementsBySelector(this.colorSwatches ,
			'input[type="image"]');
	},

	/**
	 * generates html for the moreviews btn
	 */
	 getProductImageTools : function() {
		var template = this.templates.MORE_VIEWS_SITEWIDE;
		return this.objV.arrayVariantStyleColors[0].hasLargerImage || this.objP.hasAlternateImage ?
					template.evaluate({btnPath : this.resources.BTN_PATH, app : this.type, text: this.moreViewsBtnText}) : '';
	},

	/**
	 * generates html for product name
	 * @param brandName
	 * @param styleName
	 */
	getProductNameText : function(brandName, styleName) {
		return '<span class="productBrand">'+brandName+'</span> <span class="productName">' + styleName + '</span>';
	},

	/**
	 * setter for productname html
	 */
	setProductNameText : function() {
		this.objProductNameText.innerHTML = this.getProductNameText(this.objP.strVendorName, this.objP.strProductStyleName);
	},

	/**
	 * prepares the price array for price point sort
	 * @param array
	 * @param color
	 * @param index
	 */
	addPriceHelper : function(array, color, index) {
			var price = 0;
				if(color.strPartialMupMessage!=''){
						//price = Number(color.strRegularPrice.substring(1));
						price = this.getPriceDigits(color.strRegularPrice);
				} else if( color.strSalePrice) {
						//price = Number(color.strSalePrice.substring(1));
						price = this.getPriceDigits(color.strSalePrice);
				} else if( color.strRegularPrice ) {
					//price = Number(color.strRegularPrice.substring(1));
					price = this.getPriceDigits(color.strRegularPrice);
				}
			color.index = index;
			color.price = price;
			array.push(price);
			return array;
		},
	/**
	 * This method is returns the price digits only
	 */
	getPriceDigits : function(price){
			var currencySignIndex = price.indexOf(resourceBundleValues.currency);
			var productPrice = price;
			if(currencySignIndex != -1 ){
				productPrice = price.replace(resourceBundleValues.currency,'');
			}
			productPrice = productPrice.replace(/[^\d.,]+/,'');
			return productPrice;
		},

	sortNumber : function(a,b) { return b-a; },

	/**
	 * finds all the color variants that has the same price
	 * @param array
	 * @param price
	 * @param index
	 */
	groupPriceHelper : function(array, price, index) {
		array.push(this.objV.arrayVariantStyleColors.findAll(
			function(color) {
				return color.price == price;
			}
		));
		return array;
	},

	/**
	 * groups color array base on price, uses stable sort to keep original color order
	 */
	generatePricePoints : function() {
		var objV = this.objV;
		if(!objV.isStyleColorSorted) {
			var tmpArray1 = [], tmpArray2 = [];
			objV.pricePoints = objV.arrayVariantStyleColors.inject(tmpArray1,
					this.addPriceHelper.bind(this)).uniq();
			objV.arrayVariantStyleColors = objV.pricePoints.sort(this.sortNumber).inject(tmpArray2, this.groupPriceHelper.bind(this)).flatten();
			objV.defaultVariantStyleColor = objV.arrayVariantStyleColors.findIndex(function(color) {return color.index == 0;});
			objV.isStyleColorSorted = true;
		}
	},

	/**
	 * clears all states for the current product, clear user selected attributes
	 */
	purgeDataObjects : function() {
		this.arrayAllSizeDimension1.length = 0;
		this.arrayAllSizeDimension2.length = 0;

		this.intTab = 0;
		//this.activeColor = -1;
		this.activeSizeDimension1 = -1;
		this.activeSizeDimension2 = -1;

		//this.selectedColor = -1;
		this.selectedSizeDimension1 = -1;
		this.selectedSizeDimension2 = -1;

		this.isColorError = false;
		this.isSizeDimension1Error = false;
		this.isSizeDimension2Error = false;
		this.isBagError = false;
	},

	/**
	 * clear all err flags and hides err div
	 */
	clearErrors : function() {
		[this.objColorErrorMsg, this.objSizeDimension1ErrorMsg, this.objSizeDimension2ErrorMsg,
			this.objBagErrorMsg].invoke('setStyle',{visibility:'hidden'});
		this.isColorError = this.isSizeDimension1Error = this.isSizeDimension2Error = false;
	},

	/**
	 * initialize the product variant, a variant is tall, petite, or regular
	 * @param strProductId
	 * @param strVariantId
	 */
	initializeVariant : function(strProductId,strVariantId) {
		this.purgeDataObjects();
		this.clearErrors();
		this.initializeData(strProductId,strVariantId);
		if (window['productPage']) {
			this.setVariantToReportingService(strVariantId);
		}
	},

	/**
	 * contains methods that are used to initialize all components
	 */
	initializeCoreComponents : function() {
		this.generatePricePoints();
		this.initializeColors();
		this.initializeSizeDimension1();
		this.initializeSizeDimension2();
		this.initializeColorCycling();
		this.initializeImages();
	},

	/**
	 * sets variant product info to the reporting service
	 */
 	setVariantToReportingService : function(strVariantId) {
		if((reportingService||{}).isActive) {
            if (window['productPage']) {
				var controller = reportingService.controller.viewManagers.productpageViewManager.controller;
			} else if (window['quickLook']) {
				var controller = reportingService.controller.viewManagers.commonViewManager.controller;
			}
			controller.setReportModel(strVariantId);
			controller.setReportTransmissionVars();
			this.setReportingService();
		}
	},
	/**
	 * creates a ProductImages obj for the current variant.
	 * NOTE: idealy each product should have 1 productImages obj, right now each variant has one which
	 * is inefficient
	 * however theres noway to know all the colors availible for each variant until the ajax data is return
	 */
	initializeImages : function() {
		var objV = this.objV;
		productImagesConstructor.app = this;
		productImagesConstructor.initializeProductImages();
		
		/*if(!objV.ProductImages) {
			objV.ProductImages = new ProductImages();
			objV.ProductImages.init(this.objP, this);
		}*/
	},

	/**
	 * search for the default selected color base on the following order:
	 * cookie -> if quicklook use quicklook default color -> if theres default color use it -> user selected color
	 */
	initializeColors : function() {
		this.selectedColor = this.objV.defaultVariantStyleColor;
		var colorLen = this.objV.arrayVariantStyleColors.length, styleColors = this.objV.arrayVariantStyleColors, i=0;

        if (this.objCookieData.color != -1) {
			for (i=0;i<colorLen;i++) {
				if (styleColors[i].strColorName == this.objCookieData.color) {
					this.selectedColor = i; break;
				}
			}
		} else if(this.objQuickLookTarget && this.objQuickLookTarget.strDefaultStyleColor != -1 && this.objQuickLookTarget.strDefaultStyleColor != '') {
			for (i=0;i<colorLen;i++) {
				if (styleColors[i].strColorCodeId == this.objQuickLookTarget.strDefaultStyleColor) {
					this.selectedColor = i;
					break;
				}
			}
		} else if (this.strDefaultStyleColorId != -1) {
			for (i=0;i<colorLen;i++) {
				if (styleColors[i].strColorCodeId == this.strDefaultStyleColorId)
				this.selectedColor = i;
			}
		} else if (this.selectedColorName != "") {
			for (i=0;i<colorLen;i++) {
				if (styleColors[i].strColorName == this.selectedColorName) {
					this.selectedColor = i;
					break;
				}
			}
		}
		this.activeColor = this.selectedColor;
		this.selectedColorName = this.objV.arrayVariantStyleColors[this.selectedColor].strColorName;
	},

	/**
	 * keeps a string to keep track of equivalent sizes, like a Small means size 0-6, and Medium is 7-9 so on
	 * SBS only
	 * @param cat
	 * @param dim
	 */
	setEquivalentSizes : function(cat, dim) {
		if (this.isFromSBS) {
			var objUserSizeSelections = this.objUserSizeSelections;
			var strKey = cat + ',' + dim;
			if (objUserSizeSelections && objUserSizeSelections.arraySizeCategories[strKey]) {
				this.selectedSize = objUserSizeSelections.arraySizeCategories[strKey].strSizeDimensionValueId + ',';
				if (objUserSizeSelections.arraySizeCategories[strKey].strSizeDimensionAlphaSize != "") {
					this.strEquivalentSizes = ","+objUserSizeSelections.arraySizeCategories[strKey].strSizeDimensionAlphaSize.replace(/\s/g,"") + ",";
				}
			}
		 }
	},

	/**
	 * initialize size dimension1
	 * @see initializeDim1Helper
	 */
	initializeSizeDimension1 : function() {
		var objV = this.objV, sizeInfo = objV.objStyleSizeInfo, dimCount = sizeInfo.intSizeDimensionsCount;
		var hlpContext = this;
		/**
		 * helper function for initializing size 1 skus
		 * @param str
		 * @param size
		 * @param index
		 */
		function initializeDim1Helper(str, size, index) {
			hlpContext.arrayAllSizeDimension1.push({strName:size[0], strId:size[1]});
			var strEquivalentSizes = hlpContext.strEquivalentSizes;

			if (strEquivalentSizes && strEquivalentSizes.indexOf("," + size[0] + ",") != -1) {
				str += "," + size[1];
			}

			if ( (str.indexOf(size[1]) != -1 && hlpContext.previouseSelectedSize1 == -1) || (hlpContext.previouseSelectedSize1 == size[0])  ) {
				hlpContext.selectedSizeDimension1 = index;
			}

			return str;
		}

		if (dimCount >= 1) {
			this.selectedSize = '';
			this.setEquivalentSizes(sizeInfo.intSizeCategoryId,sizeInfo.strSizeDimension1Id);
			var dimArray = this.arrayAllSizeDimension1;
			this.previouseSelectedSize1 = this.selectedSizeDimension1Name || this.objCookieData.s1;
			sizeInfo.strSizeDimension1ListOptions.split("||").invoke('split', '^,^').inject(this.selectedSize, initializeDim1Helper);

			if(dimArray.size() == 1) { this.selectedSizeDimension1 = 0; }
			if(sizeInfo.hasSizeDimension1Default) { this.selectedSizeDimension1 = sizeInfo.strDefaultSizeDimension1SizeCodeID; }
			this.activeSizeDimension1 = this.selectedSizeDimension1;
			if (dimArray[this.selectedSizeDimension1]) { this.selectedSizeDimension1Name = dimArray[this.selectedSizeDimension1].strName; }
		} else { this.selectedSizeDimension1 = this.activeSizeDimension1 = -1; }
	},

	/**
	 * initialize size dimension2
	 * @see initializeDim2Helper
	 */
	initializeSizeDimension2 : function() {
		var objV = this.objV, sizeInfo = objV.objStyleSizeInfo, dimCount = sizeInfo.intSizeDimensionsCount;
		var hlpContext = this;
		/**
		 * helper functions for initializing size 2 skus
		 * @param str
		 * @param size
		 * @param index
		 */
		function initializeDim2Helper(str, size, index) {
			hlpContext.arrayAllSizeDimension2.push({strName: size[0], strId: size[1]});
			var strEquivalentSizes = hlpContext.strEquivalentSizes;

			if (strEquivalentSizes && strEquivalentSizes.indexOf("," + size[0] + ",") != -1) {
				str += "," + size[1];
			}

			if ( (str.indexOf(size[1]) != -1 && hlpContext.previouseSelectedSize2 == -1) || (hlpContext.previouseSelectedSize2 == size[0])  ) {
				hlpContext.selectedSizeDimension2 = index;
				hlpContext.selectedSizeDimension2Name = size[0];
			}

			return str;
		}

		if (dimCount == 2) {
			this.selectedSize = '';
			this.setEquivalentSizes(sizeInfo.intSizeCategoryId,sizeInfo.strSizeDimension2Id);
			var dimArray = this.arrayAllSizeDimension2;
			this.previouseSelectedSize2 = this.selectedSizeDimension2Name || this.objCookieData.s2;
			sizeInfo.strSizeDimension2ListOptions.split("||").invoke('split', '^,^').inject(this.selectedSize, initializeDim2Helper);

			if(dimArray.size() == 1) { this.selectedSizeDimension2 = 0; }
			if(sizeInfo.hasSizeDimension2Default) { this.selectedSizeDimension2 = sizeInfo.strDefaultSizeDimension2SizeCodeID; }
			this.activeSizeDimension2 = this.selectedSizeDimension2;
		} else { this.selectedSizeDimension2 = this.activeSizeDimension2 = -1; }
	},

	/**
	 * initialize the ajax data, starts populating the dom
	 * @param strProductId
	 * @param strVariantId
	 */
	initializeData : function(strProductId,strVariantId) {
		this.objP = objGIDPageViewAdapter.objGIDProducts.arrayProducts[strProductId];
		this.strVariantId = (strVariantId ? strVariantId : this.objP.strDefaultVariantId);
		this.objV = this.objP.arrayVariantStyles[this.strVariantId];
		this.initializeCoreComponents();
	 },

	/**
	 * prints the pirce and promo msg for each price group
	 * @param color
	 */
	getColorSwatchPriceHTML : function(color) {
		var str = [];
		if (!this.objP.isGiftCard) {
			if ( !color.strSalePrice ) {
				str.push(color.strRegularPrice);
			} else {
				str.push('<strike>' + color.strRegularPrice +'</strike> <span class="salePrice">' + color.strSalePrice + '</span>');
			}
			if (color.strPartialMupMessage != '') str.push(' <span class="quickLookMupMessage">' + color.strPartialMupMessage + '</span>');
		}
		str.push('<br class="clear"/>');
		return str.join('');
	},

	/**
	 * geneate html for the actual color swatch
	 * @param color
	 * @param index
	 */
	getColorSwatchHTML : function(color, index) {
		var hostName = location.protocol + '//' + location.host + '/';
		var NOT_APPLICABLE_IMAGE_PATH = hostName + 'Asset_Archive/pcm/assets/common/Not_Applicable.gif';
		var swatchSrc = color.styleColorImagesMap['S'].src;
		// For Piperlime stylecolors since 5.60 - true color swatch images are used, but for old ones use p1 thumbnail instead.
		if(this.brandCode == '4' && swatchSrc == NOT_APPLICABLE_IMAGE_PATH) {
			swatchSrc = color.arrayVariantStyleColorImages["MainThumb"].strImagePath; // show P1 thumbnail as swatch
			color.Swatch[index].src = swatchSrc; // so productImages.do would use it in larger views popup
		}
		return this.templates.COLOR_SWATCH.evaluate( {
			index : index,
			id : this.id||'',
			label: this.strColorLabel + ' '+ resourceBundleValues.product.productColor,
			swatchSize : brandConst.INTCOLORSWATCHWIDTH[this.brandCode],
			swatchSize2 : brandConst.INTCOLORSWATCHWIDTH[this.brandCode] + 2,
			path : swatchSrc,
			altText :  color.strColorName + ' '+ resourceBundleValues.product.productImage,
			app : this.type
		} );
	},

	/**
	 * generates color swatch html
	 * @see getColorSwatches Helper
	 */
	getColorSwatches : function() {
		this.objV.arrayVariantStyleColors.oldColor = this.objV.arrayVariantStyleColors[0];
		var swatchHTML = [];
		var hlpContext = this;
		/**
		 * generate the pricing info as well as the color swatch html
		 * @param array
		 * @param color
		 * @param index
		 * @see getColorSwatchHTML
		 * @see getColorSwatchPriceHTML
		 */
		function getColorSwatchesHelper(array, color, index) {
			var oldColor = hlpContext.objV.arrayVariantStyleColors.oldColor;
			if( color.price != oldColor.price ) {
				if( index != 0 ) { array.push(hlpContext.templates.MUP_SEPERATOR) ; }
				array.push(hlpContext.getColorSwatchPriceHTML(color));
			}
			array.push(hlpContext.getColorSwatchHTML(color, index));
			hlpContext.objV.arrayVariantStyleColors.oldColor = color;
			return array;
		}
		swatchHTML.push(this.getColorSwatchPriceHTML(this.objV.arrayVariantStyleColors.oldColor));
		this.objV.arrayVariantStyleColors.inject(swatchHTML, getColorSwatchesHelper);
		swatchHTML.push(this.templates.DIV_CLEAR);
		return swatchHTML.join('');
	},

	/**
	 * generates html for size selection btns
	 * @param size
	 * @param index
	 * @param type
	 */
	getSizeSwatchHTML : function(size, index, type) {
		var label = type==1 ? this.strSizeDim1Label + ' size ': this.strSizeDim2Label + ' inseam ';
		return this.templates.DIM_SWATCH.evaluate({
			index : index,
			id : this.id||'',
			type : type,
			label:  label,
			size: size.strName.replace('(', '<br/>('),
			app : this.type
		});
	},

	/**
	 * master method for size btn html generation
	 * @param type
	 */
	getSizeDimensionSwatches : function(type) {
		var sizeDim = type == 1 ? this.arrayAllSizeDimension1 : this.arrayAllSizeDimension2, swatchHTML = [];
		var hlpContext = this;
		/**
		 * helper functions for size btn html geneation
		 * @param array
		 * @param color
		 * @param index
		 * @see getSizeSwatchHTML
		 */
		function getDimesionSwatchesHelper(array, color, index) {
			array.push(hlpContext.getSizeSwatchHTML(color, index, hlpContext.setSizeDimension));
			return array;
		}
		this.setSizeDimension = type;
		sizeDim.inject(swatchHTML, getDimesionSwatchesHelper);
		swatchHTML.push(this.templates.DIV_CLEAR);
		return swatchHTML.join('');
	},

	/**
	 * prints the html for the sold out msg when an unavalible color/size combo selected
	 */
	getSoldOutBanner : function() {
		var objSid = this.objV.arrayVariantStyleColors[this.activeColor];
		var strVariant = this.objV.strVariantName;
		var strHTMLTop = 		'<div class="top clearfix outer"><div class="topLeft"></div><div class="topCenter inner"></div><div class="topRight"></div></div>' +
												'<div id="productSoldOutMsgCenter" class="inner">';
		var strHTMLBottom = '</div>' +
												'<div class="bottom clearfix outer"><div class="bottomLeft"></div><div class="bottomCenter inner"></div><div class="bottomRight"></div></div>';



		var str = strHTMLTop + objSid.strColorName;
		if (this.objV.objStyleSizeInfo.intSizeDimensionsCount >= 1) {
			str += ' ' + this.strOutOfStockMessageWithSizes + ' ' + this.arrayAllSizeDimension1[this.activeSizeDimension1].strName;
			if (this.objV.objStyleSizeInfo.intSizeDimensionsCount == 2) {
				str += ' ' + this.arrayAllSizeDimension2[this.activeSizeDimension2].strName;
			}
			if (this.strRegularVariant != strVariant.toLowerCase()) str += " " + strVariant;
		} else {
			str += ' ' + this.strOutOfStockMessageWithoutSizes;
		}
		str += strHTMLBottom;
		return str;
	},

	getOnOrderBanner : function() {
		var objSid = this.objV.arrayVariantStyleColors[this.activeColor];
		var strVariant = this.objV.strVariantName;
		var strHTML = 	'<div id="productOnOrderMsgLeft"></div>' +
						'<div id="productOnOrderMsgCenter">' +
							this.strOnOrderHoverMessage + ' <span class="onOrderShipDate">' + this.strOnOrderHoverMessageShipDate + '</span>' +
						'</div>' +
						'<div id="productOnOrderMsgRight">' +
							'<div id="productOnOrderMsgRightTop"></div>' +
							'<div id="productOnOrderMsgRightBottom">' +
								'<div id="productOnOrderMsgRightBottom-arrow-down-1"></div>' +
								'<div id="productOnOrderMsgRightBottom-arrow-down-2"></div>' +
							'</div>' +
						'</div>';
		return strHTML;
	},

	/**
	 * gets price text html
	 * @param strPrice
	 * @param strSalePrice
	 */
	getPriceText : function(strPrice,strSalePrice) {
		return (strSalePrice == '' ? strPrice : '<strike>' + strPrice + '</strike><span class="salePrice">' + strSalePrice + '</span>');
	},

	/**
	 * prints html for user selection on top of the add to bag btn
	 * @param color
	 * @param size1
	 * @param size2
	 * @param variant
	 */
	getSelectionConfirmText : function(color,size1,size2,variant) {
		return color + (size1 != '' ? ', ' + size1 : '')
			+ (size2 != '' ? ' ' + size2 : '')
			+ (variant.toLowerCase() != this.strRegularVariant ? ' ' + variant : '');
	},

	/**
	 * checks if a valid color/size combo is selected when add to bag is clicked
	 * @param isAvailable
	 * @param isDisabled
	 */
	getAddtoBag : function(isAvailable, isDisabled) {
		var dimCount = this.objV.objStyleSizeInfo.intSizeDimensionsCount;
		var isBtnOn = this.selectedSizeDimension1 != -1 && !isDisabled && isAvailable &&
		   ((this.selectedSizeDimension2 != -1 && dimCount == 2)^dimCount == 1);

        var classes = this.type == 'shoppingBagPage' ? 'universalButtonSprite universalButtonSpriteUpdateItem' : 'quicklook-sprites sprite-button_add_to_bag_';
        var btnO = this.type == 'shoppingBagPage' ? 'O' : 'o';
		this.objAddtoBag.setAttribute('isDisabled',!isBtnOn);
		this.objAddtoBag.className = isBtnOn ? classes + btnO + 'n' : isAvailable ? classes + btnO + 'ff' : classes + btnO + 'ut';
		this.objAddtoBag.alt = isBtnOn ? this.strAddToBagAltTextActive : isAvailable ? this.strAddToBagAltText : this.strAddToBagAltTextOut;
		this.objAddtoBag.onclick = isBtnOn ? this.addToBag.bind(this) : this.checkErrors.bind(this);
	},

	/**
	 * builds the 'fabric & care' bullet html in the following order:
	 * 1 Percent/Content
	 * 2 Fabric Copy Bullets
	 * 3 Fabric Copy Links
	 * 4 Flammability Warning
	 * 5 Care Instruction Text
	 * 6 Country of Origin
	 * @param careCopy - preformatted default Fabric Copy Bullets and Fabric Copy Links
	 */
	getFabricCare : function(careCopy) {
        var isPiperlime = brandConst.BRAND_ID == "PL", isAthleta = brandConst.BRAND_ID == 'AT';
		var careText = [], percentContent = [],
			bullet = this.templates.BULLET, percent = this.templates.PERCENT,
			contentSize = this.objP.arrayFabricContent.size();

		if (contentSize > 0) {
			this.objP.arrayFabricContent.inject(percentContent,
					function(str, content, index) {
						str.push(percent.evaluate(
							{ percent : parseInt(content.strPercent), name : content.strName, divider : index < contentSize - 1 ? ', ' : '. ' }));
						return str;
					});
			careText.push(bullet.evaluate({content: percentContent.join('')}));
		}
        if (careCopy != '') {
        	// careText is passed in pre-formatted. Just push it onto the end of the string as-is.
        	careText.push(careCopy);
        }
        if (this.objP.strFlammableWarningText != "") {
			careText.push(bullet.evaluate({content:this.objP.strFlammableWarningText}));
		}
		if (this.objP.strCareInstructionText != "") {
			careText.push(bullet.evaluate({content : this.objP.strCareInstructionText}));
		}
		if (brandConst.BRAND_LOCALE === 'en_US') {
	        if(!(isPiperlime || isAthleta) ) { 
	        	if (this.objP.isImported) {        		
	        		careText.push(bullet.evaluate({content : this.strIsImported})); 
	        	}
	        }
		}

		return careText.join('');
	},

	/**
	 * prints the content for each tab in quicklook
	 * @param tab
	 */
	getTabContent : function(tab) {
		var bullet = this.templates.BULLET, content = [];
		var infoBlocks = tab.arrayInfoTabInfoBlocks;
		var hlpContext = this;

		function getTabContentHelper(array, block) {
			if(block.hasLink) {
				if (!window['objO']) { // no links in outfit
					if (block.isSizeChart) {
						array.push(bullet.evaluate({content:'<a href="javascript:' + hlpContext.type + '.openSizeChart()' + block.strDisplayText + '</a>'}));
					} else {
						block.strLink = (block.isExternal ? block.strExternalLink : block.strTemplateAction + ".do?cid="+block.intBusinessId);
						if (block.isPopUp) {
							array.push(bullet.evaluate({content:'<a href="javascript:' + hlpContext.type + '.openInfoPopUp(\'' + block.strLink + '\','+block.intPopUpWidth+','+block.intPopUpHeight+');">' + block.strDisplayText + '</a>'}));
						} else {
							array.push(bullet.evaluate({content:'<a href="'+block.strLink+'" ' + (block.isExternal ? 'target="_top"' : '') + '>' + block.strDisplayText + '</a>'}));
						}
					}
				}
			} else {
				array.push(bullet.evaluate({content:block.strDisplayText}));
			}
			return array;
		}

		if( infoBlocks ) {
			infoBlocks.inject(content, getTabContentHelper);
		}
		return content.join('');
	},

	/**
	 * prints size 1 html when user selectes size 1 btn
	 * @param strName
	 */
	getSizeDimension1Text : function(strName) {
		return this.templates.SIZE_LABEL_TEXT.evaluate({label:this.strSizeDim1Label, dispName:this.objV.objStyleSizeInfo.strSizeDimension1Name, name: strName});
	},

	/**
	 * prints size 2 html
	 * @param strName
	 */
	getSizeDimension2Text : function(strName) {
		return this.templates.SIZE_LABEL_TEXT.evaluate({label:this.strSizeDim2Label, dispName:this.objV.objStyleSizeInfo.strSizeDimension2Name, name: strName});
	},

	/**
	 * parse err msg from add to bag ajax and display err msg accordingly
	 *
	 */
	getBagError : function() {
		var errorArray = [], strSelection = "", vowelExp = /^[aeiou]/i;

		if (this.isColorError) {
			errorArray.push(this.objP.strStyleColorDisplayName);
		}
		if (this.isSizeDimension1Error) {
			errorArray.push(this.objV.objStyleSizeInfo.strSizeDimension1Name);
		}
		if (this.isSizeDimension2Error) {
			errorArray.push(this.objV.objStyleSizeInfo.strSizeDimension2Name);
		}
		if (errorArray.length == 1) {
			strSelection = errorArray[0];
		} else if (errorArray.length == 2) {
			strSelection = errorArray[0] + " & " + errorArray[1];
		} else {
			strSelection = errorArray[0] + ", " + errorArray[1] + " & " + errorArray[2];
		}
		var strMsg = (strSelection.search(vowelExp) == -1 ? this.strBagErrorPrefix : this.strBagErrorPrefix2) + ' ' + strSelection + ' ' + this.strBagErrorSuffix;

		return this.templates.BAG_ERROR.evaluate({msg: strMsg});
	},

	/**
	 * returns html for user selected color
	 * @param strName
	 */
	getTextColor : function(strName) {
		return this.templates.TEXT_COLOR.evaluate({label: this.strColorLabel , colorLabel:this.objP.strStyleColorDisplayName, name: strName});
	},

	/**
	 * initialize omniture obj
	 *
	 * Modified 1/29/2008 Byung Kim -- added productBrandCode & productBrandAbbr
	 */
	initializeOmni : function() {
		this.reporting.setProductBrandInfo(this.objP.brandCode);
        /**
 		* Modified:  Keo 07/28/08 - Added wrapper for G to H
 		*/
		if(!(reportingService||{}).isActive){
			if (omni && omni[this.omniView]) {
				omni.strProductColorName = this.objV.arrayVariantStyleColors[this.selectedColor].strColorName;
	            omni.strProductStyleColorId = this.objV.arrayVariantStyleColors[this.selectedColor].strColorCodeId;
	            omni[this.omniView](this.objP.strProductId,this.objP.strProductStyleName);
			}
		}
	},

	setReportingService : function (viewType, app) {
		if(!(reportingService||{}).isActive) return;
		var commonModel = reportingService.controller.viewManagers.commonViewManager.model;
		var productBrandCode = this.objP.brandCode;
		var brandSite = gidLib.reporting.getBrandSite(productBrandCode);
		commonModel.commonProductBrandCode = productBrandCode;
		commonModel.commonProductBrandAbbr = (brandSite ? brandSite.brandAbbr : "");
		reportingService.controller.viewManagers[(viewType||this.type.toLowerCase()) + 'ViewManager'].controller.getReportRequest(app);
	},

	/**
	 * action taken when the product image is clicked on quickLook
	 */
	goProductPage : function() {
		var strCookieVal =	this.objV.arrayVariantStyleColors[this.selectedColor].strColorName + "," +
							(this.arrayAllSizeDimension1[this.selectedSizeDimension1] ? this.arrayAllSizeDimension1[this.selectedSizeDimension1].strName : "") + "," +
							(this.arrayAllSizeDimension2[this.selectedSizeDimension2] ? this.arrayAllSizeDimension2[this.selectedSizeDimension2].strName : "") + "," +
							this.strSelectedQty;
		setCookieVar("globalSession","selectionData",strCookieVal);
		var isSearchActive = window.location.pathname.indexOf('/browse/search.do') != -1 || window.location.search.indexOf('searchCID') != -1;
		var cid = isSearchActive ? 'searchCID' : 'cid';
		var defaultStyleColorId = quickLook.objQuickLookTarget.strDefaultStyleColor || '';
		var cidVal = isSearchActive ? quickLook.objQuickLookTarget.strCategoryId : getQuerystringParam("cid");
		if(cidVal == null && isSearchActive) {
			cidVal = getQuerystringParam("searchCID");
		}
        var strURL = (brandConst.CATALOG_2_ACTIVE == 'true' ? '/browse/' + brandConst.PRODUCT_2_ACTION + '?catalog2Active=true&' + cid + '=' : '/browse/product.do?' + cid + '=') + cidVal + '&pid=' + this.objP.strProductId;
		strURL += "&vid=" + this.objV.strVariantId;
		strURL += (defaultStyleColorId != '' ? '&scid=' + defaultStyleColorId : '');
		if (this.isFromSBS) strURL += this.strSBSUrlSuffix;
		window.location.href = strURL;
	},

	getOnOrderWindow : function(strDate) { return  this.strOnOrderMessage + " <span class='quickLookOnOrderDate'>" + strDate.replace(/\s/g,"&nbsp;") + "</span>"; },
	getLowInventoryWindow : function() { return this.strLowInventoryMessage; },
	getMarketingFlag : function(obj) { return obj.isImageType ? '' : this.templates.MARKETING_FLAG.evaluate({name: obj.strMarketingFlagName}); },
	getProductOrigin : function() { 
		if (this.objP.isImported) return this.strIsImported; 
		},


	/**
	 * reporting is a subclass of GID.Browse.Base that deals with reporting functionality
	 * @base GID.Browse.Base
	 */
	reporting : {

		/**
		 * setProductBrandInfo sets the omni.productBrandCode & omni.productBrandAbbr vars using the provided brandCode & gidBrandSiteConstruct
		 * @author Byung Kim
		 * @date 1/30/2008
		 */
		setProductBrandInfo:function(brandCode) {
			if (window["omni"] && brandCode) {
				var brandSite = gidLib.reporting.getBrandSite(brandCode);
				omni.productBrandCode = brandCode;
				omni.productBrandAbbr = (brandSite ? brandSite.brandAbbr : "");
			}
		}
	}

};
Object.extend(GID.Browse.Base, brandProperties||{});

/**
* QuickLook - Constructor for object handling in the QuickLook Feature
* @constructor
* @author Yoshi Chen
* @date 12/04/2007
*/

var quickLook = Object.extend(window['quickLook']||{},  GID.Browse.Base);

Object.extend(quickLook, {
	arrayAllSizeDimension1: [],
	arrayAllSizeDimension2: [],
	id : '_ql',
	templates : Object.extend(gidLib.clone(GID.Browse.Base.templates), {

		QL_SWATCH_CONTENT : new Template( '#{colorSwatches}<div id="quickLookSizes"><div id="quickLookSize1" class="#{quickLookSize1Class}">#{size1Swatches}</div><div id="quickLookSize2" class="quickLookHalfSizeRight">#{size2Swatches}</div></div>'),

		COLOR_SWATCH_TEMP : new Template(
					'<div id="quickLookColorText" class="quickLookSelectionLabel"></div>' +
					'<div id="quickLookMarketingFlagColor"></div>' +
					'<div id="quickLookColorSwatches">#{colorSwatches}</div>'),

		SIZE_DIM1_TEMP : new Template(
				 '<div id="quickLookSize1Text" class="quickLookSelectionLabel"></div>' +
				'<div id="quickLookMarketingFlagSize"></div>' +
				'<div id="quickLookSize1Swatches">#{size1Swatches}</div>'),

		SIZE_DIM2_TEMP : new Template(
				'<div id="quickLookSize2Text" class="quickLookSelectionLabel"></div>' +
				'<div id="quickLookMarketingFlagColor"></div>' +
				'<div id="quickLookSize2Swatches">#{size2Swatches}</div>'),

		VARIANTS_TEMP : new Template('<div id="quickLookVariants">#{variants}</div>'),


		QL_SIZE_CHART : new Template('<div id="quickLookSizeChart" class="quickLookSizeChart"><a href="javascript:quickLook.openSizeChart();"><img src="/assets/common/clear.gif" class="quicklook-sprites sprite-sizeChart" border="0" alt="#{sizeChart}"/></a></div>'),

		QUICK_LOOK : new Template(
			'<div id="quickLookWindowTop" class="quickLookWindowTop">' +
				'<div id="quickLookWindowTopLogo" class="quickLookWindowTopLogo cursorMove">' +
					'<img id="quickLookLogo" src="#{badgePath}badge_quicklook.gif" class="quickLookLogo"/>'+
				'</div>' +
				'<div id="quickLookWindowClose" class="quickLookWindowClose">' +
					'<img src="/assets/common/clear.gif" class="universalButtonSprite universalButtonSpriteQuickLookClose" alt="#{closeText}" id="quickLookClose" onclick="quickLook.closeQuickLook();"/>' +
				'</div>' +
			'</div>' +
			'<div id="quickLookWindowContent" class="quickLookWindowContent clearfix">' +
			'<div id="quickLookPageError" class="quickLookPageError pageError"></div>' +
			'<div id="quickLookContentLeft" class="quickLookContentLeft clearfix">' +
				'<div id="quickLookProductImage" class="quickLookProductImage"><a href="javascript:quickLook.goProductPage();"><img src="#{mainImg}" name="quicklook_product_image" id="quicklook_product_image"/><img src="#{mainImg}" name="quicklook_outfit_image" id="quicklook_outfit_image" style="display: none; "/></a></div>' +
				'<div id="quickLookProductImageTools" class="quickLookProductImageTools">#{imgTools}</div>' +
				'<div id="imageThumbs#{id}"></div>' +
			'</div>' +
			'<div id="quickLookContentRight" class="quickLookContentRight">' +

				'<div id="quickLookVendorName" class="quickLookVendorName">#{vendorName}</div>' +
				'<div id="quickLookProductName" class="quickLookProductName"><a href="javascript:quickLook.goProductPage();">#{productName}</a></div>' +
				'<div id="quickLookMarketingFlagStyle" class="quickLookMarketingFlagStyle"></div>' +
				'<div id="quickLookMarketingCallOut" class="quickLookMarketingCallOut"></div>' +

				'<div id="quickLookInfoTabs" class="quickLookInfoTabs clearfix">' +
        			'<ul>' +
					'<li onclick="quickLook.setTab(0);" onMouseOver="quickLook.tabOver(0);" onMouseOut="quickLook.tabOut(0);" alt="" name="tab0" id="tab0" class="infoTab on" />' +
					resourceBundleValues.product.sizeTabText +
					'</li>' +
					'<li onclick="quickLook.setTab(1);" onMouseOver="quickLook.tabOver(1);" onMouseOut="quickLook.tabOut(1);" alt="" name="tab1" id="tab1" class="infoTab off"/>' +
					resourceBundleValues.product.overviewTabText +
					'</li>' +
	    			'</ul>' +
				'</div>' +

				'<div id="quickLookSwatches" class="quickLookSwatches">' +
		            '<div id="quickLookProductNumber">\##{pid}<div id="productPageVendorId" style="display:none;"></div>#{hasVariants}' +
					'<div id="quickLookProductOriginCopy" class="quickLookProductOriginCopy"></div></div>' +


					'<div id="quickLookGIDPromoMessage" class="quickLookGIDPromoMessage"></div>' +
					'<div id="quickLookMupMessageStyle" class="quickLookMupMessageStyle quickLookMupMessage"></div>' +
					'#{swatches}' +
					'#{giftCard}' +
				'</div>' +
				'<div id="quickLookTabArea" class="quickLookTabArea quickLookTabContent"></div>' +
				'<div id="quickLookAddToBagArea" class="quickLookAddToBagArea">' +
					'<div id="quickLookQtyArea" class="quickLookQtyArea clearfix">' +
					'<div id="quickLookQtyLabel" class="quickLookQtyLabel"><label for="quickLookQty">#{qtyLabel}</label></div>' +
					'<div id="quickLookQtyForm" class="quickLookQtyForm"><select id="quickLookQty" name="quickLookQty" onChange="quickLook.setSelectedQty()"></select></div>' +
					'</div>#{confirmation}' +
					'<div id="quickLookConfirmationAreaBottom" class="quickLookConfirmationAreaBottom"></div>' +
				'</div>' +
				'#{productReturnSource}' +
			'</div>' +
		'</div>' +
		'<div id="quickLookWindowBottom" class="quickLookWindowBottom"></div>'),

		CONFIRM_AREA : new Template(
			'<div id="quickLookConfirmationArea" class="quickLookConfirmationArea">' +
				'<div class="clearfix">' +
					'<input type="image" isDisabled="true" id="quickLookAddtoBag" onclick="quickLook.checkErrors();" src="/assets/common/clear.gif" class="quicklook-sprites sprite-button_add_to_bag_off"/>' +
					'<div id="quickLookConfirmText" class="quickLookConfirmText"></div>' +
					'<div id="quickLookPriceText" class="quickLookPriceText price"></div>' +
				'</div>' +
				'<div id="quickLookInventoryStatusWindow" class="quickLookInventoryStatusWindow"></div>' +
			'</div>')
	}),

    QuickLookLauncherMap : {
	   objQuickLook : 'quickLookWindow',
	   objQuickLookLauncher : "quickLookLauncher"},
	initializeQuickLook : function() {
		gidLib.loadDomObjMap(this, this.QuickLookLauncherMap);
		
		this.objQuickLookLauncher.src = '/gid/assets/common/quicklook/'+brandConst.BRAND_LOCALE+'/button_quicklook_launcher_on.gif';
		
		if (window["objSBS"] && objSBS.isFilterEngaged) this.isFromSBS = true;
        this.isLoaded = true;
        this.launcherHalfWidth = this.objQuickLookLauncher.width / 2;
        this.launcherHalfHeight = this.objQuickLookLauncher.height / 2;
	},
    setUserSizeSelections : function(strSizeCategoryDimension,strSizeSelections) {
		if (!this.objUserSizeSelections) this.objUserSizeSelections = {};
		var objUserSelections = this.objUserSizeSelections;
		if (!objUserSelections.arraySizeCategories) objUserSelections.arraySizeCategories = [];
		with(objUserSelections) {
			var arraySizeSelections = strSizeSelections.split("||");
			var arraySizeValues = arraySizeSelections[0].split("^,^");
			arraySizeCategories[strSizeCategoryDimension] = {strSizeDimensionValueId: arraySizeValues[0], strSizeDimensionAlphaSize: arraySizeValues[1]};
		}
	},
    isQuickLookOpen : false,
    initializing: false,
	openQuickLookLauncher : function(strProductId,strDefaultStyleColor,strCategoryId,strVariantId,targetImg, isCrossSell, brandCode, customProperties) {
		if (this.isLoaded) {
			var blnOpen = false;
			if (this.isQuickLookOpen) {
				if (this.objP.strProductId != strProductId) blnOpen = true;
			} else {
				blnOpen = true;
			}
            brandCode = brandCode||gidBrandSiteConstruct.currentBrandCode;

            var constants = customProperties || brandProperties;

            if (blnOpen) {
				var targetObj = $(targetImg);
                if(!targetObj) return;
                var cumulativeOffset = Position.cumulativeOffset(targetObj);
                var realOffset =  Position.realOffset(targetObj);
                var scrollTop = (document.documentElement && document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop);
                var scrollLeft = (document.documentElement && document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft);
                var position = [cumulativeOffset[0]-(realOffset[0]-scrollLeft),cumulativeOffset[1]-(realOffset[1]-scrollTop)];
				/* targetImgCount is required to test strDefaultStyleColor the imageLoader.categoryProductsMap
                 * is constructed differently based on whether the item is STYLECOLOR or not in the JSP */
                var targetImgCount = targetImg.split("_");
				var isStyleColor = ( targetImgCount.length == 3 );
                var imgLoaderObjRef = strCategoryId + "_" + (isStyleColor ? strProductId : strDefaultStyleColor) + "_" + strDefaultStyleColor;
                if(!isCrossSell  && (window['imageLoader'] && imageLoader.categoryProductsMap && imageLoader.categoryProductsMap[imgLoaderObjRef])){
	                var selectedStyleID = imageLoader.categoryProductsMap[imgLoaderObjRef].selectedScid;
	                strDefaultStyleColor = selectedStyleID;
                }
                var target = this.objQuickLookTarget = {
					strProductId : strProductId,
					strDefaultStyleColor : strDefaultStyleColor,
					strCategoryId : strCategoryId,
					strVariantId : strVariantId,
					position : position,
					isCrossSell: isCrossSell,
					obj : targetObj,
                    brandCode: brandCode
                };

                var targetWidth = target.obj.width;
                var targetHeight = target.obj.height;
                this.quickLookLauncherBoundBox = {top:target.position[1],left:target.position[0],right:target.position[0] + targetWidth, btm:target.position[1] + targetHeight};
                this.objQuickLookLauncher.setStyle({left: (target.position[0] + (targetWidth * 0.5) - this.launcherHalfWidth) + 'px',
					top: (target.position[1] + (targetHeight * 0.8 - this.launcherHalfHeight)) + 'px', visibility: 'visible'});

                objGIDPageViewAdapter.initializeGidProducts();
                quickLook.isLoaded = true;
            }
		}
	},
    logTeaLeafEvent: function(itemSource, eventType) {
        var tlevt = new TeaLeaf.Event("GUI", eventType);

        var tlAddNameValueArray = ["Name", itemSource.name,
                                     "Id", itemSource.id,
                                     "ElementType", itemSource.type,
                                     "TagName", itemSource.tagName,
                                     "XPath", TeaLeaf.Client.tlGetXPathFromNode(itemSource)];
        tlevt.tlAddData(tlAddNameValueArray);
        tlevt.tlSend();
    },

	closeQuickLookLauncher : function(e) {
		if(e && this.quickLookLauncherBoundBox && !gidLib.isMouseOut(Event.pointerX(e), Event.pointerY(e), this.quickLookLauncherBoundBox)) {
			return;
		}
		if(this.isLoaded && this.objQuickLookTarget) {
			this.objQuickLookLauncher.style.visibility = 'hidden';
			//TODO: AAL - why are we resetting this here? Due to WCD?
			this.objQuickLookLauncher.src = '/gid/assets/common/quicklook/'+brandConst.BRAND_LOCALE+'/button_quicklook_launcher_on.gif';
			this.objQuickLookTarget.strProductId = 0;
		}
	},

	QuickLookDivObjMap : {
		objStyleMupMessage : 'quickLookMupMessageStyle',
		objGIDPromoMessage : 'quickLookGIDPromoMessage',
		objStyleColorMarketingFlag : 'quickLookMarketingFlagColor',
		objSkuMarketingFlag : 'quickLookMarketingFlagSize',
		objStyleMarketingFlag : 'quickLookMarketingFlagStyle',
		objStyleMarketingCallOut : 'quickLookMarketingCallOut',
		objSoldOutMsg : "productSoldOutMsg",
		objOnOrderMsg : "productOnOrderMsg",
	   objColorErrorMsg : "productColorError",
	   objSizeDimension1ErrorMsg : "productSizeDimension1Error",
	   objSizeDimension2ErrorMsg : "productSizeDimension2Error",
	   objBagErrorMsg : "productBagError",
		objTextSizeDimension1 : 'quickLookSize1Text',
		objTextSizeDimension2 : 'quickLookSize2Text',
		objTextColor : 'quickLookColorText',
		objPriceText : 'quickLookPriceText',
		objAddtoBag : 'quickLookAddtoBag',
		objSelectedConfirmText : 'quickLookConfirmText',
		objInventoryStatusWindow : 'quickLookInventoryStatusWindow',
		objTabArea : 'quickLookTabArea',
		objQtyDropDown : 'quickLookQty',
		size1Swatches : 'quickLookSize1Swatches',
		size2Swatches : 'quickLookSize2Swatches',
		colorSwatches : 'quickLookColorSwatches',
		objProductOriginCopy : 'quickLookProductOriginCopy',
		objInlineBagError : 'quickLookPageError',
		p1Image : 'quicklook_product_image',
        p1ImageHolder: 'quickLookProductImage',
        p1OutfitImage: 'quicklook_outfit_image'
	 },

	baseInitializeData : GID.Browse.Base.initializeData,
	baseInitializeVariant : GID.Browse.Base.initializeVariant,

	strDefaultCategoryId : getQuerystringParam('cid'),
	type: 'quickLook',
	p1Type : 'QL',
	thumbType: 'T',
	omniView : 'setQuickLookView',
	/**
	 * overites GID.Browse.Base variant method, calls original method 1st
	 * @param strProductId
	 * @param strVariantId
	 */
	initializeVariant : function(strProductId,strVariantId) {
		this.baseInitializeVariant(strProductId,strVariantId);
		this.loadQuickLook();
		this.setVariantToReportingService(strVariantId);
	},
	/**
	 * opens quicklook, checks if ajax data is cached, if not fetch data using ajax (whose callback will in turn call this function again)
	 * @param strTarget
	 */
	launchQuickLook : function(strTarget) {
        this.logTeaLeafEvent(this.objQuickLookTarget.obj,'mouseover');
        //this.logTeaLeafEvent(this.objQuickLookLauncher,'click');
        TeaLeaf.Client.tlProcessNode('quickLookWindow');

		var strProductId = this.objQuickLookTarget.strProductId, isCrossSell = this.objQuickLookTarget.isCrossSell;
		var strStyleColorId = (this.objQuickLookTarget.strDefaultStyleColor != -1 ? this.objQuickLookTarget.strDefaultStyleColor : undefined);

        if (strTarget && strTarget.getDimensions) {
			this.objQuickLookTarget.position = returnObjPosition(strTarget);
			this.objQuickLookTarget.obj = $(strTarget);
        }

		if (!objInlineBag.isOpen) {
			if (!this.isQuickLookOpen || brandConst.QUICKLOOK_ALLOW_MULTIPLE) {
                this.brandCode = this.objQuickLookTarget.brandCode;
                this.brandSite = gidBrandSiteConstruct.gidBrandSites[this.brandCode];
                this.resources.BTN_PATH = this.brandSite.unsecureUrl + brandConst.resources.BTN_PATH;
                this.resources.ASSET_PATH = this.brandSite.unsecureUrl + brandConst.resources.ASSET_PATH;
                var product = objGIDPageViewAdapter.objGIDProducts.arrayProducts[strProductId];
				if (product) {
					this.closeQuickLookLauncher();
					this.dataLoaderAction = "auto";
					this.isQuickLookOpen = true;
					this.initializeData(strProductId);
                    this.setupQuickLookBoundBox();
					this.loadQuickLookAnimate();
					if(isCrossSell) { this.goCrossSell(strProductId);}
					if(!(window['reportingService']||{}).isActive) {
						this.initializeOmni();
					}
					else {
						this.setReportingService();
					}
					this.reopenQuickLook = false;

					document.fire("quicklook:load");
				} else {
					document.fire("quicklook:request");
					if (!this.isFromSBS) {
						var variantId = this.strDefaultStyleVariant;
						if (this.objQuickLookTarget.strVariantId && this.objQuickLookTarget.strVariantId != "-1") {
							variantId = this.objQuickLookTarget.strVariantId;
						}
						this.loadProductData(strProductId,"auto",variantId,strStyleColorId, this.brandCode);
					} else {
						this.loadProductData(strProductId,"auto",undefined,strStyleColorId, this.brandCode);
					}
				}
			} else {
				this.reopenQuickLook = true;
				this.closeQuickLook();
			}
		} else {
			setTimeout('quickLook.launchQuickLook()', 250);
		}
	},

	/**
	 * overwrites base intializedata
	 * @param strProductId
	 * @param strVariantId
	 */
	initializeData : function(strProductId,strVariantId) {
		this.baseInitializeData(strProductId,strVariantId);
		this.initializeTabs();
		this.productThumbs = new ProductThumbs(this);
	},

	/**
	 * finds the overview tab index for quicklook
	 * @param tab
	 */
	findOverviewTab : function(tab) {
		this.objP.arrayInfoTabs.overviewIndex++;
		return tab.strInfoTabId == this.OVERVIEWTABID;
	},

	/**
	 * initialize tabs for quicklook
	 */
	initializeTabs : function() {
		this.objP.arrayInfoTabs.overviewIndex = -1;
		this.objP.arrayInfoTabs.find(this.findOverviewTab.bind(this));
		this.intTabOverviewIndex = this.objP.arrayInfoTabs.overviewIndex;
	},

	/**
	 * defines a bound box base on browser dimension and scroll length, so quicklook always opens
	 * within the window
	 */
	setupQuickLookBoundBox : function() {
		if(!document.body) return;
		this.minX = window.pageXOffset || document.documentElement.scrollLeft;
		this.minY = window.pageYOffset || document.documentElement.scrollTop;
		this.browserW = window.innerWidth || document.documentElement.clientWidth;
		this.browserH = window.innerHeight || document.documentElement.clientHeight;
		this.maxX = this.browserW + this.minX;
		this.maxY = this.browserH + this.minY;
	},

	/**
	 * opens quicklook with scriptaculous fade effect
	 */
	loadQuickLookAnimate : function() {
		this.loadQuickLook();
		this.objQuickLook.addClassName('brand' + brandConst.BRAND_CODE);
		this.quickLookHeight = this.objQuickLook.offsetHeight;
		this.quickLookWidth = this.objQuickLook.offsetWidth;
		this.quickLookX = this.objQuickLookTarget.position[0] - this.objQuickLook.offsetWidth * 0.5;
		this.quickLookY = this.objQuickLookTarget.position[1] - this.objQuickLook.offsetHeight * 0.5;

		 var x = this.quickLookX < this.minX ? this.minX + 50 : this.quickLookX > this.maxX - this.quickLookWidth ? this.maxX - this.quickLookWidth - 50 : this.quickLookX;
		var y = this.quickLookY < this.minY ? this.minY + 50 : this.quickLookY > this.maxY - this.quickLookHeight? this.maxY - this.quickLookHeight - 50 : this.quickLookY;

		this.objQuickLook.style.display = 'none';
		setObjPosition(this.objQuickLook, (x<0 ? 50 : x), (y<0? 50 : y));

        new Effect.Appear(this.objQuickLook.id, {duration: 0.5,
            afterFinish:function() {
                quickLook.hiddenDropDowns = gidLib.hideDropDownsUnderElement(quickLook.objQuickLook, 'select[name!="quickLookQty"]');
            }} );
    },

	/**
	 * generates quicklook html and initialize default user states
	 */
	loadQuickLook : function() {
        this.objQuickLook.innerHTML = this.getQuickLookFrame(this.id);
		this.productThumbs.generateThumbs();
		//this.productThumbs.setLargeImage(this.productThumbs.selectedThumb);
		
		if(this.enableDrag) {
			this.objP.draggable = new Draggable(this.objQuickLook.id, {handle:'quickLookWindowTop',
                onStart: function() { gidLib.showDropDowns(quickLook.hiddenDropDowns); },
                onEnd: function() { quickLook.hiddenDropDowns = gidLib.hideDropDownsUnderElement(quickLook.objQuickLook, 'select[name!="quickLookQty"]'); }
            });
		}
		gidLib.loadDomObjMap(this, this.commonDomObjMap);
		gidLib.loadDomObjMap(this, this.QuickLookDivObjMap);
		this.setSwatchButtons();
		//this.productThumbs.setLargeImage(this.productThumbs.selectedThumb);
		this.setCommonDimensionSizeWidth(1, this.size1Swatches, this.MINIMUMSIZESWATCHWIDTH);
		this.setCommonDimensionSizeWidth(2, this.size2Swatches);

		this.initializePromotions();
		this.initializeMarketingFlags();
		this.setQtyDropDown();
		this.setInfoTabArea();
		this.setProductOriginCopy();
		this.setGIDPromoMessage();

		this.setColorSwatches();
		this.setSizeDimensionSwatches(1, this.size1Swatches);
		this.setSizeDimensionSwatches(2, this.size2Swatches);
		this.updateDataLabels();
	},

	/**
	 * prints product detail html
	 */
	setInfoTabArea : function() {
		if(this.intTabOverviewIndex >= 0) {
			this.objTabArea = replaceHTML(this.objTabArea,
					this.templates.OVERVIEW_TEMP.evaluate({app: this.type, content: this.getOverViewTabHTML(), id: this.objP.strProductId, link: this.strProductDetailLink}) +
					'<div id="quickLookProductVendorId" style="display:none;"></div>');
		}
		this.objProductVendorInfo = $('quickLookProductVendorId');
	},

	/**
	 * close quicklook with fade effect
	 */
	closeQuickLook : function() {
		/**
		 * helper method to handle quicklook after close event
		 * @see closeQuickLook
		 */
		var hlpContext = this;
		function closeQuickLookHelper() {
			with(hlpContext) {
                gidLib.showDropDowns(quickLook.hiddenDropDowns);
                clearQuickLook();
				isQuickLookOpen = false;
				closeRelatedWindows();
				if(reopenQuickLook) {
					launchQuickLook();
				}
			}
		}
		new Effect.Fade(this.objQuickLook, {
			afterFinish: closeQuickLookHelper, duration: 0.5});
	},

	/**
	 * resets quicklook div position, and clear user states
	 */
	clearQuickLook : function() {
		this.clearErrors();
        this.objQuickLook.removeClassName('brand' + this.brandCode);
        this.resources.ASSET_PATH = brandConst.resources.ASSET_PATH;
        this.resources.BTN_PATH = brandConst.resources.BTN_PATH;
		this.objQuickLook.setStyle({top: '-5000px', left: '0px', display:'block'});
		this.objSoldOutMsg.style.visibility = "hidden";
		this.objOnOrderMsg.style.visibility = "hidden";
		if (objInlineBag && (!objInlineBag.isOpen && !objInlineBag.doOpenBag)) gidLib.showDropDowns(this.hiddenDropDowns);
		this.objSoldOutMsg.update();
		this.objOnOrderMsg.update();
		this.activeColor = -1;
		this.activeSizeDimension1 = -1;
		this.activeSizeDimension2 = -1;
		this.selectedColor = -1;
		this.selectedSizeDimension1 = -1;
		this.selectedSizeDimension2 = -1;
		this.selectedColorName = "";
		this.selectedSizeDimension1Name = "";
		this.selectedSizeDimension2Name = "";
		this.intTab = 0;
		this.arrayAllSizeDimension1 = [];
		this.arrayAllSizeDimension2 = [];
		this.strSelectedQty = 1;
		this.dataLoaderAction = "";
	},

	/**
	 * methods to handle quicklook tab mouse over/out events
	 * @param n
	 */
	tabOver : function(n) {
		if (n != this.intTab) {
			var className = 'infoTab over';
            $('tab' + n).className = className;
		}
	},

	tabOut : function(n) {
		if (n != this.intTab) {
			var className = 'infoTab off';
            $('tab' + n).className = className;
		}
	},

	setTab : function(n) {
		if (n != this.intTab) {
			$('tab' + this.intTab).className = 'infoTab off';
			$('tab' + n).className = 'infoTab on';
			this.clearErrors();
			this.setTabContent(n);
			this.intTab = n;
		}
	},

	setTabContent : function(n) {
		if (n == 0) {
			this.objTabArea.style.display = "none";
			$("quickLookSwatches").style.display = "block";
		} else {
			var intH = $("quickLookSwatches").offsetHeight;
			this.objTabArea.style.height = (intH + this.TABCONTENTOFFSET) + "px";
			this.objTabArea.style.display = "block";
			$("quickLookSwatches").style.display = "none";
		}
	},

	postLoadData : function() {
		var objC = objGIDPageViewAdapter.objGIDCategory;
		if (this.intDataLoadCounter < objC.arrayProducts.length) {
			var strProductId = objC.arrayProducts[this.intDataLoadCounter].strCatalogItemID;
			if (objGIDPageViewAdapter.objGIDProducts.arrayProducts[strProductId] == null)  {
				this.loadProductData(strProductId);
			} else {
				this.loadNextProduct();
			}
		}
	},

	/**
	 * generates swatch html for quicklook
	 */
	getQuickLookSwatchContent : function() {
		quickLookSize1Class = "";
		var variants = (this.objP.hasSplitVariants || this.objP.hasMergeVariants ? this.templates.VARIANTS_TEMP.evaluate({variants:this.getVariants()}) : '');
		var size1Swatches = (this.objV.objStyleSizeInfo.intSizeDimensionsCount >=1 ? this.templates.SIZE_DIM1_TEMP.evaluate({size1Swatches:this.getSizeDimensionSwatches(1)}) : '');
		var size2Swatches = (this.objV.objStyleSizeInfo.intSizeDimensionsCount == 2 ? this.templates.SIZE_DIM2_TEMP.evaluate({size2Swatches: this.getSizeDimensionSwatches(2)}) : '');
		if (size2Swatches == '') {
			size1Swatches += variants;
		} else {
			size2Swatches += variants;
			quickLookSize1Class = "quickLookHalfSize";
		}


		return this.templates.QL_SWATCH_CONTENT.evaluate({
			colorSwatches : this.templates.COLOR_SWATCH_TEMP.evaluate({colorSwatches:this.getColorSwatches()}),
			size1Swatches : size1Swatches,
			size2Swatches : size2Swatches,
			variants : variants,
			quickLookSize1Class : quickLookSize1Class
		});
	},

    arrayTabImages: [{
            objOff: "tab_size_off",
            objOver:"tab_size_on",
            objOn: "tab_size_over"
        },
        {
            objOff : "tab_overview_off",
            objOn : "tab_overview_on",
            objOver : "tab_overview_over"
    }],

	/**
	 * generates html for quicklook
	 * @param id
	 */
	getQuickLookFrame : function(id) {
        var qlFrameMarkup = this.templates.QUICK_LOOK.evaluate({
			swatches : this.getQuickLookSwatchContent(),
			imgTools : (this.objV.arrayVariantStyleColors[0].hasLargerImage || this.objP.hasAlternateImage) ?
			 this.templates.MORE_VIEWS.evaluate({btnPath: this.resources.BTN_PATH, app: this.type}): '',
            brandName: 'brand' + this.brandCode,
            badgePath: brandConst["BRAND"+ brandConst.BRAND_CODE +"_BADGE_CONTENT_PATH"],
			id : id||this.id,
			mainImg : this.objV.arrayVariantStyleColors[this.activeColor].styleColorImagesMap['QL'].src,
			width : this.QUICKLOOKIMGWIDTH,
			height : this.QUICKLOOKIMGHEIGHT,
			productName : this.objP.strProductStyleName,
			vendorName : this.objP.strVendorName,
			assetPath : this.resources.ASSET_PATH,
            qtyLabel : this.strQuantityLabel,
			giftCard :  !this.objP.isGiftCard && (this.objP.sizeChartId||'') != '' ? this.templates.QL_SIZE_CHART.evaluate({sizeChart:resourceBundleValues.product.sizeChart}): '',
			confirmation : this.templates.CONFIRM_AREA.evaluate({id:this.id}),
			closeText : resourceBundleValues.product.closeQuicklook,
			pid: this.objP.strProductId,
			productReturnSource : quickLook.productReturnSource
		});
        return qlFrameMarkup;
	},

	/**
	 * print avalible variants as links
	 */
	getVariants : function() {
		var str = ['<p>' + this.strOtherVariants + ' '];
		var hlpContext = this;

		function getVariantHelper(array, variant) {
			if(variant && variant != hlpContext.objV) {
				array.push(hlpContext.templates.VARIANT.evaluate({
							catalogItemId : variant.strCatalogItemId,
							variantId : variant.strVariantId,
							name : variant.strVariantName.toLowerCase(),
							app : hlpContext.type
						}));
			}
			return array;
		}
		Object.values(this.objP.arrayVariantStyles).inject(str, getVariantHelper);
		str.push('</p><p class="legal">(' + this.strVariantLegalCopy + ')</p>');
		return str.join('');
	}
});

/**
 * ProductImages contains img objects for the current variant
 * @constructor
 * @author yoshi
 */

var PostLoadImage = Class.create();
PostLoadImage.prototype = {
	isLoaded: false,
	initialize: function(cropType) {this.cropType = cropType;},
	load: function(i, type, src, isPostLoad) {
		if(this.isLoaded) {
			ProductZoom.isImageReady = true;
			ProductZoom.imageLoadHandler({src: src});
			return;
		} else if(!isPostLoad) {
			document.fire("imageFetch:begin");
			
		}
		
		//alert('img loading, i:' + i + ' type:' + type + ' src: ' + src);
		ProductZoom.dragImg.addClassName('zoomLoading');
		ProductZoom.isImageReady = false;
		var img = new Image();
		img.type = type;
		img.index = i;
		img.imageType = this.cropType;
		
		var onloadEvent = isPostLoad ? 'imagePostLoadHandler' : 'imageLoadHandler';
		img.onload = ProductZoom[onloadEvent];
		//img.onabort = ProductZoom.imageAbortHandler;
		//img.onerror = ProductZoom.imageAbortHandler;
		
		if(clientBrowser.isIE7){
		    function testImg(){
		        if(img.complete != null && img.complete == true){ 
		        		img.onload();
		                return;
		        }
		        setTimeout(testImg, 1000);
		    }
		    setTimeout(testImg, 1000);
		}

		
		img.src = src;
	}
};

var productImagesConstructor = {
	templates : {
		ALT_TEXT_THUMB : resourceBundleValues.product.additionalView+' '
	},
	ALT_VIEW_IMG_TYPES: ['P01', 'VLI', 'Z', 'T', 'VI', 'QL'],
	POST_LOAD_TYPES: {
		VLI: true, AV1_VLI: true, AV2_VLI:true, AV3_VLI:true, AV4_VLI:true, AV5_VLI:true, AV6_VLI:true, AV7_VLI:true, AV8_VLI:true, AV9_VLI:true,
		Z:true,AV1_Z: true, AV2_Z:true, AV3_Z:true, AV4_Z:true, AV5_Z:true, AV6_Z:true, AV7_Z:true, AV8_Z:true, AV9_Z:true
	},
	OLD_CTNT_TYPE_TO_NEW_MAP: {
		LFT: ['AV1_T'],
		LFVLI: ['AV1_VLI', 'AV1', 'AV1_VI', 'AV1_QL'],
		LZ: ['AV1_Z'],
		DRT: ['AV2_T'],
		DRVLI: ['AV2_VLI', 'AV2', 'AV2_VI', 'AV2_QL'],
		RZ: ['AV2_Z'],
		TPT: ['AV3_T'],
		TPVLI: ['AV3_VLI', 'AV3', 'AV3_VI', 'AV3_QL'],
		TZ: ['AV3_Z'],
		BTT: ['AV4_T'],
		BTVLI: ['AV4_VLI', 'AV4', 'AV4_VI', 'AV4_QL'],
		BTMZ: ['AV4_Z'],
		FRT: ['AV5_T'],
		FRVLI: ['AV5_VLI', 'AV5', 'AV5_VI', 'AV5_QL'],
		FRTZ: ['AV5_Z'],
		BKT: ['AV6_T'],
		BKVLI: ['AV6_VLI', 'AV6', 'AV6_VI', 'AV6_QL'],
		BKZ: ['AV6_Z']
	},
	IMAGE_TYPES: {
		P01:'Main', AV1:'Main', AV2:'Main', AV3:'Main', AV4:'Main', AV5:'Main', AV6:'Main', AV7:'Main', AV8:'Main', AV9:'Main',
		QL:'QuickLook', AV1_QL:'QuickLook', AV2_QL:'QuickLook', AV3_QL:'QuickLook', AV4_QL:'QuickLook', AV5_QL:'QuickLook', AV6_QL:'QuickLook', AV7_QL:'QuickLook', AV8_QL:'QuickLook', AV9_QL:'QuickLook',
		VI:'Category', AV1_VI:'Category', AV2_VI:'Category', AV3_VI:'Category', AV4_VI:'Category', AV5_VI:'Category', AV6_VI:'Category', AV7_VI:'Category', AV8_VI:'Category', AV9_VI:'Category',
		T:'Thumb', AV1_T:'Thumb', AV2_T:'Thumb', AV3_T:'Thumb', AV4_T:'Thumb', AV5_T:'Thumb', AV6_T:'Thumb', AV7_T:'Thumb', AV8_T:'Thumb', AV9_T:'Thumb',
		VLI:'Large', AV1_VLI:'Large', AV2_VLI:'Large', AV3_VLI:'Large', AV4_VLI:'Large', AV5_VLI:'Large', AV6_VLI:'Large', AV7_VLI:'Large', AV8_VLI:'Large', AV9_VLI:'Large',
		Z:'Zoom', AV1_Z:'Zoom', AV2_Z:'Zoom', AV3_Z:'Zoom', AV4_Z:'Zoom', AV5_Z:'Zoom', AV6_Z:'Zoom', AV7_Z:'Zoom', AV8_Z:'Zoom', AV9_Z:'Zoom',
		OVI1:'Outfit', OVI2:'Outfit',
		S:'Swatch'

	},
	STYLE_IMG_NAME_MAP: {
		Z: 'Zoom', T: 'Thumb', VLI: '', VI: '', QL: '', P01: ''
	},
	getImageObj: function(context) {
		var color = context.color;
		var imageType = context.imgType;
		var imageTypeName = context.imgTypeName;
		var postLoadTypes = productImagesConstructor.POST_LOAD_TYPES;
		
		var imgObj = postLoadTypes[imageType] ? new PostLoadImage(imageType) : new Image();
		Object.extend(imgObj, {
			src: context.path,
		    name: color ? color.strColorName : '',
		    altText: color ? color.strColorName + ' '+ resourceBundleValues.product.productText +
		    ((imageTypeName == 'Thumb') ? ' '+ resourceBundleValues.product.thumbnail : '')
		    + ' '+resourceBundleValues.product.imageText : '',
		    type: imageTypeName == 'Outfit' || context.isOutfit ? 'outfit' : 'color',
		    thumbType: (imageType.split('_')||[])[0]||imageType,
		    color: color ? context.colorIndex : ''
		});
		
		return imgObj;
	},
	
	createStyleImages: function(context, styleImage) {
		var altViewImageTypes = productImagesConstructor.ALT_VIEW_IMG_TYPES;
		var styleImageNameMap = productImagesConstructor.STYLE_IMG_NAME_MAP;
		var imageTypesMap = productImagesConstructor.IMAGE_TYPES;
		var styleImagesMap = context.styleImagesMap;
		var isOutfit = styleImage.isOutfit;

		var avName = 'AV' + context.avCount;
		var isOutfit = styleImage.isOutfit;
		for(var i=0; i<altViewImageTypes.length; i++) {
			var suffix = altViewImageTypes[i];
			var type = styleImageNameMap[suffix];
			var imageType = avName + (suffix == 'P01' ? '' : '_' + suffix);
			
			var path =  styleImage['str' + type + 'ImagePath'];
			if(!path) continue;
			
			context.imgTypeName = imageTypesMap[imageType];
			context.path = path;
			context.imgType = imageType;
			context.isOutfit = isOutfit;
			
			styleImagesMap[imageType] = productImagesConstructor.getImageObj(context);
			context.isOutfit = false;
		}
		context.avCount = context.avCount + 1;

		
		return context;
	},
	
	processStyleImages: function(app) {
		
		var product = app.objP;
		var styleImages = product.objProductStyleImages;
		
		if(!styleImages) return;
		
		var context = {styleImagesMap: {}, avCount: 1};
		styleImages.arrayAlternateViewImages.inject(context, productImagesConstructor.createStyleImages);
		styleImages.arrayWaysToWearImages.inject(context, productImagesConstructor.createStyleImages);
		
		if( product.objCrossSellInfo && product.objCrossSellInfo.isPhotoOutfit ) {
			var crossSell = product.objCrossSellInfo;
			var crossSellObj = {};
			crossSellObj.strThumbImagePath = crossSell.strImageSrc;
			crossSellObj.strImagePath = crossSell.strImageSrc.replace('oviv','odv');
			crossSellObj.isOutfit = true;
			var crossSellArray = new Array();
			crossSellArray.push(crossSellObj);
			crossSellArray.inject(context, productImagesConstructor.createStyleImages);
		}
		
		product.styleImagesMap = context.styleImagesMap;
	},
	
	processImage: function(context, imageType, index) {
		var imageTypesMap = productImagesConstructor.IMAGE_TYPES;
		var color = context.color;
		var styleColorImagesMap = color.styleColorImagesMap;
		var path = styleColorImagesMap[imageType];
		if(!path) { return context; }
		
		var oldToNewMap = productImagesConstructor.OLD_CTNT_TYPE_TO_NEW_MAP;
		var imageTypes = [imageType];
		if(!imageTypesMap[imageType]) {
			var imageTypes = oldToNewMap[imageType];
			if(!imageTypes) { return context; }
		}
		
		for(var i=0; i<imageTypes.length; i++) {
			var imgType = imageTypes[i];
			
			if(styleColorImagesMap[imgType] && !imageTypesMap[imageType]) {
				continue;
			}
			
			context.path = path;
			context.imgTypeName = imageTypesMap[imgType];
			context.imgType = imgType;
			
			styleColorImagesMap[imgType] = productImagesConstructor.getImageObj(context);
		}
		
		return context;
	},
	
	processColorImages: function(context, color, index) {
		var imageTypes = context.imageTypes;
		var styleColorImagesMap = color.styleColorImagesMap;
		context.avCount = 0;
		context.thumbCount = 0;
		context.zoomCount = 0;
		context.color = color;
		context.colorIndex = index;
		$H(styleColorImagesMap).keys().inject(context, productImagesConstructor.processImage);
		
		/*productImagesConstructor.processStyleImages(context);
		if(context.thumbCount > 0) {
			color.showThumbs = true;
		}*/
		
		return context;
	},
	preProcessImages: function() {
		var app = productImagesConstructor.app;
		var objV = app.objV;
		var colors = objV.arrayVariantStyleColors;
		var context = {};
		colors.inject(context, productImagesConstructor.processColorImages);
		
		productImagesConstructor.processStyleImages(app);
	},	
    		
    
/**
 * populates internal img array from parent, could be product page, or quicklook, etc
 * @param product
 * @param app
 * @author yoshi
 */
    initializeProductImages : function() {
    	var app = productImagesConstructor.app;
		var objV = app.objV;
		if(objV.isImagesInitialized) return;
		var product = app.objP;
		var styleImages = product.objProductStyleImages;
		productImagesConstructor.preProcessImages();
		objV.isImagesInitialized = true;
	}
};

/**
 * ProductThumbs Class - contains functions related to alt view images and nanos
 * @constructor
 * @author yoshi
 */
var ProductThumbs = Class.create();
ProductThumbs.prototype = {
	maxThumbCount: 9,
	THUMBS_ARRAY: {
		productPage:['T', 'AV1_T', 'AV2_T', 'AV3_T', 'AV4_T', 'AV5_T', 'AV6_T', 'AV7_T', 'AV8_T', 'AV9_T'],
		viewLarger:['QL', 'AV1_QL', 'AV2_QL', 'AV3_QL', 'AV4_QL', 'AV5_QL', 'AV6_QL', 'AV7_QL', 'AV8_QL', 'AV9_QL'],
		quickLook:['T', 'AV1_T', 'AV2_T', 'AV3_T', 'AV4_T', 'AV5_T', 'AV6_T', 'AV7_T', 'AV8_T', 'AV9_T']
	},
	// html template for thumbnails
	THUMBSTAG : new Template( '<input type="image" class="#{className}" color="#{color}" thumbType="#{thumbType}" thumbImageType="#{thumbImageType}" onclick="#{app}.productThumbs.setLargeImage(this, true);" onmouseover="#{app}.productThumbs.chgPreviewImg(this, \'over\');" onmouseout="#{app}.productThumbs.chgPreviewImg(this, \'out\');" index="#{index}" alt="#{altText}" id="thumbImage#{index}#{id}" src="#{src}"/>'),
	dummyImgPath : '/assets/common/navigation/en_US/indicator-large.gif',
	p1TypeMap: {
		viewLarger: 'VLI',
		productPage: 'P01',
		quickLook: 'QL',
		shoppingBagPage: 'VI',
		outfitPage: 'VI'
	},
	initialize : function(app) {
		this.app = app;
		this.productClassTypId = this.app.objP? this.app.objP.productClassTypId : this.app.selectedColor;
		this.objV = this.app.objV;
		this.zoom = this.app.ProductZoom;
		this.p1type = this.app.p1type;
		this.showOutfitThumb = true;
	},

	generateThumbHelper: function(accumulator, thumb, index) {
		if(!(thumb.type == 'outfit') || accumulator.showOutfitThumb) {
			accumulator.html.push(accumulator.THUMBSTAG.evaluate(
				{
					className: accumulator.thumbClass,
					index : index,
					id : accumulator.id,
					src : thumb.src,
					app : accumulator.type,
					altText : thumb.altText,
					thumbType: thumb.thumbType,
					thumbImageType: thumb.type,
					color: thumb.color
				}));
		}
		return accumulator;
	},

	getColorThumbsArray: function(styleColorImagesMap, thumbCount, isFirst) {
		var thumbsArray = this.THUMBS_ARRAY[this.app.type];
		var thumbObjArray = [];
		
		for(var i=0; i<thumbsArray.length; i++) {
			if(thumbCount > this.maxThumbCount) break;
			var thumbType = thumbsArray[i];
			if(!isFirst && thumbType == this.app.thumbType) continue;
			var thumbObj = styleColorImagesMap[thumbType];
			if(!thumbObj) continue;
		
			thumbObjArray.push(thumbObj);
			thumbCount = thumbCount + 1;
		}
		
		return thumbObjArray;
	},
	
	getThumbsArray: function(firstColor, thumbType) {
		var colors = this.objV.arrayVariantStyleColors;
		var thumbCount = 0;
		var thumbObjArray = [];
		var firstThumbObjArray = this.getColorThumbsArray(firstColor.styleColorImagesMap, thumbCount, true);
		thumbCount = thumbCount + firstThumbObjArray.length;
		
		if(brandConst.isCombineColorImages) {
			for(var i=0; i<colors.length; i++) {
				var color = colors[i];
				var styleColorImagesMap = color.styleColorImagesMap;
				
				if(color.strColorCodeId == firstColor.strColorCodeId) continue;
				
				var colorThumbs = this.getColorThumbsArray(styleColorImagesMap, thumbCount);
				thumbObjArray = thumbObjArray.concat(colorThumbs);
				thumbCount = thumbCount + colorThumbs.length;
			}
		}
		
		var styleImagesMap = this.app.objP.styleImagesMap;
		if(thumbCount < this.maxThumbCount && styleImagesMap) {
			var styleThumbs = this.getColorThumbsArray(styleImagesMap, thumbCount);
			thumbObjArray = thumbObjArray.concat(styleThumbs);
			thumbCount = thumbCount + styleThumbs.length;
		}
		
		firstColor.showThumbs = thumbCount > 1;
		var thumbsObjArray = firstThumbObjArray.concat(thumbObjArray);
		return thumbsObjArray;
	},
/**
 * generates thumbs html
 * @param {boolean} regenerate force html generation, even if html is already generated for this variant
 * @author yoshi
 */
	generateThumbs :  function(regenerate) {

		var id = (this.app.id||'');
		var type = this.app.type;
		this.thumbContainer = $('imageThumbs' + id );
		var objV = this.objV;
		
		var color = objV.arrayVariantStyleColors[this.app.selectedColor];
		
		if(!color['thumbnails']) {
			color['thumbnails'] = {};
		}
		
		var thumbnails = color.thumbnails;
		if(!thumbnails[type]) {
			thumbnails[type] = {};
		}
		
		if(!thumbnails[type].thumbHTML || regenerate)  {
			var thumbType = this.app.thumbType;
			var colorThumbs = this.getThumbsArray(color, thumbType);
			if(!colorThumbs || colorThumbs.length <= 1) return;
			
			var thumbClasses = this.getThumbClasses();
			var accumulator = {
					html: [],
					showOutfitThumb: this.showOutfitThumb,
					thumbClass: thumbClasses.normal,
					id: (this.app.id||''),
					type: type,
					THUMBSTAG: this.THUMBSTAG
			};
			
			colorThumbs.inject(accumulator, this.generateThumbHelper);
			thumbnails[type].thumbHTML = accumulator.html.join('');
		 }
		this.thumbContainer.innerHTML = thumbnails[type].thumbHTML;
		thumbnails[type].thumbs = $$('#imageThumbs' + id + ' input[type="image"]');
		thumbnails[type].p1Thumb = thumbnails[type].thumbs[0];
		this.selectedThumb = thumbnails[type].p1Thumb;
		this.selectedThumbIndex = 0;
		
		var thumbClasses = this.getThumbClasses();
		this.selectedThumb.className = thumbClasses.selected;
	},

/**
 * updates the main thumb, which is the 1st nth thumb, for BRONG it is the 1st thumb, for PL it is the 1st 7 thumb
 * @param i
 * @author yoshi
 */
	updateP1 : function(i) {
		this.generateThumbs();
	},

/**
 * call from setcolor in GID.Browse.Base, sets color and update p1 accordingly
 * @param i
 * @param swatch
 * @author yoshi
 */
	setColor : function(i, swatch) {
		this.app.selectedColor = i;
		this.app.activeColor = i;
		this.app.selectedColorName = swatch.name;
		var color = this.objV.arrayVariantStyleColors[i];
		
		if(this.thumbContainer) {
			this.updateP1(i);
		}
		if( this.zoom ) {
			this.zoom.setNoZoomMsg('');
			this.zoom.enableZoom = true;
		}
	},

/**
 * call when mouse over
 * @param index
 * @author yoshi
 */
	swatchOver : function(index) {
		this.app.activeColor = index;
		var p1Type = this.p1TypeMap[this.app.type]
		var img = this.objV.arrayVariantStyleColors[index].styleColorImagesMap[p1Type];
		setImgSrc(this.app.p1Image, img.src);
		
	},

/**
 *  called when mouse out
 * @author yoshi
 */
	swatchOut : function() {
		this.chgPreviewImg(null, 'out');
	},

	getImages: function(btn) {
		var colorIndex = (btn ? btn.getAttribute('color') :
			(this.selectedThumb ? this.selectedThumb.getAttribute('color') : this.app.selectedColor));
		if(colorIndex == null || colorIndex.length == 0) {
			imgs = this.app.objP.styleImagesMap;
		} else {
			var colors = this.objV.arrayVariantStyleColors;
			imgs = colors[colorIndex].styleColorImagesMap;
		}
		
		return imgs;
	},
/**
 * sets the main image src
 * @param imgs
 * @param index
 * @author yoshi
 */
	setP1Image : function(btn) {
		var imgs = this.getImages(btn);
		var srcImg = this.app.p1Image;
		srcImg.style.display = 'inline';
		
		var p1Type = this.p1TypeMap[this.app.type];
		var appThumbType = this.app.thumbType;
		var thumbType = btn ? btn.getAttribute('thumbType') : appThumbType;

        var prefix, suffix;
		if(thumbType == this.app.thumbType) {
			prefix = p1Type;
			suffix = '';
		} else {
			prefix = thumbType;
			suffix = p1Type == 'P01' ? '' : '_' + p1Type;
		}

        var img = imgs[prefix + suffix];
		srcImg.src = img.src;
		img.isLoaded = true;
	},

/**
 * fires omniture
 * @param shouldFire
 * @author yoshi
 */
	fireOmniture : function(shouldFire, fromViewLarger) {
		var app = this.parent || this.app;
		if(( !app.nanoViewClicked && shouldFire) || (!app.nanoViewClicked && fromViewLarger && shouldFire)) {
			app.nanoViewClicked = true;
			if (fromViewLarger){
				app.isFromViewLargerPopup = true;
			}
			if(!(reportingService||{}).isActive){
				omni.setAltViews(app.objP.strProductId);
			}
			else {
				this.app.setReportingService('altViews', app);
			}
		}
	},

/**
 * changes the thumb image and updates p1
 * @param btn
 * @param action
 * @param {boolean} isFromViewLarger flag to check if caller is from the view larger window
 * @author yoshi
 */
	chgPreviewImg : function(btn, action, isFromViewLarger) {
		//var color = this.objV.arrayVariantStyleColors[this.app.selectedColor];
		//var index = action=='over' ? Number(btn.getAttribute('index')) : this.selectedThumbIndex;
		this.setP1Image(action == 'out' ? this.selectedThumb : btn);

        if(!btn) return;
        var thumbClasses = this.getThumbClasses();
		btn.className = action=='over' ? thumbClasses.selected : btn != this.selectedThumb ? thumbClasses.normal : thumbClasses.selected;
		
		if(isFromViewLarger){
			this.fireOmniture(true, isFromViewLarger);
		}
	},

	getThumbClasses: function() {
		var classThumbs='thumbs';
	    var classThumbsSelected='thumbSelected';
	    if(this.productClassTypId == brandConst.productClassTypeId.APPAREL && brandConst.BRAND_CODE == 4) {
    	    classThumbs = 'thumbs fiveInARowThumbSize';
    	    classThumbsSelected='thumbSelected fiveInARowThumbSize';
	    }
	    
	    return {normal: classThumbs, selected: classThumbsSelected};
		
	},
	
	setLargeImage : function(btn, isFromThumb) {
		
        var objV = this.objV;
        var selectedColor = this.app.selectedColor;
        var color = objV.arrayVariantStyleColors[selectedColor];
        var thumbnails = color.thumbnails;
        
        btn = btn || this.selectedThumb;
		var index = 0;
		if(btn && color.showThumbs && this.thumbContainer ) {
			var thumbClasses = this.getThumbClasses();
			thumbnails[this.app.type].thumbs[this.selectedThumbIndex].className = thumbClasses.normal;
			
			btn.className = thumbClasses.selected;
			index = Number(btn.getAttribute('index'));
		}
		this.selectedThumbIndex = index;
		this.selectedThumb = btn;
        this.setP1Image(btn);
        var fromViewLarger = this.type == "viewLarger";
		this.fireOmniture(isFromThumb, fromViewLarger);


	}
};

/**
* productPage - Handles functions which are specific to Product Page /browse/product.do
* @constructor
* @author Unknown
* @date 12/04/2007
*/
var productService = Object.clone(GID.Browse.Base);


Object.extend(productService, {
    type: 'productPage',
    p1Type : 'P01',
    thumbType: 'T',
    omniView : 'setProductView',
    strVariantId : "",
	externalOverrides: {
		// An array of RegExp objects
		reviewSuppressionRegExpList : new Array()
	},


	/**
 	* templates - HTML Templates for productPage obj
 	* @constructor
 	* This is a method of class productPage
 	* Modified:  Keo 12/04/07 - Added Comments
 	* @author Unknown
 	*/
    templates : Object.extend(gidLib.clone(GID.Browse.Base.templates), {
    	/**
 		* DIM1_SWATCH - Wrapper HTML for Dimension 1 Set
 		* This is a method of class templates
 		* Modified:  Keo 12/14/07 - Added Comments
 		* @author Unkown
 		*/
        DIM1_SWATCH : new Template(
            '<div id="sizeDimensionSwatchContent"><div id="sizeDimension1SwatchContent"><div id="textSizeDimension1"></div>' +
            '<div id="productPageMarketingFlagSize"></div><div id="sizeDimension1Swatches">#{swatches}</div></div>'),
		/**
 		* DIM2_SWATCH - Wrapper HTML for Dimension 2 Set
 		* This is a method of class templates
 		* Modified:  Keo 12/14/07 - Added Comments
 		* @author Unkown
 		*/
        DIM2_SWATCH : new Template(
            '<div id="sizeDimension2SwatchContent"><div id="textSizeDimension2"></div><div id="sizeDimension2Swatches">#{swatches}</div></div>' +
            '<div id="sizeDimensionDivider"></div><div class="clear"></div>'),
		/**
 		* SIZE_CHART - Wrapper HTML for Size Chart
 		* This is a method of class templates
 		* Modified:  Keo 12/14/07 - Added Comments
 		* @author Unkown
 		*/
        SIZE_CHART : '</div><div class="clear"></div><div id="quickLookSizeChart" align="right"><a href="javascript:productPage.openSizeChart();"><img src="/assets/common/clear.gif" class="quicklook-sprites sprite-sizeChart" border="0" alt="'+resourceBundleValues.product.sizeChart+'"/></a></div>',
        SIZE_CHART_SITEWIDE : '</div><div class="clear"></div><div id="quickLookSizeChart" align="right"><a href="javascript:productPage.openSizeChart();"><img src="/assets/common/clear.gif" class="sizeChartImage" border="0" alt="'+resourceBundleValues.product.sizeChart+'"/></a></div>',
		/**
 		* NO_ZOOM_MSG - Wrapper HTML for No Zoom Message
 		* This is a method of class templates
 		* Modified:  Keo 12/14/07 - Added Comments
 		* @author Unkown
 		*/
        NO_ZOOM_MSG : new Template(
            '<div class="top clearfix outer"><div class="topLeft"></div><div class="topCenter inner"></div><div class="topRight"></div></div><div id="productSoldOutMsgCenter" class="inner">#{msg}</div><div class="bottom clearfix outer"><div class="bottomLeft"></div><div class="bottomCenter inner"></div><div class="bottomRight"></div></div>'),
		/**
 		* PP_SWATCH_CONTENT - Wrapper HTML for Product Swatches
 		* This is a method of class templates
 		* Modified:  Keo 12/14/07 - Added Comments
 		* @author Unkown
 		*/
        PP_SWATCH_CONTENT : new Template(
            '<div id="productNumber">\##{pid}<div id="productPageVendorId" style="display:none;"></div>#{hasVariants}</div><div id="productPageGIDPromoMessage"></div>' +
            '<div id="productPageMupMessageStyle" class="productPageMupMessageStyle quickLookMupMessage"></div><div id="textColor"></div><div id="productPageMarketingFlagColor"></div>' +
            '#{swatch}'),
		/**
 		* SHOP_OUTFIT - Wrapper HTML for Outfit Product
 		* This is a method of class templates
 		* Modified:  Keo 12/14/07 - Added Comments
 		* @author Unkown
 		*/
        SHOP_OUTFIT : new Template('<div class="#{brandName} crossSellProduct"><a href="javascript:productPage.goOutfit();" style="text-align: center;"><img id="crossSellOutfit" src="#{outfit_img}" alt="outfit ideas" /></a><p style="text-align: center;"><a href="javascript:productPage.goOutfit();" class="productName">shop outfit</span></a></p></div>'),
		/**
 		* CROSSSELL_PRODUCT - Wrapper HTML for Cross Sell Items
 		* This is a method of class templates
 		* Modified:  Keo 12/14/07 - Added Comments
 		* @author Unkown
 		*/
        CROSSSELL_PRODUCT : new Template(
                '<div class="#{brandName} crossSellProduct" >' +
                        '<div class="#{brandName} imgDiv">' +
                            '<a href="#{product_link}">' +
                                '<img id="crossSell#{index}" onmouseover="quickLook.openQuickLookLauncher(\'#{id}\',\'#{scid}\', null,-1,\'crossSell#{index}\', true, \'#{brandCode}\');" onmouseout="quickLook.closeQuickLookLauncher(event);" onClick="javascript:productPage.goCrossSell(#{id});" src="#{img_src}" alt="#{img_alt}" class="#{brandName} crossSellImage">' +
                                '#{badging}' +
                            '</a>' +
                        '</div>' +
				'<p><a href="#{product_link}" onclick="javascript:productPage.goCrossSell();" class="productName" >#{nameDisplay}</a></p>' +
				'<p>#{price}</p>' +
				'<p class="productMups" >#{mups}</p></div>')
    }),
	/**
 	* ProductDomMap - Preparing obj and mapping of html elements and id's
 	* This is a method of class productPage
 	* Modified:  Keo 12/04/07 - Added Comments
 	* @author Unknown
 	*/
    ProductDomMap : {
        objSelectedColorText : "currentColorSelectionText",
        p1Image : 'product_image',
        p1ImageHolder: 'product_image_bg',
        p1OutfitImage: 'product_outfit_image',
        objProductNameText : "productNameText",
        objSwatches : "swatchContent",
        objSelectedConfirmText : "selectionConfirmText",
        objPriceText : "priceText",
        objAddtoBag : "addToBagBtn",
        objInventoryStatusWindow : "inventoryStatusWindow",
        objTabWindow : "tabWindow",
        objCrossSellTab : "crossSellBox",
        objQtyDropDown : "qtyDropDown",
        objVariantButtons : "variantButtons",
        objProductOriginCopy : "quickLookProductOriginCopy",
        objInlineBagError : "pageError"
    },
	/**
 	* ProductFlagDomMap - Preparing obj and mapping of html elements and id's
 	* This is a method of class productPage
 	* Modified:  Keo 12/04/07 - Added Comments
 	* @author Unknown
 	*/
    ProductFlagDomMap : {
        objStyleMupMessage : 'productPageMupMessageStyle',
        objGIDPromoMessage : 'productPageGIDPromoMessage',
        objStyleColorMarketingFlag : 'productPageMarketingFlagColor',
        objSkuMarketingFlag : 'productPageMarketingFlagSize',
        objStyleMarketingFlag : 'productPageMarketingFlagStyle',
        objStyleMarketingCallOut : 'productPageMarketingCallOut'
    },
	/**
 	* resources - Define asset path for Product Page
 	* This is a method of class productPage
 	* Modified:  Keo 12/04/07 - Added Comments
 	* @author Unknown
 	*/
    resources : Object.extend(gidLib.clone(GID.Browse.Base.resources), {ASSET_PATH : "/assets/browse/product/en/"}),
	/**
 	* INT1SIZEMAXWIDTH, INT2SIZEMAXWIDTH - Define width of Marketing Messages
 	* This is a method of class productPage
 	* Modified:  Keo 12/04/07 - Added Comments
 	* @author Unknown
 	*/
    INT1SIZEMAXWIDTH : 280,
	INT2SIZEMAXWIDTH : 145,
	INTCOLORTEXTWIDTH : 240,
	/**
 	* SOLDOUTMSGDX, SOLDOUTMSGDY - Define position of Sold Out message
 	* This is a method of class productPage
 	* Modified:  Keo 12/04/07 - Added Comments
 	* @author Unknown
 	*/
	SOLDOUTMSGDX : 20,
	SOLDOUTMSGDY : 140,

    baseInitializeData : GID.Browse.Base.initializeData,
    baseInitializeVariant : GID.Browse.Base.initializeVariant,
    enableBigZoom: false,
   	/**
 	* initializeData - Retrieve and prepare required initial data / cookie data, productThumbs, productZoom
 	* This is a method of class productPage
 	* @param {string} strProductId Product ID
 	* @param {string} strVariantId Variant Type
 	* Modified:  Keo 12/04/07 - Added Comments
 	* @author Unknown
 	*/
    initializeData : function(strProductId,strVariantId) {
        this.initializeCookieData();
        this.baseInitializeData(strProductId,strVariantId);
        if (this.strDefaultSkuId) this.initializeSkuSelection();
        
        //var imgs = this.objV.arrayVariantStyleColors[productPage.selectedColor].styleColorImages;
        
        //var crossSellOffset = this.objP.objCrossSellInfo && this.objP.objCrossSellInfo.isPhotoOutfit ? 1 : 0;
        

        this.ProductZoom = ProductZoom;
        this.ProductZoom.init({drag:'drag', zoom:'zoom',bg:'product_image'})

        this.productThumbs = new ProductThumbs(this);
        this.productThumbs.generateThumbs();
    },
   	/**
 	* initializeVariant - Initiated function when Variant tab is selected. regular/tall
 	* This is a method of class productPage
 	* @param {string} strProductId Product ID
 	* @param {string} strVariantId Variant Type
 	* Modified:  Keo 12/04/07 - Added Comments
 	* @author Unknown
 	*/
    initializeVariant : function(strProductId,strVariantId) {
        this.baseInitializeVariant(strProductId,strVariantId);
        this.loadProduct();
    },
   	/**
 	* checkReviewSuppression - Check to see if reviews are suppressed for this product
 	* This is a method of class productPage
 	* @param {RegExp} regexp Regular expression to check the productsStyleName against
	* @author aliebling
 	*/
	checkReviewSuppression : function() {
        var suppressReviews = false;

		if (this.objP.strProductStyleName && this.externalOverrides.reviewSuppressionRegExpList) {
			var results = this.externalOverrides.reviewSuppressionRegExpList.invoke('test',this.objP.strProductStyleName);
			suppressReviews = results.any();
		}

		if (suppressReviews) {
			if (window["productReviewService"]) {
				productReviewService.controller.externalOverrides.showProductReviews = false;
			}

			if ($('noReviews'))
				$('noReviews').style.display = "none";
		}
	},
   	/**
 	* initializeProduct - Initiated function on page load
 	* This is a method of class productPage
 	* @param {string} strProductId Product ID
 	* @param {string} strVariantId Variant Type
 	* Modified:  Keo 12/04/07 - Added Comments
 	* Modified: Byung 7/15/08 - added hook for productReviewService init.view
 	* @author Unknown
 	*/
    initializeProduct : function(strProductId,strVariantId) {
 		var hasReviewsProcessingPending = false;
 		this.isProductPageReadyTimeSet = false;
        this.brandCode = gidBrandSiteConstruct.currentBrandCode;
        this.brandSite = gidBrandSiteConstruct.gidBrandSites[this.brandCode];
        this.brandName = 'brand' + this.brandCode;
        this.initializeData(strProductId,strVariantId);
        this.loadProduct();

 		this.checkReviewSuppression();

        if (window["productReviewService"]) {
        	hasReviewsProcessingPending = true;
        	this.callProductPageReportingService(hasReviewsProcessingPending);
        	productReviewService.controller.init.main();
        } else {
			this.callProductPageReportingService(hasReviewsProcessingPending);
        }
        
        document.fire('initializeProduct:done');
	},
   	/**
 	* loadProduct - Initial Load Product Data and Assets,
 	* 				this function is called on page load or selecting variant tabs
 	* This is a method of class productPage
 	* Modified:  Keo 12/04/07 - Added Comments
 	* @author Unknown
 	*/
    loadProduct : function() {
        gidLib.loadDomObjMap(this, this.ProductDomMap);
        this.productThumbs.setLargeImage(this.productThumbs.selectedThumb);
        this.initializeTabs();
		this.initializeVariantButtons();
		this.initializeSwatches();
        gidLib.loadDomObjMap(this, this.ProductFlagDomMap);
        gidLib.loadDomObjMap(this, this.commonDomObjMap);
        this.initializeMarketingFlags();
		this.setPriceText();
		this.setSelectionConfirmText();
		this.initializePromotions();
		this.setGIDPromoMessage();

		this.setColorSwatches();
		this.setSizeDimensionSwatches(1, this.size1Swatches);
		this.setSizeDimensionSwatches(2, this.size2Swatches);

		this.setProductNameText();
		this.setQtyDropDown();
		this.initializeOtherViews();
		this.updateDataLabels();

		this.isLoaded = true;
        this.initializeFlagErrors();
        setCookieVar("globalSession","csP",unUnicode(this.objP.strProductStyleName) + "|" + location.pathname+location.search+location.hash);
		setSideNavHeight();
    },
   	/**
 	* initializeCookieData - productPage cookie handler, looks for selected color/size swatches, quantity
 	* This is a method of class productPage
 	* Modified:  Keo 12/04/07 - Added Comments
 	* @author Unknown
 	*/
    initializeCookieData : function() {
		var strCookieVal = getCookieVar("globalSession","selectionData");
		if (strCookieVal != "") {
			var arrayCookieValues = strCookieVal.split(",");
			this.objCookieData.color = arrayCookieValues[0];
			this.objCookieData.s1 = arrayCookieValues[1];
			this.objCookieData.s2 = arrayCookieValues[2];
			this.objCookieData.qty = arrayCookieValues[3];
			this.strSelectedQty = arrayCookieValues[3];
		}
		setCookieVar("globalSession","selectionData","");
	},
   	/**
 	* initializeTabs - Sets available product tabs
 	* This is a method of class productPage
 	* Modified:  Keo 12/04/07 - Added Comments
 	* @author Unknown
 	*/
	initializeTabs : function() {
	        this.objTabWindow.innerHTML = this.getOverViewTabHTML();
	},
   	/**
 	* initializeSwatches - Sets available Color Swatches
 	* This is a method of class productPage
 	* Modified:  Keo 12/04/07 - Added Comments
 	* @author Unknown
 	*/
    initializeSwatches : function() {
	    if(!this.objV.swatchHTML) { this.objV.swatchHTML = this.getSwatchContent(); }
	    this.objSwatches.innerHTML = this.objV.swatchHTML;

	    this.size1Swatches = $('sizeDimension1SwatchContent');
	    this.size2Swatches = $('sizeDimension2SwatchContent');
	    this.colorSwatches = $('colorSwatchContent');
	    this.objTextSizeDimension1= $("textSizeDimension1");
	    this.objTextSizeDimension2= $("textSizeDimension2");
	    this.objTextColor = $("textColor");
	    this.objProductVendorInfo = $("productPageVendorId");

        this.setSwatchButtons();

        this.setCommonDimensionSizeWidth(1, this.size1Swatches, this.MINIMUMSIZESWATCHWIDTH);
	    this.setCommonDimensionSizeWidth(2, this.size2Swatches);
	},
   	/**
 	* setCurrentColorSelectionText - Sets and Style the selected color and swatch name
 	* This is a method of class productPage
 	* Modified:  Keo 12/04/07 - Added Comments
 	* @author Unknown
 	*/
	setCurrentColorSelectionText : function() {
		var objSid = this.objV.arrayVariantStyleColors[this.activeColor];
		var strColorName = objSid.strColorName;
		this.objSelectedColorText = replaceHTML(this.objSelectedColorText,this.getCurrentColorSelectionText(strColorName));
	},
   	/**
 	* getSwatchContent - Construct HTML for tab area content section of size/color selection
 	* This is a method of class productPage
 	* Modified:  Keo 12/04/07 - Added Comments
 	* @author Unknown
 	*/
    getSwatchContent : function() {
        var strHTML = [], sizeInfo = this.objV.objStyleSizeInfo;
        var swatch = {
            id : this.id,
            pid : this.objP.strProductId,
            hasVariants : this.objP.hasSplitVariants || this.objP.hasMergeVariants ? '<br/>' + this.strVariantLegalCopy : '',
            swatch : '<div id="colorSwatchContent">' + this.getColorSwatches() + '</div>'
        };
        strHTML.push(this.templates.PP_SWATCH_CONTENT.evaluate(swatch));
        if (sizeInfo.intSizeDimensionsCount > 0) {
            strHTML.push(this.templates.DIM1_SWATCH.evaluate({ swatches : this.getSizeDimensionSwatches(1) }));

            if (sizeInfo.intSizeDimensionsCount == 2) {
                strHTML.push(this.templates.DIM2_SWATCH.evaluate({ swatches : this.getSizeDimensionSwatches(2) }));
            }
        }
        var template = this.templates.SIZE_CHART_SITEWIDE;
        strHTML.push(!this.objP.isGiftCard && (this.objP.sizeChartId||'') != '' ? template : '');
        return strHTML.join('');
    },
   	/**
 	* initializeVariantButtons - Construct HTML for tab area content section of size/color selection
 	* This is a method of class productPage
 	* Modified:  Keo 12/04/07 - Added Comments
 	* @author Unknown
 	*/
    initializeVariantButtons : function() {
		if (this.objP.hasSplitVariants || this.objP.hasMergeVariants) {
			this.objVariantButtons = replaceHTML(this.objVariantButtons,this.getVariantButtons());
		} else {
            var cssObj = {height : '10px', marginTop : '0px'};
	        this.objVariantButtons.setStyle(cssObj);
	    }
	},
   	/**
 	* initializeOtherViews - Adding "Roll on to zoom in" image
 	* This is a method of class productPage
 	* Modified:  Keo 12/04/07 - Added Comments
 	* @author Unknown
 	*/
	initializeOtherViews : function() {
        if(!$('moreViewsBtn')) {
            new Insertion.After('zoomToolPic', this.getProductImageTools());
        }
    },
   	/**
 	* getCurrentColorSelectionText - Gets the selected color and return the Full Color Text
 	* This is a method of class productPage
 	* @param {string} strName Name of the Color
 	* Modified:  Keo 12/04/07 - Added Comments
 	* @author Unknown
 	*/
    getCurrentColorSelectionText : function(strName) {
        return this.strCurrentColorSelectionText + ' ' + strName;
    },
   	/**
 	* getTabHTML - No being used
 	* This is a method of class productPage
 	* Modified:  Keo 12/04/07 - Added Comments
 	* @author Unknown
 	*/
    getTabHTML : function(n) {
        return '<div id="tabContent" class="quickLookTabContent">' + this.getTabContent(n) + '</div>';
    },

   	/**
 	* getVariantButtons - Construct HTML for tab style. Executed when this.obP.hasSplitVariants or this.objP.hasMergeVariants is true
 	* This is a method of class productPage
 	* Modified:  Keo 12/04/07 - Added Comments
 	* @author Unknown
 	*/
    getVariantButtons : function() {
        var tabHTML = ['<ul class="clearfix">'];
        var hlpContext = this;
        /**
        * variantButtonsHelper - Construct HTML for tab style return an array of Tabs
        * This is a method of class productPage
        * @param {object} array Name of the Color
        * @param {string} variant Variant type of product
        * Modified:  Keo 12/04/07 - Added Comments
        * @author Unknown
        */
        function variantButtonsHelper(array, variant) {
            if(variant) {
            	with(hlpContext) {
            		var template = templates.VARIANT_BTNS_SITEWIDE;
            		var btnOn = objV == variant;
                    array.push(
                                template.evaluate({
                                    onclick : objV == variant ? '' : 'javascript:productPage.loadVariantProduct(' + variant.strCatalogItemId + (variant.strVariantId ? ',' + variant.strVariantId : '') + ');',
                                    path : resources.ASSET_PATH,
                                    state : btnOn ? 'on' : 'off',
                                    name : variant.strVariantName.toLowerCase(),
                                    style : objV == variant ? '' : 'cursor : pointer;'
                                })
                            ); }
                }
            return array;
        }

        Object.values(this.objP.arrayVariantStyles).inject(tabHTML, variantButtonsHelper);
        tabHTML.push('</ul>');
        return tabHTML.join('');
    },
   	/**
 	* getCrossSellTab - Checks for available Cross Sell Items and construct the HTML for the bottom of the page.
 	* This is a method of class productPage
 	* Modified:  Keo 12/04/07 - Added Comments
 	* @author Unknown
 	*/
    getCrossSellTab : function() {
        var crossSell = this.objP.objCrossSellInfo, strHTML = [], counter = brandConst.MAX_CROSSSELL;
        var isOutfit = this.objP.objCrossSellInfo.isPhotoOutfit, isSBS = this.isFromSBS, isSBSSuffix = this.strSBSUrlSuffix;
        var outfitTitleStyle = $('crossSellTop'), templates = this.templates;

        strHTML.push('<div id="crossSellContent" class="clearfix">');

        if (isOutfit) {
            outfitTitleStyle.className = 'productpage-sprites sprite-crossSellTopOutfit_bkg';
            outfitTitleStyle.alt = productPage.crossSellOutfitHeaderText;
        }

        crossSell.arrayProducts.inject(strHTML,
                    function(array, product, index) {
                        if(counter == 0) { throw $break;}
                        counter--;
                        var strProductLink = product.strProductLink + (isSBS ? isSBSSuffix : "");
                        var brandName = 'brand' + product.brandCode;
                        if (product.strVendorName != "") {
                        	var productNameAlt = product.strVendorName + product.strName;
                        	var productNameDisplay = '<span class="vendorName">' + product.strVendorName + '</span>' + product.strName;
                        } else {
                        	var productNameAlt = product.strName;
                        	var productNameDisplay = product.strName;
                        }
                        array.push(templates.CROSSSELL_PRODUCT.evaluate({
                            product_link : strProductLink,
                            brandCode: product.brandCode,
                            brandName: brandName,
                            index : index,
                            img_src : product.strImgSrc,
                            img_alt : productNameAlt,
                            nameDisplay : productNameDisplay,
                            price : product.strPrice,
                            mups : product.mups,
                            id : String(product.intCatalogItemId).substring(0,6),
                            scid : String(product.intCatalogItemId).length != 9 ? '' : product.intCatalogItemId,
                            badging: productPage.brandCode != product.brandCode ?
                                     '<div class="brandBadge"><img id="badge' + index + '" src="/gid/assets/common/en/BadgeCrosssell' + product.brandCode + '.gif" /></div>':''
                        }));
                        return array;
                    }
                );

        strHTML.push('</div>');

        this.crossSellId = this.objP.strProductId;
        if(quickLook) { quickLook.crossSellId = this.crossSellId; }
        return strHTML.join('');
    },

    /**
     * callProductPageReportingService initializes and calls the reporting service
     */
    callProductPageReportingService : function(hasReviewsProcessingPending) {
		// Set page ready and page load values for the product page
		if (!this.isProductPageReadyTimeSet) {
			if (window["performanceMonitor"]) {
				var performanceMonitorController = performanceMonitor.controller;
				performanceMonitorController.pageOnPageReadyHandler();
				performanceMonitorController.setTimeToReadyAsTimeToPageReady();
				performanceMonitorController.setTimeToLoadAsTimeToPageReady();
				this.isProductPageReadyTimeSet = true;
			}
		}

		if (!hasReviewsProcessingPending && this.isProductPageReadyTimeSet) {
			// call reporting service
			if(!(reportingService||{}).isActive) {
				productPage.initializeOmni();
			}
			else {
				productPage.setReportingService();
			}
		}
    },

    goWriteAReviewPage : function() {
		if (productPage) {
			var pageId = productPage.productId;
			var variant = productPage.objV.arrayVariantStyleColors[productPage.activeColor].strColorCodeId.substring(6);
			window.location.href = "/browse/writeareview.do?pageId=" + pageId + "&variant=" + variant + "&source=web";
		}
		return false;
    }

});
/**
* ProductZoom - Zoom and Crop feature. Handle all functions
* 				that are relation to the ZOOM feature on a Product Page
* @constructor
* @author Unknown
* @date 12/04/2007
*/
var ProductZoom = {
   	/**
 	* NO_ZOOM_MSG - Message output is actually Out of Stock
 	* This is a method of class ProductZoom
 	* Modified:  Keo 12/04/07 - Added Comments
 	* @author Unknown
 	*/
    NO_ZOOM_MSG : productService.templates.NO_ZOOM_MSG,
   	/**
 	* init - Preparing objects and measurements required for ZOOM Feature
 	* This is a method of class ProductZoom
 	* @param {object} s Container of the Zoom Object
 	* Modified:  Keo 12/12/07 - Added Comments
 	* @author Unknown
 	*/
    isReady: false,
    isImageReady: false,
    isMouseOver: false,
    hasFetchBegin: false,
    init : function(s) {
        this.imgHolder = $(s.drag + 'Layer');
        this.background = $(s.bg + '_bg');
        //this.tint = $(s.tint);
		this.dragBox = $(s.drag + 'Box');
        this.zoomBox = $(s.zoom + 'Layer');
        //this.zoomLadingImg = $('zoomLoadingImage');
		var bg = this.bgImg = $(s.bg);
		this.dragImg = $(s.drag + 'Img');
		this.zoomImg = $(s.zoom + 'Img');
        this.qtyDropDown = $('qtyDropDown');
        this.zoomToolPic = $('zoomToolPic');
        this.app = productPage;
        this.hiddenDropDowns = [];
        
        var imgs = productPage.objV.arrayVariantStyleColors[productPage.selectedColor].styleColorImagesMap;

        this.sDeltaH = this.imgHolder.offsetHeight - this.dragBox.offsetHeight;
		this.sDeltaW = this.imgHolder.offsetWidth - this.dragBox.offsetWidth;
        this.halfW = this.dragBox.offsetWidth / 2;
		this.halfH = this.dragBox.offsetHeight / 2;

        this.enableZoom = this.isReady = true;
        this.setupBoundBox();
        
        //var hasZoomImage = this.app.hasZoomImage;
        
        //this.zoomImg.onabort = function(e) { alert('image loading aborted, src: ' + this.src); }

        this.zoomImg.alt = this.zoomAltTxt;
        this.aspectRatio = bg.height / bg.width;
        this.hScale = this.wScale = 1;
		
        this._mouseover = this.mouseOverHandler.bind(this);
		this._mouseout = this.mouseOutHandler.bind(this);
		this._mousemove = this.mouseMoveHandler.bind(this);

        //Event.observe(this.dragImg, "mouseout", this._mouseout );
        Event.observe(this.imgHolder, "mouseover", this._mouseover );
        //Event.observe(this.imgHolder, "mousemove", this._mousemove );
        Event.observe(document, 'imageFetch:ready', this.imageReadyHandler);
        Event.observe(document, 'imageFetch:begin', this.imageFetchBeginHandler);
        Event.observe(document, 'imageFetch:end', this.imageFetchEndHandler);
        
        //Event.observe(document, 'initializeProduct:done', this.postLoadImageHandler);
        
    },
   
    imageAbortHandler: function(e) {
    	//alert('image loading aborted, index:' + this.index + ' type: ' + this.type + ' src: ' + this.src);
    },
    
    imageFetchBeginHandler: function(e) {
    	ProductZoom.hasFetchBegin = true;
    },
    
    imageFetchEndHandler: function(e) {
    	ProductZoom.hasFetchBegin = false;
    },
    
    imageReadyHandler: function(e) {
    	//alert(ProductZoom.isMouseOver);
    	ProductZoom.dragImg.removeClassName('zoomLoading');
		if(ProductZoom.isZoomBoxVisible || !ProductZoom.isMouseOver) return;
    	//ProductZoom.showZoomBox();
    },
    
    imagePostLoadHandler: function(e) {
    	//alert(this.type);
    	var imgs = productPage.productThumbs.getImages();
    	imgs[this.imageType].isLoaded = true;
    },
    
    imageLoadHandler: function(e) {
    	var imgs = productPage.productThumbs.getImages();
    	var img = imgs[this.imageType];
		
    	if(img.isLoaded) return;

		ProductZoom.zoomImg.src = this.src;
		img.width= this.width;
		img.height = this.height;
		ProductZoom.prepareZoomImage(img);
		
		
		//we should be on product page
		img.isLoaded = true;
	
		document.fire("imageFetch:end");
    	document.fire("imageFetch:ready");
    },
    prepareZoomImage: function(postLoadImg) {
		//if(!ProductZoom.app.hasZoomImage) return;
    	if(!postLoadImg.isPrepared) {
			var zoomBox = ProductZoom.zoomBox;
			var w = postLoadImg.width || ProductZoom.zoomImg.width;
			var h = postLoadImg.height || ProductZoom.zoomImg.height;
			var dw = 0, dh = 0;
			var h1 = h, w1 = w;
			var zoomAspectRatio = h / w;
			var p1AspectRatio = ProductZoom.aspectRatio;
			
			if(p1AspectRatio > zoomAspectRatio) {
				h1 = p1AspectRatio * w;
				dh = Math.abs(h1 - h);
			} else {
				w1 = h / p1AspectRatio;
				dw = Math.abs(w1 - w);
			}
	
			var zDeltaH = h1 - zoomBox.offsetHeight;
			var zDeltaW = w1 - zoomBox.offsetWidth;
	
	    	Object.extend(postLoadImg, {
	    		zoomHeightOffset: dh * 0.5,
	    		zoomWidthOffset: dw * 0.5,
	    		hScale: zDeltaH / ProductZoom.sDeltaH,
	    		wScale: zDeltaW / ProductZoom.sDeltaW,
	    		isPrepared: true
	    	});
    	}
    	
    	ProductZoom.hScale = postLoadImg.hScale;
    	ProductZoom.wScale = postLoadImg.wScale;
    	ProductZoom.zoomHeightOffset = postLoadImg.zoomHeightOffset;
    	ProductZoom.zoomWidthOffset = postLoadImg.zoomWidthOffset;
		ProductZoom.mouseMoveHandler(ProductZoom.e);
	},
   	/**
 	* setupBoundBox - Gets the main image area and set it to the area where the ZOOM Box will appear on mouseOver
 	* This is a method of class ProductZoom
 	* Modified:  Keo 12/12/07 - Added Comments
 	* @author Unknown
 	*/
    setupBoundBox : function() {
        if(!this.isReady) return;
        var bgPos = Position.cumulativeOffset(this.background);
        var bgWidth = this.background.offsetWidth;
        var bgHeight = this.background.offsetHeight;
        
		this.boundBox = {
				left:this.halfW, top:this.halfH, 
				right: bgWidth - this.halfW, btm: bgHeight - this.halfH };
		this.boundBoxNoDragBox = {
				left: 0, top: 0, 
				right: bgWidth, btm: bgHeight};
		
		var absPos0 = bgPos[0] + bgWidth;
		var absPos1 = bgPos[1] + bgHeight;
		this.boundBoxAbsolute = {
				left: bgPos[0] + this.halfW, top: bgPos[1] + this.halfH, 
				right: absPos0 - this.halfW, btm: absPos1 - this.halfH};
		this.boundBoxNoDragBoxAbsolute = {
				left: bgPos[0], top: bgPos[1], 
				right: absPos0, btm: absPos1};
		//this.dragBound = {left: 0, right: bgWidth - 2 * this.halfW, top: 0, btm: bgHeight - 2 * this.halfH};

        this.imgHolder.setStyle({left : bgPos[0] + 'px', top : bgPos[1] + 'px', display : 'block'});
        //var top = bgPos[1] + bgHeight - this.zoomBox.offsetHeight;
        //if (this.app.hasZoomImage) {
        var top = bgPos[1];
        //}
        this.zoomBox.setStyle({left:(bgPos[0] + bgWidth) + 'px', top:top+ 'px'});
    },
   	/**
 	* setNoZoomMsg - Message output is actually Out of Stock.  Only called when out of stock
 	* This is a method of class ProductZoom
 	* Modified:  Keo 12/12/07 - Added Comments
 	* @author Unknown
 	*/
    setNoZoomMsg : function(msg) {
        this.app.initializeSoldOutMsg();
        var errMsg = msg != '' ? this.NO_ZOOM_MSG.evaluate({msg : msg}) : '';
        this.app.setSoldOutMsgStatus(errMsg);
    },
   	/**
 	* mouseOutHandler - Hides the ZOOM box, Reset to normal image.
 	* 					Calls Omniture tracking with a boolean for the usage of ZOOM Feature
 	* This is a method of class ProductZoom
 	* Modified:  Keo 12/12/07 - Added Comments
 	* @author Unknown
 	*/
    mouseOutHandler : function(e) {
    	ProductZoom.isZoomBoxVisible = false;
		ProductZoom.isZoomReady = false;
		ProductZoom.zoomImg.src = '/assets/common/clear.gif';
		
    	Event.stopObserving(this.imgHolder,'mousemove', this._mousemove);
    	
        if( !this.enableZoom ) {
        	this.setNoZoomMsg('');
            //Event.stopObserving(this.background,'mouseout', this._mouseout);
            return;
        }
        
        //Event.stopObserving(document.body, "mousemove", this._mousemove );
        if( this.fadeEffect )
        	this.fadeEffect.cancel();

        var time = new Date(), outtime = time.getTime();
        if( !this.hasEngaged ) {
            var shouldFireOmniture = outtime - this.engageTime > 2000;
            if( shouldFireOmniture ) {
            	if(!(reportingService||{}).isActive){
                	omni.setZoomView(productPage.objP.strProductId);
            	} else {
            		ProductZoom.app.setReportingService('zoomView', ProductZoom.app);
            	}
            	this.hasEngaged = true;
            }
        }
        
        this.dragBox.style.visibility = 'hidden';
        //gidLib.showDropDowns(this.hiddenDropDowns);
        this.zoomBox.setStyle({zIndex:'-10', opacity:0, filter:'alpha(opacity:0)'});
        //this.tint.setStyle({opacity:0,filter:'alpha(opacity:0)'});
        this.zoomToolPic.className = 'zoomToolBtn over';
        this.zoomToolPic.alt = productPage.rollOnToZoomIn;
        //debugger;
        this.isMouseOver = false;
        Event.observe(this.imgHolder,'mouseover', this._mouseover);
    },
   	/**
 	* mouseOverHandler - Shows the ZOOM box, Sets opacity for main image.
 	* 					 Starts engage time for Omniture capture
 	* This is a method of class ProductZoom
 	* Modified:  Keo 12/12/07 - Added Comments
 	* @author Unknown
 	*/
	mouseOverHandler : function(e) {
		if(this.isMouseOver) return;
		var objV = productPage.objV;
		var color = objV.arrayVariantStyleColors[productPage.selectedColor];

		var thumbs = productPage.productThumbs;
	    var imgs = thumbs.getImages();
	    var zoomType = 'Z';
	    if(color.showThumbs) {
	    	var selectedThumb = thumbs.selectedThumb;
	    	var thumbType = selectedThumb.getAttribute('thumbType');
	    	if(thumbType != 'T') {
	    		zoomType = thumbType + '_Z';
	    	}
	    }
	    var hasZoomImage = productPage.enableBigZoom && imgs[zoomType];
	    if (hasZoomImage) {
			this.dragBox.addClassName('bigZoom');
	        this.zoomBox.addClassName('bigZoom');
		} else {
			this.dragBox.removeClassName('bigZoom');
	        this.zoomBox.removeClassName('bigZoom');
		}
	    
	    this.sDeltaH = this.imgHolder.offsetHeight - this.dragBox.offsetHeight;
		this.sDeltaW = this.imgHolder.offsetWidth - this.dragBox.offsetWidth;
        this.halfW = this.dragBox.offsetWidth / 2;
		this.halfH = this.dragBox.offsetHeight / 2;

        this.enableZoom = this.isReady = true;
        this.setupBoundBox();
	    
        var thumbIndex = thumbs.selectedThumbIndex != null ? thumbs.selectedThumbIndex :
        	productPage.selectedColor;
		var imgType = color.showThumbs ? 
				thumbs.selectedThumb.getAttribute('thumbImageType') : 'color';
	    var zoomEnabled = this.enableZoom = !(imgType == 'outfit');
		if(!zoomEnabled) {
			this.setNoZoomMsg(productPage.noZoomImageAvailable);
        	return;
		}
		Event.stopObserving(this.imgHolder, 'mouseover', this._mouseover);
		this.isMouseOver = true;
		ProductZoom.e = e;
		ProductZoom.mouseMoveHandler(e);
		Event.observe(document, "mousemove", this._mousemove );
		this.dragBox.style.visibility = 'visible';
		
		this.loadZoomImage(imgs, hasZoomImage);
    },
    
    showZoomBox: function() {	
    	ProductZoom.isZoomBoxVisible = true;
    	ProductZoom.dragImg.removeClassName('zoomLoading');
    	this.zoomBox.setStyle({zIndex:'10', opacity:0,filter:'alpha(opacity:0)',visibility:'visible'});
        this.fadeEffect = new Effect.Opacity(this.zoomBox.id, {duration:0.5, from:0, to:1});
        
        //this.hiddenDropDowns = gidLib.hideDropDownsUnderElement(this.zoomBox);
    	this.zoomToolPic.className = 'zoomToolBtn out';
    	this.zoomToolPic.alt = productPage.rollOffToZoomOutText;
        
        if( !this.hasEngaged ) {
            var time = new Date();
            this.engageTime = time.getTime();
        }
    },
   	/**
 	* mouseMoveHandler - Handles the dragging of the ZOOM box and match the Zoom Box to the zoomed in image.
 	* This is a method of class ProductZoom
 	* Modified:  Keo 12/12/07 - Added Comments
 	* @author Unknown
 	*/
	mouseMoveHandler : function(e) {
		ProductZoom.e = e;
    	var absBox = this.boundBoxNoDragBoxAbsolute;
        var x = e.pageX - absBox.left, y = e.pageY - absBox.top
        var boundBox = this.boundBox;
        if(gidLib.isMouseOut(x, y, this.boundBoxNoDragBox)) { this.mouseOutHandler(e); return; }
        
        var dragStyle = this.dragBox.style;
		var zoomStyle = this.zoomImg.style;
        var left = x > boundBox.right ? boundBox.right : x < boundBox.left ? boundBox.left : x;
		var top = y > boundBox.btm ? boundBox.btm : y < boundBox.top ? boundBox.top : y;
		
		left = left - this.halfW;
		top = top - this.halfH;
	
        dragStyle.left = left+'px';
        dragStyle.top = top+'px';
        zoomStyle.left = (-left * this.wScale + (ProductZoom.zoomWidthOffset||0)) + 'px',
        zoomStyle.top = (-top * this.hScale + (ProductZoom.zoomHeightOffset||0))+ 'px';
    },
    
    loadZoomImage: function(imgs, hasZoomImage) {
    	ProductZoom.isZoomReady = false;
		ProductZoom.showZoomBox();
    	
    	var color = productPage.objV.arrayVariantStyleColors[productPage.selectedColor];
    	//var imgs = color.styleColorImagesMap;

    	var prefix = ''
	    if(color.showThumbs) {
	    	var thumbs = productPage.productThumbs;
	    	var thumbType = thumbs.selectedThumb.getAttribute('thumbType');
	    	if(thumbType != 'T') {
	    		prefix = thumbType;
	    	}
	    	
	    }
    	var suffix = hasZoomImage ? 'Z' : 'VLI';
	    var zoomType = imgs[prefix + (prefix != '' ? '_' : '') + suffix] ?
	    	prefix + (prefix != '' ? '_' : '') + suffix :
	    		prefix + (prefix != '' ? '_' : '') + 'VLI'
    	
    	var img = imgs[zoomType];
        if(img.isLoaded) {
        	this.zoomImg.src = img.src;
        	this.prepareZoomImage(img);
        	return;
        }
        img.load(index, img.cropType, img.src);
	}
};

	/**
	 * SearchDex Pixel
	 */

var SearchDexPixel = {
	init : function (){
		var sid = gidLib.getCookie("JSESSIONID");
		var locale = gidLib.getCookie("locale");
    	if (locale != null 
				&& locale != undefined 
				&& locale != "" 
				&& locale.indexOf("|||") != -1) {
			
    		locale = locale.replace("|||", "");
		}
		if (window.location.href.match(/product/)){
			var pgtype = 'product';
			var pginfo = gidLib.getQuerystringParam("pid");
			var brandcode = window.brandConst.BRAND_CODE;
			SearchDexPixel.setPixel(sid,pgtype,pginfo,brandcode);
		}
	},
	setPixel: function(sid, pgtype, pginfo, brandcode) {
		var rnd;
		rnd = Math.round(Math.random() * 2147483647);
		var searchDexPixel = document.createElement('p');
		document.body.appendChild(searchDexPixel);
		searchDexPixel.innerHTML = '<img src="/ifc/ifc.gif?rnd=' + rnd + '&sid=' + sid + '&pt=' + pgtype + '&pi=' + encodeURIComponent(pginfo)
		+ "&br=" + brandcode + "&locale=" + locale + "&ref=" + encodeURIComponent(document.referrer) + '" width="1" height="1" border="0" />';

	}
};

	
// var isMobileBrowserProcessingActive = true;

var BrowserService = Class.create();
BrowserService.prototype = {
	initialize:function() {
	},
	constants:{
	},
	model:{
		isBrowserServiceActive:true
	},
	controller:{
		init:{
			main:function() {
				var isBrowserServiceActive = browserService.model.isBrowserServiceActive;
				var mobileBrowserManager = browserService.controller.managers.mobileBrowserManager;
				if (isBrowserServiceActive) {
					mobileBrowserManager.controller.init.main();
				}
			}
		},
		managers:{
			mobileBrowserManager:{
				constants:{
					DESKTOP_SITE_ACTIVE_PARAMETER_NAME:"desktopSiteActive",
					DESKTOP_SITE_ACTIVE_DATE_PARAMETER_NAME:"desktopSiteActiveDate",
					REFERRER_PARAMETER_NAME:"referrer",
					DESKTOP_SITE_ACTIVE_MAX_TIME_FOR_MOBILE:1800000,
					MOBILE_BROWSERS_TO_REDIRECT:[
					"iphone",
					"ipod",
					"android"],
					GID_MOBILE_URL:"gidmobile.com",
					SESSION_COOKIE_NAME:"globalSession",
					MOBILE_BROWSER_BUSINESS_UNIT_ID_REDIRECT_URL_MAP:{
						1:"http://m.gap.com",
						2:"http://m.bananarepublic.gap.com",
						3:"http://m.oldnavy.gap.com",
						4:"http://m.piperlime.gap.com",
						10:"http://m.athleta.gap.com"
					}
				},
				model:{
					isMobileBrowserManagerActive:true,
					isMobileBrowserToRedirect:null,
					mobileBrowserToRedirectUserAgent:null,
					isMobileBrowserProcessingActiveOverride:null
				},
				controller:{
					init:{
						main:function() {
							var mobileBrowserManager = browserService.controller.managers.mobileBrowserManager;
							var constants = mobileBrowserManager.constants;
							mobileBrowserManager.controller.init.setIsMobileBrowserManagerActive();
							var isMobileBrowserManagerActive = mobileBrowserManager.model.isMobileBrowserManagerActive;
							if (isMobileBrowserManagerActive) {
								/* Detect mobile browser to support.  If mobile browser detected detect if 
								 * desktopSiteActive parameter is in the querystring or the cookie.
								 * If desktopSiteActive do not redirect, otherwise redirect the user to the 
								 * mobile site.
								 */
								var isCurrentBusinessUnitMobileSiteEnabled = mobileBrowserManager.controller.isCurrentBusinessUnitMobileSiteEnabled();
								var isMobileBrowserToRedirect = mobileBrowserManager.controller.isMobileBrowserToRedirect();
								if (isCurrentBusinessUnitMobileSiteEnabled && isMobileBrowserToRedirect) {
									var isDesktopSiteActive = mobileBrowserManager.controller.isDesktopSiteActive();
									if (!isDesktopSiteActive) {
										mobileBrowserManager.controller.redirectMobileBrowser();
									}
								}
							}
						},
						setIsMobileBrowserManagerActive:function() {
							var mobileBrowserManager = browserService.controller.managers.mobileBrowserManager;
							var constants = mobileBrowserManager.constants;
							/* Check for isMobileBrowserProcessingActive override */
							if (window["isMobileBrowserProcessingActive"] != undefined) {
								if (isMobileBrowserProcessingActive != undefined && isMobileBrowserProcessingActive != null) {
									if (isMobileBrowserProcessingActive != true) {
										mobileBrowserManager.model.isMobileBrowserManagerActive = false;
										mobileBrowserManager.model.isMobileBrowserProcessingActiveOverride = isMobileBrowserProcessingActive;
									}
								}
							}
							/* Check for gidmobile.com third party */
							var GID_MOBILE_URL = constants.GID_MOBILE_URL;
							var locationHostname = window.location.hostname;
							locationHostname = locationHostname.toLowerCase();
							if (locationHostname.indexOf(GID_MOBILE_URL) != -1) {
								/* gidmobile.com detected so disable the mobileBrowserManager */
								mobileBrowserManager.model.isMobileBrowserManagerActive = false;
							}
						}
					},
					isCurrentBusinessUnitMobileSiteEnabled:function() {
						var isCurrentBusinessUnitMobileSiteEnabled = false;
						var mobileBrowserManager = browserService.controller.managers.mobileBrowserManager;
						var MOBILE_BROWSER_BUSINESS_UNIT_ID_REDIRECT_URL_MAP = mobileBrowserManager.constants.MOBILE_BROWSER_BUSINESS_UNIT_ID_REDIRECT_URL_MAP;
						if (window["gidBrandSiteConstruct"]) {
							var businessUnitId = gidBrandSiteConstruct.currentBrandCode;
							var redirectBaseUrl = MOBILE_BROWSER_BUSINESS_UNIT_ID_REDIRECT_URL_MAP[businessUnitId];
							if (redirectBaseUrl != undefined && redirectBaseUrl != null && redirectBaseUrl != "") {
								isCurrentBusinessUnitMobileSiteEnabled = true;
							}
						}
						return isCurrentBusinessUnitMobileSiteEnabled;
					},
					isMobileBrowserToRedirect:function() {
						var mobileBrowserManager = browserService.controller.managers.mobileBrowserManager;
						var model = mobileBrowserManager.model;
						var mobileBrowsersToRedirect = mobileBrowserManager.constants.MOBILE_BROWSERS_TO_REDIRECT;
						var isMobileBrowserToRedirect = false;
						var userAgent = navigator.userAgent;
						var userAgentFormatted = userAgent.toLowerCase();
						for (var a = 0; a < mobileBrowsersToRedirect.length; a++) {
							var mobileBrowserToRedirect = mobileBrowsersToRedirect[a];
							if (userAgentFormatted.search(mobileBrowserToRedirect) != -1) {
								isMobileBrowserToRedirect = true;
								model.isMobileBrowserToRedirect = true;
								model.mobileBrowserToRedirectUserAgent = userAgent;
								break;
							}
						}
						return isMobileBrowserToRedirect;
					},
					isDesktopSiteActive:function() {
						var isDesktopSiteActive = false;
						var mobileBrowserManager = browserService.controller.managers.mobileBrowserManager;
						var DESKTOP_SITE_ACTIVE_PARAMETER_NAME = mobileBrowserManager.constants.DESKTOP_SITE_ACTIVE_PARAMETER_NAME;
						var DESKTOP_SITE_ACTIVE_DATE_PARAMETER_NAME = mobileBrowserManager.constants.DESKTOP_SITE_ACTIVE_DATE_PARAMETER_NAME;
						var DESKTOP_SITE_ACTIVE_MAX_TIME_FOR_MOBILE = mobileBrowserManager.constants.DESKTOP_SITE_ACTIVE_MAX_TIME_FOR_MOBILE;
						var SESSION_COOKIE_NAME = mobileBrowserManager.constants.SESSION_COOKIE_NAME;
						var desktopSiteActiveQueryParameterValue = gidLib.getQuerystringParam(DESKTOP_SITE_ACTIVE_PARAMETER_NAME);
						var desktopSiteActiveCookieParameterValue = gidLib.getCookieVar(SESSION_COOKIE_NAME, DESKTOP_SITE_ACTIVE_PARAMETER_NAME);
						var desktopSiteActiveDate = null;
						var desktopSiteActiveDateFromCookie = null;
						var desktopSiteActiveDateFromCookieTime = null;
						var desktopSiteActiveDateFromCookieTimeDelta = null;
						var nowDate = null;
						var nowTime = null;
						/* detect if desktopSiteActive parameter is
						 * in the querystring or the cookie.
						 */
						if (desktopSiteActiveQueryParameterValue == "true") {
							/* query param is true so set the cookie value for subsequent requests */
							isDesktopSiteActive = true;
							gidLib.setCookieVar(SESSION_COOKIE_NAME, DESKTOP_SITE_ACTIVE_PARAMETER_NAME, "true");
							desktopSiteActiveDate = new Date();
							gidLib.dateUtils.setDateToCookieVar(SESSION_COOKIE_NAME, DESKTOP_SITE_ACTIVE_DATE_PARAMETER_NAME, desktopSiteActiveDate);
						} else if (desktopSiteActiveQueryParameterValue == "false") {
							gidLib.setCookieVar(SESSION_COOKIE_NAME, DESKTOP_SITE_ACTIVE_PARAMETER_NAME, "false");
						} else if (desktopSiteActiveCookieParameterValue == "true") {
							/* cookie parameter true so check the time threshold */
							desktopSiteActiveDateFromCookie = gidLib.dateUtils.getDateFromCookieVar(SESSION_COOKIE_NAME, DESKTOP_SITE_ACTIVE_DATE_PARAMETER_NAME);
							if (desktopSiteActiveDateFromCookie != null) {
								desktopSiteActiveDateFromCookieTime = desktopSiteActiveDateFromCookie.getTime();
								nowDate = new Date();
								nowTime = nowDate.getTime();
								desktopSiteActiveDateFromCookieTimeDelta = nowTime - desktopSiteActiveDateFromCookieTime;
								if (desktopSiteActiveDateFromCookieTimeDelta < DESKTOP_SITE_ACTIVE_MAX_TIME_FOR_MOBILE) {
									isDesktopSiteActive = true;
								} else {
									gidLib.setCookieVar(SESSION_COOKIE_NAME, DESKTOP_SITE_ACTIVE_PARAMETER_NAME, "false");
								}
							}
							
						}
						return isDesktopSiteActive;
					},
					redirectMobileBrowser:function() {
						var mobileBrowserManager = browserService.controller.managers.mobileBrowserManager;
						var MOBILE_BROWSER_BUSINESS_UNIT_ID_REDIRECT_URL_MAP = mobileBrowserManager.constants.MOBILE_BROWSER_BUSINESS_UNIT_ID_REDIRECT_URL_MAP;
						var businessUnitId = gidBrandSiteConstruct.currentBrandCode;
						var redirectBaseUrl = MOBILE_BROWSER_BUSINESS_UNIT_ID_REDIRECT_URL_MAP[businessUnitId];
						var locationPathname = window.location.pathname;
						var locationSearch = window.location.search;
						var redirectUrl = redirectBaseUrl + locationPathname;
						var referrer = document.referrer;
						var referrerNameValue = null;
						var isLocationSearch = false;
						if (locationSearch != null && locationSearch != undefined && locationSearch.length > 0) {
							isLocationSearch = true;
							redirectUrl = redirectUrl + locationSearch;
						}
						if (referrer != null && referrer != undefined && referrer != "") {
							var referrerEncoded = encodeURIComponent(referrer);
							var REFERRER_PARAMETER_NAME = mobileBrowserManager.constants.REFERRER_PARAMETER_NAME;
							referrerNameValue = REFERRER_PARAMETER_NAME + "=" + referrerEncoded;
							if (isLocationSearch) {
								referrerNameValue = "&" + referrerNameValue;
							} else {
								referrerNameValue = "?" + referrerNameValue;
							}
							redirectUrl = redirectUrl + referrerNameValue;
						}
						window.location = redirectUrl;
					}
				}
			}
		}
	}
}
performanceMonitorService.controller.monitors.ecom.checkout.orderConfirmMonitor = new performanceMonitorService.constructors.AbstractPagePerformanceMonitor();
Object.extend(performanceMonitorService.controller.monitors.ecom.checkout.orderConfirmMonitor, {
	performanceMonitorName: "Order Confirm Performance Monitor",
	performanceMonitorClassPath: "performanceMonitorService.controller.monitors.ecom.checkout.orderConfirmMonitor",
	controller: {
		init: {
			main: function() {
				var controller = performanceMonitorService.controller;
				var previousModel = controller.monitors.ecom.checkout.orderConfirmMonitor.model.previousPage;

				previousModel.pageLoadTimeDelta
					= controller.monitors.ecom.siteWide.siteWideMonitor.model.previousPage.pageLoadTimeDelta;
				previousModel.domReadyTimeDelta
					= controller.monitors.ecom.siteWide.siteWideMonitor.model.previousPage.domReadyTimeDelta;
			}
		},
		handlers: {
			setEventHandlers: function() {}
		}
	},
	model: {
		previousPage: {
			pageLoadTimeDelta: null,
			domReadyTimeDelta: null
		}
	}
});
/**
 * This Service Class is concerned with interacting with the personalization service 
 * in order to collect and display personalized data for a given user on a page.
 * @author Andrew Southwick
 */
var PersonalizationService = Class.create();
PersonalizationService.prototype = {
	initialize: function() {
	},
	constants: {
		PERSONALIZATION_SERVICE_PATH:"/resources/personalization/v1",
		STRING_TRUE:"true",
		SITE_EXPRESSION_PREFIX:"^https?://[^/]*(",
		LOCAL_SITE_PROD_ENVIRONMENT_EXPRESSION_SUFFIX:"|gap.com|gap.eu|gapcanada.ca|bananarepublic.ca|oldnavy.ca|thegap.co.uk|bananarepublic.co.uk|bananarepublic.com|oldnavy.com|piperlime.com|athleta.com)",
		LOCAL_SITE_TEST_ENVIRONMENT_EXPRESSION_SUFFIX:"|gidgol.com|gidgol.ca|gidgol.co.uk|gidapps.com|gidapps.ca|gidapps.eu|gidbrol.com|gidonol.com|gidplol.com|gidatol.com)",
		templates: {
			ANONYMOUS_USER_ACCOUNT_LINK_TEMPLATE:null,
			ANONYMOUS_USER_ACCOUNT_LINK_TEMPLATE_TEXT:"<div class=\"unknownUser\"><a class=\"alt\" href=\"/profile/account_summary.do?locale=#{userContext.localeCode}\">#{resourceBundleValues.profile.yourAccount}</a></div>",
			RECOGNIZED_USER_ACCOUNT_LINK_TEMPLATE:null,
			RECOGNIZED_USER_ACCOUNT_LINK_TEMPLATE_TEXT:"<div class=\"knownUser\"><a class=\"knownUser\" href=\"/profile/account_summary.do?locale=#{userContext.localeCode}\">#{customerAccount.firstName}#{resourceBundleValues.profile.personalizedMyAccount}</a></div>",
			LOGGED_IN_USER_ACCOUNT_LINK_TEMPLATE:null,
			LOGGED_IN_USER_ACCOUNT_LINK_TEMPLATE_TEXT:"<div class=\"knownUser\"><a class=\"knownUser\" href=\"/profile/account_summary.do?locale=#{userContext.localeCode}\">#{customerAccount.firstName}#{resourceBundleValues.profile.personalizedMyAccount}</a></div>",
			ANONYMOUS_USER_SIGN_IN_LINK_TEMPLATE:null,
			ANONYMOUS_USER_SIGN_IN_LINK_TEMPLATE_TEXT:"| <a href=\"/profile/sign_in.do\" onclick=\"return siteNavigation.goSignIn(this.href)\">#{resourceBundleValues.profile.signIn}</a>",
			RECOGNIZED_USER_SIGN_OUT_LINK_TEMPLATE:null,
			RECOGNIZED_USER_SIGN_OUT_LINK_TEMPLATE_TEXT:"| <a href=\"/profile/sign_out.do\" onclick=\"return siteNavigation.goSignOut(this.href)\">#{resourceBundleValues.profile.isRecognizedUser}</a>",
			LOGGED_IN_USER_SIGN_OUT_LINK_TEMPLATE:null,
			LOGGED_IN_USER_SIGN_OUT_LINK_TEMPLATE_TEXT:"| <a href=\"/profile/sign_out.do\" onclick=\"return siteNavigation.goSignOut(this.href)\">#{resourceBundleValues.profile.signOut}</a>",
			ORDER_HISTORY_LINK_TEMPLATE:null,
			ORDER_HISTORY_LINK_TEMPLATE_TEXT:"<a href=\"/profile/order_history.do?isNav=true&locale=#{userContext.localeCode}\">#{resourceBundleValues.profile.orderStatus}</a>",
			GLOBAL_SHIPPING_LINK_TEMPLATE:null,
			GLOBAL_SHIPPING_LINK_TEMPLATE_TEXT:"| <a id=\"showShippingTo\" href=\"#\" onclick=\"return siteNavigation.showShippingOptInOverlay()\">#{resourceBundleValues.profile.shippingTo}: <img id=\"shippingToCountryFlag\" alt=\"#{userContext.globalShippingCountryCode}\" title=\"#{userContext.globalShippingCountryName}\" src=\"/gid/assets/profile/internationalShipping/icons/#{userContext.globalShippingCountryCode}.gif\"/></a>",
			INLINE_BAG_SUMMARY_LINK_TEMPLATE:null,
			INLINE_BAG_SUMMARY_LINK_TEMPLATE_TEXT:"#{shoppingBag.totalItemCount} #{evaluatedMessages.inlineBagQuantityMessage} <a href=\"/buy/shopping_bag.do\">#{resourceBundleValues.inLineBag.yourBag}</a>",
			LOCAL_SITE_PROD_ENVIRONMENT_EXPRESSION_TEMPLATE:null,
			LOCAL_SITE_PROD_ENVIRONMENT_EXPRESSION_TEMPLATE_TEXT:"^https?://[^/]*(#{brandUrl}|gap.com|gap.eu|gapcanada.ca|bananarepublic.ca|oldnavy.ca|thegap.co.uk|bananarepublic.co.uk|bananarepublic.com|oldnavy.com|piperlime.com|athleta.com)",
			LOCAL_SITE_TEST_ENVIRONMENT_EXPRESSION_TEMPLATE:null,
			LOCAL_SITE_TEST_ENVIRONMENT_EXPRESSION_TEMPLATE_TEXT:"^https?://[^/]*(gidgol.com|gidgol.ca|gidgol.co.uk|gidgol.eu|gidapps.com|gidapps.ca|gidapps.eu|gidbrol.com|gidonol.com|gidplol.com|gidatol.com)"
		},
		elementIds:{
			SIGN_IN_YOUR_ACCOUNT:"signInYourAccount",
			SIGN_IN_SIGN_IN:"signInSignIn",
			SIGN_IN_ORDER_STATUS:"signInOrderStatus",
			SHIPPING_TO:"shippingTo",
			INLINE_BAG_SUMMARY:"inlineBagTopPriceLayer"
		},
		requestParameters:{
			SD_REFERER_PARAMETER:"sdReferer",
			TID_PARAMETER:"tid",
			AP_PARAMETER:"ap",
			SEM_PARAMETER:"sem",
			REFERRER_PARAMETER:"referrer",
			MKWID_PARAMETER:"mkwid",
			CREATIVE_PARAMETER:"creative",
			AKAMAI_COUNTRY_CODE_PARAMETER:"akamaiCountryCode",
			CID_PARAMETER:"cid",
			SIZE_CATEGORY_IDS_PARAMETER:"sizeCategoryIds",
			ORIGIN : "origin"
		}
	},
	constructors: {
	},
	api: {

	},
	model: {
		isDomReady:false,
		isPersonalizationDataReady:false,
		personalizationData:null,
		globalShipping:{
			isGlobalShippingActive:false
		},
		origin : null
	},
	controller: {
		init: {
			main: function(origin) {
				personalizationService.model.origin = origin;
				personalizationService.controller.init.setConstants();
				var domLoadedHandler = personalizationService.controller.handlers.domLoadedHandler;
				Event.observe(document, "dom:loaded", domLoadedHandler);
				var getPersonalizationData =
					personalizationService.controller.getPersonalizationData.bind(personalizationService);
				getPersonalizationData();
			},
			setConstants:function() {
				var constants = personalizationService.constants;
				var resourceBundlePersonalizedMyAccount = resourceBundleValues.profile.personalizedMyAccount;
				var resourceBundleYourAccount = resourceBundleValues.profile.yourAccount;
				constants.templates.ANONYMOUS_USER_ACCOUNT_LINK_TEMPLATE = new Template(constants.templates.ANONYMOUS_USER_ACCOUNT_LINK_TEMPLATE_TEXT);
				constants.templates.RECOGNIZED_USER_ACCOUNT_LINK_TEMPLATE = new Template(constants.templates.RECOGNIZED_USER_ACCOUNT_LINK_TEMPLATE_TEXT);
				constants.templates.LOGGED_IN_USER_ACCOUNT_LINK_TEMPLATE = new Template(constants.templates.LOGGED_IN_USER_ACCOUNT_LINK_TEMPLATE_TEXT);
				
				constants.templates.ANONYMOUS_USER_SIGN_IN_LINK_TEMPLATE = new Template(constants.templates.ANONYMOUS_USER_SIGN_IN_LINK_TEMPLATE_TEXT);
				constants.templates.RECOGNIZED_USER_SIGN_OUT_LINK_TEMPLATE = new Template(constants.templates.RECOGNIZED_USER_SIGN_OUT_LINK_TEMPLATE_TEXT);
				constants.templates.LOGGED_IN_USER_SIGN_OUT_LINK_TEMPLATE = new Template(constants.templates.LOGGED_IN_USER_SIGN_OUT_LINK_TEMPLATE_TEXT);
				
				constants.templates.ORDER_HISTORY_LINK_TEMPLATE = new Template(constants.templates.ORDER_HISTORY_LINK_TEMPLATE_TEXT);
				constants.templates.GLOBAL_SHIPPING_LINK_TEMPLATE = new Template(constants.templates.GLOBAL_SHIPPING_LINK_TEMPLATE_TEXT);
				constants.templates.INLINE_BAG_SUMMARY_LINK_TEMPLATE = new Template(constants.templates.INLINE_BAG_SUMMARY_LINK_TEMPLATE_TEXT);
				
				constants.templates.LOCAL_SITE_PROD_ENVIRONMENT_EXPRESSION_TEMPLATE = new Template(constants.templates.LOCAL_SITE_PROD_ENVIRONMENT_EXPRESSION_TEMPLATE_TEXT);
				constants.templates.LOCAL_SITE_TEST_ENVIRONMENT_EXPRESSION_TEMPLATE = new Template(constants.templates.LOCAL_SITE_TEST_ENVIRONMENT_EXPRESSION_TEMPLATE_TEXT);
			}
		},
		handlers: {
			domLoadedHandler: function() {
				personalizationService.model.isDomReady = true;
			}
		},
		getPersonalizationData:function() {
			document.fire("personalizationDataRequest:begin");
			var personalizationServiceUrl = this.controller.getPersonalizationServiceUrl();
			new Ajax.Request(
					personalizationServiceUrl,
				{
					method:'get',
					onSuccess:personalizationService.controller.personalizationDataResponseHandlers.successHandler,
					onException:personalizationService.controller.personalizationDataResponseHandlers.exceptionHandler
				}
			);
		},
		getPersonalizationServiceUrl:function() {
			var PERSONALIZATION_SERVICE_PATH = personalizationService.constants.PERSONALIZATION_SERVICE_PATH;
			var requestProtocol = null;
			var requestHost = window.location.host;
			if (window.location.protocol == "http:") {
				requestProtocol = "http:";
			} else {
				requestProtocol = "https:";
			}
			var shopperId = personalizationService.controller.getShopperId();
			var personalizationServiceUrl = requestProtocol + "//" + requestHost + PERSONALIZATION_SERVICE_PATH + "/" + shopperId;
			var queryParametersUrlFormatted = personalizationService.controller.getQueryParametersUrlFormatted();
			if (queryParametersUrlFormatted != null) {
				personalizationServiceUrl = personalizationServiceUrl + "?" + queryParametersUrlFormatted;
			}
			return personalizationServiceUrl;
		},
		getQueryParametersUrlFormatted:function() {
			var constants = personalizationService.constants;
			var requestParameters = constants.requestParameters;
			var model = personalizationService.model;
			var queryParametersUrlFormatted = null;
			var queryParameters = [];
			var isSdReferer = false;
			var constants = personalizationService.constants;
			var STRING_TRUE = constants.STRING_TRUE;
			/* sdReferer */
			var SD_REFERER_PARAMETER = requestParameters.SD_REFERER_PARAMETER;
			var sdRefererParameter = gidLib.getQuerystringParam(SD_REFERER_PARAMETER, true);
			if (sdRefererParameter != null && sdRefererParameter != "") {
				var sdRefererParameterEncoded = encodeURIComponent(sdRefererParameter);
				queryParameters.push(SD_REFERER_PARAMETER + "=" + sdRefererParameterEncoded);
				isSdReferer = true;
			}
			/* referrer */
			var REFERRER_PARAMETER = requestParameters.REFERRER_PARAMETER;
			var isReferrerAThirdPartySite = personalizationService.controller.isReferrerAThirdPartySite();
			if (!isSdReferer && isReferrerAThirdPartySite) {
				var referrerEncoded = encodeURIComponent(model.referrer);
				queryParameters.push(REFERRER_PARAMETER + "=" + referrerEncoded);
			}
			/* tid */
			var TID_PARAMETER = requestParameters.TID_PARAMETER;
			var tidQueryParameter = gidLib.getQuerystringParam(TID_PARAMETER);
			if (tidQueryParameter != null && tidQueryParameter != "") {
				var tidQueryParameterEncoded = encodeURIComponent(tidQueryParameter);
				queryParameters.push(TID_PARAMETER + "=" + tidQueryParameterEncoded);
			}
			/* ap */
			var AP_PARAMETER = requestParameters.AP_PARAMETER;
			var apQueryParameter = gidLib.getQuerystringParam(AP_PARAMETER);
			if (apQueryParameter != null && apQueryParameter != "") {
				var apQueryParameterEncoded = encodeURIComponent(apQueryParameter);
				queryParameters.push(AP_PARAMETER + "=" + apQueryParameterEncoded);
			}
			/* mkwid */
			var MKWID_PARAMETER = requestParameters.MKWID_PARAMETER;
			var mkwidQueryParameter = gidLib.getQuerystringParam(MKWID_PARAMETER);
			if (mkwidQueryParameter != null && mkwidQueryParameter != "") {
				var mkwidQueryParameterEncoded = encodeURIComponent(mkwidQueryParameter);
				queryParameters.push(MKWID_PARAMETER + "=" + mkwidQueryParameterEncoded);
			}
			/* creative */
			var CREATIVE_PARAMETER = requestParameters.CREATIVE_PARAMETER;
			var creativeQueryParameter = gidLib.getQuerystringParam(CREATIVE_PARAMETER);
			if (creativeQueryParameter != null && creativeQueryParameter != "") {
				var creativeQueryParameterEncoded = encodeURIComponent(creativeQueryParameter);
				queryParameters.push(CREATIVE_PARAMETER + "=" + creativeQueryParameterEncoded);
			}
			/* sem */
			var SEM_PARAMETER = requestParameters.SEM_PARAMETER;
			var semParameter = gidLib.getQuerystringParam(SEM_PARAMETER, true);
			if (semParameter == "true") {
				var semParameterEncoded = encodeURIComponent(semParameter);
				queryParameters.push(SEM_PARAMETER + "=" + semParameterEncoded);
			} else if (isReferrerAThirdPartySite){
				queryParameters.push(SEM_PARAMETER + "=" + STRING_TRUE);
			}
			/* akamaiCountryCode */
			var AKAMAI_COUNTRY_CODE_PARAMETER = requestParameters.AKAMAI_COUNTRY_CODE_PARAMETER;
			var akamaiCountryCodeParameter = gidLib.getQuerystringParam(AKAMAI_COUNTRY_CODE_PARAMETER, true);
			if (akamaiCountryCodeParameter != null && akamaiCountryCodeParameter != "") {
				var akamaiCountryCodeParameterEncoded = encodeURIComponent(akamaiCountryCodeParameter);
				queryParameters.push(AKAMAI_COUNTRY_CODE_PARAMETER + "=" + akamaiCountryCodeParameterEncoded);
			}
			/* cid */
			if (window["categoryInfo"]) {
				var CID_PARAMETER = requestParameters.CID_PARAMETER;
				var cidParameter = categoryInfo.businessCatalogItemId;
				var cidParameterEncoded = encodeURIComponent(cidParameter);
				queryParameters.push(CID_PARAMETER + "=" + cidParameterEncoded);
			}
			/* sizeCategoryIds */
			if (window["categoryInfo"]) {
				if (categoryInfo.isShopBySize) {
					var SIZE_CATEGORY_IDS_PARAMETER = requestParameters.SIZE_CATEGORY_IDS_PARAMETER;
					var sizeCategoryIds = categoryInfo.sizeCategoryIds;
					var sizeCategoryIdsEncoded = encodeURIComponent(sizeCategoryIds);
					queryParameters.push(SIZE_CATEGORY_IDS_PARAMETER + "=" + sizeCategoryIdsEncoded);
				}
			}
			
			if (personalizationService.model.origin) {
				queryParameters.push(requestParameters.ORIGIN + "=" + personalizationService.model.origin);
			}
			if (queryParameters.length > 0) {
				queryParametersUrlFormatted = queryParameters.join("&");
			}
			return queryParametersUrlFormatted;
		},
		getShopperId:function() {
			var shopperId = (getCookie("unknownShopperId") + "").replace(/\|\|\|/,"");
			if (shopperId == "" || shopperId == "0" || shopperId == "null" || shopperId == null) {
				shopperId = "initialize";
			}
			var shopperIdEncoded = encodeURIComponent(shopperId);
			return shopperIdEncoded;
		},
		personalizationDataResponseHandlers:{
			successHandler:function(response) {
				personalizationService.model.personalizationData = response.responseJSON;
				personalizationService.model.isPersonalizationDataReady = true;
				document.fire("personalizationDataRequest:end");
				document.fire("personalizationData:ready");
				var isDomReady = personalizationService.model.isDomReady;
				var updateDomWithPersonalizationData = personalizationService.view.updateDomWithPersonalizationData;
				if (isDomReady) {
					/* Update DOM with Personalization data */
					updateDomWithPersonalizationData();
				} else {
					/* Wait for DOM event to fire */
					Event.observe(document, "dom:loaded", updateDomWithPersonalizationData);
				}
			},
			exceptionHandler:function(response, e) {
				document.fire("personalizationDataRequest:end");
				document.fire("personalizationDataRequest:exception");
				throw(e);
			}
		},
		isReferrerAThirdPartySite:function() {
			var isReferrerAThirdPartySite = false;
			var isReferrerALocalSite = false;
			var isTestEnvironment = false;
			var referrer = document.referrer;
			var brandUrl = brandConst.BRAND_URL;
			var constants = personalizationService.constants;
			var model = personalizationService.model;
			
			
			var expressionContract = {"brandUrl":brandUrl};
			var expressionValue = constants.templates.LOCAL_SITE_PROD_ENVIRONMENT_EXPRESSION_TEMPLATE.evaluate(expressionContract);
			var expressionValueForTestEnvironments = constants.templates.LOCAL_SITE_TEST_ENVIRONMENT_EXPRESSION_TEMPLATE.evaluate(expressionContract);
			var urlExpression = new RegExp(expressionValue, "i");
			var urlExpressionForTestEnvironments = new RegExp(expressionValueForTestEnvironments, "i");
			if (referrer != null && referrer != undefined && referrer != "") {
				isReferrerALocalSite = urlExpression.test(referrer);
				if (isReferrerALocalSite == false) {
					// Test to see if the environment is a test environment - using test environment expression.
					isReferrerALocalSite = urlExpressionForTestEnvironments.test(referrer);
					if (isReferrerALocalSite == true) {
						isTestEnvironment = true;
					}
				}
			} else {
				/* Referrer does not exist so default to local */
				isReferrerALocalSite = true;
			}
			if (!isReferrerALocalSite) {
				isReferrerAThirdPartySite = true;
			}
			model.referrer = referrer;
			model.expressionValue = expressionValue;
			model.isReferrerALocalSite = isReferrerALocalSite;
			model.isReferrerATestEnvironment = isTestEnvironment;
			model.isReferrerAThirdPartySite = isReferrerAThirdPartySite;
			return isReferrerAThirdPartySite;
		}
	},
	view:{
		updateDomWithPersonalizationData:function() {
			var personalizationData = personalizationService.model.personalizationData;
			var constants = personalizationService.constants;
			var STRING_TRUE = constants.STRING_TRUE;
			if (personalizationData != undefined && personalizationData != null) {
				var userContext = personalizationData.personalizationInfoV1.userContext;
				var customerAccount = personalizationData.personalizationInfoV1.customerAccount;
				var shoppingBag = personalizationData.personalizationInfoV1.shoppingBag;
				var isAnonymousUser = userContext.isAnonymousUser;
				var isLoggedInUser = userContext.isLoggedInUser;
				var isRecognizedUser = userContext.isRecognizedUser;
				var personalizationDisplayContract = {
					"userContext":userContext,
					"shoppingBag":shoppingBag,
					"customerAccount":customerAccount,
					"resourceBundleValues":resourceBundleValues,
					commonDomElements:{},
					evaluatedMessages:{},
					"personalizationData":personalizationData
				};
				/* Get elements to update */
				with (personalizationDisplayContract) {
					commonDomElements.signInYourAccountElementId = constants.elementIds.SIGN_IN_YOUR_ACCOUNT;
					commonDomElements.signInYourAccountElement = $(commonDomElements.signInYourAccountElementId);
					commonDomElements.signInSignInElementId = constants.elementIds.SIGN_IN_SIGN_IN;
					commonDomElements.signInSignInElement = $(commonDomElements.signInSignInElementId);
					commonDomElements.signInOrderStatusElementId = constants.elementIds.SIGN_IN_ORDER_STATUS;
					commonDomElements.signInOrderStatusElement = $(commonDomElements.signInOrderStatusElementId);
					commonDomElements.shippingToElementId = constants.elementIds.SHIPPING_TO;
					commonDomElements.shippingToElement = $(commonDomElements.shippingToElementId);
					commonDomElements.inlineBagSummaryElementId = constants.elementIds.INLINE_BAG_SUMMARY;
					commonDomElements.inlineBagSummaryElement = $(commonDomElements.inlineBagSummaryElementId);
				}

				var signInYourAccountElement = personalizationDisplayContract.commonDomElements.signInYourAccountElement;
				var hasStandardDomElementsToUpdate = false;
				if (signInYourAccountElement != null && signInYourAccountElement != undefined) {
					hasStandardDomElementsToUpdate = true;
				}
				if (hasStandardDomElementsToUpdate) {
					/* Update elements based on state */
					if (isAnonymousUser == STRING_TRUE) {
						personalizationService.view.anonymousUserDomDisplayHelper(personalizationDisplayContract);
					} else if (isRecognizedUser == STRING_TRUE) {
						personalizationService.view.recognizedUserDomDisplayHelper(personalizationDisplayContract);
					} else if (isLoggedInUser == STRING_TRUE) {
						personalizationService.view.loggedInUserDomDisplayHelper(personalizationDisplayContract);
					}
					personalizationService.view.allUsersDomDisplayHelper(personalizationDisplayContract);
				}
			}
		},
		anonymousUserDomDisplayHelper:function(personalizationDisplayContract) {
			var constants = personalizationService.constants;
			/* signInYourAccountElement */
			var signInYourAccountContentXhtml = constants.templates.ANONYMOUS_USER_ACCOUNT_LINK_TEMPLATE.evaluate(personalizationDisplayContract);
			personalizationDisplayContract.commonDomElements.signInYourAccountElement.update(signInYourAccountContentXhtml);
			/* signInSignInElement */
			var signInSignInContentXhtml = constants.templates.ANONYMOUS_USER_SIGN_IN_LINK_TEMPLATE.evaluate(personalizationDisplayContract);
			personalizationDisplayContract.commonDomElements.signInSignInElement.update(signInSignInContentXhtml);
		},
		recognizedUserDomDisplayHelper:function(personalizationDisplayContract) {
			var constants = personalizationService.constants;
			/* signInYourAccountElement */
			var signInYourAccountContentXhtml = constants.templates.RECOGNIZED_USER_ACCOUNT_LINK_TEMPLATE.evaluate(personalizationDisplayContract);
			personalizationDisplayContract.commonDomElements.signInYourAccountElement.update(signInYourAccountContentXhtml);
			/* signInSignInElement */
			var signInSignInContentXhtml = constants.templates.RECOGNIZED_USER_SIGN_OUT_LINK_TEMPLATE.evaluate(personalizationDisplayContract);
			personalizationDisplayContract.commonDomElements.signInSignInElement.update(signInSignInContentXhtml);
		},
		loggedInUserDomDisplayHelper:function(personalizationDisplayContract) {
			var constants = personalizationService.constants;
			/* signInYourAccountElement */
			var signInYourAccountContentXhtml = constants.templates.LOGGED_IN_USER_ACCOUNT_LINK_TEMPLATE.evaluate(personalizationDisplayContract);
			personalizationDisplayContract.commonDomElements.signInYourAccountElement.update(signInYourAccountContentXhtml);
			/* signInSignInElement */
			var signInSignInContentXhtml = constants.templates.LOGGED_IN_USER_SIGN_OUT_LINK_TEMPLATE.evaluate(personalizationDisplayContract);
			personalizationDisplayContract.commonDomElements.signInSignInElement.update(signInSignInContentXhtml);
		},
		allUsersDomDisplayHelper:function(personalizationDisplayContract) {
			var constants = personalizationService.constants;
			var commonDomElements = personalizationDisplayContract.commonDomElements;
			var evaluatedMessages = personalizationDisplayContract.evaluatedMessages;
			var shoppingBag = personalizationDisplayContract.shoppingBag;
			/* signInOrderStatusElement */
			var signInOrderStatusContentXhtml = constants.templates.ORDER_HISTORY_LINK_TEMPLATE.evaluate(personalizationDisplayContract);
			commonDomElements.signInOrderStatusElement.update(signInOrderStatusContentXhtml);
			/* shippingToElement */
			var shippingToElement = commonDomElements.shippingToElement;
			if (shippingToElement != null) {
				/* shippingToElement may be null if globalShipping is not enabled. */
				var shippingToContentXhtml = constants.templates.GLOBAL_SHIPPING_LINK_TEMPLATE.evaluate(personalizationDisplayContract);
				shippingToElement.update(shippingToContentXhtml);
			}
			/* inlineBagSummaryElement */
			/* This element may be null on the shopping bag page */
			var inlineBagSummaryElement = commonDomElements.inlineBagSummaryElement;
			if (inlineBagSummaryElement != null) {
					
				if (shoppingBag.totalItemCount == "1") {
					evaluatedMessages.inlineBagQuantityMessage = resourceBundleValues.inLineBag.inlineBagItemSingular;
				} else {
					evaluatedMessages.inlineBagQuantityMessage = resourceBundleValues.inLineBag.inlineBagItemPlural;
				}
				var inlineBagSummaryContentXhtml = constants.templates.INLINE_BAG_SUMMARY_LINK_TEMPLATE.evaluate(personalizationDisplayContract);
				inlineBagSummaryElement.update(inlineBagSummaryContentXhtml);
			}
		}
	}
}

var personalizationService = new PersonalizationService();
/**
 * This Service Class is concerned with processing information related to the category page.
 * The principle concern is loading the product grid based on the personalization service response.
 * @author Andrew Southwick
 */
var CategoryService = Class.create();
CategoryService.prototype = {
	initialize: function() {
	},
	constants: {
		CATEGORY_PRODUCT_GRID_PATH:"/browse/categoryProductGrid.do",
		requestParameters:{
			CID_PARAMETER:"cid",
			SEM_PARAMETER:"sem",
			FILTERED_TAB_ACTIVE_PARAMETER:"actFltr",
			SORT_BY_PARAMETER:"sortBy",
			PAGE_ID_PARAMETER:"pageID",
			GLOBAL_SHIPPING_CURRENCY_CODE_PARAMETER:"globalShippingCurrencyCode",
			GLOBAL_SHIPPING_COUNTRY_CODE_PARAMETER:"globalShippingCountryCode",
			PROMO_ID_PARAMETER:"promoid",
			PROMO_TYPE_PARAMETER:"promotype",
			PROMO_REF_ID_PARAMETER:"promorefid"
		},
		PRODUCT_GRID_CLICKSTREAM_VIEW_ALL_ACTIVE:"1",
		PRODUCT_GRID_CLICKSTREAM_VIEW_ALL_INACTIVE:"0",
		PRODUCT_GRID_VIEW_ALL_PARAMETER_VALUE:"-1",
		PRODUCT_GRID_VIEW_FIRST_PAGE_PARAMETER_VALUE:"0"
	},
	constructors: {
	},
	api: {
	},
	model: {
		categoryProductGridXhtml:null,
		isDomReady:false,
		isProductGridRequested:false,
		productGridReadyTimerId:null,
		isProductGridReadyTimerCleared:false
	},
	controller: {
		init: {
			main: function() {
				if (window["categoryInfo"]) {
					var personalizationDataReadyHandler = categoryService.controller.handlers.personalizationDataReadyHandler.bind(categoryService);
					var isPersonalizationDataReady = personalizationService.model.isPersonalizationDataReady;
					if (isPersonalizationDataReady) {
						personalizationDataReadyHandler();
					} else {
						Event.observe(document, "personalizationData:ready", personalizationDataReadyHandler);
					}
					var domLoadedHandler = categoryService.controller.handlers.domLoadedHandler;
					Event.observe(document, "dom:loaded", domLoadedHandler);
					if (categoryInfo.categoryPageCachingActive == "true" && categoryInfo.isShopBySize) {
						Event.observe(document, 'categoryProductGrid:ready', categoryService.controller.init.setShopBySize);
					}
				}
			},
			setShopBySize:function() {
				objSBS.initializeSBS();
			}
		},
		getCategoryProductGrid:function() {
			document.fire("categoryProductGridRequest:begin");
			var categoryProductGridUrl = categoryService.controller.getCategoryProductGridUrl();
			new Ajax.Request(
					categoryProductGridUrl,
				{
					method:'get',
					onSuccess:categoryService.controller.categoryProductGridResponseHandlers.successHandler,
					onException:categoryService.controller.categoryProductGridResponseHandlers.exceptionHandler
				}
			);
		},
		getCategoryProductGridUrl:function() {
			var CATEGORY_PRODUCT_GRID_PATH = categoryService.constants.CATEGORY_PRODUCT_GRID_PATH;
			var requestProtocol = null;
			var requestHost = window.location.host;
			if (window.location.protocol == "http:") {
				requestProtocol = "http:";
			} else {
				requestProtocol = "https:";
			}
			var categoryProductGridUrl = requestProtocol + "//" + requestHost + CATEGORY_PRODUCT_GRID_PATH;
			var queryParametersUrlFormatted = categoryService.controller.getQueryParametersUrlFormatted();
			if (queryParametersUrlFormatted != null) {
				categoryProductGridUrl = categoryProductGridUrl + "?" + queryParametersUrlFormatted;
			}
			return categoryProductGridUrl;
		},
		getQueryParametersUrlFormatted:function() {
			var constants = categoryService.constants;
			var requestParameters = constants.requestParameters;
			var model = categoryService.model;
			var queryParametersUrlFormatted = null;
			var queryParameters = [];
			var clickStream = personalizationService.model.personalizationData.personalizationInfoV1.userContext.clickStream;
			var userContext = personalizationService.model.personalizationData.personalizationInfoV1.userContext;
			var isReferrerAThirdPartySite = personalizationService.controller.isReferrerAThirdPartySite();
			/* cid */
			if (window["categoryInfo"]) {
				var CID_PARAMETER = requestParameters.CID_PARAMETER;
				var cidParameter = categoryInfo.businessCatalogItemId;
				if (cidParameter != undefined && cidParameter != null && cidParameter != "" && cidParameter.length > 0) {
					var cidParameterEncoded = encodeURIComponent(cidParameter);
					queryParameters.push(CID_PARAMETER + "=" + cidParameterEncoded);
				} else {
					/* Detect if the promoid parameter is present which means this is an mpp.do request. */
					var PROMO_ID_PARAMETER = requestParameters.PROMO_ID_PARAMETER;
					var PROMO_REF_ID_PARAMETER = requestParameters.PROMO_REF_ID_PARAMETER;
					var PROMO_TYPE_PARAMETER = requestParameters.PROMO_TYPE_PARAMETER;
					
					var promoIdParameter = gidLib.getQuerystringParam("promoid", true);
					if (promoIdParameter != "" && promoIdParameter != undefined && promoIdParameter != null) {
						var promoRefIdParameter = gidLib.getQuerystringParam("promorefid", true);
						var promoTypeParameter = gidLib.getQuerystringParam("promotype", true);
						/* promoid */
						var promoIdParameterEncoded = encodeURIComponent(promoIdParameter);
						queryParameters.push(PROMO_ID_PARAMETER + "=" + promoIdParameterEncoded);
						/* promorefid */
						var promoRefIdParameterEncoded = encodeURIComponent(promoRefIdParameter);
						queryParameters.push(PROMO_REF_ID_PARAMETER + "=" + promoRefIdParameterEncoded);
						/* promotype */
						var promoTypeParameterEncoded = encodeURIComponent(promoTypeParameter);
						queryParameters.push(PROMO_TYPE_PARAMETER + "=" + promoTypeParameterEncoded);
					}
				}
			}
			/* actFltr */
			if (clickStream["isFilteredTabActive"]) {
				var FILTERED_TAB_ACTIVE_PARAMETER = requestParameters.FILTERED_TAB_ACTIVE_PARAMETER;
				var filteredTabActiveParameter = clickStream.isFilteredTabActive;
				var filteredTabActiveParameterEncoded = encodeURIComponent(filteredTabActiveParameter);
				queryParameters.push(FILTERED_TAB_ACTIVE_PARAMETER + "=" + filteredTabActiveParameterEncoded);
			}
			/* sem */
			var SEM_PARAMETER = requestParameters.SEM_PARAMETER;
			var semParameter = gidLib.getQuerystringParam(SEM_PARAMETER, true);
			if (semParameter == "true") {
				var semParameterEncoded = encodeURIComponent(semParameter);
				queryParameters.push(SEM_PARAMETER + "=" + semParameterEncoded);
			} else if (isReferrerAThirdPartySite){
				queryParameters.push(SEM_PARAMETER + "=" + "true");
			}
			/* sortBy */
			var SORT_BY_PARAMETER = requestParameters.SORT_BY_PARAMETER;
			var sortByParameter = "0";
			queryParameters.push(SORT_BY_PARAMETER + "=" + sortByParameter);
			
			/* sizeCategoryUserSelections */
			if (clickStream["sizeCategoryUserSelections"]) {
				var sizeCategoryUserSelectionsParameters =
					categoryService.controller.getSizeCategoryUserSelectionsParameters(clickStream);
				if (sizeCategoryUserSelectionsParameters != null) {
					queryParameters.push(sizeCategoryUserSelectionsParameters);
				};
			}
			
			/* pageID */
			var pageIdParameterValue = categoryService.controller.getPageIdParameter(clickStream);
			var PAGE_ID_PARAMETER = constants.requestParameters.PAGE_ID_PARAMETER;
			queryParameters.push(PAGE_ID_PARAMETER + "=" + pageIdParameterValue);
			
			/* globalShippingCurrencyCode */
			var globalShippingCurrencyCodeParameterValue = categoryService.controller.getGlobalShippingCurrencyCodeParameter(userContext);
			if (globalShippingCurrencyCodeParameterValue != null) {
				var GLOBAL_SHIPPING_CURRENCY_CODE_PARAMETER = constants.requestParameters.GLOBAL_SHIPPING_CURRENCY_CODE_PARAMETER;
				queryParameters.push(GLOBAL_SHIPPING_CURRENCY_CODE_PARAMETER + "=" + globalShippingCurrencyCodeParameterValue);
			}
			/* globalShippingCountryCode */
			var globalShippingCountryCodeParameterValue = categoryService.controller.getGlobalShippingCountryCodeParameter(userContext);
			if (globalShippingCountryCodeParameterValue != null) {
				var GLOBAL_SHIPPING_COUNTRY_CODE_PARAMETER = constants.requestParameters.GLOBAL_SHIPPING_COUNTRY_CODE_PARAMETER;
				queryParameters.push(GLOBAL_SHIPPING_COUNTRY_CODE_PARAMETER + "=" + globalShippingCountryCodeParameterValue);
			}
			
			if (queryParameters.length > 0) {
				queryParametersUrlFormatted = queryParameters.join("&");
			}
			return queryParametersUrlFormatted;
		},
		getPageIdParameter:function(clickStream) {
			var pageIdParameter = null;
			var constants = categoryService.constants;
			/* First check if there is a pageID in the querystring, and if so use that value,
			 * otherwise check the viewAll status in clickstream, and adjust the pageID to match
			 * the clickStream.viewAll value.
			 */
			var PAGE_ID_PARAMETER = constants.requestParameters.PAGE_ID_PARAMETER;
			var pageIdQueryParameterValue = gidLib.getQuerystringParam(PAGE_ID_PARAMETER);
			if (pageIdQueryParameterValue != null && pageIdQueryParameterValue != "") {
				/* parameter has value - so use this parameter */
				pageIdParameter = pageIdQueryParameterValue;
			} else {
				if (clickStream["viewAll"]) {
					var clickStreamViewAll = clickStream.viewAll;
					if (clickStreamViewAll != null) {
						if (clickStreamViewAll == constants.PRODUCT_GRID_CLICKSTREAM_VIEW_ALL_ACTIVE) {
							pageIdParameter = constants.PRODUCT_GRID_VIEW_ALL_PARAMETER_VALUE;
						} else {
							pageIdParameter = constants.PRODUCT_GRID_VIEW_FIRST_PAGE_PARAMETER_VALUE;
						}
					}
				}
			}
			return pageIdParameter;
		},
		getGlobalShippingCurrencyCodeParameter:function(userContext) {
			var currencyCodeParameter = null;
			if (userContext && userContext["globalShippingCurrencyCode"]) {
				currencyCodeParameter = userContext.globalShippingCurrencyCode;
			}
			return currencyCodeParameter;
		},
		getGlobalShippingCountryCodeParameter:function(userContext) {
			var countryCodeParameter = null;
			if (userContext && userContext["globalShippingCountryCode"]) {
				countryCodeParameter = userContext.globalShippingCountryCode;
			}
			return countryCodeParameter;
		},
		getSizeCategoryUserSelectionsParameters:function(clickStream) {
			var sizeCategoryUserSelectionsParameters = null;
			if (clickStream["sizeCategoryUserSelections"]) {
				var sizeCategoryUserSelections = clickStream.sizeCategoryUserSelections;
				if (sizeCategoryUserSelections.length > 0) {
					var sizeCategoryUserSelectionsParametersArray = [];
					var sizeCategoryUserSelectionsArray = null;
					/* Create the sizeCategoryUserSelectionsArray */
					if (sizeCategoryUserSelections.indexOf("||") != -1) {
						/* Multiple size categories detected */
						sizeCategoryUserSelectionsArray = sizeCategoryUserSelections.split("||");
					} else {
						/* Single size category detected */
						sizeCategoryUserSelectionsArray = [];
						sizeCategoryUserSelectionsArray[0] = sizeCategoryUserSelections;
					}
					if (sizeCategoryUserSelectionsArray != null && sizeCategoryUserSelectionsArray.length > 0) {
						var isSizeCategoryWithUserSelections = false;
						var sizeCategoryParameters = null;
						var sizeCategoryParametersArray = null;
						var sizeCategoryId = null;
						var sizeValuesUserSelection = null;
						var sizeValuesParameters = null;
						var sizeValuesParametersArray = null;
						var sizeCategoryUserSelection = null;
						for (var i = 0; i < sizeCategoryUserSelectionsArray.length; i++) {
							isSizeCategoryWithUserSelections = false;
							sizeCategoryParameters = null;
							sizeCategoryParametersArray = [];
							sizeCategoryId = null;
							sizeValuesUserSelection = null;
							sizeValuesParametersArray = [];
							sizeCategoryUserSelection = sizeCategoryUserSelectionsArray[i];
							if (sizeCategoryUserSelection.indexOf("=")) {
								sizeCategoryUserSelectionArray = sizeCategoryUserSelection.split("=");
								sizeCategoryId = sizeCategoryUserSelectionArray[0];
								sizeValuesUserSelection = sizeCategoryUserSelectionArray[1];
								if (sizeValuesUserSelection != null && sizeValuesUserSelection.length > 0 && sizeValuesUserSelection != null) {
									var sizeValuesUserSelectionArray = sizeValuesUserSelection.split(",");
									for (j = 0; j < sizeValuesUserSelectionArray.length; j++) {
										var sizeValue = sizeValuesUserSelectionArray[j];
										if (sizeValue != null && sizeValue.length > 0 && sizeValue != "null") {
											isSizeCategoryWithUserSelections = true;
											sizeValuesParametersArray.push(sizeValue);
										}
									}
									if (isSizeCategoryWithUserSelections) {
										sizeValuesParameters = sizeValuesParametersArray.join(",");
										sizeCategoryParametersArray.push("szCatId" + "=" + sizeCategoryId);
										sizeCategoryParametersArray.push("szCatValue" + sizeCategoryId + "=" + sizeValuesParameters);
										sizeCategoryParameters = sizeCategoryParametersArray.join("&");
										sizeCategoryUserSelectionsParametersArray.push(sizeCategoryParameters);
									}
								}
							}
						}
					}
					if (sizeCategoryUserSelectionsParametersArray.length > 0) {
						sizeCategoryUserSelectionsParameters = sizeCategoryUserSelectionsParametersArray.join("&");
					}
				}
			}
			return sizeCategoryUserSelectionsParameters;
		},
		categoryProductGridResponseHandlers:{
			successHandler:function(response) {
				categoryService.model.categoryProductGridXhtml = response.responseText;
				document.fire("categoryProductGridRequest:end");
				var isDomReady = personalizationService.model.isDomReady;
				var updateDomWithCategoryProductGrid = categoryService.view.updateDomWithCategoryProductGrid;
				if (isDomReady) {
					/* Update DOM with category product grid */
					updateDomWithCategoryProductGrid();
				} else {
					/* Wait for DOM event to fire */
					Event.observe(document, "dom:loaded", updateDomWithCategoryProductGrid);
				}
			},
			exceptionHandler:function(response, e) {
				document.fire("categoryProductGridRequest:end");
				document.fire("categoryProductGridRequest:exception");
				throw(e);
			}
		},
		handlers: {
			/**
			 * 
			 */
			personalizationDataReadyHandler: function() {
				var isProductGridRequested = categoryService.model.isProductGridRequested;
				if (!isProductGridRequested) {
					categoryService.model.isProductGridRequested = true;
					this.controller.getCategoryProductGrid();
				}
			},
			domLoadedHandler: function() {
				categoryService.model.isDomReady = true;
			}
		}
	},
	view:{
		updateDomWithCategoryProductGrid:function() {
			var categoryProductGridXhtml = categoryService.model.categoryProductGridXhtml;
			categoryService.model.categoryProductGridXhtml.evalScripts();
			if (categoryProductGridXhtml != undefined && categoryProductGridXhtml != null) {
				var categoryProductGridElement = $("categoryProductGrid");
				categoryProductGridElement.update(categoryProductGridXhtml);
				var productGridReadyTimerId = setTimeout("categoryService.view.productGridReadyTimer()",50);
				categoryService.model.productGridReadyTimerId = productGridReadyTimerId;
			}
		},
		productGridReadyTimer:function() {
			var filterToolsElement = $("filterTools");
			var productGridReadyTimerId = categoryService.model.productGridReadyTimerId;
			if (filterToolsElement) {
				clearTimeout(productGridReadyTimerId);
				categoryService.model.isProductGridReadyTimerCleared = true;
				document.fire('categoryProductGrid:ready');
			}
		}
	}
}

var categoryService = new CategoryService();
/**
 * This Service Class is concerned with organizing marketMessage logic related to the application
 * such that respective application and content related methods can be queued and executed in a 
 * controllable sequence.
 * @author Andrew Southwick, Filipe Sabella
 */
var MarketMessageService = Class.create();
MarketMessageService.prototype = {
	initialize: function() {
	},
	constants: {
	},
	constructors: {
	},
	api: {
	},
	model: {
		isMarketMessageServiceActive: true,
        originCountryCode: "",
        isCanadaMarketCandidate: "false",
        isEuMarketCandidate: "false",
        canadaMarketEmailRegistrationUrl: "",
        euMarketEmailRegistrationUrl: "",
        geoLocation:""
	},
	controller: {
		init: {
			main: function() {
				var isMarketMessageServiceActive = marketMessageService.model.isMarketMessageServiceActive;
				if (isMarketMessageServiceActive) {
					var personalizationDataReadyHandler = marketMessageService.controller.handlers.personalizationDataReadyHandler.bind(marketMessageService);
					Event.observe(document, "personalizationData:ready", personalizationDataReadyHandler);
				}
			}
		},
		handlers: {
			/**
			 * Method is called on the domReady event.
			 */
			personalizationDataReadyHandler: function() {
				this.controller.setMarketMessageServiceModel();
				document.fire("marketMessageService:ready");
			}
		},
		setMarketMessageServiceModel: function() {
			var model = marketMessageService.model;
			var marketingMessageInfo = personalizationService.model.personalizationData.personalizationInfoV1.marketingMessageInfo;
			model.originCountryCode = marketingMessageInfo.originCountryCode ? marketingMessageInfo.originCountryCode : "";
			model.isCanadaMarketCandidate = marketingMessageInfo.isCanadaMarketCandidate ? marketingMessageInfo.isCanadaMarketCandidate : "false";
			model.isEuMarketCandidate = marketingMessageInfo.isEuMarketCandidate ? marketingMessageInfo.isEuMarketCandidate : "false";
			model.canadaMarketEmailRegistrationUrl = marketingMessageInfo.canadaMarketEmailRegistrationUrl ? marketingMessageInfo.canadaMarketEmailRegistrationUrl : "";
			model.euMarketEmailRegistrationUrl = marketingMessageInfo.euMarketEmailRegistrationUrl ? marketingMessageInfo.euMarketEmailRegistrationUrl : "";
			model.geoLocation = marketingMessageInfo.geoLocation ? marketingMessageInfo.geoLocation : "";
			var setLegacyModel = marketMessageService.controller.setLegacyModel;
			setLegacyModel();
		},
		setLegacyModel:function() {
			var model = marketMessageService.model;
			marketMessageService.requestOriginCountryCode = model.originCountryCode;
			marketMessageService.isCanadaMarketCandidate = model.isCanadaMarketCandidate;
			marketMessageService.isEuMarketCandidate = model.isEuMarketCandidate;
			marketMessageService.canadaMarketEmailRegistrationUrl = model.canadaMarketEmailRegistrationUrl;
			marketMessageService.euMarketEmailRegistrationUrl = model.euMarketEmailRegistrationUrl;
			marketMessageService.geoLocation = model.geoLocation;
		}
	}
}

var marketMessageService = new MarketMessageService();
marketMessageService.controller.init.main();
/*******************************************************************************
 * @title: navigationManagementService.js
 * @author: Aaron Liebling
 * @date: 02-12-2011
 * @rev: 1.0
 * @desc: Navigation Service - This Service Class is concerned with organizing
 *        processing logic related to site navigation and display of vi
 *        pages/related functionality (search, facets, etc...)
 * 
 * @assumes: prototype.js 1.5 rel.
 * @assumes: no additional dependencies.
 ******************************************************************************/

var NavigationService = Class.create();

NavigationService.prototype = {
	initialize : function() {
	},
	constants : {
		catalogItemSubtype: {
			CATEGORY: 1,
			PRODUCTSET:10,
			PHOTOGRAPHIC_OUTFIT: 11,
			NONPHOTOGRAPHIC_OUTFIT: 12,
			STYLE: 13,
			STYLECOLOR: 14,
			SKU: 15,
			FOREIGN_STYLE: 16,
			FOREIGN_STYLECOLOR: 17
		}
	},
	constructors : {
		AbstractNavigationManager:function() {
			this.initialize = function() {
			};
			this.constants = {};
			this.model = {
				isActive:false
			};
			this.controller = {
				init:{
					main:function() {
			
					}
				},
				handlers: {
					domReadyHandler:function() {
					}
				}
			};
			this.view = {};
		}
	},
	api : {
		addNavigationManagerToRegistry:function(navigationManager) {
			navigationService.model.navigationManagerRegistry.push(navigationManager);
		}
	},
	model: {
		isActive:false,
		isDomReady:false,
		navigationManagerRegistry:[]
	},
	controller: {
		init: {
			main: function() {
				var domReadyHandler = navigationService.controller.handlers.domReadyHandler;
				processingService.api.addApplicationMethodToRegistry(domReadyHandler);

				if (navigationService.model.isActive) {
					
					// Call init.main() for all active registered navigation managers 
					var navigationManagerRegistryIterator = function (navigationManager, index) {
						navigationManager.model.isActive = navigationService.model.isActive;
						
						if (navigationManager.model.isActive) {
							navigationManager.controller.init.main();
						}
					};
					
					navigationService.model.navigationManagerRegistry.each(navigationManagerRegistryIterator);
					
				}
			}
		},
		managers:{
			flyoutNavigationManager:null,
			categoryFacetedSearchManager:null
		},
		handlers: {
			domReadyHandler:function() {
				navigationService.model.isDomReady = true;
				
				navigationService.view.setDomElementsForNavigationService();
				
				// Call domReadyHandler for all active registered navigation managers 
				var navigationManagerRegistryIterator = function (navigationManager, index) {
					if (navigationManager.model.isActive) {
						navigationManager.controller.handlers.domReadyHandler();
					}
				};
				
				navigationService.model.navigationManagerRegistry.each(navigationManagerRegistryIterator);
				
			}
		}
	},
	view : {
		/**
		 * Display the correct DOM elements and removing any unneeded elements
		 */
		setDomElementsForNavigationService:function() {
			var searchDiv = null;
			var unusedSearchDiv = null;
			
			if (navigationService.model.isActive) {
				searchDiv = $('topFacetedSearch');
				unusedSearchDiv = $('topSearch');
			} else {
				searchDiv = $('topSearch');
				unusedSearchDiv = $('topFacetedSearch');
			}
			
			if (searchDiv) {
				searchDiv.style.display = "block";
			}

			if (unusedSearchDiv) {
				unusedSearchDiv.remove();
			}
		}
	}
};



var navigationService = new NavigationService();
navigationService.controller.managers.categoryFacetedSearchManager = new navigationService.constructors.AbstractNavigationManager();

Object
		.extend(
				navigationService.controller.managers.categoryFacetedSearchManager,
				{
					constants : {
						PRODUCT_SEARCH_SERVICE_PATH : "/resources/productSearch/v1",
						DEFAULT_SEARCH_TEXT : "search",
						PRODUCT_GRID_ROW_BREAK : '<div class="clear5"/></div>',
						PRODUCT_PLACEHOLDER : '<div class="productCatItem"><div class="facetedSearchImgDiv imgDiv facetedGridProdImg"></div></div>',
						FACET_ID_PREFIX : "facet_",
						FACET_ID_PREFIX_LEFT_RAIL : "facet_lr_",
						FACET_ON_CHANGE_ACTION : "navigationService.controller.managers.categoryFacetedSearchManager.controller.filterByFacets()",
						productType : {
							PRODUCT_STYLE : "productStyle",
							PRODUCT_STYLE_COLOR : "productStyleColor"
						},
						requestParameters : {
							SEARCH_TEXT_PARAMETER : "searchText",
							CID_PARAMETER : "cid",
							GLOBAL_SHIPPING_CURRENCY_CODE_PARAMETER : "globalShippingCurrencyCode",
							GLOBAL_SHIPPING_COUNTRY_CODE_PARAMETER : "globalShippingCountryCode",
							PAGE_ID_PARAMETER : "pageId",
							SORT_BY_PARAMETER: "sortBy"
						},
						templates : {
							flyoutDisplay :{
								FACET_TEMPLATE: null,
								FACET_TEMPLATE_TEXT: '<div id="facet_#{facetNameId}" onmouseover="navigationService.controller.managers.categoryFacetedSearchManager.util.sizeFlyoutExpandContract(this);" onmouseout="navigationService.controller.managers.categoryFacetedSearchManager.util.sizeFlyoutExpandContract(this);" onclick="void(0);">'
										+ '<div id="#{facetNameId}_name" class="facetName clearfix">#{facetName} <span class="sprite-caret">&nbsp;</span></div>'
										+ '<div>Select one or more #{facetNameId}s</div>'
										+ '<div class="current-selection hideMe" id="#{facetNameId}_current_selection">'
										+ '<span class="clearAllButton hideMe">#{facetClearAll}</span>'
										+ '<div class="clearfix"></div>'
										+ '<ul class="current-selection-list" title="Your #{facetName} selections" alt="your #{facetName} selections"></ul>'
										+ '</div>'
										+ '#{facetDisplayContent}</div>',
								FACET_FLYOUT_CONTAINER_TEMPLATE: null,
								FACET_FLYOUT_CONTAINER_TEMPLATE_TEXT:'<div id="flyout_#{facetNameId}" class="hideMe facet-flyout">#{flyoutDisplayContent}<div class="facet_size_footer"><span class="clearAllButton hideMe">#{facetClearAll}</span></div></div>',
								FACET_FLYOUT_CLEAR_ALL_SELECTIONS_TEMPLATE : null,
								FACET_FLYOUT_CLEAR_ALL_SELECTIONS_TEMPLATE_TEXT : "<a href='#' class='sprite-button_clear' title='Clear' alt='Clear' onclick='#{clearAction}'>&nbsp;</a>"
							},		
							/**
							 * PRODUCT - Wrapper HTML for a product grid entry
							 */
							PAGE_PLACEHOLDER_TEMPLATE : null,
							PAGE_PLACEHOLDER_TEMPLATE_TEXT : '<div id="#{productGridPage}#{pageCount}" class="productGridPage">#{placeholderContent}</div>',
							PRODUCT_TEMPLATE : null,
							PRODUCT_TEMPLATE_TEXT : '<div id="categoryProductItem_#{pid}#{cidIdParam}" class="brand#{brandName} productCatItem" >'
									+ '<div id="categoryProductItemImageContainer_#{pid}#{cidIdParam}" class="brand#{brandName} facetedSearchImgDiv imgDiv productPlaceholder">'
									+ '<a href="#{categoryProductLink}">'
									+ '<img id="cimg_#{productImgId}" class="gridProdImg" alt="" src="#{productImagePath}" onmouseout="quickLook.closeQuickLookLauncher(event);" onmouseover="quickLook.openQuickLookLauncher(\'#{qlParam1}\',\'#{scid}\',\'#{cid}\',\'#{svid}\',\'cimg_#{productImgId}\', false,\'#{brandName}\');" />'
									+ '</a>'
									+ '</div>'
									+ '<a class="productItemName" href="#{categoryProductLink}">#{categoryProductName}</a>'
									+ '#{pricingHTML}'
									+ '#{mupAndMarketing}'
									+ '</div>',
							PRODUCT_LINK_TEMPLATE : null,
							PRODUCT_LINK_TEMPLATE_TEXT : '/browse/product.do?#{cidVidParam}pid=#{pid}',
							CHILD_CATEGORY_HEADER_TEMPLATE : null,
							CHILD_CATEGORY_HEADER_TEMPLATE_TEXT : '<div class="clear5"></div><h2 class="header4">#{childCategoryName}</h2><div class="divider1"></div>',
							MUP_TEMPLATE : null,
							MUP_TEMPLATE_TEXT : '<span class="mupSupMessage">#{mupMessage}</span>',
							PRODUCT_SEARCH_ALT_KEYWORD_LINK_TEMPLATE : null,
							PRODUCT_SEARCH_ALT_KEYWORD_LINK_TEMPLATE_TEXT : '<a href="/browse/search.do?searchText=#{keywordTxt}">#{keywordTxt}</a>',
							MARKETING_TEMPLATE : null,
							MARKETING_TEMPLATE_TEXT : '<div id="marketingFlagContainer_#{pid}#{cidIdParam}">' + '<span id="styleMarketingFlag_#{pid}#{cidIdParam}" class="productMarketingFlag productColorMarketingFlag">#{marketingFlagName}</span>' + '</div>',
							CROSS_BRAND_SEARCH_RESULT_TEMPLATE : null,
							CROSS_BRAND_SEARCH_RESULT_TEMPLATE_TEXT : '<li class="#{brandSelected}"><a href="#{crossBrandSearchUrl}">#{crossBrandName} (<span id="#{crossBrandResultNumId}">#{crossBrandTotalItemCount}</span>)</a></li>',
							FACET_TEMPLATE : null,
							FACET_TEMPLATE_TEXT : "<div id='#{facetId}' class='facetDiv'><div id='#{facetId}_name' class='facetName'>" +
								"#{facetName}</div>" +
								"#{facetClearAll}" + 
								"#{facetOptions}</div>",
							CLEAR_ALL_FACET_SELECTIONS_TEMPLATE: null,
							CLEAR_ALL_FACET_SELECTIONS_TEMPLATE_TEXT: "<div id='#{facetElementId}_clearAllDiv' class='facetClearAllDiv'>" +
									"<input id='#{facetElementId}_clearAll' alt='Clear' value='clear' class='' type='button' onclick='#{clearAction}'/></div>"
						},
						elementIds : {
							PRODUCT_GRID : "tabTableOff",
							PRODUCT_GRID_PAGE : "productGridPage",
							PRODUCT_SEARCH_GRID : "searchProductIteration",
							PRODUCT_NO_SEARCH_RESULTS : "noSearchResultsContainer",
							PRODUCT_NO_SEARCH_RESULTS_TEXT_CONTAINER : "noResultsShell",
							PRODUCT_SEARCH_RESULTS : "searchResultsInfoContainer",
							PRODUCT_SEARCH_TERM_ALT_SUGGESTIONS : "searchExtraSuggestions",
							PRODUCT_SEARCH_CORRECTED_SEARCH : "correctedSearch",
							PRODUCT_SEARCH_SPELL_CORRECTED_RESULTS_CONTAINER : "spellCorrectedSearchResultsContainer",
							PRODUCT_SEARCH_RESULTS_CONTAINER : "searchResultsContainer",
							PRODUCT_SEARCH_TERM_TEXT_CLASS : "strKeyword",
							PRODUCT_SEARCH_RESULTS_SINGULAR : "originalResultsSingular",
							PRODUCT_SEARCH_RESULTS_PLURAL : "originalResultsPlural",
							PRODUCT_SEARCH_RESULTS_BRAND_TEXT : "searchedBrand",
							PRODUCT_SEARCH_NO_RESULTS_KEYWORD_TEXT : "strNoResultsSearchTerm",
							PRODUCT_SEARCH_RESULTS_KEYWORD_TEXT : "strSearchTerm",
							PRODUCT_SEARCH_ORIGINAL_KEYWORD_TEXT : "strOriginalSearchTerm",
							PRODUCT_SEARCH_CORRECTED_KEYWORD_TEXT : "strCorrectedSearchTerm",
							PRODUCT_SEARCH_SPELL_CORRECTED_RESULTS_COUNT_TEXT : "numOfSpellCorrectedResults",
							PRODUCT_SEARCH_RESULTS_COUNT_TEXT : "numOfResults",
							PRODUCT_CATEGORY_NAME_HEADER : "categoryNameHeader",
							PRODUCT_SUBCATEGORY_NAME_HEADER : "subcatname",
							PRODUCT_SEARCH_HELPER_TOOLS_CONTAINER: "productToolsLineTop", 
							PRODUCT_SEARCH_CROSSBRAND_RESULTS_CONTAINER: "crossBrandSearchResultsContainer",
							PRODUCT_NO_SEARCH_RESULTS_CHECK_CROSSBRANDS_TEXT: "checkCrossBrandsText",
							FACETS: "sideNavFacets",
							TOTAL_ITEM_COUNT_DIV : "totalItemCountDiv"
						},
						paginationStatus : {
							NOT_YET_REQUESTED : "notYetRequested",
							LOADING : "loading",
							LOADED : "loaded"
						},
						pricingDisplay : {
							PRODUCT_PRICING_BRAND_BREAK : '<span class=\"brandBreak\">&#160;</span>',
							PRODUCT_PRICING_DISPLAY_CLASS : 'priceDisplay',
							PRODUCT_PRICING_DISPLAY_STRIKE_CLASS : 'priceDisplayStrike',
							PRODUCT_PRICING_DISPLAY_BRAND_BREAK_CLASS : 'brandBreak',
							PRODUCT_PRICING_DISPLAY_SALE_CLASS : 'priceDisplaySale'
						}
					},
					constructors : {
						AbstractFacetManager:function() {
							this.initialize = function() {};
							this.constants = {};
							this.model = {
								isActive:false
							};
							this.controller = {
								init:{
									main : function() {}
								},
								handlers : {
								}
							};
							this.view = {
								// The abstract renderer returns an empty string
								renderFacet : function(facetContract) {
									return "";
								},
								setSelectedOptions : function(facet) {},
								clearSelectedOptions : function(facet) {},
								setFacetState : function(facet) {},
								// Returns an array of ids for all selected options
								getSelectedOptionIds : function(facet) { return [];}
							};
						}
					},
					model : {
						isSbsFacetActive: true,
						isActive : false,
						isDataReady : false,
						isKeywordSearch : false,
						hasSearchResults : false,
						isInitialPageLoad : true,
						data : null,
						pagination : {
							productRowsPerPage : 0,
							hasUserScrolled : false,
							maximumY : 0,
							pageData : [],
							pageStatus : []
						}
					},
					controller : {
						facetManagers:{
							categorySingleSelectListFacetManager : null,
							categoryMultiSelectListFacetManager : null
						},
						facetManagerRegistry : {
							price : null,
							size : null
						},	
						init : {
							main : function() {
								var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
								var personalizationDataReadyHandler = categoryFacetedSearchManager.controller.handlers.personalizationDataReadyHandler
										.bind(navigationService);
								var isPersonalizationDataReady = personalizationService.model.isPersonalizationDataReady;

								if (isPersonalizationDataReady) {
									personalizationDataReadyHandler();
								} else {
									Event.observe(document,
											"personalizationData:ready",
											personalizationDataReadyHandler);
								}
							},
							setConstants : function() {
								var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
								var constants = categoryFacetedSearchManager.constants;

								constants.templates.PAGE_PLACEHOLDER_TEMPLATE = new Template(
										constants.templates.PAGE_PLACEHOLDER_TEMPLATE_TEXT);
								constants.templates.PRODUCT_TEMPLATE = new Template(
										constants.templates.PRODUCT_TEMPLATE_TEXT);
								constants.templates.PRODUCT_LINK_TEMPLATE = new Template(
										constants.templates.PRODUCT_LINK_TEMPLATE_TEXT);
								constants.templates.CHILD_CATEGORY_HEADER_TEMPLATE = new Template(
										constants.templates.CHILD_CATEGORY_HEADER_TEMPLATE_TEXT);
								constants.templates.MARKETING_TEMPLATE = new Template(
										constants.templates.MARKETING_TEMPLATE_TEXT);
								constants.templates.MUP_TEMPLATE = new Template(
										constants.templates.MUP_TEMPLATE_TEXT);
								constants.templates.PRODUCT_SEARCH_ALT_KEYWORD_LINK_TEMPLATE = new Template(
										constants.templates.PRODUCT_SEARCH_ALT_KEYWORD_LINK_TEMPLATE_TEXT);
								constants.templates.CROSS_BRAND_SEARCH_RESULT_TEMPLATE = new Template(
										constants.templates.CROSS_BRAND_SEARCH_RESULT_TEMPLATE_TEXT);
								// Facet specific templates
								constants.templates.FACET_TEMPLATE = new Template(
										constants.templates.FACET_TEMPLATE_TEXT);
								constants.templates.CLEAR_ALL_FACET_SELECTIONS_TEMPLATE = new Template(
										constants.templates.CLEAR_ALL_FACET_SELECTIONS_TEMPLATE_TEXT);
								// Flyout specific templates
								constants.templates.flyoutDisplay.FACET_TEMPLATE = new Template(
										constants.templates.flyoutDisplay.FACET_TEMPLATE_TEXT);
								constants.templates.flyoutDisplay.FACET_FLYOUT_CONTAINER_TEMPLATE = new Template(
										constants.templates.flyoutDisplay.FACET_FLYOUT_CONTAINER_TEMPLATE_TEXT);
								
								
								
								
							},
							// Set up mappings for facet managers
							initializeFacetManagerRegistry : function() {
								var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
								var isSbsFacetActive = categoryFacetedSearchManager.model.isSbsFacetActive;
								var isSbsFacetActiveUrlParamActive = (gidLib.getQuerystringParam("isSbsFacetActive", true)||'true') == "true";
								
								// @TODO REMOVE This is temp to test the flyout
								var queryParmsObj = $H(window.location.search.toQueryParams());
								var displayFlyout = queryParmsObj.get('displayFlyout');
								if (displayFlyout && displayFlyout == "true"){
									categoryFacetedSearchManager.controller.facetManagers.categorySizeFacetManager.controller.init.main();
									categoryFacetedSearchManager.controller.facetManagerRegistry.size = categoryFacetedSearchManager.controller.facetManagers.categorySizeFacetManager;	
								} else {
									categoryFacetedSearchManager.controller.facetManagerRegistry.size = (isSbsFacetActive && isSbsFacetActiveUrlParamActive) ?
											categoryFacetedSearchManager.controller.facetManagers.categorySbsFacetManager
											: categoryFacetedSearchManager.controller.facetManagers.categoryMultiSelectListFacetManager;
								}
							}
						},
						handlers : {
							domReadyHandler : function() {
								var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
								if (categoryFacetedSearchManager.model.isDataReady) {
									categoryFacetedSearchManager.controller
											.getCategoryFacetedSearchView();
								} else {
									// process after event in the future
									Event
											.observe(
													document,
													"categoryFacetedSearchData:ready",
													categoryFacetedSearchManager.controller.getCategoryFacetedSearchView);
								}
							},
							personalizationDataReadyHandler : function() {
								var model = navigationService.controller.managers.categoryFacetedSearchManager.model;
								var isDataReady = model.isDataReady;
								if (!isDataReady) {
									navigationService.controller.managers.categoryFacetedSearchManager.controller.init
											.setConstants();
									navigationService.controller.managers.categoryFacetedSearchManager.controller.init
											.initializeFacetManagerRegistry();
									navigationService.controller.managers.categoryFacetedSearchManager.controller
											.getCategoryFacetedSearchData(0);
								}
							},
							getCategoryFacetedSearchDataHandlers : {
								successHandler : function(response) {
									var model = navigationService.controller.managers.categoryFacetedSearchManager.model;

									model.data = response.responseJSON;
									model.isDataReady = true;

									model.hasSearchResults = (!Object
											.isUndefined(response.responseJSON.productCategoryFacetedSearch.productCategory.childCategories) || !Object
											.isUndefined(response.responseJSON.productCategoryFacetedSearch.productCategory.childProducts));
									
									document
											.fire("categoryFacetedSearchDataRequest:end");
									document
											.fire("categoryFacetedSearchData:ready");
								},
								exceptionHandler : function(response, e) {
									document
											.fire("categoryFacetedSearchDataRequest:end");
									document
											.fire("categoryFacetedSearchDataRequest:exception");
									throw (e);
								}
							},
							pagination : {
								scrollingHandler : function(event) {
									var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
									var yValue = document.viewport
											.getScrollOffsets()[1];

									if (yValue > categoryFacetedSearchManager.model.pagination.maximumY) {
										categoryFacetedSearchManager.model.pagination.maximumY = yValue;
										categoryFacetedSearchManager.model.pagination.hasUserScrolled = true;
									}
								},
								pollScrollingHandler : function() {
									if (navigationService.controller.managers.categoryFacetedSearchManager.model.pagination.hasUserScrolled) {
										var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;

										var currentPage = categoryFacetedSearchManager.controller.pagination
												.getCurrentPage();
										categoryFacetedSearchManager.controller.pagination
												.requestUninitializedPages(currentPage);
										categoryFacetedSearchManager.model.pagination.hasUserScrolled = false;
									}
								}
							}
						},
						pagination : {
							pollScrollingPeriodicExecutor : null,
							getCurrentPage : function() {

								var productGridBaseY = $('tabTableOff').offsetTop;
								var productGridPageHeight = $('productGridPage0').offsetHeight;

								var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
								var y = categoryFacetedSearchManager.model.pagination.maximumY;

								y -= productGridBaseY;
								var maxProductPageReached = y
										/ productGridPageHeight;

								var currentPage = Math
										.floor(maxProductPageReached);

								return currentPage;
							},
							// Pages are loaded in the following order (assuming
							// they are unloaded:
							// 1. current page (highestPageToLoad-1 if
							// highestPage != lastPage)
							// 2. page prior to the current page
							// 3. page after the current page
							// 4. all other pages prior to the current page
							requestUninitializedPages : function(currentPageId) {
								var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
								var lastPageId = categoryFacetedSearchManager.model.data.productCategoryFacetedSearch.productCategory.productCategoryPaginator.pageNumberTotal - 1;

								if (currentPageId > lastPageId) {
									currentPageId = lastPageId;
								}

								categoryFacetedSearchManager.controller.pagination
										.loadPage(currentPageId);

								// Check if we need to load the prior page
								// (assume page 0 already loaded)
								if (currentPageId > 2) {
									categoryFacetedSearchManager.controller.pagination
											.loadPage(currentPageId - 1);
								}

								var nextPageId = currentPageId + 1;
								if (nextPageId <= lastPageId) {
									categoryFacetedSearchManager.controller.pagination
											.loadPage(nextPageId);
								}

								// Loading last page so stop listening
								if (nextPageId >= lastPageId) {
									Event
											.stopObserving(
													window,
													"scroll",
													categoryFacetedSearchManager.controller.handlers.pagination.scrollingHandler);
									categoryFacetedSearchManager.controller.pagination.pollScrollingPeriodicExecutor
											.stop();
								}

								// Load rest of previous pages
								for ( var pageId = currentPageId - 2; pageId >= 1; pageId--) {
									categoryFacetedSearchManager.controller.pagination
											.loadPage(pageId);
								}
							},
							loadPage : function(pageId) {
								var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;

								if (categoryFacetedSearchManager.model.pagination.pageStatus[pageId] == categoryFacetedSearchManager.constants.paginationStatus.NOT_YET_REQUESTED) {
									categoryFacetedSearchManager.controller
											.getCategoryFacetedSearchData(pageId);
									categoryFacetedSearchManager.model.pagination.pageStatus[pageId] = categoryFacetedSearchManager.constants.paginationStatus.LOADING;
								}
							}
						},
						getCategoryFacetedSearchData : function(pageId) {
							// Request for a new page 0 clears the product grid
							// TODO: and displays the spinner
							var productGridId = navigationService.controller.managers.categoryFacetedSearchManager.constants.elementIds.PRODUCT_GRID;
							if (pageId == 0 && $(productGridId)) {
								$(productGridId).update("");
								$(navigationService.controller.managers.categoryFacetedSearchManager.constants.elementIds.TOTAL_ITEM_COUNT_DIV).update("");

							}
							
							var getCategoryFacetedSearchDataHandlers = navigationService.controller.managers.categoryFacetedSearchManager.controller.handlers.getCategoryFacetedSearchDataHandlers;

							document
									.fire("categoryFacetedSearchDataRequest:begin");

							var productSearchServiceUrl = navigationService.controller.managers.categoryFacetedSearchManager.controller
									.getProductSearchServiceUrl(pageId);
							new Ajax.Request(
									productSearchServiceUrl,
									{
										method : 'get',
										onSuccess : getCategoryFacetedSearchDataHandlers.successHandler,
										onException : getCategoryFacetedSearchDataHandlers.exceptionHandler
									});
						},
						getProductSearchServiceUrl : function(pageId) {
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;

							var PRODUCT_SEARCH_SERVICE_PATH = categoryFacetedSearchManager.constants.PRODUCT_SEARCH_SERVICE_PATH;
							var requestProtocol = "http:";
							var requestHost = window.location.host;
							var searchText = categoryFacetedSearchManager.controller
									.getSearchText();

							var productSearchServiceUrl = requestProtocol
									+ "//" + requestHost
									+ PRODUCT_SEARCH_SERVICE_PATH + "/"
									+ searchText;
							var queryParametersUrlFormatted = categoryFacetedSearchManager.controller
									.getQueryParametersUrlFormatted(pageId);
							if (queryParametersUrlFormatted != null) {
								productSearchServiceUrl = productSearchServiceUrl
										+ "?" + queryParametersUrlFormatted;
							}
							return productSearchServiceUrl;
						},
						/**
						 * PChu 7/13 - We first return the auto-corrected
						 * version of the search text if the initial search came
						 * back with auto-corrected text. This allows for
						 * pagination to work if the user originally provided
						 * search text that didn't result in a direct match.
						 */
						getSearchText : function() {
							var constants = navigationService.controller.managers.categoryFacetedSearchManager.constants;
							var model = navigationService.controller.managers.categoryFacetedSearchManager.model;
							var searchText = constants.DEFAULT_SEARCH_TEXT;
							if(model.data != null && 
									!Object.isUndefined(model.data.productCategoryFacetedSearch.autoCorrectedText)){
								searchText = model.data.productCategoryFacetedSearch.autoCorrectedText;
							} else if(model.data != null && 
									!Object.isUndefined(model.data.productCategoryFacetedSearch.searchText)){
								searchText = model.data.productCategoryFacetedSearch.searchText;
							} else {
								// otherwise, return the search text from the
								// query parameter
								var SEARCH_TEXT_PARAMETER = constants.requestParameters.SEARCH_TEXT_PARAMETER;
	
								var searchTextParameter = gidLib
										.getQuerystringParam(SEARCH_TEXT_PARAMETER);
								if (searchTextParameter != null
										&& searchTextParameter != "") {
									if (searchTextParameter != constants.DEFAULT_SEARCH_TEXT) {
										model.isKeywordSearch = true;
									}
	
									var searchTextParameterEncoded = encodeURIComponent(searchTextParameter);
									searchText = searchTextParameterEncoded;
								}
							}
							return searchText;
						},
						getQueryParametersUrlFormatted : function(pageId) {
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
							var constants = categoryFacetedSearchManager.constants;
							var requestParameters = constants.requestParameters;
							var model = categoryFacetedSearchManager.model;
							var userContext = personalizationService.model.personalizationData.personalizationInfoV1.userContext;

							var queryParametersUrlFormatted = null;
							var queryParameters = $H();

							var CID_PARAMETER = requestParameters.CID_PARAMETER;
							var cidParameter = gidLib
									.getQuerystringParam(CID_PARAMETER);
							if (cidParameter != null && cidParameter != "") {
								var cidParameterEncoded = encodeURIComponent(cidParameter);
								queryParameters.set(CID_PARAMETER,
										cidParameterEncoded);
							}
							
							var SORTBY_PARAMETER = requestParameters.SORT_BY_PARAMETER;
							var sortByParameter = gidLib.getQuerystringParam(SORTBY_PARAMETER);
							if (sortByParameter != null && sortByParameter != "") {
							            var sortByParameterEncoded = encodeURIComponent(sortByParameter);
							            queryParameters.set(SORTBY_PARAMETER, sortByParameterEncoded);
							}
							
							if (pageId != 0) {
								var pageIdParameterEncoded = encodeURIComponent(pageId);
								queryParameters.set(
										requestParameters.PAGE_ID_PARAMETER,
										pageIdParameterEncoded);
							}

							/* globalShippingCurrencyCode */
							var globalShippingCurrencyCodeParameterValue = categoryFacetedSearchManager.controller
									.getGlobalShippingCurrencyCodeParameter(userContext);
							if (globalShippingCurrencyCodeParameterValue != null) {
								var GLOBAL_SHIPPING_CURRENCY_CODE_PARAMETER = requestParameters.GLOBAL_SHIPPING_CURRENCY_CODE_PARAMETER;
								queryParameters
										.set(
												GLOBAL_SHIPPING_CURRENCY_CODE_PARAMETER,
												globalShippingCurrencyCodeParameterValue);
							}
							/* globalShippingCountryCode */
							var globalShippingCountryCodeParameterValue = categoryFacetedSearchManager.controller
									.getGlobalShippingCountryCodeParameter(userContext);
							if (globalShippingCountryCodeParameterValue != null) {
								var GLOBAL_SHIPPING_COUNTRY_CODE_PARAMETER = requestParameters.GLOBAL_SHIPPING_COUNTRY_CODE_PARAMETER;
								queryParameters
										.set(
												GLOBAL_SHIPPING_COUNTRY_CODE_PARAMETER,
												globalShippingCountryCodeParameterValue);
							}

							// facet selections
							if (categoryFacetedSearchManager.model.isDataReady) {
								if (categoryFacetedSearchManager.model.data.productCategoryFacetedSearch.searchFacetInfo != null) {
									var searchFacetList = categoryFacetedSearchManager.model.data.productCategoryFacetedSearch.searchFacetInfo.searchFacetList;							

									if (!Object.isArray(searchFacetList)) {
										searchFacetList = [searchFacetList];
									}
		
									searchFacetList.each(function(facet, i) {
										if (categoryFacetedSearchManager.controller.facetManagerRegistry[facet.searchFacetId] != null) {
											queryParameters.set(
													facet.searchFacetId,
													categoryFacetedSearchManager.controller.facetManagerRegistry[facet.searchFacetId].view.getSelectedOptionIds(facet)
											);
										} else {
											console.log("no facet manager mapped for " + facet.searchFacetId);
										}
									});
								}
							}
							
							queryParametersUrlFormatted = queryParameters
									.toQueryString();
							return queryParametersUrlFormatted;
						},
						getGlobalShippingCurrencyCodeParameter : function(
								userContext) {
							var currencyCodeParameter = null;
							if (!Object
									.isUndefined(userContext.globalShippingCurrencyCode)) {
								currencyCodeParameter = userContext.globalShippingCurrencyCode;
							}
							return currencyCodeParameter;
						},
						getGlobalShippingCountryCodeParameter : function(
								userContext) {
							var countryCodeParameter = null;
							if (!Object
									.isUndefined(userContext.globalShippingCountryCode)) {
								countryCodeParameter = userContext.globalShippingCountryCode;
							}
							return countryCodeParameter;
						},
						getCategoryFacetedSearchView : function() {
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
							var constants = categoryFacetedSearchManager.constants;
							var productCategoryFacetedSearch = categoryFacetedSearchManager.model.data.productCategoryFacetedSearch;

							var noResultsElem = $(constants.elementIds.PRODUCT_NO_SEARCH_RESULTS);

							// do not display the grid only show no results page
							if (categoryFacetedSearchManager.model.hasSearchResults) {
								// NAV20-404: Will need to also check if the
								// searchText == the 1st resulting products
								// businessCatalogItemId. If so then we need to
								// redirect to the product pg.
								categoryFacetedSearchManager.view
										.setCategoryFacetedSearchDataToView();
								
								noResultsElem.addClassName("hideMe");
								$(constants.elementIds.TOTAL_ITEM_COUNT_DIV).removeClassName("hideMe");
								
							} else {
								// DO NOT initialize facets
								// on left nav if no results found
								categoryFacetedSearchManager.view.initializeFacets();

								// only display the 'No Results' messaging at
								// the page top if it's the initial page
								// load and not when requesting additional pages
								if(Object.isUndefined(productCategoryFacetedSearch.productCategory.productCategoryPaginator) || productCategoryFacetedSearch.productCategory.productCategoryPaginator.pageNumberRequested == 0){
									categoryFacetedSearchManager.view
											.setCategoryFacetedSearchNoResultsToView();
									noResultsElem.removeClassName("hideMe");
									$(constants.elementIds.TOTAL_ITEM_COUNT_DIV).addClassName("hideMe");
								} else {
									noResultsElem.addClassName("hideMe");
									$(constants.elementIds.TOTAL_ITEM_COUNT_DIV).removeClassName("hideMe");
								}
								
								// Report no results
								reportingService.controller.viewManagers.categoryFacetedSearchViewManager.controller.getReportRequest();
							}
								
						},
						/**
						 * Update the page content, filtered by selected facets
						 */
						filterByFacets : function() {
							navigationService.controller.managers.categoryFacetedSearchManager.controller
								.getCategoryFacetedSearchData(0);
							
							Event.observe(
								document,
								"categoryFacetedSearchData:ready",
								navigationService.controller.managers.categoryFacetedSearchManager.controller.getCategoryFacetedSearchView
							); 							
						},
						/**
						 * Retrieve a facet by searchFacetId
						 */
						getFacet : function(searchFacetId) {
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
							var searchFacetList = categoryFacetedSearchManager.model.data.productCategoryFacetedSearch.searchFacetInfo.searchFacetList;							

							var foundFacet = null;
							
							if (!Object.isArray(searchFacetList)) {
								searchFacetList = [searchFacetList];
							}

							searchFacetList.each(function(facet, i) {
								if (facet.searchFacetId == searchFacetId) {
									foundFacet = facet;
									return false;
						}
							});
							
							return foundFacet;
						}

					},
					view : {
						/**
						 * Set the data from the model in to the DOM
						 */
						setCategoryFacetedSearchDataToView : function() {
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
							var productCategoryFacetedSearch = categoryFacetedSearchManager.model.data.productCategoryFacetedSearch;
							var productCategory = productCategoryFacetedSearch.productCategory;

							var pageNumberRequested = productCategory.productCategoryPaginator.pageNumberRequested;

							// Special handling for the first page
							// (to handle creation of placeholder divs for all
							// following pages
							if (pageNumberRequested == 0) {
								categoryFacetedSearchManager.view
										.initializeProductGrid(productCategory);
								
								// If this is a keyword search then set the
								// header
								if (categoryFacetedSearchManager.model.isKeywordSearch) {
									categoryFacetedSearchManager.view
											.setKeywordSearchResultsHeader();
								}
							
								if(categoryFacetedSearchManager.model.isInitialPageLoad){
									// initialize facets
									categoryFacetedSearchManager.view.initializeFacets();
									categoryFacetedSearchManager.model.isInitialPageLoad = false;
								
								}
							}

							categoryFacetedSearchManager.view.setFacetsOptionsState();

							// Omniture call must be made AFTER the selected facets are set (it looks at the dom for selected values
							if (pageNumberRequested == 0) {
								var facetedSearchAppManager = reportingService.controller.appManagers.facetedSearchAppManager;
								facetedSearchAppManager.setNumberOfSearchResults(productCategoryFacetedSearch.totalItemCount);
								facetedSearchAppManager.setFacets(productCategoryFacetedSearch.searchFacetInfo.searchFacetList);
							
								reportingService.controller.viewManagers.categoryFacetedSearchViewManager.controller.getReportRequest();
							}
							
							categoryFacetedSearchManager.view.setTotalItemCount();

							categoryFacetedSearchManager.view
									.productGridDomDisplayHelper(productCategory);

							// Set default heights for other pages
							if (pageNumberRequested == 0) {
								categoryFacetedSearchManager.view.setDefaultPageHeights
										.defer();
							}

							// Recalculate left nav height
							siteNavigation.sideNav.setSideNavHeight.defer();
						},
						setCategoryFacetedSearchNoResultsToView : function() {
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
							var productCategoryFacetedSearch = categoryFacetedSearchManager.model.data.productCategoryFacetedSearch;
							var productCategory = productCategoryFacetedSearch.productCategory;
							var constants = categoryFacetedSearchManager.constants;

							var noResultsElem = $(constants.elementIds.PRODUCT_NO_SEARCH_RESULTS);
							// @TODO NAV20-519
							var noResultsTextContainer = $(constants.elementIds.PRODUCT_NO_SEARCH_RESULTS_TEXT_CONTAINER);
							var noResultsCheckCrossBrandsText = $(constants.elementIds.PRODUCT_NO_SEARCH_RESULTS_CHECK_CROSSBRANDS_TEXT);
							var searchTermTxtElem = $(constants.elementIds.PRODUCT_SEARCH_NO_RESULTS_KEYWORD_TEXT);
							var categoryNameHeaderElem = $(constants.elementIds.PRODUCT_CATEGORY_NAME_HEADER);
							var searchText = productCategoryFacetedSearch.searchText;
								
							// @TODO NAV20-519: Will need to refactor to allow
							// for cross brand search box
							// My thought is to remove it from the dom and then
							// insert it within the
							// no results section and then unhide it
							
							var hasCrossBrandResults = false;
							
							/*
							 * @TODO UnComment once we go into "true production
							 * See line 577
							 * if(!Object.isUndefined(productCategoryFacetedSearch.businessUnitTotalItemCountList)){
							 * hasCrossBrandResults =
							 * categoryFacetedSearchManager.view.hasCrossBrandSearchResults(productCategoryFacetedSearch.businessUnitTotalItemCountList); }
							 */
							
							if (hasCrossBrandResults) {
								categoryFacetedSearchManager.view
										.getCrossBrandViewHelper(productCategoryFacetedSearch);
							
								var crossBrandListContainer = $(constants.elementIds.PRODUCT_SEARCH_CROSSBRAND_RESULTS_CONTAINER).remove();
								noResultsTextContainer.insert({before: crossBrandListContainer});
								
								if(noResultsCheckCrossBrandsText && noResultsCheckCrossBrandsText.hasClassName("hideMe")){
									noResultsCheckCrossBrandsText.removeClassName("hideMe");	
								}
							}
							if (categoryNameHeaderElem) {
								categoryNameHeaderElem.addClassName("hideMe");
							}

							if (searchTermTxtElem) {
								searchTermTxtElem.update(searchText);
							}
							
							if (noResultsElem
									&& noResultsElem.hasClassName("hideMe")) {
								noResultsElem.removeClassName("hideMe");
							}
						},
						setKeywordSearchResultsHeader : function() {
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
							var productCategoryFacetedSearch = categoryFacetedSearchManager.model.data.productCategoryFacetedSearch;
							var productCategory = productCategoryFacetedSearch.productCategory;
							var constants = categoryFacetedSearchManager.constants;
							var isSpellCorrectSearch = false;

							// response data
							var searchText = productCategoryFacetedSearch.searchText;
							var totalItemCount = productCategoryFacetedSearch.totalItemCount;
							var correctedSearchText = productCategoryFacetedSearch.autoCorrectedText;
							isSpellCorrectSearch = (!Object
									.isUndefined(correctedSearchText));

							// NAV20-224 BEGIN
							var hasCrossBrandResults = false;
							
							/*
							 * @TODO UnComment once we go into "true production
							 * see line 528
							 * if(!Object.isUndefined(productCategoryFacetedSearch.businessUnitTotalItemCountList)){
							 * hasCrossBrandResults =
							 * categoryFacetedSearchManager.view.hasCrossBrandSearchResults(productCategoryFacetedSearch.businessUnitTotalItemCountList); }
							 */
							
							// from here go to cross brand view helper
							if (hasCrossBrandResults) {
								// categoryFacetedSearchManager.view.getCrossBrandViewHelper(productCategoryFacetedSearch);
							}

							// Display singular/plural as appropriate
							if (totalItemCount > 1) {
								$(constants.elementIds.PRODUCT_SEARCH_RESULTS_SINGULAR).remove();
							} else {
								$(constants.elementIds.PRODUCT_SEARCH_RESULTS_PLURAL).remove();								
							}

							$(constants.elementIds.PRODUCT_SEARCH_RESULTS_BRAND_TEXT).update(brandConst.BRAND_NAME);

							// common container elements
							var searchResultsElem = $(constants.elementIds.PRODUCT_SEARCH_RESULTS);
							var categoryNameHeaderElem = $(constants.elementIds.PRODUCT_CATEGORY_NAME_HEADER);
							var productToolsElem = $(constants.elementIds.PRODUCT_SEARCH_HELPER_TOOLS_CONTAINER);	
							
							var searchTermTxtElem = (isSpellCorrectSearch) ? $(constants.elementIds.PRODUCT_SEARCH_ORIGINAL_KEYWORD_TEXT)
									: $(constants.elementIds.PRODUCT_SEARCH_RESULTS_KEYWORD_TEXT);
							var searchResultsCountTxtElem = (isSpellCorrectSearch) ? $(constants.elementIds.PRODUCT_SEARCH_SPELL_CORRECTED_RESULTS_COUNT_TEXT)
									: $(constants.elementIds.PRODUCT_SEARCH_RESULTS_COUNT_TEXT);

							if (categoryNameHeaderElem) {
								categoryNameHeaderElem.addClassName("hideMe");
							}

							if (searchResultsElem
									&& searchResultsElem.hasClassName("hideMe")) {
								searchResultsElem.removeClassName("hideMe");
							}

							if (searchTermTxtElem) {
								searchTermTxtElem.update(searchText);
							}

							if (searchResultsCountTxtElem) {
								searchResultsCountTxtElem
										.update(totalItemCount);
							}
							
							// Display product tools
							if (productToolsElem && productToolsElem.hasClassName("hideMe")){
								productToolsElem.removeClassName("hideMe");
							}
							
							if (isSpellCorrectSearch) {
								// spell corrected specific elements
								var spellCorrectedResultsContainer = $(constants.elementIds.PRODUCT_SEARCH_SPELL_CORRECTED_RESULTS_CONTAINER);
								var searchTermAltSuggestionsElem = $(constants.elementIds.PRODUCT_SEARCH_TERM_ALT_SUGGESTIONS);
								var correctedSearchElem = $(constants.elementIds.PRODUCT_SEARCH_CORRECTED_SEARCH);
								var searchTermSuggestedTxtElem = $(constants.elementIds.PRODUCT_SEARCH_CORRECTED_KEYWORD_TEXT);

								var alternateCorrectedSearchText; 
								if (Object.isUndefined(productCategoryFacetedSearch.spellingSuggestions)) {
									alternateCorrectedSearchText = $A(categoryFacetedSearchManager.model.data.spellingSuggestions);
								} else {
									alternateCorrectedSearchText = (typeof(productCategoryFacetedSearch.spellingSuggestions) === 'string')
										? $A([productCategoryFacetedSearch.spellingSuggestions])
										: $A(productCategoryFacetedSearch.spellingSuggestions);
								}

								spellCorrectedResultsContainer
										.removeClassName("hideMe");

								searchTermSuggestedTxtElem
										.update(correctedSearchText);
								// also means that there may be alternative
								// spelling suggestions
								
								
								var correctedSearchListSize = alternateCorrectedSearchText.size();
								if (correctedSearchListSize > 0) {
									var altKeywords = "";

									alternateCorrectedSearchText
											.each(function(term, i) {
												var altKeywordsLinkContract = {
													keywordTxt : term
												};

												altKeywords += constants.templates.PRODUCT_SEARCH_ALT_KEYWORD_LINK_TEMPLATE
														.evaluate(altKeywordsLinkContract);

												if (i + 1 < correctedSearchListSize) {
													altKeywords += ", ";
												}
											});

									searchTermAltSuggestionsElem.insert(
											altKeywords + "?").removeClassName(
											"hideMe");
								}
							} else {
								// simple search specific elements
								var resultsContainer = $(constants.elementIds.PRODUCT_SEARCH_RESULTS_CONTAINER);
								resultsContainer.removeClassName("hideMe");								
							}
						},
						/**
						 * Initialize all facet information
						 */
						initializeFacets : function() {
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;

							if (categoryFacetedSearchManager.model.data.productCategoryFacetedSearch.searchFacetInfo != null) {
								var searchFacetList = categoryFacetedSearchManager.model.data.productCategoryFacetedSearch.searchFacetInfo.searchFacetList;							
	
								var facetsContentXhtml = "";
								
								if (!Object.isArray(searchFacetList)) {
									searchFacetList = [searchFacetList];
								}
	
								// Generate XHTML
								searchFacetList.each(function(facet, i) {
									facetsContentXhtml += categoryFacetedSearchManager.controller.facetManagerRegistry[facet.searchFacetId].view.renderFacet(facet);
								});

                                var sideNavFacet = $(categoryFacetedSearchManager.constants.elementIds.FACETS);
								if (sideNavFacet) sideNavFacet.update(facetsContentXhtml);
							}
						},
						/**
						 * Set the selected facets
						 */
						setFacetsOptionsState : function() {
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
							if (categoryFacetedSearchManager.model.data.productCategoryFacetedSearch.searchFacetInfo != null) {
								var searchFacetList = categoryFacetedSearchManager.model.data.productCategoryFacetedSearch.searchFacetInfo.searchFacetList;							
	
								if (!Object.isArray(searchFacetList)) {
									searchFacetList = [searchFacetList];
								}
	
								searchFacetList.each(function(facet, i) {
									categoryFacetedSearchManager.controller.facetManagerRegistry[facet.searchFacetId].view.setFacetState(facet);
								});
							}
						},	
						/**
						 * Update the totla item count in the top productToolsLine
						 */
						setTotalItemCount : function() {
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
							var productCategoryFacetedSearch = categoryFacetedSearchManager.model.data.productCategoryFacetedSearch;
							var constants = categoryFacetedSearchManager.constants;
						
							$(constants.elementIds.TOTAL_ITEM_COUNT_DIV).update(productCategoryFacetedSearch.totalItemCount + " items");
						},
													

						/**
						 * Initialize the product grid with place holders for
						 * all pages
						 */
						initializeProductGrid : function(productCategory) {
							var paginator = productCategory.productCategoryPaginator;
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
							var constants = categoryFacetedSearchManager.constants;

							var productGridContentXhtml = "";

							var placeholderContent = categoryFacetedSearchManager.view
									.getPlaceholderContent(paginator);
							var productGridId = constants.elementIds.PRODUCT_GRID;

							// set the product grid page
							if (categoryFacetedSearchManager.model.isKeywordSearch) {
								productGridId = constants.elementIds.PRODUCT_SEARCH_GRID;
							}

							var pageCount = productCategory.productCategoryPaginator.pageNumberTotal;
							for ( var pageId = 0; pageId < pageCount; pageId++) {
								var pagePlaceholderContract = {
									productGridPage : constants.elementIds.PRODUCT_GRID_PAGE,
									pageCount : pageId,
									placeholderContent : placeholderContent
								};

								productGridContentXhtml += constants.templates.PAGE_PLACEHOLDER_TEMPLATE
										.evaluate(pagePlaceholderContract);

								// Initialize all pages as not yet requested
								categoryFacetedSearchManager.model.pagination.pageStatus[pageId] = constants.paginationStatus.NOT_YET_REQUESTED;
							}

							$(productGridId).update(productGridContentXhtml);

							// Set the number of rows per page
							categoryFacetedSearchManager.model.pagination.productRowsPerPage = paginator.productPerPageCount
									/ paginator.productPerRowCount;

							// Add the scroll listener
							Event
									.observe(
											window,
											"scroll",
											categoryFacetedSearchManager.controller.handlers.pagination.scrollingHandler);

							// Initialize the PeriodicExecuter to check for
							// scrolling
							categoryFacetedSearchManager.controller.pagination.pollScrollingPeriodicExecutor = new PeriodicalExecuter(
									categoryFacetedSearchManager.controller.handlers.pagination.pollScrollingHandler,
									.5);

						},
						// Returns placeholder content for the as-yet unloaded
						// pages
						getPlaceholderContent : function(paginator) {
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
							var constants = categoryFacetedSearchManager.constants;

							var placeholderContent = "";

							for ( var placeholderCount = 0; placeholderCount < paginator.productPerPageCount; placeholderCount++) {
								placeholderContent += constants.PRODUCT_PLACEHOLDER;

								if (placeholderCount + 1
										% paginator.productPerRowCount == 0) {
									placeholderContent += constants.PRODUCT_GRID_ROW_BREAK;
								}
							}

							return placeholderContent;
						},
						// Sets all pages after the first to have the same
						// default height as the first page.
						// This default height will be removed as each page is
						// loaded
						setDefaultPageHeights : function() {
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
							var constants = categoryFacetedSearchManager.constants;
							var productCategory = categoryFacetedSearchManager.model.data.productCategoryFacetedSearch.productCategory;

							var defaultPageHeight = $(constants.elementIds.PRODUCT_GRID_PAGE
									+ "0").offsetHeight;

							var pageCount = productCategory.productCategoryPaginator.pageNumberTotal;
							for ( var pageId = 1; pageId < pageCount; pageId++) {
								$(constants.elementIds.PRODUCT_GRID_PAGE
										+ pageId).style.height = defaultPageHeight
										+ "px";
							}
						},
						/**
						 * Set product data in to the DOM
						 */
						productGridDomDisplayHelper : function(productCategory) {
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
							var constants = categoryFacetedSearchManager.constants;
							var paginator = productCategory.productCategoryPaginator;

							var pageNumber = paginator.pageNumberRequested;

							// Mark the page as loaded and store its
							// productCategory
							categoryFacetedSearchManager.model.pagination.pageStatus[pageNumber] = constants.paginationStatus.LOADED;
							categoryFacetedSearchManager.model.pagination.pageData[pageNumber] = productCategory;

							var productGridContentXhtml = "";

							var products = productCategory.childProducts;
							if (products != null) {
								productGridContentXhtml += categoryFacetedSearchManager.view
										.categoryProductsDomDisplayHelper(
												productCategory,
												productCategory.productCategoryPaginator.productPerRowCount);
							} else {
								var childCategories = productCategory.childCategories;

								if (!Object.isArray(childCategories)) {
									var childCategory = childCategories;
									childCategories = new Array();
									childCategories[0] = childCategory;
								}
								var childCategoryHeaderContract = null;
								var $childCategories = $A(childCategories);
								$childCategories
										.each(function(childCategory, i) {
											var childCategoryName = childCategory.name;

											// support for grand child
											// categories
											if (!Object
													.isUndefined(childCategory.childCategories)) {
												var grandChildCategories = childCategory.childCategories;
												if (!Object
														.isArray(grandChildCategories)) {
													var grandChildCategory = grandChildCategories;
													grandChildCategories = new Array();
													grandChildCategories[0] = grandChildCategory;
												}
												var $grandChildCategories = $A(grandChildCategories);
												
												$grandChildCategories
														.each(function(
																grandChildCategory,
																idx) {
															if (Object
																	.isUndefined(grandChildCategory.childProducts)) {
																return false;
															}
															childCategoryName += ": "
																	+ grandChildCategory.name;
															childCategoryHeaderContract = {
																childCategoryName : childCategoryName
															};
															// may need to
															// remove the
															// category heading
															// for outfits
															productGridContentXhtml += constants.templates.CHILD_CATEGORY_HEADER_TEMPLATE
																	.evaluate(childCategoryHeaderContract);
															productGridContentXhtml += categoryFacetedSearchManager.view
																	.categoryProductsDomDisplayHelper(
																			grandChildCategory,
																			productCategory.productCategoryPaginator.productPerRowCount);

														});
											} else {
												// continue to next iteration if
												// there are no products
												if (Object
														.isUndefined(childCategory.childProducts)) {
													return false;
												}
												
												function headerExists(headerName) {
													var exists = false;
													$$('h2.header4').each(function (header) {
														if (header.textContent == headerName)
															exists = true;
													})
													return exists;
												}
												
												var childCategoryHeaderContract = {
													childCategoryName : childCategory.name
												};
												// may need to remove the
												// category heading for outfits
												if (!headerExists(childCategory.name)) {
													productGridContentXhtml += constants.templates.CHILD_CATEGORY_HEADER_TEMPLATE
															.evaluate(childCategoryHeaderContract);
												}
												productGridContentXhtml += categoryFacetedSearchManager.view
														.categoryProductsDomDisplayHelper(
																childCategory,
																productCategory.productCategoryPaginator.productPerRowCount);
											}
										});
							}

							var pageDiv = $(constants.elementIds.PRODUCT_GRID_PAGE
									+ pageNumber);
							// TEST: set the Number of results where applicable
							// Update the content
							pageDiv.update(productGridContentXhtml);

							// Reset height to default
							pageDiv.style.height = "";
						},
						categoryProductsDomDisplayHelper : function(
								productCategory, productPerRowCount) {
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
							var constants = categoryFacetedSearchManager.constants;

							var contentXhtml = "";

							var products = productCategory.childProducts;
							var svid = productCategory.defaultSizeVariantId;

							var cid = productCategory.businessCatalogItemId;
							var cidIdParam = "";
							if (cid != null && cid != "") {
								cidIdParam = "_" + cid;
							}

							if (!Object.isArray(products)) {
								var product = products;
								products = new Array();
								products[0] = product;
							}

							var $products = $A(products);

							$products.each(function(product, i) {
								// this should be refactored at some point to
									// not recreate this Hash in every loop
									var productContract = $H( {
										cid : cid,
										cidIdParam : cidIdParam,
										svid : svid
									});
									try {
										productContract = categoryFacetedSearchManager.view
												.buildProductElementHelper(
														product, i,
														productContract);
										productContract
												.set(
														'categoryProductLink',
														categoryFacetedSearchManager.view
																.productLinkHelper(
																		productCategory,
																		product));
										productContract
												.toTemplateReplacements();
									} catch (e) {
										console.log(e.message);
										return false;
									}

									contentXhtml += constants.templates.PRODUCT_TEMPLATE
											.evaluate(productContract);

									// Add line break if needed
									if (i != 0
											&& (i + 1) % productPerRowCount == 0) {
										contentXhtml += constants.PRODUCT_GRID_ROW_BREAK;
									}
								});

							return contentXhtml;
						},
						buildProductElementHelper : function(product, i,
								productContractObj) {
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
							var constants = categoryFacetedSearchManager.constants;

							// continue to next iteration if this is an outfit
							if (!Object.isUndefined(product.outfitId)) {
								domDisplayException = new categoryFacetedSearchManager.view.DomDisplayException(
										"InvalidProductNotSupported");
								throw domDisplayException;
							}

							// set up local vars
							var cidId = productContractObj.get('cidIdParam');
							// continue to next iteration if this is an outfit
							if (Object
									.isUndefined(product.businessCatalogItemId)) {
								domDisplayException = new categoryFacetedSearchManager.view.DomDisplayException(
										"InvalidProductBusinessCatalogItemIdNotFound");
								throw domDisplayException;
							}

							var pid = product.businessCatalogItemId;
							var brandNumber = brandConst.BRAND_CODE;

							// set the product name
							// if vendor property exists add vendor name
							var productName = '';
							if (!Object.isUndefined(product.vendor)) {
								// per NAV20-406 add webVendorName check and
								// allow pass through
								if (!Object
										.isUndefined(product.vendor.webVendorName)) {
									productName += '<span class="vendorName">' + product.vendor.webVendorName + '</span>&nbsp;';
								} else {
									console
											.log("Product vendor name expected but not found for productId: "
													+ pid);
								}
							}

							if (Object.isUndefined(product.name)) {
								domDisplayException = new categoryFacetedSearchManager.view.DomDisplayException(
										"InvalidProductNameNotFound for productId: "
												+ pid);
								throw domDisplayException;
							}

							productName += product.name;

							// set up style color and product image vars
							var productImagePath = "";
							var scid = "";
							var quickLookParam1 = "";
							var productImgId = "";
							if (!Object
									.isUndefined(product.defaultProductStyleColor)) {
								if (Object
										.isUndefined(product.defaultProductStyleColor.quicklookImage)) {
									// per NAV20-406 add product imagepath check
									// and allow pass through
									productImagePath = "/assets/common/clear.gif";
									console
											.log("InvalidProductImagePathNotFound for productId: "
													+ pid);
								} else {
									productImagePath = "/"
											+ product.defaultProductStyleColor.quicklookImage.path;
								}
								scid = product.defaultProductStyleColor.businessCatalogItemId;
								quickLookParam1 = product.defaultProductStyleColor.parentBusinessCatalogItemId;
								productImgId = quickLookParam1 + cidId;
							} else {
								if (Object.isUndefined(product.quicklookImage)) {
									// per NAV20-406 add product imagepath check
									// and allow pass through
									productImagePath = "/assets/common/clear.gif";
									console
											.log("InvalidProductImagePathNotFound for productId: "
													+ pid);
								} else {
									productImagePath = "/"
											+ product.quicklookImage.path;
								}
								scid = pid;
								quickLookParam1 = product.parentBusinessCatalogItemId;
								productImgId = quickLookParam1 + '_' + pid
										+ cidId;
							}

							// set up pricing display
							var pricingDisplayHtml = "";
							if (Object.isUndefined(product.priceFormatXhtml)) {
								domDisplayException = new categoryFacetedSearchManager.view.DomDisplayException(
										"InvalidProductPriceFormatNotFound for productId: "
												+ pid);
								throw domDisplayException;
							} else {
								pricingDisplayHtml = product.priceFormatXhtml;
							}

							productContractObj.set('pid', pid);
							productContractObj.set('scid', scid);
							productContractObj.set('brandName', brandNumber);
							productContractObj.set('categoryProductName',
									productName);
							productContractObj.set('mupAndMarketing',
									categoryFacetedSearchManager.view
											.productMupAndMarketingHelper(
													cidId, product));
							productContractObj.set('productImagePath',
									productImagePath);
							productContractObj.set('qlParam1', quickLookParam1);
							productContractObj
									.set('productImgId', productImgId);
							productContractObj.set('pricingHTML',
									pricingDisplayHtml);

							return productContractObj;
						},
						productLinkHelper : function(productCategory, product) {
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
							var constants = categoryFacetedSearchManager.constants;

							var productLinkContract = {
								cidVidParam : "",
								pid : product.businessCatalogItemId
							};

							if (productCategory.businessCatalogItemId != null
									&& productCategory.businessCatalogItemId != "") {
								// TODO: Need to determine vid on product level
								// when no cid present
								productLinkContract.cidVidParam = "cid="
										+ productCategory.businessCatalogItemId
										+ "&vid="
										+ productCategory.defaultSizeVariantId
										+ "&";
							}

							productLink = constants.templates.PRODUCT_LINK_TEMPLATE
									.evaluate(productLinkContract);

							return productLink;
						},

						productMupAndMarketingHelper : function(cidIdParam,
								product) {
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
							var constants = categoryFacetedSearchManager.constants;

							var mupText = "";
							var marketingText = "";

							var mupMessage = product.mupMessage;

							if (mupMessage != null) {
								mupText = constants.templates.MUP_TEMPLATE
										.evaluate( {
											mupMessage : mupMessage
										});
							}

							var marketingFlag = product.marketingFlag;

							if (marketingFlag != null) {
								var productMarketingContract = {
									cidIdParam : cidIdParam,
									pid : product.businessCatalogItemId,
									marketingFlagName : marketingFlag.marketingFlagName
								};

								marketingText = constants.templates.MARKETING_TEMPLATE
										.evaluate(productMarketingContract);
							}

							return mupText + marketingText;
						},
						getProductType : function(product) {
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;

							var productType = null;
							if (product.catalogItemTypeId == "3") {
								if (product.catalogItemSubtypeId == "13") {
									productType = categoryFacetedSearchManager.constants.productType.PRODUCT_STYLE;
								} else if (product.catalogItemSubtypeId == "14") {
									productType = categoryFacetedSearchManager.constants.productType.PRODUCT_STYLE_COLOR;
								}
							}

							return productType;
						},
						getCrossBrandViewHelper : function(productCategoryFacetedSearch) {
							var objGidBrandSites = window['gidBrandSiteConstruct'];
							if (Object.isUndefined(objGidBrandSites)) {
								categoryFacetedSearchException = new categoryFacetedSearchManager.view.CategoryFacetedSearchException(
										"DEPENDENCY_OBJECT_NOT_FOUND: Cannot find gidBrandSiteConstruct");
								throw categoryFacetedSearchException;
							}

							// can also check hasGidBrandSites()
							var crossBrands = objGidBrandSites.tempBrandSites; // universal
							// brand
							// ordered
							if (Object.isUndefined(crossBrands)) {
								categoryFacetedSearchException = new categoryFacetedSearchManager.view.CategoryFacetedSearchException(
										"DEPENDENCY_OBJECT_NOT_FOUND: Cannot find gidBrandSiteConstruct.gidBrandSites");
								throw categoryFacetedSearchException;
							}
							
							crossBrands = $A(crossBrands).compact();
							var currentBrandCode = objGidBrandSites.currentBrandCode;
							var constants = navigationService.controller.managers.categoryFacetedSearchManager.constants;
							/*
							 * ASSUMPTION: businessUnitTotalItemCountMap data
							 * structure is as follows:
							 * businessUnitTotalItemCountMap[brandCode] =
							 * brandResultCount
							 */
							var crossBrandSearchResults = productCategoryFacetedSearch.businessUnitTotalItemCountList;

							var thisBrand;
							var orderedCrossBrands = crossBrands.toArray();
							crossBrands.each(function(brand, idx) {
								if (brand.brandCode == currentBrandCode) {
									// slice it from the cloned array and add it
									// to the top
									thisBrand = brand;
									var topBrand = orderedCrossBrands.splice(idx, 1);
									orderedCrossBrands.splice(0,0,topBrand[0]);
								} else {
									return false;
								}
							});

							// Now loop through the crossBrands in universal
							// order
							// and populate the DOM
							var crossBrandListContainer = $(constants.elementIds.PRODUCT_SEARCH_CROSSBRAND_RESULTS_CONTAINER);
							var crossBrandDomList = $('crossBrandSearchResults');
							
							var locationQueryStringHash = $H(window.location.search.toQueryParams());
							
							// Update searchText queryParam with corrected
							// search term if applicable
							if(!Object.isUndefined(locationQueryStringHash.get('searchText')) && !Object.isUndefined(productCategoryFacetedSearch.autoCorrectedText)){
								locationQueryStringHash.set('searchText', productCategoryFacetedSearch.autoCorrectedText);
							}
							
							var currentSearchUrl = window.location.pathname + '?' + locationQueryStringHash.toQueryString();
							
							var resultCountElemId = thisBrand.brandAbbr + "_ResultCount";
							var brandSearchUrl = currentSearchUrl;
							var crossBrandListContent = "";

							var crossBrandListContract = $H( {
								brandSelected : "selected",
								crossBrandSearchUrl : brandSearchUrl,
								crossBrandName : thisBrand.brandDisplayName,
								crossBrandResultNumId : resultCountElemId
										.camelize(),
								crossBrandTotalItemCount : productCategoryFacetedSearch.totalItemCount
							});
							
							// @TODO NAV20-519 Comment out following 2 lines
							// crossBrandListContent +=
							// navigationService.controller.managers.categoryFacetedSearchManager.constants.templates.CROSS_BRAND_SEARCH_RESULT_TEMPLATE
							// .evaluate(crossBrandListContract);

							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
							var hasCrossBrandResultsToDisplay = 0;

							// @TODO NAV20-519 Loop through crossBrands
							orderedCrossBrands.each(function(brand, idx){
								var brandSearchResults = categoryFacetedSearchManager.view.getCrossBrandSearchResults(
														crossBrandSearchResults, brand.brandCode);

								if (Object.isUndefined(brandSearchResults)) 
									return false;

								// Do not display if brand result count
								// == 0
								if (brandSearchResults.searchFacetBrandResultCount == 0)
									return false;
								
								// @TODO NAV20-519
								hasCrossBrandResultsToDisplay = ++hasCrossBrandResultsToDisplay;
								var isSelectedBrand = "";
								/** @TODO NAV20-519 * */
								if (brand.brandCode == currentBrandCode) {
									isSelectedBrand = "selected";
								}
								/** * */										
								resultCountElemId = brand.brandAbbr + "_ResultCount";
								brandSearchUrl = brand.unsecureUrl + currentSearchUrl;

								crossBrandListContract.set('brandSelected', isSelectedBrand);
								crossBrandListContract.set('crossBrandSearchUrl',brandSearchUrl);
								crossBrandListContract.set('crossBrandName',brand.brandDisplayName);
								crossBrandListContract.set('crossBrandResultNumId',resultCountElemId.camelize());
								crossBrandListContract.set('crossBrandTotalItemCount',brandSearchResults.searchFacetBrandResultCount);

								crossBrandListContent += navigationService.controller.managers.categoryFacetedSearchManager.constants.templates.CROSS_BRAND_SEARCH_RESULT_TEMPLATE
										.evaluate(crossBrandListContract);
							});
							// @TODO NAV20-519
							if(hasCrossBrandResultsToDisplay > 0){
								crossBrandListContainer.removeClassName("hideMe");
								crossBrandDomList.insert(crossBrandListContent);
							}
						},
						DomDisplayException : function(message) {
							this.message = message;
							this.name = "DomDisplayException";
						},
						CategoryFacetedSearchException : function(message) {
							this.message = message;
							this.name = "CategoryFacetedSearchException";
						},
						getCrossBrandSearchResults : function(crossBrandSearchResults, brandCode) {
							for ( var count = 0; count < crossBrandSearchResults.length; count++) {
								if (crossBrandSearchResults[count].searchFacetBrandResultName == brandCode) {
									return crossBrandSearchResults[count];
								}
							}
						},
						hasCrossBrandSearchResults : function(crossBrandSearchResults){
							var objGidBrandSites = window['gidBrandSiteConstruct'];
							if (Object.isUndefined(objGidBrandSites)) {
								categoryFacetedSearchException = new categoryFacetedSearchManager.view.CategoryFacetedSearchException(
										"DEPENDENCY_OBJECT_NOT_FOUND: Cannot find gidBrandSiteConstruct");
								throw categoryFacetedSearchException;
							}
							
							var currentBrandCode = objGidBrandSites.currentBrandCode;
							var hasResults = false;
							
							crossBrandSearchResults.each(function(brandResults, idx){
								// skip if current brand
								if(brandResults.searchFacetBrandResultName == currentBrandCode){
									return false;
								}
								
								if(brandResults.searchFacetBrandResultCount == 0){
									return false;
								}
								hasResults = true;
							});
							
							return hasResults;
						}
					},
					util : {
						sizeFlyoutExpandContract : function(el){
							$el = Element.extend(el);
						    var elChildren = $el.childElements();
						    if(!$el.hasClassName('flyout_activated')){
						    	$el.addClassName('flyout_activated');
						          elChildren.each(function(elChild,i){
						                if(elChild.hasClassName('facet-flyout')){
						                      elChild.removeClassName('hideMe');
						                }
						                if(elChild.hasClassName('facetName')){
						                	var facetTitleArrow = elChild.firstDescendant();
						                	if(!facetTitleArrow.hasClassName('hideMe')){ 
						                		facetTitleArrow.addClassName('hideMe');
						                	}
						                }
						          });
						    } else {
						          elChildren.each(function(elChild,i){
						                el.removeClassName('flyout_activated');
						                if(elChild.hasClassName('facet-flyout')){
						                      elChild.addClassName('hideMe');
						                }
						                if(elChild.hasClassName('facetName')){
						                	var facetTitleArrow = elChild.firstDescendant();
						                	if(facetTitleArrow.hasClassName('hideMe')){ 
						                		facetTitleArrow.removeClassName('hideMe');
						                	}
						                }
						          });
						    }
						},
						sizeFlyoutOpen : function(el){
							Element.extend(el);
							var elChildren = el.childElements();
							el.addClassName('flyout_activated');
						    elChildren.each(function(elChild,i){
						    	if(elChild.hasClassName('facet-flyout')){
						        	elChild.removeClassName('hideMe');
						        }
						    });
						},
						sizeFlyoutClose : function(el){
							Element.extend(el);
							var elChildren = el.childElements();
						    el.removeClassName('flyout_activated');
						    elChildren.each(function(elChild,i){
						        if(elChild.hasClassName('facet-flyout')){
						              elChild.addClassName('hideMe');
						        }
						  	});
						},
						sizeOptionHover : function(el){
							Element.extend(el);
							if(!el.hasClassName('size_option_hover')){
								el.addClassName('size_option_hover');
							} else {
								el.removeClassName('size_option_hover');
							}
						},
						toggleSizeOption : function(el){
							Element.extend(el);
							if(!el.hasClassName('size_option_selected')){
								el.addClassName('size_option_selected');
							} else {
								el.removeClassName('size_option_selected');
							}
						}
					} 
				});
navigationService.controller.managers.categoryFacetedSearchManager.controller.facetManagers.categorySingleSelectListFacetManager = new navigationService.controller.managers.categoryFacetedSearchManager.constructors.AbstractFacetManager();

Object
		.extend(
				navigationService.controller.managers.categoryFacetedSearchManager.controller.facetManagers.categorySingleSelectListFacetManager,
				{
					view : {
						// The abstract renderer returns an empty string
						renderFacet : function(facet) {
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
								
							var facetManager = categoryFacetedSearchManager.controller.facetManagers.categorySingleSelectListFacetManager;
							var constants = categoryFacetedSearchManager.constants;

							var optionsXhtml = "";
							
							// TODO: support proper display of multiple option groups
							if (!Object.isArray(facet.searchFacetOptionGroupList)) {
								facet.searchFacetOptionGroupList = [facet.searchFacetOptionGroupList];
							}
							
							facet.searchFacetOptionGroupList.each(function(facetOptionGroup, i) {
								if (!facetOptionGroup) return;
								if (!Object.isArray(facetOptionGroup.searchFacetOptionList)) {
									facetOptionGroup.searchFacetOptionList = [facetOptionGroup.searchFacetOptionList];
								}
								
								facetOptionGroup.searchFacetOptionList.each(function(facetOption, i) {
									var disabled = "";
									if (facetOption.isActive != "true") {
										disabled = "disabled";
									}
									
									optionsXhtml += "<div id='" + constants.FACET_ID_PREFIX + facet.searchFacetId + "_" + facetOption.searchFacetOptionId + 
										"Div' class='facetOptionRadioDiv'>" + 
										"<input type='radio' class='facetOptionRadio' name='" + 
										constants.FACET_ID_PREFIX + facet.searchFacetId + 
										"' id='" + constants.FACET_ID_PREFIX + facet.searchFacetId + "_" + facetOption.searchFacetOptionId + 
										"' value='" + facetOption.searchFacetOptionValue + "' onclick='" + 
										constants.FACET_ON_CHANGE_ACTION + "' "+ disabled + "/>" + 
										facetOption.searchFacetOptionName + "</div>"
								});
							});

							var facetContract = {
								facetId : facet.searchFacetId,
								facetName : facet.searchFacetName,
								facetClearAll : "",
								facetOptions : optionsXhtml
							};

//							if (constants.templates.FACET_TEMPLATE == null) {
//								constants.templates.FACET_TEMPLATE = new Template(constants.templates.FACET_TEMPLATE_TEXT);
//							}
							var xhtml = constants.templates.FACET_TEMPLATE.evaluate(facetContract);
							
							return xhtml;
						},
						getSelectedOptionIds : function(facet) {
							var constants = navigationService.controller.managers.categoryFacetedSearchManager.constants;
							
							var selectedOptions = [];

							if (!Object.isArray(facet.searchFacetOptionGroupList)) {
								facet.searchFacetOptionGroupList = [facet.searchFacetOptionGroupList];
							}
							
							facet.searchFacetOptionGroupList.each(function(facetOptionGroup, i) {
								if (!Object.isArray(facetOptionGroup.searchFacetOptionList)) {
									facetOptionGroup.searchFacetOptionList = [facetOptionGroup.searchFacetOptionList];
								}

								facetOptionGroup.searchFacetOptionList.each(function(facetOption, i) {
									if ($(constants.FACET_ID_PREFIX + facet.searchFacetId + "_" + facetOption.searchFacetOptionId).checked) {
										selectedOptions.push(facetOption.searchFacetOptionValue);
									}
								});
							});
							
							return selectedOptions;
						},
						// For the categorySingleSelectListFacetManager, only a single option  sohuld ever be selected.
						// If multiple options are selected, only the last one will actually be selected on the page
						setSelectedOptions: function(facet) {
							var constants = navigationService.controller.managers.categoryFacetedSearchManager.constants;

							if (!Object.isArray(facet.searchFacetOptionGroupList)) {
								facet.searchFacetOptionGroupList = [facet.searchFacetOptionGroupList];
							}
							
							facet.searchFacetOptionGroupList.each(function(facetOptionGroup, i) {
								facetOptionGroup.searchFacetOptionList.each(function(facetOption, i) {
									if (facetOption.isSelected == "true") {
										$(constants.FACET_ID_PREFIX + facet.searchFacetId + "_" + facetOption.searchFacetOptionId).checked = true;
									}
								});
							});
						},
						setDisabledOptions: function(facet) {
							var constants = navigationService.controller.managers.categoryFacetedSearchManager.controller.facetManagers.categorySingleSelectListFacetManager.constants;

							if (!Object.isArray(facet.searchFacetOptionGroupList)) {
								facet.searchFacetOptionGroupList = [facet.searchFacetOptionGroupList];
							}
							
							facet.searchFacetOptionGroupList.each(function(facetOptionGroup, i) {
								facetOptionGroup.searchFacetOptionList.each(function(facetOption, i) {
									if (facetOption.isSelected == "true") {
										$(constants.FACET_ID_PREFIX + facet.searchFacetId + "_" + facetOption.searchFacetOptionId).checked = true;
									}
								});
							});
						},
						setFacetState : function(facet){
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
							var facetManager = categoryFacetedSearchManager.controller.facetManagers.categorySingleSelectFacetManager;
							facetManager.view.setSelectedOptions(facet);
						}						
					}
				});
navigationService.controller.managers.categoryFacetedSearchManager.controller.facetManagers.categoryMultiSelectListFacetManager = new navigationService.controller.managers.categoryFacetedSearchManager.constructors.AbstractFacetManager();

Object
		.extend(
				navigationService.controller.managers.categoryFacetedSearchManager.controller.facetManagers.categoryMultiSelectListFacetManager,
				{
					view : {
						// The abstract renderer returns an empty string
						renderFacet : function(facet) {
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;

							var facetManager = categoryFacetedSearchManager.controller.facetManagers.categoryMultiSelectListFacetManager;
							var constants = categoryFacetedSearchManager.constants;

							var clearAll = "";
							
							if (facetManager.view.getSelectedOptionIds(facet).length > 0) {
								// Render the Clear all button
								clearAll = facetManager.view.renderClearAllAction(facet);
							}
							
							var optionsXhtml = "";
							
							// TODO: support proper display of multiple option groups
							if (!Object.isArray(facet.searchFacetOptionGroupList)) {
								facet.searchFacetOptionGroupList = [facet.searchFacetOptionGroupList];
							}
							
							facet.searchFacetOptionGroupList.each(function(facetOptionGroup, i) {
								if (!facetOptionGroup) return;
								if (!Object.isArray(facetOptionGroup.searchFacetOptionList)) {
									facetOptionGroup.searchFacetOptionList = [facetOptionGroup.searchFacetOptionList];
								}
								
								facetOptionGroup.searchFacetOptionList.each(function(facetOption, i) {
									var disabled = "";
									if (facetOption.isActive != "true") {
										disabled = "disabled";
									}

									optionsXhtml += "<div id='" + constants.FACET_ID_PREFIX + facet.searchFacetId + "_" + facetOption.searchFacetOptionId + 
										"Div' class='facetOptionRadioDiv'>" + 
										"<input type='checkbox' class='facetOptionRadio' name='" + 
										constants.FACET_ID_PREFIX + facet.searchFacetId + 
										"' id='" + constants.FACET_ID_PREFIX + facet.searchFacetId + "_" + facetOption.searchFacetOptionId + 
										"' value='" + facetOption.searchFacetOptionValue + "' onclick='" + 
										constants.FACET_ON_CHANGE_ACTION + "' "+ disabled + "/>" + 
										facetOption.searchFacetOptionName + "</div>"
								});
							});

							var facetContract = {
								facetId : facet.searchFacetId,
								facetName : facet.searchFacetName,
								facetClearAll : clearAll,
								facetOptions : optionsXhtml
							};
							
							// @REMOVED see categoryFacetedSearchManager.setConstants()
							//if (constants.templates.FACET_TEMPLATE == null) {
							//	constants.templates.FACET_TEMPLATE = new Template(constants.templates.FACET_TEMPLATE_TEXT);
							//}
							var xhtml = constants.templates.FACET_TEMPLATE.evaluate(facetContract);
							
							return xhtml;
							
							
						},
						getSelectedOptionIds : function(facet) {
							var constants = navigationService.controller.managers.categoryFacetedSearchManager.constants;
							
							var selectedOptions = [];

							if (!Object.isArray(facet.searchFacetOptionGroupList)) {
								facet.searchFacetOptionGroupList = [facet.searchFacetOptionGroupList];
							}
							
							facet.searchFacetOptionGroupList.each(function(facetOptionGroup, i) {
								if (!facetOptionGroup) return;
								if (!Object.isArray(facetOptionGroup.searchFacetOptionList)) {
									facetOptionGroup.searchFacetOptionList = [facetOptionGroup.searchFacetOptionList];
								}

								facetOptionGroup.searchFacetOptionList.each(function(facetOption, i) {
									if (
										$(constants.FACET_ID_PREFIX + facet.searchFacetId + "_" + facetOption.searchFacetOptionId) != null &&
										$(constants.FACET_ID_PREFIX + facet.searchFacetId + "_" + facetOption.searchFacetOptionId).checked
									) {
										selectedOptions.push(facetOption.searchFacetOptionValue);
									}
								});
							});
							
							return selectedOptions;
						},						
						setSelectedOptions : function(facet) {
							var constants = navigationService.controller.managers.categoryFacetedSearchManager.constants;

							if (!Object.isArray(facet.searchFacetOptionGroupList)) {
								facet.searchFacetOptionGroupList = [facet.searchFacetOptionGroupList];
							}
							
							facet.searchFacetOptionGroupList.each(function(facetOptionGroup, i) {
								if (!facetOptionGroup) return;
								facetOptionGroup.searchFacetOptionList.each(function(facetOption, i) {
									if (facetOption.isSelected == "true") {
										$(constants.FACET_ID_PREFIX + facet.searchFacetId + "_" + facetOption.searchFacetOptionId).checked = true;
									}
								});
							});
						},
						clearSelectedOptions : function(facet) {
							var constants = navigationService.controller.managers.categoryFacetedSearchManager.constants;

							if (!Object.isArray(facet.searchFacetOptionGroupList)) {
								facet.searchFacetOptionGroupList = [facet.searchFacetOptionGroupList];
							}
							
							facet.searchFacetOptionGroupList.each(function(facetOptionGroup, i) {
								if (!facetOptionGroup) return;
								facetOptionGroup.searchFacetOptionList.each(function(facetOption, i) {
										$(constants.FACET_ID_PREFIX + facet.searchFacetId + "_" + facetOption.searchFacetOptionId).checked = false;
								});
							});							
						},
						renderClearAllAction: function(facet){
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
							var facetManager = categoryFacetedSearchManager.controller.facetManagers.categoryMultiSelectListFacetManager;
							var constants = categoryFacetedSearchManager.constants;

							var clearAction = "navigationService.controller.managers.categoryFacetedSearchManager.controller.facetManagers.categoryMultiSelectListFacetManager.view.clearSelectedOptions(navigationService.controller.managers.categoryFacetedSearchManager.controller.getFacet(\"" + 
							facet.searchFacetId + "\"));" + constants.FACET_ON_CHANGE_ACTION;
						
							var clearAllDisplayContract = {
								facetElementId : constants.FACET_ID_PREFIX + facet.searchFacetId,
								clearAction : clearAction
							};

							// @REMOVED see categoryFacetedSearchManager.setConstants()
//							if (constants.templates.CLEAR_ALL_FACET_SELECTIONS_TEMPLATE == null) {
//								constants.templates.CLEAR_ALL_FACET_SELECTIONS_TEMPLATE = new Template(constants.templates.CLEAR_ALL_FACET_SELECTIONS_TEMPLATE_TEXT);
//							}
							
							var clearAllDisplay = constants.templates.CLEAR_ALL_FACET_SELECTIONS_TEMPLATE.evaluate(clearAllDisplayContract);
							return clearAllDisplay;
						},
						setFacetOptionsState : function(facet){
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
							var facetManager = categoryFacetedSearchManager.controller.facetManagers.categoryMultiSelectFacetManager;
							facetManager.view.clearSelectedOptions(facet);
							facetManager.view.setSelectedOptions(facet);
						}
					}
				});
navigationService.controller.managers.categoryFacetedSearchManager.controller.facetManagers.categorySizeFacetManager = new navigationService.controller.managers.categoryFacetedSearchManager.constructors.AbstractFacetManager();

Object
		.extend(
				navigationService.controller.managers.categoryFacetedSearchManager.controller.facetManagers.categorySizeFacetManager,
				{	
					constants: {
						templates : {
							FACET_FLYOUT_BODY_TEMPLATE: null,
							FACET_FLYOUT_BODY_TEMPLATE_TEXT: '<div id="#{facetNameId}_variants" class="hideMe">'
									+ '<label for="regular_sizes" class="size_variant_option"><input disabled checked id="regular_sizes" type="checkbox" />&nbsp;Regular</label>'
									+ '<label for="petite_sizes" class="size_variant_option"><input disabled id="petite_sizes" type="checkbox" />&nbsp;Petite</label>'
									+ '<label for="tall_sizes" class="size_variant_option"><input disabled id="tall_sizes" type="checkbox" />&nbsp;Tall</label>'
									+ '</div>'
									+ '<div id="#{facetNameId}_select_text">Select one or more #{facetNameId}s</div>'
									+ '<div class="clearfix"></div>'
									+ '<div id="#{facetNameId}_flyout_body"><div class="#{facetNameId}_facet_category">'
							    	+ '<div class="#{facetNameId}_facet_category_title"><span class="heading">Tops</span><br/>'
							    	+ '<span class="variant">Regular</span></div>'
							    	+ '<ul id="tops_regular" class="ui-buttonset">#{facetOptions}</ul>'
							    	+ '<div class="clearfix"></div></div></div>',	
					    	FACET_SIZE_LIST_TEMPLATE: null,
							FACET_SIZE_LIST_TEMPLATE_TEXT: '<li class="#{facetNameId}_option #{facetOptionElementisActive}" onmouseover="navigationService.controller.managers.categoryFacetedSearchManager.util.sizeOptionHover(this);" onmouseout="navigationService.controller.managers.categoryFacetedSearchManager.util.sizeOptionHover(this);" onclick="navigationService.controller.managers.categoryFacetedSearchManager.util.toggleSizeOption(this);">'
									+ '<label alt="#{facetOptionValueText}" title="#{facetOptionValueText}" for="#{facetOptionElementId}" class="ui-button-text single-line ui-button ui-widget ui-state-default ui-button-text-only">'
									+ '<input type="checkbox" #{facetOptionElementIsSelectable} onclick="#{facetOptionOnChangeAction}" name="#{facetId}[#{facetOptionId}]" alt="#{facetOptionValueText}" value="#{facetOptionValue}" id="#{facetOptionElementId}" class="ui-helper-hidden-accessible" />'
									+ '#{facetOptionValueText}'
									+ '</label></li>'
						}
					},
					view : {
						// The abstract renderer returns an empty string
						renderFacet : function(facet) {
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;

							var facetManager = categoryFacetedSearchManager.controller.facetManagers.categorySizeFacetManager;
							var constants = facetManager.constants;

							var clearAll = "";
							
							// Render the Clear all button
							clearAll = facetManager.view.renderClearAllAction(facet);

							var optionsXhtml = "";
							optionsXhtml = facetManager.view.renderFlyoutDisplayHelper(facet,clearAll);
							
							var totalItemCount = categoryFacetedSearchManager.model.data.productCategoryFacetedSearch.totalItemCount;
							
							var facetContract = {
								facetId : facet.searchFacetId,
								facetName : facet.searchFacetName,
								facetNameId: facet.searchFacetName.toLowerCase(),
								totalItemCount : totalItemCount,
								facetClearAll : clearAll,
								facetDisplayContent : optionsXhtml
							};

							if (categoryFacetedSearchManager.constants.templates.flyoutDisplay.FACET_TEMPLATE == null) {
								categoryFacetedSearchManager.constants.templates.flyoutDisplay.FACET_TEMPLATE = new Template(categoryFacetedSearchManager.constants.templates.flyoutDisplay.FACET_TEMPLATE_TEXT);
							}
							var xhtml = categoryFacetedSearchManager.constants.templates.flyoutDisplay.FACET_TEMPLATE.evaluate(facetContract);
							
							return xhtml;
							
							
						},
						renderFlyoutDisplayHelper: function(facet, clearAll){
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
							var facetManager = categoryFacetedSearchManager.controller.facetManagers.categorySizeFacetManager;
							var constants = categoryFacetedSearchManager.constants;
							var sizeConstants = facetManager.constants;
							
							//FACET_VARIANT_DISPLAY_CONTAINER_TEMPLATE
							var flyoutContent = "";
							var optionsContent = "";
							
							optionsContent = facetManager.view.renderOptionsDisplayHelper(facet);
		
							var flyoutDisplayContract =  $H({
								facetElementId : constants.FACET_ID_PREFIX + facet.searchFacetId,
								facetName : facet.searchFacetName,
								facetNameId: facet.searchFacetName.toLowerCase(),
								facetClearAll: clearAll,
								facetOptions: optionsContent
							});
							
							if (sizeConstants.templates.FACET_FLYOUT_BODY_TEMPLATE == null) {
								sizeConstants.templates.FACET_FLYOUT_BODY_TEMPLATE = new Template(sizeConstants.templates.FACET_FLYOUT_BODY_TEMPLATE_TEXT);
							}
							
							flyoutContent = sizeConstants.templates.FACET_FLYOUT_BODY_TEMPLATE.evaluate(flyoutDisplayContract);
							
							flyoutDisplayContract.set("flyoutDisplayContent", flyoutContent);
							
							var flyoutDisplay = constants.templates.flyoutDisplay.FACET_FLYOUT_CONTAINER_TEMPLATE.evaluate(flyoutDisplayContract);
								
							return flyoutDisplay;
						},
						renderOptionsDisplayHelper: function(facet){
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
							var facetManager = categoryFacetedSearchManager.controller.facetManagers.categorySizeFacetManager;
							var constants = categoryFacetedSearchManager.constants;
							var sizeConstants = facetManager.constants;
							
							var facetOptionsContent = "";

							if (sizeConstants.templates.FACET_SIZE_LIST_TEMPLATE == null) {
								sizeConstants.templates.FACET_SIZE_LIST_TEMPLATE = new Template(sizeConstants.templates.FACET_SIZE_LIST_TEMPLATE_TEXT);
							}
							
							var facetOptionsContract = $H( {
								facetElementId : constants.FACET_ID_PREFIX + facet.searchFacetId,
								facetName : facet.searchFacetName,
								facetNameId: facet.searchFacetName.toLowerCase(),
								facetId: facet.searchFacetId
							});
							
							// TODO: support proper display of multiple option groups
							if (!Object.isArray(facet.searchFacetOptionGroupList)) {
								facet.searchFacetOptionGroupList = [facet.searchFacetOptionGroupList];
							}
							//FACET_SIZE_LIST_TEMPLATE
							facet.searchFacetOptionGroupList.each(function(facetOptionGroup, i) {
								if (!Object.isArray(facetOptionGroup.searchFacetOptionList)) {
									facetOptionGroup.searchFacetOptionList = [facetOptionGroup.searchFacetOptionList];
								}
								
								facetOptionGroup.searchFacetOptionList.each(function(facetOption, idx) {
									
									facetOptionsContract.set('facetOptionName', facetOption.searchFacetOptionName.replace(/.5|\s1\/2/g, '&frac12'));
									facetOptionsContract.set('facetOptionId', facetOption.searchFacetOptionId);
									facetOptionsContract.set('facetOptionValue', facetOption.searchFacetOptionValue);
									facetOptionsContract.set('facetOptionValueText', facetOption.searchFacetOptionValue.replace(/.5|\s1\/2/g, '&frac12'));
									facetOptionsContract.set('facetOptionElementId', constants.FACET_ID_PREFIX + facet.searchFacetId + "_" + facetOption.searchFacetOptionId);
									facetOptionsContract.set('facetOptionElementIdx', idx);
									facetOptionsContract.set('facetOptionOnChangeAction', constants.FACET_ON_CHANGE_ACTION);
									
									facetOptionsContent += sizeConstants.templates.FACET_SIZE_LIST_TEMPLATE.evaluate(facetOptionsContract);
								
								});
							});
							
							return facetOptionsContent;
						},
						toggleLeftRail : function(facet,leftRailFacetContainer){
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
							var facetManager = categoryFacetedSearchManager.controller.facetManagers.categorySizeFacetManager;
							var constants = categoryFacetedSearchManager.constants;

							if (facetManager.view.getSelectedOptionIds(facet).length > 0) {
								if(leftRailFacetContainer.hasClassName("hideMe")){
									leftRailFacetContainer.removeClassName("hideMe");
								}
							} else {
								if(!leftRailFacetContainer.hasClassName("hideMe")){
									leftRailFacetContainer.addClassName("hideMe");
								}
							}
						},
						getSelectedOptionIds : function(facet) {
							var constants = navigationService.controller.managers.categoryFacetedSearchManager.constants;
							
							var selectedOptions = [];

							if (!Object.isArray(facet.searchFacetOptionGroupList)) {
								facet.searchFacetOptionGroupList = [facet.searchFacetOptionGroupList];
							}
							
							facet.searchFacetOptionGroupList.each(function(facetOptionGroup, i) {
								if (!Object.isArray(facetOptionGroup.searchFacetOptionList)) {
									facetOptionGroup.searchFacetOptionList = [facetOptionGroup.searchFacetOptionList];
								}
								
								var selectedOptElem = "";
								facetOptionGroup.searchFacetOptionList.each(function(facetOption, i) {
									selectedOptElem = $(constants.FACET_ID_PREFIX + facet.searchFacetId + "_" + facetOption.searchFacetOptionId);
									
									if (
											selectedOptElem != null &&
											selectedOptElem.checked
									) {
										selectedOptions.push(facetOption.searchFacetOptionValue);
									}
								});
							});
							
							return selectedOptions;
						},						
						setSelectedOptions : function(facet) {
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
							var facetManager = categoryFacetedSearchManager.controller.facetManagers.categorySizeFacetManager;
							var constants = categoryFacetedSearchManager.constants;

							if (!Object.isArray(facet.searchFacetOptionGroupList)) {
								facet.searchFacetOptionGroupList = [facet.searchFacetOptionGroupList];
							}
							
							// SNAV-14
							var leftRailSelectionContainer = $(facet.searchFacetId +"_current_selection");
							var leftRailListContainer = "";
							
							if(leftRailSelectionContainer){
								leftRailListContainer = leftRailSelectionContainer.select('ul.current-selection-list')[0];
								Element.extend(leftRailListContainer);
							}
							var leftRailSelectedItem = "";
							var thisElem = "";
							var thisElemGrandParent = "";
							
							facet.searchFacetOptionGroupList.each(function(facetOptionGroup, i) {
								facetOptionGroup.searchFacetOptionList.each(function(facetOption, i) {
									if (facetOption.isSelected == "true") {
										thisElem = $(constants.FACET_ID_PREFIX + facet.searchFacetId + "_" + facetOption.searchFacetOptionId);
										thisElem.checked = true;
										thisElemGrandParent = thisElem.ancestors()[1];
										thisElemGrandParent.addClassName('selected');
										if(leftRailListContainer != ""){// SNAV-14: create the li element
											leftRailSelectedItem = "<li title='"+ facetOption.searchFacetOptionName.replace(/.5|\s1\/2/g, '&frac12') +"' alt='"+ facetOption.searchFacetOptionName.replace(/.5|\s1\/2/g, '&frac12') +"' class='current-selection-list-item selected option' id='" + constants.FACET_ID_PREFIX_LEFT_RAIL + facet.searchFacetId + "_" + facetOption.searchFacetOptionId +"'><span alt='Clear "+facetOption.searchFacetOptionName.replace(/.5|\s1\/2/g, '&frac12')+"' class=''>&nbsp;<!-- empty--></span>"+ facetOption.searchFacetOptionName.replace(/.5|\s1\/2/g, '&frac12') +"</li>";
											leftRailListContainer.insert({bottom: leftRailSelectedItem});
											if(leftRailSelectionContainer.hasClassName("hideMe")){
												leftRailSelectionContainer.removeClassName("hideMe");
											}
										}
									}
								});
							});
						},
						clearSelectedOptions : function(facet) {
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
							var facetManager = categoryFacetedSearchManager.controller.facetManagers.categorySizeFacetManager;
							var constants = categoryFacetedSearchManager.constants;

							if (!Object.isArray(facet.searchFacetOptionGroupList)) {
								facet.searchFacetOptionGroupList = [facet.searchFacetOptionGroupList];
							}
							
							// SNAV-14
							var leftRailSelectionContainer = $(facet.searchFacetId +"_current_selection");
							var leftRailListContainer = "";
							
							if(leftRailSelectionContainer){
								leftRailListContainer = leftRailSelectionContainer.select('ul.current-selection-list'); 
								facetManager.view.toggleLeftRail(facet, leftRailSelectionContainer);
							}
							var leftRailSelectedItem = "";
							var thisElem = "";
							var thisElemGrandParent = "";
							facet.searchFacetOptionGroupList.each(function(facetOptionGroup, i) {
								
								facetOptionGroup.searchFacetOptionList.each(function(facetOption, i) {
									thisElem = $(constants.FACET_ID_PREFIX + facet.searchFacetId + "_" + facetOption.searchFacetOptionId);
									thisElem.checked = false;
									thisElemGrandParent = thisElem.ancestors()[1];
									thisElemGrandParent.removeClassName('selected');
									
									leftRailSelectedItem = $(constants.FACET_ID_PREFIX_LEFT_RAIL + facet.searchFacetId + "_" + facetOption.searchFacetOptionId);
									if(leftRailSelectedItem){// SNAV-14: remove the li element
										leftRailSelectedItem.remove();
									}
								});
							});
						}, 
						renderClearAllAction: function(facet){
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
							var facetManager = categoryFacetedSearchManager.controller.facetManagers.categorySizeFacetManager;
							var constants = categoryFacetedSearchManager.constants;

							var clearAction = "navigationService.controller.managers.categoryFacetedSearchManager.controller.facetManagers.categorySizeFacetManager.view.clearSelectedOptions(navigationService.controller.managers.categoryFacetedSearchManager.controller.getFacet(\"" + 
							facet.searchFacetId + "\"));" + constants.FACET_ON_CHANGE_ACTION + ";return false;";
						
							var clearAllDisplayContract = {
								facetElementId : constants.FACET_ID_PREFIX + facet.searchFacetId,
								clearAction : clearAction
							};

							if (constants.templates.flyoutDisplay.FACET_FLYOUT_CLEAR_ALL_SELECTIONS_TEMPLATE == null) {
								constants.templates.flyoutDisplay.FACET_FLYOUT_CLEAR_ALL_SELECTIONS_TEMPLATE = new Template(constants.templates.flyoutDisplay.FACET_FLYOUT_CLEAR_ALL_SELECTIONS_TEMPLATE_TEXT);
							}
							
							var clearAllDisplay = constants.templates.flyoutDisplay.FACET_FLYOUT_CLEAR_ALL_SELECTIONS_TEMPLATE.evaluate(clearAllDisplayContract);
								
							return clearAllDisplay;
						},
						setClearButton : function(facet){
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
							var facetManager = categoryFacetedSearchManager.controller.facetManagers.categorySizeFacetManager;
							//the elements that 
							var clearElements = $$('#facet_'+ facet.searchFacetName.toLowerCase() + " .clearAllButton");							
							var displayClearButton = facetManager.view.getSelectedOptionIds(facet).length > 0;
							clearElements.each(function(el,i){
								if(displayClearButton){
									el.removeClassName('hideMe');
								} else {
									el.addClassName('hideMe');
								}
							});							
						},
						setDisabledOptions : function(facet){
							var constants = navigationService.controller.managers.categoryFacetedSearchManager.constants;

							if (!Object.isArray(facet.searchFacetOptionGroupList)) {
								facet.searchFacetOptionGroupList = [facet.searchFacetOptionGroupList];
							}
							
							var thisElem = "";
							var thisElemGrandParent = "";
							facet.searchFacetOptionGroupList.each(function(facetOptionGroup, i) {
								var disabled = "";
								facetOptionGroup.searchFacetOptionList.each(function(facetOption, i) {
									thisElem = $(constants.FACET_ID_PREFIX + facet.searchFacetId + "_" + facetOption.searchFacetOptionId);
									thisElemGrandParent = thisElem.ancestors()[1];
									if (facetOption.isActive == "true") {
										thisElem.enable();
										thisElemGrandParent.removeClassName('unavailable');
									} else {
										thisElem.checked = false;
										thisElem.disable();
										thisElemGrandParent.addClassName('unavailable');
									}
									
								});
							});
						},
						setFacetState : function(facet){
							var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
							var facetManager = categoryFacetedSearchManager.controller.facetManagers.categorySizeFacetManager;
							facetManager.view.clearSelectedOptions(facet);
							facetManager.view.setSelectedOptions(facet);
							facetManager.view.setClearButton(facet);
							facetManager.view.setDisabledOptions(facet);
						}
					}
				});
// throw away SBS facet manager for pre peak

navigationService.controller.managers.categoryFacetedSearchManager.controller.facetManagers.categorySbsFacetManager =
	new navigationService.controller.managers.categoryFacetedSearchManager.constructors.AbstractFacetManager();

Object.extend(
navigationService.controller.managers.categoryFacetedSearchManager.controller.facetManagers.categorySbsFacetManager,
{
	view : {
		isOptionSelected: false,
		HALF_SBS_BAR_HEIGHT: 24,
		RESET_TEXT: '&nbsp;',
		CHECKBOX_TEMPLATE: new Template("<span><input type='checkbox' class='facetOptionRadio' name='#{name}' id='#{id}' value='#{value}' onclick='#{action}' #{disabled}/> <label for='#{id}' class='#{disabled}'>#{optionName}</label></span>"),
		buildSBSFacetOptionsHtml: function(accumulator, facetOption, index) {
			var template = accumulator.manager.view.CHECKBOX_TEMPLATE;
			var name = accumulator.name;
			var id = name + "_" + facetOption.searchFacetOptionId;
			var value = facetOption.searchFacetOptionValue;
			var action = accumulator.action;
			var disabled = facetOption.isActive != "true" ? 'disabled' : '';
			
			accumulator.html.push(template.evaluate({
				id: id, name: name, action: action, value: value,
				optionName: facetOption.searchFacetOptionName.replace(/\.5|\s1\/2/g, '&frac12'),
				disabled: disabled
			}));
			
			return accumulator;
		},
		processFacetGroupData: function(accumulator, facetGroupKey, index) {
			var facetGroups = accumulator.facetGroups;
			var facet = accumulator.facet;
			var facetManager = accumulator.manager;
			var constants = accumulator.constants;
			
			var name = constants.FACET_ID_PREFIX + facet.searchFacetId;
			var action = constants.FACET_ON_CHANGE_ACTION;
			
			var context = { html: [], name: name, action: action, manager: facetManager };
			
			
			var facetOptions = facetGroups[facetGroupKey];
			var marginTop = facetManager.view.HALF_SBS_BAR_HEIGHT - Math.ceil(facetOptions.length / 6) * 8;
			accumulator.marginTop = marginTop > 0 ? marginTop : 0;
			facetOptions.inject(context, facetManager.view.buildSBSFacetOptionsHtml);
			accumulator.html = context.html.join('');
			
			return accumulator;
		},
		resetSbsOptions: function(e) {
			var controller = navigationService.controller.managers.categoryFacetedSearchManager.controller;
			controller.facetManagers.categoryMultiSelectListFacetManager.view.clearSelectedOptions(controller.getFacet('size'));
			controller.filterByFacets();
		},
		toggleSelected: function(e) {
			var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
			var facetManager = categoryFacetedSearchManager.controller.facetManagers.categorySbsFacetManager;
			
			var option = e.currentTarget || e.srcElement;
			option.next().setStyle({fontWeight: option.checked ? 'bold' : 'normal'});
			
			var isOptionSelected = false;
			var facetOptions = facetManager.facetOptions.pluck('checked');
			for (var i=0; i < facetOptions.length; i++) {
				isOptionSelected = isOptionSelected || facetOptions[i];
			}
			
			var clearBtn = facetManager.view.clearBtn;
			if (clearBtn) {
				clearBtn.setStyle({display: isOptionSelected ? 'inline-block' : 'none'});
			}
		},
		openSizeChart: function(e) {
			var query = '?cid=' + (e.currentTarget || e.srcElement).getAttribute('sizeChart');
		    window.open("/browse/sizeChart.do" + query, "SBSsizeChart", "toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=no,width=" + brandProperties.SIZECHARTWINDOWWIDTH + ",height=" + brandProperties.SIZECHARTWINDOWHEIGHT);
		},
		// The abstract renderer returns an empty string
		renderFacet : function(facet) {
			var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
			var facetManager = categoryFacetedSearchManager.controller.facetManagers.categorySbsFacetManager;
			var constants = categoryFacetedSearchManager.constants;
			
			var accumulator = {
					html: '',
					manager: facetManager,
					facet: facet,
					constants: constants,
					facetGroups: facet.searchFacetOptionGroupList
			};
			
			var facetGroupOptions = $H(facet.searchFacetOptionGroupList).keys();
			facetGroupOptions.inject(accumulator, facetManager.view.processFacetGroupData);
			
			var sbsFacet = $('productSbsFacet');
			var optionFacet = sbsFacet.select('.sbsFacetOptions')[0];
			optionFacet.update(accumulator.html);
			optionFacet.select('span').each(function(option) { Event.observe(option.firstChild, 'click', facetManager.view.toggleSelected)});
			optionFacet.setStyle({marginTop: accumulator.marginTop + 'px'});
			sbsFacet.setStyle({display: 'block', visibility: 'visible'});
			facetManager.facetOptions = optionFacet.select('span input[type="checkbox"]');
			
			var clearBtn = optionFacet.next();
			//clearBtn.update(facetManager.view.RESET_TEXT);
			clearBtn.observe('click', facetManager.view.resetSbsOptions);
			clearBtn.setStyle({marginTop: accumulator.marginTop + 'px'});
			facetManager.view.clearBtn = clearBtn;
			
			var sizeChartBtn = clearBtn.next().select('a img')[0];
			Event.observe(sizeChartBtn, 'click', facetManager.view.openSizeChart);
			
			return '';
		},
		getSelectedOptionIds : function(facet) {
			var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
			var facetManager = categoryFacetedSearchManager.controller.facetManagers.categorySbsFacetManager;
			var constants = categoryFacetedSearchManager.constants;
			var name = constants.FACET_ID_PREFIX + facet.searchFacetId;
			var selectedOptions = $$('div #productSbsFacet .sbsFacetOptions input[name="' + name + '"]').findAll(function(input) { return input.checked; });
			
			return selectedOptions.pluck('value');
		},
		// For the categorySingleSelectListFacetManager, only a single option  sohuld ever be selected.
		// If multiple options are selected, only the last one will actually be selected on the page
		setSelectedOptions: function(facet) {
			var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
			var facetManager = categoryFacetedSearchManager.controller.facetManagers.categorySbsFacetManager;
			var constants = categoryFacetedSearchManager.constants;
			var name = constants.FACET_ID_PREFIX + facet.searchFacetId;
			
			var groupList = facet.searchFacetOptionGroupList;
			if (!groupList) return;
			
			var facetOptionSelected = facet.searchFacetOptionGroupList.searchFacetOptionList.pluck('isSelected');
			$$('div #productSbsFacet .sbsFacetOptions input[name="' + name + '"]').each(function(input, index) {
				if(facetOptionSelected[index] == 'true') {
					input.checked = true;
				}
				facetManager.view.toggleSelected({currentTarget: input});
			});
			
		},
		setFacetState : function(facet){
			var categoryFacetedSearchManager = navigationService.controller.managers.categoryFacetedSearchManager;
			//var controller = categoryFacetedSearchManager.controller; 
			var facetManager = categoryFacetedSearchManager.controller.facetManagers.categorySbsFacetManager;
			//controller.facetManagers.categoryMultiSelectListFacetManager.view.clearSelectedOptions(controller.getFacet('dim1'));
			facetManager.view.setSelectedOptions(facet);
		}
	}
});

