/** @namespace global namespace for all JS code on bobbibrowncosmetics.com site */
var bb = {};
var BehaviorRollover = 'def_rollover'; // why not just Rollover.classIndicator ?? CM

/** @namespace namespace for page-specific JS code */
bb.page = {};

bb.urlRoot = window.location.protocol + "//" + window.location.hostname;
//var uri = location.href.parseUri();
//var URL_DOMAIN = uri.protocol + "://" + uri.host;
var URL_DOMAIN = bb.urlRoot; 
bb.page.PRODUCT_IMG_PATH = "/images/products/photos/";
bb.page.TOS_MSG = "* Temporarily Out of Stock";
bb.page.SOLDOUT_MSG = "* Sold Out";


bb.page.isCMS = function() {
//    return typeof(cmsPageLoaded) === "function";
    return !!( $$('div.cms_layer')[0] );
};

var el = $;

Element.addMethods({
   addBehavior: function(e,b) {
		$(e).className = b;
   }   
});

// like old whenEl()

bb.page.initSubnav = function() {
    var globalNav = $('global-nav');
    if (globalNav) {
        var subnav = globalNav.select('div.subnav');
        for ( var i=0, len=subnav.length; i<len; i++ ) {
            var liNodes = subnav[i].select('li.subnav-cat');
    
            for ( var j=0, liLen=liNodes.length; j<liLen; j++ ) {
                if (j%2 === 0) {
                    liNodes[j].className += " odd";
                } else {
                    liNodes[j].className += " even";
                }
            }
        }
    }
};

var ServerErrors = Class.create({
	pageErrors: [],
	initialize: function (pageErrors) {
        this.pageErrors = pageErrors || [];
	},
	_findAll: function (type) {
		if (type) {
			return this.pageErrors.findAll(
				function(err) {
					return err.TYPE == type;
				});
		} else return this.pageErrors;
	},
	hasKey: function (key) {
		return this.pageErrors.pluck('KEY').include(key);
	},
	messages: function (type) {
		return this._findAll(type).pluck('MESSAGE');
	},
	keys: function (type) {
		return this._findAll(type).pluck('KEY');
	},
    removeByKey: function(keyToRemove) {
        this.pageErrors = this.pageErrors.findAll(function(errorObj) {
            return errorObj.KEY !== keyToRemove;
        });
        return this.pageErrors;
    }
});


bb.page.initPromoLinks = function() {
    var openBtns = $$('a.expand_link');
    for (var i=0, len=openBtns.length; i<len; i++) {
        var oBtn = openBtns[i];
        oBtn.observe("click", function(evt) {
            bb.page.openPromos();
        });
    }
    var closeBtns = $$('.promos_area .btn');
    for (var j=0, len=closeBtns.length; j<len; j++) {
        var cBtn = closeBtns[j];
        cBtn.observe("click", function(evt) {
            bb.page.closePromos();
        });
    }
    var promoEmailInputEle = $$('div.promo input.email-input')[0];
    bb.page.isInputFieldActive = false;
    if (promoEmailInputEle) {
    	
    promoEmailInputEle.observe("focus", function(focusEvt){
        bb.page.isInputFieldActive = true;
    });
    promoEmailInputEle.observe("blur", function(focusEvt){
        bb.page.isInputFieldActive = false;
    });
    }
};


bb.page.closePromos = function() {
    if (!bb.page.isInputFieldActive) {
        Effect.BlindUp('promo_full', {duration: 0.5});
        $('promo_links').appear({duration: 0.5});
    }
};

bb.page.closeSlider = function(formElement) {
    var sliderId = formElement.up(2).identify();
	new Effect.SlideUp(sliderId);
};

bb.page.openPromos = function() {
    Effect.BlindDown('promo_full', {duration: 0.5});
    $('promo_links').fade({duration: 0.5});
};

bb.page.initEmailSignup = function() {
    var emailSignupForms = $$("form.email-signup");

    emailSignupForms.each(function(formEle) {
        var emailSubmitLink = formEle.select(".email-submit-btn")[0];
        var emailInput = formEle.select("input.email-input")[0];
        var lsInput = formEle.select("input.ls-input")[0];
		var specialrefInput = formEle.select("input.specialref-input")[0];
//        emailInput.value = "Enter your e-mail for our mailing list.";
        var hasDefaultValue = function() {
            var defaultVal = bb.page.emailSigninDefaultValue;
            var hasDefault = false;
            if (defaultVal &&
                    defaultVal.length &&
                    defaultVal.length > 1) {
                hasDefault = (emailInput.value === defaultVal);
            }
            return hasDefault;
        };
        var submitEmailForm = function(formElement) {
            progressObj.start();
            var email = emailInput.value;
			var last_source = lsInput.value;
			var special_ref = specialrefInput.value;
            var onSuccessHandler = function(response) {
                var repsonseObj = response.responseText.evalJSON(true);
    			if (!bb.page.errors) {
                    bb.page.errors = new ServerErrors();
    			}
                bb.page.errors.removeByKey("email_signup");
                if (repsonseObj[0] && repsonseObj[0].result && repsonseObj[0].result.status == 1) {

                    var user_id = repsonseObj[0].result.cm_user_id;
                    if (user_id) {
                        console.log(user_id);
                        document.fire("CM:NavSignup",user_id);
                    }

					/* if a form has the class slider, it will call the slider close method */
					if (formElement.hasClassName('slider')) {
						bb.page.closeSlider(formElement);
					} else {
                    	bb.page.closePromos();
                    }
                    bb.templateFactory.get("popup-email-thankyou",false,"tmpl").evaluateCallback({
                        callback: function(html) {
//                            console.log(html);
                            var popupWrapperNode = new Element("div", {"class":"popup"});
                            
                            $(document.body).insert(popupWrapperNode);
                            popupWrapperNode.update(html);
                            bb.overlay.launchPopover(popupWrapperNode);
                            bb.sIFR.replaceAll();
                            var closeBtnNode = popupWrapperNode.select(".close")[0];
                            closeBtnNode.observe("click", function(evt) {
                                bb.overlay.hide();
                                evt.preventDefault();
                            });
                        }
                    });
                } else {
                    if (repsonseObj[0].result.errors && repsonseObj[0].result.errors.length > 0) {
    	                for (var i=0, len=repsonseObj[0].result.errors.length; i<len; i++) {
    						bb.page.errors.pageErrors.push({
    							MESSAGE : repsonseObj[0].result.errors[i],
    							KEY     : "email_signup"
    						});
    					}
    				} else {
    					bb.page.errors.pageErrors.push({
    						MESSAGE: "There was an error processing your request.",
    						KEY     : "email_signup"
    					});
    				}
                }
    			var errorContainerNode = formElement.select(".errors-container")[0];
    			bb.page.initErrorDisplay(errorContainerNode);
            };
            var jsonrpcArgs = {
                method : "email.signup",
                params : [{
                    "EMAIL_ADDRESS": email,
					"LAST_SOURCE" : last_source,
					"SPECIAL_REFERENCE" : special_ref
                }],
                onSuccess: function(response) {
                    progressObj.revert();
                    onSuccessHandler(response);

                },
                onFailure: function(response) {
                    progressObj.revert();
                    console.log("fail");
                    console.log(response);
                }
            };
            bb.JSONRPC.fetch(jsonrpcArgs);
        };

        emailInput.observe("focus", function(focusEvt) {
            if (hasDefaultValue()) {
                emailInput.value = "";
            }
            emailInput.observe("keypress", function(keyEvt) {
                if (Event.KEY_RETURN === keyEvt.keyCode) {
                    //alert("Return key pressed");
                    submitEmailForm(formEle);
                }
            });
        });
        emailInput.observe("blur", function(blurEvt) {
            emailInput.stopObserving("keypress");
        });
        formEle.observe("submit", function(submitEvt) {
            submitEvt.preventDefault();
        });
        var loadingMsg = formEle.select(".loading")[0];
        var progressObj = new bb.Progress({
            progressNode: loadingMsg,
            containerNode: emailSubmitLink
        });        
        emailSubmitLink.observe("click", function(clickEvt) {
//            console.log(clickEvt);
//            var ele = clickEvt.target;
//            alert(!!ele);
////            alert(ele.className);
            var parents = emailSubmitLink.ancestors();
            var currentForm = parents.find(function(ancstr) {
                return ancstr.match("form");
            });
            submitEmailForm(currentForm);
            clickEvt.preventDefault();
        });

    });

};

bb.page.initSearch = function() {
	var searchInput = $$('.globalnav .search input')[0];
	if (searchInput) {
		searchInput.value = "Search";
		searchInput.observe("focus", function() {
			searchInput.value = "";
		});
	}
}

bb.page.initGnav = function() {
 	var gnavDropDowns = $$(".prod li .subnav");
 	var gnavLinks = $$(".prod li .prod_link .rollover_state");
	gnavDropDowns.each(function(ele,index) {
	    var u = ele.up();
		u.observe("mouseover", function(evt) {
			ele.addClassName("gnav-show");
			gnavLinks[index].addClassName("gnav-show");
		});
		u.observe("mouseout", function(evt) {
			ele.removeClassName("gnav-show");
			gnavLinks[index].removeClassName("gnav-show");
		});
	});
}

bb.page.fillFormField = function(containerNode, inputID, value) {
    var txtInput = containerNode.select('#' + inputID)[0];
    if (txtInput) {
        txtInput.value = value;
    }
};

bb.page.selectOption = function(slct, valueToSelect, fireEvent) {
    if (valueToSelect && valueToSelect.length && valueToSelect.length > 0) {
        if (Object.isElement(slct) && slct.tagName.toLowerCase() === "select") {
            var optionToSelect = null;
            var optsArray = $A(slct.options);
            var opt = optsArray.detect(function(opt, idx) {
                if (opt.value === valueToSelect) {
                    return opt;
                }
            });
            slct.selectedIndex = optsArray.indexOf(opt);
            if (fireEvent) {
                // create DOM Event object manually & fire it.
                if( document.createEvent ) { // Mozilla
                    var evObj = document.createEvent('Event');
                    evObj.initEvent( 'change', true, true);
                    slct.dispatchEvent(evObj);
                } else if( document.createEventObject ) { // IE
                    var evObj = document.createEventObject();
                    slct.fireEvent('onchange', evObj);
                }
            }
        }
    }
};

/**
 * This singleton class provides access to ELC's JSON-RPC methods via AJAX.
 */
bb.JSONRPC = (function(){
    var requestBaseURL = bb.urlRoot + "/jsonrpc.json";
    var ID = 0;
    var ajaxOptions = {
        onSuccess: function (response) {
            console.log('JSON-RPC success');
            console.log(response.responseText);
        },
        onFailure: function (response) {
            console.log('JSON-RPC failure');
            console.log(response.responseText);
        }
    };
    return {
        fetch: function(args) {
//            method, params,
            if (!args.method) {
                return;
            }
            ID++;
            if (args.onSuccess) {
                ajaxOptions.onSuccess = args.onSuccess;
            }
            if (args.onFailure) {
                ajaxOptions.onFailure = args.onFailure;
            }
            var jsonrpcQueryParameterValue = {
                method: args.method,
                id: ID
            };
            if (typeof args.params !== 'object') {
                jsonrpcQueryParameterValue.params = args.params.evalJSON();
            } else {
                jsonrpcQueryParameterValue.params = args.params;
            }
            ajaxOptions.method = 'post';
            jsonrpcQueryParameterValue = (Object.toJSON(jsonrpcQueryParameterValue));
            ajaxOptions.parameters = 'JSONRPC=[' + jsonrpcQueryParameterValue + ']';
            var requestURL = requestBaseURL + '?dbgmethod=' + args.method;
            var ajaxReq = new Ajax.Request( requestURL, ajaxOptions );

            return ID;
        }
    };
})();

/**
 * @namespace Contains classes and methods that operate on product data
 */
bb.productData = {};
//    MISC_FLAG is '5' = online exclusive
//    MISC_FLAG is '3' = new shades
//    MISC_FLAG is '9' = award winners
//    MISC_FLAG is '1' = new
bb.productData.isAwardWinning = function(productData) {
    if (productData && productData.MISC_FLAG) {
        return productData.MISC_FLAG == 9;
    } else {
        return false;
    }
};
bb.productData.isNew = function(productData) {
    if (productData && productData.MISC_FLAG) {
        return productData.MISC_FLAG == 1;
    } else {
        return false;
    }
};
bb.productData.isNewShades = function(productData) {
    if (productData && productData.MISC_FLAG) {
        return productData.MISC_FLAG == 3;
    } else {
        return false;
    }
};

bb.productData.setCallOut = function(callOutNode,productData) {
	if (productData.SUBHEADER) {
		callOutNode.update(productData.SUBHEADER);
	} else {
		callOutNode.update("");
	}          
}

//
//# INVENTORY STATUS CODES and shoppability
//# 1 = Active (shoppable)
//# 2 = Temporarily Out Of Stock (shoppable)
//# 3 = Coming Soon (shoppable or not, depending on brand)
//# 4 = Do Not Display (not displayed, except on dev)
//# 5 = Inactive (not shoppable, remove from cart)
//# 6 = Promotional (not shoppable, but not removed from cart)
//# 7 = Sold Out (not shoppable, remove from cart)
//# 8 = Promotional Sold Out (not shoppable, remove from cart)
//# 9 = Promotional Inactive (not shoppable, remove from cart)
//
//# DISPLAY STATUS CODES
//# 0 = not displayable, corresponds to INVENTORY STATUS 5
//# 1 = displayable, corresponds to INVENTORY STATUS 1, 2, 3, 6, 7
//# 2 = displayable on dev only, corresponds to INVENTORY STATUS 4
/*
* tests a sku obj to see if it is out of stock.
* checks the INVENTORY_STATUS field.
*/
bb.productData.isTos = function(sku) {
	return (sku && sku.INVENTORY_STATUS === 2 && (!sku.VIRTUAL_SKU_ID));
};
/*
* tests a sku obj to see if it is Sold Out.
* checks the INVENTORY_STATUS field.
*/
bb.productData.isSoldOut = function(sku) {
	return (sku && sku.INVENTORY_STATUS === 7);
};
/*
* tests a sku obj to see if it is Displayable.
*/
bb.productData.isDoNotDisplay = function(sku) {
	return (sku && sku.INVENTORY_STATUS === 4);
};
/*
* tests a product obj to see if any of its SKUs are shoppable.
*/
bb.productData.isProductShoppable = function(productData) {
    var shoppable = true;
    if (productData && productData.sku && productData.sku.length > 0) {
//         productData.sku.each(function(skuData) {
//             if (bb.productData.isDoNotDisplay(skuData)) {
//                 shoppable = false;
//             }
//         });
    } else {
        shoppable = false;
    }
    return shoppable;
};

/**
 * Create and return an Array of Product Data objects. Each Product Data object contains an array
 * of SKU Data objects; this SKU array is made up of the SKUs that are associated with each Product.
 * @methodof bb.productData
 */
bb.productData.mergeSkusIntoProducts = function(inputProductsData, inputSkusData) {
    var mergedProductDataArray = [];
    if (typeof inputProductsData === "object" && typeof inputSkusData === "object" ) {
        // Iterate through all product objects
        for (var p in inputProductsData) {
            // Create a hash that will store one product
            var outputProductsData = {};
            // Copy the current product's data into that hash
            outputProductsData = inputProductsData[p];
            // Get SKU Array from current Product
            var skuIDsArray = inputProductsData[p].sku;
            // Create new SKU array to hold SKU data objects
            var outputSkuArray = [];
            // Iterate through SKU ID Array
            for (var s=0, len=skuIDsArray.length; s<len; s++) {
                // Create a hash that will store one SKU
                var outputSkuObject = {};
                // use current SKU ID to retrieve data from the original skuData object
                // Copy the current SKU's data into that hash
                outputSkuObject = inputSkusData[skuIDsArray[s]];
                // insert that hash into the new SKU array
                outputSkuArray.push(outputSkuObject);
            // End Iterate
            }
            // replace value of current Product's sku field with the new SKU Array
            outputProductsData.sku = outputSkuArray;
            // insert the new Product Hash into the merged data Array
            mergedProductDataArray.push(outputProductsData);
        // End Iterate
        }
    }
    // return the merged data array
    return mergedProductDataArray;
};
/**
 * Create and return a string that displays the range of prices for the passed product data.
 * If there is only one unique price among the product's SKUs, return that price alone.
 * @param {Object} prod merged product data object
 * @see bb.productData.mergeSkusIntoProducts
 * @methodof bb.productData
 */
bb.productData.formatPriceRange = function(prod) {
    var price_string = '';
    if (bb.validateArray(prod.sku)) {
        var prod_skus_length = prod.sku.length;
        var uniquePricesCount = prod.sku.pluck('PRODUCT_PRICE').uniq().length;
        if (uniquePricesCount > 1)  {
            var sortedSkus = prod.sku.sortBy(function(s){return s.PRODUCT_PRICE;});
            price_string = sortedSkus[0].FORMATTED_PRICE;
            price_string += " - " + sortedSkus[prod_skus_length-1].FORMATTED_PRICE;
        } else {
            price_string = prod.sku[0].FORMATTED_PRICE;
        }
        return price_string;
    } else {
        return '';
    }
};

/**
 * Create and return a URL that links to a specifc single product page.
 * @param {String} productID value of the PRODUCT_ID field for the given product.
 * @param {String} categoryID category whose MPP will be linked to in the SPP breadcrumb.
 * @return {String} URL for the SPP
 * @methodof bb.productData
 */
bb.productData.convertProductURL = function(oldURL) {
    var queryStringPosition = oldURL.indexOf("?");
    var newURL = "/templates/products/spp/index.tmpl";
    newURL += oldURL.substring(queryStringPosition);
    return newURL;
};

bb.productData.truncateDescription = function(description, productURL) {
//    var trunactedDescription = description.stripTags();
    if (description) {
        var trunactedDescription = description;
        var charPositionToBreak = trunactedDescription.indexOf(" ", 165);
        if (charPositionToBreak > -1) {
            trunactedDescription = trunactedDescription.substring(0, charPositionToBreak);
            trunactedDescription += "...";
            if (productURL && productURL.length > 0) {
                // add link to SPP
                var linkString = " <a href='" + productURL + "'><em>";
                linkString += "more</em></a>";
                trunactedDescription += linkString;
            }
        }
        return trunactedDescription;
    }
}

/**
 * This function validates that its parameter is an Array with at least one member.
 * @param {Array} arr a javscript or prototype array
 */
bb.validateArray = function(arr) {
    return (arr && Object.isArray(arr) && arr.length > 0);
};


/**
 * This class toggles the visibility of two nodes. It's generally used for displaying
 * "loading" messages.
 */
bb.Progress = function (args) {
    var progressNode = null;
    var containerNode = null;
    var progressObj = {};
    progressObj = Object.extend(progressObj,args);
    progressObj.start = function() {
        if ((typeof progressObj.containerNode !== "undefined") &&
            (typeof progressObj.progressNode !== "undefined")) {
            progressObj.containerNode.hide();
            progressObj.progressNode.show();
        }
    };
    progressObj.revert = function() {
        if ((typeof progressObj.containerNode !== "undefined") &&
            (typeof progressObj.progressNode !== "undefined")) {
            progressObj.containerNode.show();
            progressObj.progressNode.hide();
        }
    };
    return progressObj;
};



bb.UIMessage = Class.create({
    REGEX_SYNTAX: /(^|.|\r|\n)(::(\w+)::)/,
    initialize: function(args) {
        var self = this;
        this.options = Object.extend({
            messageKeys: [],
            callback: function () {}
        }, args || {});
        bb.JSONRPC.fetch({
            method : 'javascript.uimessages',
            params : this.options.messageKeys,
            onSuccess: function() {
                self._load_data();
            },
            onFailure: function () {
                console.log('UIMessage JSON failed to load.');
            }
        });
    },
    _load_data: function (t) {
        var o = t.responseText.evalJSON()[0];
        var data = o.result;
        if (o.error == null) {
            this.keys = $H(data);
        }
        this.options.callback(this);
        document.fire("messages:loaded");
    }
});

bb.sIFR={};
bb.sIFR.cmsFontSpecs = {
    "span.h1-replace" : {
        sSelector  : ".h1-replace",
        sFlashSrc  : "/media/flash/site/knockout_featherweight.swf",
        sWmode     : "opaque",
        sFlashVars : "textcolor=#000000&leading=0&offsetTop=0&offsetBottom=0"
    },
    "span.h1-replace-red" : {
        sSelector  : ".h1-replace-red",
        sFlashSrc  : "/media/flash/site/knockout_featherweight.swf",
        sWmode     : "opaque",
        sFlashVars : "textcolor=#E40E62&leading=0&offsetTop=0&offsetBottom=0"
    },
    "span.h2-replace" : {
        sSelector  : ".h2-replace",
        sFlashSrc  : "/media/flash/site/whitney_bold.swf",
        sWmode     : "opaque",
        sFlashVars : "textcolor=#000000&leading=0&offsetTop=0&offsetBottom=12"
    },
    "span.h3-replace" : {
        sSelector  : ".h3-replace",
        sFlashSrc  : "/media/flash/site/whitney_bold.swf",
        sWmode     : "opaque",
        sFlashVars : "textcolor=#000000&leading=0&offsetTop=0&offsetBottom=12"
    },
    "span.h3-replace-red" : {
        sSelector  : ".h3-replace-red",
        sFlashSrc  : "/media/flash/site/whitney_bold.swf",
        sWmode     : "opaque",
        sFlashVars : "textcolor=#E40E62&leading=0&offsetTop=-2&offsetBottom=0"
    },
    "span.h3-replace-red-offset-bottom" : {
        sSelector  : ".h3-replace-red-offset-bottom",
        sFlashSrc  : "/media/flash/site/whitney_bold.swf",
        sWmode     : "opaque",
        sFlashVars : "textcolor=#E40E62&leading=0&offsetTop=0&offsetBottom=12"
    },
    "span.h4-replace" : {
        sSelector  : ".h4-replace",
        sFlashSrc  : "/media/flash/site/knockout_featherweight.swf",
        sWmode     : "opaque",
        sFlashVars : "textcolor=#000000&leading=0&offsetTop=0&offsetBottom=0"
    },
    "span.h5-replace" : {
        sSelector  : ".h5-replace",
        sFlashSrc  : "/media/flash/site/knockout_featherweight.swf",
        sWmode     : "opaque",
        sFlashVars : "textcolor=#000000&leading=0&offsetTop=0&offsetBottom=0"
    }
};


bb.sIFR.fontSpecs = {
    "h1.replace" : {
        sSelector  : "h1.replace",
        sFlashSrc  : "/media/flash/site/knockout_featherweight.swf",
        sWmode     : "opaque",
        sFlashVars : "textcolor=#000000&leading=0&offsetTop=0&offsetBottom=0"
    },
    "h1.replace-red" : {
        sSelector  : "h1.replace-red",
        sFlashSrc  : "/media/flash/site/knockout_featherweight.swf",
        sWmode     : "opaque",
        sFlashVars : "textcolor=#E40E62&leading=0&offsetTop=0&offsetBottom=0"
    },
    "h2.replace" : {
        sSelector  : "h2.replace",
        sFlashSrc  : "/media/flash/site/whitney_bold.swf",
        sWmode     : "opaque",
        sFlashVars : "textcolor=#000000&leading=0&offsetTop=0&offsetBottom=12"
    },
    "h3.replace" : {
        sSelector  : "h3.replace",
        sFlashSrc  : "/media/flash/site/whitney_bold.swf",
        sWmode     : "opaque",
        sFlashVars : "textcolor=#000000&leading=0&offsetTop=0&offsetBottom=12"
    },
    "h3.replace-red" : {
        sSelector  : "h3.replace-red",
        sFlashSrc  : "/media/flash/site/whitney_bold.swf",
        sWmode     : "opaque",
        sFlashVars : "textcolor=#E40E62&leading=0&offsetTop=-2&offsetBottom=0"
    },
    "h3.replace-red-offset-bottom" : {
        sSelector  : "h3.replace-red-offset-bottom",
        sFlashSrc  : "/media/flash/site/whitney_bold.swf",
        sWmode     : "opaque",
        sFlashVars : "textcolor=#E40E62&leading=0&offsetTop=0&offsetBottom=12"
    },
    "h4.replace" : {
        sSelector  : ".h4-replace",
        sFlashSrc  : "/media/flash/site/knockout_featherweight.swf",
        sWmode     : "opaque",
        sFlashVars : "textcolor=#000000&leading=0&offsetTop=0&offsetBottom=0"
    },
    "h5.replace" : {
        sSelector  : "h5.replace",
        sFlashSrc  : "/media/flash/site/knockout_featherweight.swf",
        sWmode     : "opaque",
        sFlashVars : "textcolor=#000000&leading=0&offsetTop=0&offsetBottom=0"
    }
};

bb.sIFR.replaceElement = function(selector) {

    bb.sIFR.undoReplace(selector);

    var specs = bb.sIFR.fontSpecs[selector];
    if (specs && typeof sIFR == "function") {
        sIFR.replaceElement(named(specs));
    }
};
bb.sIFR.replaceAll = function() {

    if (bb.page.isCMS()) {
         bb.sIFR.fontSpecs = Object.extend(bb.sIFR.fontSpecs, bb.sIFR.cmsFontSpecs);
    }

    for (var selector in bb.sIFR.fontSpecs) {
        bb.sIFR.replaceElement(selector);
    }
};
bb.sIFR.undoReplace = function(selector) {
    var nodes = $$(selector);
    nodes.each( function(node) {
        if (node.className.indexOf("sIFR-replaced") > -1) {
            var hiddenNode = node.select("span.sIFR-alternate")[0];
            if (hiddenNode) {
                var textNode = hiddenNode.firstChild;
                node.update(textNode.nodeValue);
                node.removeClassName("sIFR-replaced");
            }
        }
    });
};
bb.page.initErrorDisplay = function(containerNode) {
    var msgContainerNode = containerNode || $$(".error_messages")[0];
    if (msgContainerNode) {
        msgContainerNode.select("ul.error").each(function(ele) {
            ele.remove();
        });
        var hide = true;
        if (bb.page.errors) {
            var ul = new Element("ul", {"class":"error"});
            bb.page.errors.pageErrors.each(function (err) {
                ul.insert({ bottom: '<li class="' + err.TYPE + '">' + err.MESSAGE + '</li>' });
                hide = false;
            });
        }
        if (hide) {
            msgContainerNode.style.display = "none";
        } else {
            msgContainerNode.insert({'top': ul});
            msgContainerNode.style.display = "block";
			//
			// display the pop-over form
			var popoverElement = msgContainerNode.up("div.popup");
			if (popoverElement) {
				bb.overlay.launchPopover(popoverElement, ["h5"]);
			}
		}
		bb.page.initOverlayLinks();
    }
};
bb.page.displayErrors = function() {
    if (bb.page.errors) {
        var msgContainer = $$(".error_messages")[0];
        if (msgContainer) {
        	var hide = true;
            var ul = msgContainer.down('ul');
            bb.page.errors.pageErrors.each(function (err) {
                ul.insert({ bottom: '<li class="' + err.TYPE + '">' + err.MESSAGE + '</li>' });
                hide = false;
            });
            if (hide) {
	            msgContainer.style.display = "none";
            } else {
	            msgContainer.style.display = "block";
				if (bb.page.submittedFormName && bb.page.submittedFormName.length && bb.page.submittedFormName.length > 0 ) {
					var formElement = $$("#" + bb.page.submittedFormName)[0];
					if (formElement) {
						var errorsContainer = formElement.select("#errors-container")[0];
						if (errorsContainer) {
							errorsContainer.insert({'bottom': msgContainer});
						} else {
							formElement.insert({'top': msgContainer});
						}
						//
						// display the pop-over form
						var popoverElement = formElement.up("div.popup");
						if (popoverElement) {
							bb.overlay.launchPopover(popoverElement);
						}
					}
				}
	        }
        }
    }
};

/*
	* Sets the global nav on state of a category by comparing the category names from the category names
	* being sent on the mpp or spp pages.  The global category is being set in perl /templates/includes/subs/nav
*/
bb.page.setGlobalCat = function(pageCat) {
	var catID = '';
	// Find the correct category
	switch (pageCat) {
		case "What's New":
			catID="#whatsnew-nav";
			break;
		case "Makeup":
			catID="#makeup-nav";
			break;
		case "Skincare":
			catID="#skincare-nav";
			break;
		case "Brushes etc.":
			catID="#brushes_etc-nav";
			break;
		case "Brushes etc.":
			catID="#brushes_etc-nav";
			break;
		case "Gifts":
			catID="#gifts-nav";
			break;
		case "Learn":
			catID="#learn-nav";
			break;
		case "Tools and Accessories":
			catID="#learn-nav";
			break;
	}
	// Adds class to the proper gnav element
	var categoryRegState = $$(catID + ' .reg_state')[0];
	var categoryOnState = $$(catID + ' .on_state')[0];
	categoryRegState.setStyle({'visibility':'hidden'});
	categoryOnState.setStyle({'visibility':'visible'});	
}

/* Looks at URL to see if page is in learn or bobbi buzz and turns on
the gnav if either is true.*/

bb.page.setCMSNav = function() {
	/* each cms directory section is added here */
	var sectionArray = ["learn","bobbi_buzz"];
	
	/* cycles through array to see if URL matches.  if match is made, that section will be turned in gnav */	
	sectionArray.each(function(ele) {
		var sectionExp = new RegExp(ele);		
		var sectionNav = $$("#" + ele + '-nav .reg_state')[0];
		var sectionNavOn = $$("#" + ele + '-nav .on_state')[0];
		if(sectionExp.test(location.href)) {
			sectionNav.setStyle({'visibility':'hidden'});
			sectionNavOn.setStyle({'visibility':'visible'});
		};
	});
}

bb.page.getSwatchNode = function(hexValue, imgPath) {
    var swatchImagePath = imgPath || "/images/products/swatch_2.png";
    // convenience function that prepends & appends text for background-image CSS property
    var bgImgAttr = function(path) {
        return "url('" + path + "')";
    };
    var swatchStyle = {
        height: "19px",
        width : "20px",
        backgroundColor : hexValue
    };
    if (/MSIE (\d+\.\d+)/.test(navigator.userAgent) && parseFloat(RegExp.$1) < 7) {
        swatchStyle.filter = "progid:dximagetransform.Microsoft.AlphaImageLoader(src='" +
                          swatchImagePath + "', sizingMethod='image')";
        swatchStyle.backgroundImage = "none";
    } else {
        swatchStyle.backgroundImage = bgImgAttr(swatchImagePath);
    }
    var imgDiv = new Element("div");
    imgDiv.setStyle(swatchStyle);
    return imgDiv;
};

bb.page.replaceSelects = function() {
    try {
        initCustomForms();
    } catch(e) {
        console.log(e);
    }
};


logTime = function(fn, fnName) {
//    fnName = fnName.substr('function '.length);        // trim off "function "
//    fnName = fnName.substr(0, fnName.indexOf('('));        // trim off everything after the function name

    var d1 = new Date();
    var t1 = d1.getTime();

    fn();

    var d2 = new Date();
    var t2 = d2.getTime();
 
    console.log(fnName);
    console.log(t2-t1);

}

/* add page events */
//$(document).ready(function() {
document.observe("dom:loaded", function() {

//     logTime(bb.page.initSubnav, "bb.page.initSubnav");
//     logTime(bb.page.initPromoLinks, "bb.page.initPromoLinks");
//     logTime(bb.page.initEmailSignup, "bb.page.initEmailSignup");
//     logTime(bb.page.initGnav, "bb.page.initGnav");
//     logTime(bb.sIFR.replaceAll, "bb.sIFR.replaceAll");
//     logTime(bb.page.replaceSelects, "bb.page.replaceSelects");
//    logTime(bb.page.initOverlayLinks, "bb.page.initOverlayLinks");
//    logTime(bb.cart.popupView.init, "bb.cart.popupView.init");

  bb.page.initSubnav();
  bb.page.initPromoLinks();
  bb.page.initEmailSignup();
  bb.page.initSearch();
  bb.page.initGnav();
  bb.page.setCMSNav();
  bb.sIFR.replaceAll();
  bb.page.replaceSelects();
  bb.page.initOverlayLinks();
  user = new bb.User();
  bb.cart.popupView.init();
  bb.page.initQuickshopLinks();

});

bb.page.overlayLinks = bb.page.overlayLinks || {};
bb.page.initOverlayLinks = function() {
    var linksToModify = $$("a.overlay-link");
    linksToModify.each( function(link) {

        if (link.hasClassName("overlay-ready")) {
            return;
        } else {

            var styleObj = {display:"none"};
            var widthRegexResults = link.className.match(/overlay_width_(\d+)/);
            if (widthRegexResults) {
                styleObj.width = widthRegexResults[1] + "px";                
            }
            var heightRegexResults = link.className.match(/overlay_height_(\d+)/);
            if (heightRegexResults) {
                styleObj.height = heightRegexResults[1] + "px";                
            }

            var containerDiv = new Element("div", {"class":"popup fixed-size-overlay"} );
            containerDiv.setStyle(styleObj);
            document.body.appendChild(containerDiv);
            bb.page.overlayLinks[link] = containerDiv;
    
            var insertCloseLink = function(containerEle) {
                var closeLink = new Element("a", {"class": "close"});
                closeLink.insert("close");
                var closeDiv = new Element("div", {"class": "holder"});
                closeDiv.insert(closeLink);
        
                containerEle.insert({"top": closeDiv});
        
                closeLink.observe("click", function (closeClickEvt) {
                    bb.overlay.hide();
                    closeClickEvt.preventDefault();
                })
            };

            var req = new Ajax.Request(link.href,
            {
                method:'get',
                onSuccess: function(transport) {
                    var response = transport.responseText || "no response text";
                    containerDiv.update(response);
                    insertCloseLink(containerDiv);
                },
                onFailure: function(){
                    var errMsg = "Error loading " + link.href
                    console.log(errMsg);
                    containerDiv.update(errMsg);
                    insertCloseLink(containerDiv);
                }
            });
            // check for height/width classes
    
            link.observe("click", function(clickEvt) {
                clickEvt.preventDefault();
                bb.overlay.launchPopover(containerDiv);
            });        
    
            link.addClassName("overlay-ready");
        }
    });
};


var elc = {};
elc.goToURL = function (url) {
    if (url !== null && url !== '') {
        self.location.href = wsmlMakeWebServiceHref(url);
    }
};

bb.popWindow_noscroll = function (w,h,x,y) {
var subwindow_noscroll = window.open('','subWindow_noscroll_name','toolbar=no,menubar=no,location=no,scrollbars=no,width='+w+',height='+h+',left='+x+',top='+y);
subwindow_noscroll.resizeTo(w,h);
subwindow_noscroll.moveTo(x,y);
subwindow_noscroll.focus();
};

/**
 * Extends prototype's Element object 
 */
Element.addMethods({	
	
	// Return the larger of the styled height or prototype's computed height.
	// Under quirky circumstances, they don't always match.
	getComputedHeight: function(element) {
		var element = $(element);
		var styleHeight = parseInt(element.style.height);
		var protoHeight = element.getHeight();
		return ( styleHeight > protoHeight ? styleHeight : protoHeight );
	},
	
	// Return the height of the element plus the offset.
	// This will represent the "bottom" value of this element,
	// which is useful for deriving the actual height of the parent object.
	getOffsetHeight: function(element) {
		var element = $(element);
		var ht = element.getComputedHeight();
		var off = element.positionedOffset();
		return ht + off["top"];
	},
	
	// Set the height of the current element to the cumulative offset of all children.
	// This is to simplify fixing divs containing positioned content (e.g. from CMS...)
	fixContentHeight: function(element) {
		var element = $(element);
        var h = 0;
        
        element.descendants().each( function(elem) {
        	var el = $(elem);
        	// Apparently script tags have a height in IE....
        	if ( el.tagName != 'SCRIPT' ) {
	        	var ch = el.getOffsetHeight();
	        	if ( ch > h ) {
	        		h = ch;
	        	}
	        }
        });
        
        element.style.height = h+'px';
	}
	
});



var jsonRPC = Class.create({
	initialize: function () {
		var PRODUCT_JSON_URL = "/jsonrpc.json";
		this.id = 0;
		this.url = URL_DOMAIN + PRODUCT_JSON_URL;
	},
	fetch: function ( method, params, options ) {
		this.id++;
		this.options = Object.extend({
			onSuccess: function (r) {
				alert('success');
				alert(r.responseText);
			}, 
			onFailure: function (r) {
				alert('failure');
				alert(r.responseText);
			}	
		}, options || {});		
	 	var postobj = {
	 		method: method,
	 		id: this.id
	 	};
		if (typeof params != 'object') {
			postobj.params = params.evalJSON();
		} else {
			postobj.params = params;
		}
		this.options.method = 'post';
		poststring = (Object.toJSON(postobj))
		this.options.parameters = 'JSONRPC=[' + poststring + ']';
		
		// WDR: adding the method to the query string so we can see it in the apache logs.
		var targetUrl = this.url + '?dbgmethod=' + method;
		new Ajax.Request( targetUrl, this.options );
		//new Ajax.Request( this.url, this.options );
		
		return this.id;	
	}, 
	test : function () {
		alert('test');
	}
});

jsonrpc = new jsonRPC();

var Resource = Class.create({
	REGEX_SYNTAX: /(^|.|\r|\n)(::(\w+)::)/,
	initialize: function(options) { 
		this.options = Object.extend({
			CONFIG_LIST: [],
			LIMIT: [],
			CALLBACK: function () {}
		}, options); 
		var id = jsonrpc.fetch( 'javascript.resource', 
			[this.options],
			{ 
				onSuccess: this._load_data.bind(this),
				onFailure: function () { console.log( 'Resource JSON failed to load ' ) }
			}
		); 
		return id;
		
	},
	_load_data: function (t) {
		var o = t.responseText.evalJSON()[0];
		var data = o.result;
	 
		if (o.error == null) {
			this.keys = $H(data);
		}
		this.options.CALLBACK(this);
		document.fire("resources:loaded");
	}
});


	Template.prototype.evaluate = Template.prototype.evaluate.wrap(
function (fnEval,obj) {
	if ( Object.isArray(obj) 
		&& (typeof obj[0] == 'object') )
    {
		var array = $A(obj);
		var results = '';
		array.each( function (obj) {
			results += fnEval(obj);
		});
		return results;		
	} else {
		return fnEval(obj);
	}
});

var RediTemplate = Class.create( Template, {
	initialize: function ( template, pattern ) {
		this.template = template?template:'';
		this.readyState = template?1:0;
		this.pattern = pattern?pattern:Template.Pattern;
		
		this.queue = new Array();		
		return;
	},
	load: function(template) {
	    this.template = template.toString();
		this.readyState = 1;
		this.onReadyState();
	},
	_preprocess: function (object) {
		return object;
	},
	evaluateCallback: function (options) {
	    this.options = {
	      object:       {},
	      callback: 	function () {}
	    };
	    Object.extend(this.options, options || { });
		object = this._preprocess(this.options.object);
		if (this.readyState) {
			this.options.callback(this.evaluate(this.options.object));
		} else {
			this.queue.push({
				qtype: 'callback',
				obj: this.options.object,
				fnc: this.options.callback
			});
		}
		return;		
	},
	evaluateElement: function (element, options) {
		var element = $(element);
	    this.options = {
	      object:       {},
		  position: 'bottom',
	      callback: 	function () {}
	    };
		var elm;
		Object.extend(this.options, options || { });
		object = this._preprocess(this.options.object);
		if (this.readyState) {
			if (this.options.position == 'replace') {
				elm = element.removeAllContents().insert( {top: this.evaluate(this.options.object)} );
			} else {
				var position = {};
				position[this.options.position] = this.evaluate(this.options.object);
				elm = element.insert( position );
			}
			this.options.callback.delay(.01, elm);
		} else {
			this.queue.push({
				qtype: 'insert',
				elm: element,
				pos: this.options.position,
				obj: this.options.object,
				fnc: this.options.callback
			});
		}
		return;
	},
	onReadyState: function () {
		while (q = this.queue.shift()) {
			var object = q.obj;
			var qtype = q.qtype;
			var callback = q.fnc;
			var elm;
			switch (qtype) {
			case 'insert':
				var element = q.elm;
				var position = {};
				if (q.pos == "replace") {
					position['top'] = this.evaluate(q.obj);
					elm = element.removeAllContents().insert( position );					
				} else {
					position[q.pos] = this.evaluate(q.obj);
					elm = element.insert( position );					
				}
				callback.delay(.01, elm);
				break;    
			case 'callback':
				callback(this.evaluate(object));
				break;
			}		
		}
	}
});	

var TemplateFactory = Class.create( Hash, {
    dir: "/js/html-templates/",

	get: function (key, bRefresh) {
		if (typeof this._object[key] != "undefined" && !bRefresh) {
			return this._object[key];
		}
		this._object[key] = new RediTemplate();
		var filename = key + '.tmpl'	
		var url = this.dir + filename;
		var tAjax = new Ajax.Request(url, {
		    method: 'get',
		    onSuccess: function(transport) {
		        this._object[key].load(transport.responseText);
		    }.bind(this)
		});
		return this._object[key];	
	}	
});
templatefactory = new TemplateFactory();

Element.addMethods({	
	fillin : function (element, options){
	    this.options = {
		  template: null,
	      object: {},
	      position: 'bottom',
	      callback: function () {}
	    };
	    Object.extend(this.options, options || { });		
		this.options.template.evaluateElement(element, this.options);
	},
	removeAllContents: function(element) {
		element = $(element);
		if (element.childElements().length) {
			element.childElements().each(function(el){
				el.removeAllContents();
				try{
					el.remove();
				} catch (e) { /* ignore this - 'too few args' error */ }
				
			});
		}
		if (element.innerHTML) {
			element.innerHTML = '';
		}
		return element;
	},
	// This function converts multiple <br>'s to a single one
	// (thus collapsing blank lines).
	collapseBlankLines: function(element) {
		var element = $(element);
		var html = element.innerHTML;
		var re = /<br[^>]*>\s*<br[^>]*>/g;
		while ( html.match(re) ) {
			html = html.replace(re,'<br>');
		}
		element.innerHTML = html;
	}
	
});

bb.getQueryStringValue = function(keyToFind) {
    var returnVal = '';
    var queryString = window.location.search;
    if (queryString.length > 4) {
        var allPairs = queryString.substring(1, queryString.length);
        var keyValuePairStrings = allPairs.split("&");
        var keyValuePairs = {};
        keyValuePairStrings.each( function(pairString) {
            var pair = pairString.split("=");
            keyValuePairs[unescape(pair[0])] = unescape(pair[1]);
        });
        returnVal = keyValuePairs[keyToFind] || '';
    }
    return returnVal;
};






/**
 * This global method is lifted from the legacy site. Calls to this method
 * are written inline into HTML that is stored on the Database.
 * Please *DO NOT* call this method unless you are this legacy code.
 */
var popWindow = function(w,h,x,y) {
    var subwindow = window.open('','subWindow_name','toolbar=no,menubar=no,location=no,scrollbars=yes,width='+w+',height='+h+',left='+x+',top='+y);
    subwindow.resizeTo(w,h);
    subwindow.moveTo(x,y);
    subwindow.focus();
};


bb.GenericButton = Class.create({
	initialize: function (element, options) {
		this.element = $(element);
		if (!this.element) return;
		this.options = Object.extend({
		}, options || {});
		var self = this;
		$H(this.options).each( function (opt) {
			self.element.observe(opt.key, opt.value);
		});
		return this.element;
	}
	
});

//==== USER =======

bb.User = Class.create({
	first_name: '',
	last_name:  '',
	signed_in: false,
	signed_in_insecure_user: false,
	recognized_user: false,
	skin_typed: 0,
	email_optin: false,
	gwp_optin: false,
	took_skin_quiz: false,
	email_address: '',
		
	initialize: function() {
	    var self = this;
		bb.JSONRPC.fetch({
		    method : 'user.current', 
			params :[],
            onSuccess: function(response) {
                self._load_data(response);
            },
            onFailure: function () {
                console.log( 'User JSON failed to load ' )
            }
		});
	},
	_load_data: function (t) {
		var o = t.responseText.evalJSON()[0];
		var data = o.result;
		if (o.error == null) {
			Object.extend ( this, data );
		}
		document.fire("user:loaded", data);
	},
	// Return true if EITHER signed in flag is set.
	isSignedIn: function() {
		return ( this.signed_in || this.signed_in_insecure_user ? true : false );
	}
});

bb.FlyoutPane = function(args) {

};

document.observe('user:loaded', function () {
//    console.log('user:loaded');
    var headerEle = $('header');
    var utitlLinksContainerNode = headerEle.select("ul.utilitynav")[0];
    if (headerEle && utitlLinksContainerNode) {
		if (user.recognized_user && user.registered_user){
			utitlLinksContainerNode.insert({top: '<li><a href="/templates/user/account/myaccount.tmpl">My Account</a></li>'});
			utitlLinksContainerNode.insert({top: '<li class="spacer"><img src="/images/icons/spacer.gif"></li>'});
			utitlLinksContainerNode.insert({top: '<li>Hi <strong>#{first_name}</strong> | <a id="signout-link" href="/signout/index.tmpl">Sign Out</a></li>'.interpolate(user)});
		} else {
			utitlLinksContainerNode.insert({top: '<li><a id="signin-link" href="/templates/user/account/signin.tmpl" manual_cm_sp="Gnav-_-SignIn-_-Main" >Sign In/My Account</a></li>'});		
		}

        // sign-in popup
        var signinContainer = headerEle.select("#signin-form-container")[0];
        var signinLink = headerEle.select("#signin-link")[0];
        if (signinLink && signinContainer) {
            var leftOffsetValue = signinLink.getWidth() - signinContainer.getWidth() + 50;
            signinContainer.clonePosition(signinLink, {offsetTop: 30, offsetLeft: leftOffsetValue, setWidth: false, setHeight: false} );

            var mouseoutTimeout = null;
            var hideSignin = function() {            
                signinContainer.style.display = "none";
            };
            var showSignin = function() {
    			if (mouseoutTimeout) {
    				clearTimeout(mouseoutTimeout);
    			}
    			signinContainer.style.display = "block";
    		};
            signinLink.observe("mouseover", function(evt) {
                showSignin();
            });
            signinContainer.observe("mouseover", function(evt) {
                showSignin();
            });            
            var submitBtn = signinContainer.select("a#signin-submit-button")[0];
            console.log(submitBtn);
            submitBtn.observe("click", function(evt) {
                var signinEmail = signinContainer.select("input#signin-email")[0].value;
                var signinPW = signinContainer.select("input#signin-password")[0].value;
                if (signinEmail.length > 0 && signinPW > 0) {
                    console.log(signinEmail, signinPW);
                }
                    var ajaxOptions = {};
                    ajaxOptions.method = 'post';
                    ajaxOptions.parameters = {
                        EMAIL_ADDRESS : signinEmail,
                        ERROR_URL : "/modrpc.tmpl",
                        INTERNAL_REDIRECT_TARGET : "/modrpc.tmpl",
                        PASSWORD : signinPW
                    };
                    var requestURL = "/signin/modrpc.tmpl";
                    ajaxOptions.onSuccess = function(r) {
                        console.log(r);
                    };
                    var ajaxReq = new Ajax.Request( requestURL, ajaxOptions );
            });
        }
    }



});

//document.observe('dom:loaded', function () {
//	user = new bb.User();
//  cart = new Cart();	
//});

//===== CART =========

/**
 * CartButton is the base class for clickable elements that change the contents of the shopping cart.
 */
bb.CartButton = function(args) {
    var btnObj = {
        domNode: null,   
    	progressNode: null,
        progress: null,
        onSuccess: function() {},
        onFailure: function() {},
        cartMethod: "",
        cartArgs: {}
    };
    Object.extend(btnObj, args || {});
    btnObj.cartArgs.onSuccess = function () {
        if (btnObj.progress) {
    		btnObj.progress.revert();
    	}
		console.log("cart button success");
		args.onSuccess();
    };
	btnObj.cartArgs.onFailure = function () { 
	    if (btnObj.progress) {
			btnObj.progress.revert();
		}
		console.log("cart button failure");
		args.onFailure();
	};
    var _initProgress = function(btn) {
        if ($(btn.progressNode) && $(btn.domNode)) {
			btn.progress = bb.Progress({
				"progressNode": btn.progressNode, 
				"containerNode": btn.domNode
			});
		}
    };
    var _initEvent = function(btn) {
        if ($(btn.domNode)) {
            Event.observe(btn.domNode, 'click', function(evt) {
                _onClick(btn);
                evt.preventDefault();
            });
        }
    };
    var _onClick = function(btn) {
        console.log(enabled);
        if (btn.progress) {
            btn.progress.start();
        }
        console.log(btn.cartArgs);
        var cart_id = bb.cart[btn.cartMethod](btn.cartArgs);
    };
    _initProgress(btnObj);
    _initEvent(btnObj);
    var enabled = true;
    btnObj.setCartMethod = function(m) {
        if (typeof m === 'string' && m.length > 0) {
            this.cartMethod = m;
        }
    };
    btnObj.setCartArgs = function(mArgs) {
        Object.extend(this.cartArgs, mArgs || {});
    };
    btnObj.disable = function() {
        if (enabled) {
            this.domNode.stopObserving('click');
            Event.observe(this.domNode, 'click', function(evt) {
                evt.preventDefault();
            });
            enabled = false;
        }
    };
    btnObj.enable = function() {
        if (!enabled) {
            _initEvent(this);
            enabled = true;
        }
    };
    return btnObj;
};
//
 
bb.CartButton.Add = function(args) {
    var options = {
        cartMethod: 'add',
        cartArgs: {
            "increment" : true,		
    		"qty" : 1
        }
    };
    options.domNode = args.domNode;
    options.progressNode = args.progressNode;
    options.onSuccess = args.onSuccess;
    options.onFailure = args.onFailure;
    options.cartArgs.skuID = args.skuID;
    options.cartArgs.shoppingID = args.shoppingID;

    var btnObj = bb.CartButton(options);
    return btnObj;
};
 
 
 
bb.CartButton.Remove = function(args) {
    defaultArgs = {
        "domNode" : "",
        "progressNode" : "",
        cartMethod: 'remove',
        cartArgs: {
            "sku_id" : ""
        }
    };
    Object.extend(defaultArgs, args || {});
    var btnObj = bb.CartButton(defaultArgs);
    return btnObj;
};

bb.cart = function() {
    return {
        add : function(args) {
            
            if (bb.page.categoryID){
                var catid = bb.page.categoryID;
            }
            var jsonrpcArgs = {
                method : "cart.setqty",
                params : [{
                    "sku_id"      : args.skuID,
                    "shopping_id" : args.shoppingID,
                    "qty"         : args.qty,
                    "increment"   : 0,
                    "category_id"  : catid 
                }],
                onSuccess: function(response) {
                    var responseObj = response.responseText.evalJSON(true)
                    if (responseObj[0].error === null) {
                        console.log("cart success ", responseObj[0].result);
                         document.fire("cart:modify",responseObj[0],catid);
                         console.log("fire modify");
                    } else {
                        console.log("cart success w/error " + responseObj[0].error);
                    }
                    args.onSuccess();
                },
                onFailure: function(response) {
                    console.log("cart fail " + response);
                    args.onFailure();
                }
            };
            bb.JSONRPC.fetch(jsonrpcArgs); 
        }
    };
}();


        //
        // cart popup
bb.cart.popupView = function() {
    return {
        cartviewContainer : null,
        cartviewLink : null,
        display : function(args) {
            if (cartviewContainer) {
                args = args || { };
                bb.templateFactory.getTmpl(
                    "cart-popup-content", { SKU_ID: args.skuID }
                ).evaluateCallback({
                    callback: function(html) {
                        cartviewContainer.down('#cart-popup-content').innerHTML = html;
                        this._displaySwatch();
                        setTimeout('cartviewContainer.hide()', 15000);
                        cartviewContainer.show();
                    }.bind(this)
                });
            }
        },
        _displaySwatch : function() {
            var content = $('cart-popup-content');
            var swatchHolder = $('cart-popup-content').down('.swatches');
            if (!swatchHolder) { return; }
            
            var hexText = swatchHolder.down('li').innerHTML;
            var hexVals = hexText.strip().split(',');

            var listItemNode = new Element("li");

            var aNode = new Element("a");
            var swatchNode = bb.page.getSwatchNode('#'+hexVals[0]);
            aNode.update(swatchNode);
            listItemNode.update(aNode);
            swatchNode.style.backgroundColor = '#'+hexVals[0];
            
            swatchHolder.innerHTML = '';
            swatchHolder.insert(listItemNode);
        },
        hide : function() {
            if (cartviewContainer) {
                cartviewContainer.hide();
            }
        } ,
        init: function() {

            var headerEle = $('header');

            cartviewContainer = headerEle.select("div#shopping_bag_overlay")[0];
            cartviewLink = headerEle.select("a#view_shopping_bag")[0];
            if (cartviewLink && cartviewContainer) {
                var leftOffsetValue = cartviewLink.getWidth() - cartviewContainer.getWidth() + 35;
    
                cartviewContainer.clonePosition(cartviewLink, {offsetTop: 30, offsetLeft: leftOffsetValue, setWidth: false, setHeight: false} );
    
                cartviewLink.observe("click", function() {
                    //bb.cart.popupView.display();
                    document.location.href = '/templates/session/viewbag.tmpl';
                });
                var closeLink = cartviewContainer.select("a.utilitynav-popup-close")[0];
                closeLink.observe("click", function(evt) {
                    bb.cart.popupView.hide();
                });
                document.observe("cart:add:success", function(evt) {
                    bb.cart.popupView.display({
                        skuID: evt.memo.skuID
                    });
                });
            }
        }
    };
}();

//document.observe('dom:loaded', function () {
//    bb.cart.popupView.init();
//});

//========== LIGHTERBOX ==========

bb.Lighterbox = Class.create({
    initialize: function (args) {
//        console.log(args);
        var self = this;
        // make sure we have the prerequestite background
        this.backgroundNode = $('lighterwindow_background');
        if (!this.backgroundNode) {
            this.backgroundNode = new Element('div', {"class":"lighterwindow_background"});
            this.backgroundNode.setStyle({display: "none"});
            $(document.body).insert(this.backgroundNode);
        }
        this.setPageContainerNode($(document.body));
        if (args) {
            if (args.foregroundNode) {
                this.setForegroundNode(args.foregroundNode);
            }
            if (args.openButtonNode) {
                this.setOpenButtonNode(args.openButtonNode);
            }
            if (args.closeButtonNode) {
                this.setCloseButtonNode(args.closeButtonNode);
            }
            if (args.pageContainerNode) {
                this.setPageContainerNode(args.pageContainerNode);
//                console.log(this.pageContainerNode);
            }
        }
    },
    setOpenButtonNode: function (ele) {
        var self = this;
        if (Object.isElement(ele)) {
            this.openButtonNode = ele;
            this.openButtonNode.observe( 'click', function(evt) {
                self.show();
                evt.preventDefault();
            });
        }
    },
    setCloseButtonNode: function (ele) {
        var self = this;
        if (Object.isElement(ele)) {
            this.closeButtonNode = ele;
            this.closeButtonNode.observe( 'click', function(evt) {
                self.hide();
                evt.preventDefault();
            });
        }
    },
    setForegroundNode: function(ele) {
        if (Object.isElement(ele)) {
            this.foregroundNode = ele;
        }
    },
    setPageContainerNode: function(ele) {
        if (Object.isElement(ele)) {
            this.pageContainerNode = ele;
        }
    },
    show: function () {
        var self = this;
        this.fitBackgroundToPage();
        this.backgroundNode.show();
        this.centerForegroundFn = function() {
            self.centerForeground();
        };
        Event.observe( window, 'resize', self.centerForegroundFn );
        Event.observe( window, 'scroll', self.centerForegroundFn );
        this.foregroundNode.show();
        this.centerForeground();
    },
    hide: function () {
        this.foregroundNode.hide();
        this.backgroundNode.hide();
    },
    centerForeground : function() {
        var self = this;
        if (this.foregroundNode) {
            var contentDimensions = this.foregroundNode.getDimensions();
            this.foregroundNode.style.position = "absolute";
            var windowScrollOffsets = document.viewport.getScrollOffsets();
            var windowDimensions = document.viewport.getDimensions();
            var yPosition;
            if (windowDimensions.height < contentDimensions.height) {
                yPosition = 0;
                Event.stopObserving( window, 'scroll', self.centerForegroundFn );
            } else {
                yPosition = (windowDimensions.height/2) - (contentDimensions.height/2) + (windowScrollOffsets.top);
            }
            this.foregroundNode.style.top = yPosition + "px";
            var xPosition = (windowDimensions.width/2) - (contentDimensions.width/2) + (windowScrollOffsets.left);
            this.foregroundNode.style.left = xPosition + "px";
        }
    },
    fitBackgroundToPage : function() {
        if (this.backgroundNode && this.pageContainerNode) {
            var docsize = this.pageContainerNode.getDimensions();
            this.backgroundNode.setStyle({
                height: docsize.height+'px',
                width: docsize.width+'px'
            });
        }
    }
});

/**
 * @class This singleton class is a wrapper for a pop-over module.
 * It supports one visible window at a time.
 */
bb.overlay = function() {
    var win = null;
    var containerNode = null;
    var isVisible = false;
    var options = {};
    return {
        /**
         * This function displays a pop-over window. If a pop-over is already showing, the
         * launch() function will do nothing.
         * @param {Object} args.cssStyle Hash of CSS style definitions for the window.
         * Uses JS notation (i.e., "marginLeft").
         * @param {string|Node} args.content HTML, node, or text that will display in the window.
         */
        launch : function(args) {
            var self = this;
            if (!isVisible) {

                if (!containerNode) {
                    containerNode = new Element('div', {id: "overlay-container", style:"display:none"});
                    $(document.body).insert(containerNode);
                }
                if (win === null) {
                    win = new bb.Lighterbox({
                        pageContainerNode : $$('.page_wrapper')[0]
                    });
                }
                Object.extend(options, args || {});
                containerNode.setStyle(options.cssStyle);
                while (containerNode.childNodes[0]) {
                    containerNode.removeChild(containerNode.childNodes[0]);
                }
                containerNode.appendChild(options.content);
                win.setForegroundNode(containerNode);
                if (options.closeButtonNode && Object.isElement(options.closeButtonNode)) {
                    options.closeButtonNode.observe( 'click', function(evt) {
                        self.hide();
                        evt.preventDefault();
                    });
                }
                win.show();
                isVisible = true;
            }
        },
        /**
         * This function "closes" the pop-over window. It completely removes
         * the contents of the window from the DOM.
         */
        hide: function() {
            if (win !== null) {
                win.hide();
                isVisible = false;
                 var menus = $$('select.overlay-hidden');
                 menus.each(function(slct) {
                     slct.removeClassName("overlay-hidden");
                 });                
            }
        }
    };
}();

/**
 * This function launches an overlay with the passed Element as its content.
 * @see bb.overlay
 */
bb.overlay.launchPopover = function(/* Element */ popoverContentElement, /* Array */ headlineSelectors) {
    if (Object.isElement(popoverContentElement)) {
        var menus = $$('select');
        menus.each(function(slct) {
             if (!(slct.style.display === 'none' || slct.style.display === 'hidden')) {
                 if (!slct) return;
                 slct.addClassName("overlay-hidden");
             }
        });
        console.log(popoverContentElement);
        console.log(popoverContentElement.style);
        popoverContentElement.style.display = "block";
        bb.overlay.launch({ content : popoverContentElement });
        //var startDrag = new Draggable(popoverContentElement, {starteffect: false, endeffect: false});
        bb.sIFR.replaceAll();
        bb.page.replaceSelects();
    }
    
    if (headlineSelectors) {
        headlineSelectors.each( function(slctr) {
            popoverContentElement.select(slctr).each( function(ele) {
                ele.addClassName("replace");
            });            
        });
        bb.sIFR.replaceAll();
    }

};

bb.page.initQuickshopLinks = function() {
    // link class name must include cat ID & prod ID like this:
    // class="quickshop_CATEGORY22770PROD15738"
    var linksToModify = $$('a([class^=quickshop_])')
    var re = /quickshop_(\w+)/;
    linksToModify.each( function(link) {
        var reResults = re.exec(link.className);
        if (reResults.length > 1) {
            var productID = reResults[1];
            if (productID.length > 0) {
                var callbackFn = function(mergedProductData, rawProductData) {
                    console.log("dataload callback");
                    console.log(mergedProductData, rawProductData);
                    if (bb.page.productView &&
                            bb.page.productView.launchQuickshop && 
                            Object.isArray(mergedProductData) ) {
                        link.observe("click", function(e) {
                            e.preventDefault();
                            bb.page.productView.launchQuickshop(mergedProductData[0]);
                        })
                    }
                };
                var fetchDataArgs = {
                    productID : productID,
                    callback : callbackFn
                };
                if (bb.page.productView && bb.page.productView.fetchData) {
                    bb.page.productView.fetchData(fetchDataArgs);
                }
            }        
        }
    });
};

