// ################################################################################################# // Consultant Part /* Description: The Script manages how all the products, their variants and the propierties they own(like color, price, features) are displayed(enabled/disabled) and how the chart table is rendered based on the filter/selection options the user has. The selections made for a productsubgroup are stored in a session cookie, so if the user leafs the page and returns the selections will sill be the same. As you see on the page there is a chart that dynamicly changes according to the selection a user makes. This chart is dynamically rendered within this script. The productconsultant is kind of model driven. This was necessary to speed up the script. So every product/variant/property and so on, are store in arrays or objects. Changes are first made to the data model and later on commited to the view. So here is what happens basically on page initialisation: 1. Product and variant data is loaded from the db(aspx) and then registered on the javascript. 2. Based on the registered data some more data is calculated. For example the size of the chart. 3. The color and price dropdowns are filled. 4. The charttable is rendered based on the data. If the user changes the filter/selection settings: 1. Script validates products/variants and enables/disables them based on the new selection. 2. Changes are comitted to the model 3. Changes are stored to the cookie 4. Products/Variants and the chart are updated / re-rendered. For more information, call me or crawl through the code for your self :) Author: Adrian Steiner LastChange: 27.7.2007 */ // ################################################################################################# // init managers var rangeManager = new PriceRangeManager(); var criteriaManager = new CriteriaManager(); var variantColorManager = new VariantColorManager(); /* the main init methode. called once, when the page is loaded. */ function initVariantChart() { // calc the price range and pricerangestepsize rangeManager.initPriceRangeAndStep(); // init the html-dropdowns for the price selection initPriceRangeSelector(); // init the html-dropdowns for the color selection initColorSelector(); // split up the variants to their price range initVariantsPerPriceRange(); // now render the charttable initVariantChartTable(); // validate products, needed if user is a returner recheckProductCritera(); } /* set the color selector to the preselected value that was choosen by the user earlier in this session. */ function initColorSelector() { // check for the cookie selectedColor = readCookie(getCookieName("variantColor")); // nothing to init of the cookie is empty if(selectedColor == null || selectedColor == "") return; // get color selector element colorSelector = document.getElementById("color_selector"); // loop through all options to find the default selected for(key in colorSelector.options) { // set selected the stored option if(colorSelector.options[key] != null && colorSelector.options[key].value == selectedColor) colorSelector.selectedIndex = key; } } /* draws the charttable for the first time */ function initVariantChartTable() { table = new ChartTable(); table.draw(); } /* fills the two price selection drop downs with the price options. also it calculates several data we use later on. */ function initPriceRangeSelector() { // get the two selector html-elements priceRangeSelectorFrom = document.getElementsByName("criteria_price_from")[0]; priceRangeSelectorTo = document.getElementsByName("criteria_price_to")[0]; // calc the looplimit, get the max. possible product price var loopLimit = consulManager.maxProductPrice + rangeManager.priceRangeStepSize; // read the saved price range values from the cookie storedPriceFromValue = readCookie(getCookieName("price_from")); storedPriceToValue = readCookie(getCookieName("price_to")); selectedPriceFromIndex = null; selectedPriceToIndex = null; for(i=consulManager.minProductPrice; i <= loopLimit; i += rangeManager.priceRangeStepSize) { // add a new option to the drop downs priceRangeSelectorFrom.options[priceRangeSelectorFrom.length] = new Option(i + ".-", i, false, true); priceRangeSelectorTo.options[priceRangeSelectorTo.length] = new Option(i + ".-", i, false, true); if(storedPriceFromValue == i) selectedPriceFromIndex = priceRangeSelectorFrom.length - 1; if(storedPriceToValue == i) selectedPriceToIndex = priceRangeSelectorFrom.length - 1; // create a new array in the price range store rangeManager.addPriceRange(i); // store the last/highest value consulManager.highestSelectablePrice = i; } // select the initial prices if(selectedPriceFromIndex != null && selectedPriceFromIndex != "") { // select the first entry in "from" selector priceRangeSelectorFrom.selectedIndex = selectedPriceFromIndex; } else { // select the first entry in "from" selector priceRangeSelectorFrom.selectedIndex = 1; } if(selectedPriceToIndex != null && selectedPriceToIndex != "") { priceRangeSelectorTo.selectedIndex = selectedPriceToIndex; } else { // select the last entry in "to" selecter priceRangeSelectorTo.selectedIndex = priceRangeSelectorTo.length - 1; } // update the current price range consulManager.setPriceRange(); } // split up the variants into the different price ranges function initVariantsPerPriceRange() { var i = 0; // loop through all registered variants for(i=0;i < consulManager.variantPricesStore.length;i++) { // add a variant to a price range rangeManager.addVariantToPriceRange(consulManager.variantPricesStore[i]); } } /* Price Range Manager Manages the price ranges of the chart. */ function PriceRangeManager() { this.priceRangeStore = new Object(); this.priceRangeStepSize = 0; // size of the price steps (ex. 100, 200...) this.priceRangeStepsCount = 0; // howmuch steps are there? => also the cols of the chart this.realRangeCount = 0; // what? this.maxVariantsPerRange = 0; // maximum of variants that one of the ranges contains(important for chart rendering) } /* creates and adds a new price range to the manager */ PriceRangeManager.prototype.addPriceRange = function (rangeStart) { var priceRange = new PriceRange(rangeStart); this.priceRangeStore[rangeStart] = priceRange; this.realRangeCount++; } /* when a variant is registered to the conusltantmanager it will also be referenced to a price range. that's what we do here. depending on the variant's price it is assigned to the corresponding price range. */ PriceRangeManager.prototype.addVariantToPriceRange = function (variant) { varPrice = variant.price; productId = variant.productId; var priceRangeStart = null; // loop the available ranges for(key in this.priceRangeStore) { // read range from store priceRange = this.priceRangeStore[key]; // does the variant match current price range? if(varPrice >= priceRange.rangeStart && varPrice < priceRange.rangeStart + this.priceRangeStepSize) { // register the variant in price range priceRange.addVariant(variant); // double link the variant and the price range variant.priceRange = priceRange; // update the maximum variants count for price ranges, needed later to draw the chart's height this.maxVariantsPerRange = Math.max(this.maxVariantsPerRange, priceRange.variants.length); } } } // calculates height and width of the chart table // and the pricerange-stepsize and step count PriceRangeManager.prototype.initPriceRangeAndStep = function () { // calc the price range count this.priceRangeStepsCount = Math.round(consulManager.variantPricesStore.length / 2); // calc the price step size var priceRangeDifference = consulManager.maxProductPrice - consulManager.minProductPrice; var rangeStep = priceRangeDifference / this.priceRangeStepsCount; // avoid ZERO rangeSteps if(rangeStep < 50) rangeStep += 50; this.priceRangeStepSize = priceRound(rangeStep); } /* getter for price ranges. Price ranges are stored with their start price as ID */ PriceRangeManager.prototype.getPriceRange = function (rangeStart) { return this.priceRangeStore[rangeStart]; } /* PriceRange. Represents one range in the chart. For ex. 1000 - 1500.-. PriceRange has references to the variants matching it and the corresponding HTML TD elements of the chart table, so we can comfortably change the TD bachground colors. */ function PriceRange(rangeStart) { this.rangeStart = rangeStart; this.variants = new Array(); this.tdReferences = new Array(); this.activeVariants = this.variants.length; } /* adds a TD reference to this price range */ PriceRange.prototype.addVariantTableDataReference = function (chartTD) { this.tdReferences[this.tdReferences.length] = chartTD; } /* "draws" the price range in the chart table. means that the background colors of the registered TD elements are set. */ PriceRange.prototype.drawRange = function() { // set all td's of this range to active color for(i=0 ; i < this.tdReferences.length; i++) { this.tdReferences[i].getAttributeNode("bgcolor").nodeValue = "#6F9FC4"; } // now set the disable color from top to bottom limit = this.tdReferences.length - this.activeVariants; for(i=0 ; i < limit; i++) { this.tdReferences[i].getAttributeNode("bgcolor").nodeValue = "#E7D7E7"; } } /* enable a variant more for this range */ PriceRange.prototype.increaseEnabledVariants = function () { this.activeVariants++; this.drawRange(); } /* hide disable a variant for this range */ PriceRange.prototype.decreaseEnabledVariants = function () { this.activeVariants--; this.drawRange(); } /* add a variant to this pricerange. the check if the variant belongs here is done in the pricerangemanager.addVariantToPriceRange function */ PriceRange.prototype.addVariant = function (variant) { this.variants[this.variants.length] = variant; this.activeVariants++; } /* Product Consultant Class. The main part of this application. Manages everything: Products, Variants, Prices, Ranges .... */ function ProductConsultantManager(brandName, subgroupCode) { this.brandName = brandName; this.subgroupCode = subgroupCode; this.maxProductPrice = Number.MIN_VALUE; // initial maximum price this.minProductPrice = Number.MAX_VALUE; // initial minimum price this.highestSelectablePrice = 0; // the highest variant price that is selectable this.variantPricesStore = new Array(); // store for all variants prices this.linkStore = new Object(); // store for the disabled links this.productStore = new Object(); // container for all registered products this.variantChartHight = 0; // chart height => to chart object? this.priceRangeFrom = 0; // currently selected price range start this.priceRangeTo = 0; // currently selecte price range end } /* Register a product to the manager. Creates a new Product instants and stores it. Called from ProductSubgroupList.cs */ ProductConsultantManager.prototype.registerProduct = function (productId) { this.productStore[productId] = new Product(productId); } /* adds a new criteria to the criteria manager and sets a reference to the products */ ProductConsultantManager.prototype.setCriteriaForProduct = function (productId, criteriaId, criteriaValue) { this.productStore[productId].addCriteria(criteriaId, criteriaValue); } /* Set the selected price range. Called when one of the price range selects is changed. */ ProductConsultantManager.prototype.setPriceRange = function () { // read the values from the html-selects var selectorFrom = document.getElementsByName("criteria_price_from")[0]; var selectorTo = document.getElementsByName("criteria_price_to")[0]; // set the new price range this.priceRangeFrom = parseInt(selectorFrom[selectorFrom.selectedIndex].value); this.priceRangeTo = parseInt(selectorTo[selectorTo.selectedIndex].value); // store the new values to the cookie createCookie(getCookieName("price_from"), this.priceRangeFrom); createCookie(getCookieName("price_to"), this.priceRangeTo); } /* register a variant take the price, the productId and the variantcolor */ ProductConsultantManager.prototype.registerVariant = function (newVariantPrice, productId, variantColor) { minPriceCompare = parseInt(newVariantPrice) - 50; maxPriceCompare = parseInt(newVariantPrice) + 50; this.minProductPrice = Math.min(priceRound(minPriceCompare), this.minProductPrice); this.maxProductPrice = Math.max(priceRound(maxPriceCompare), this.maxProductPrice); var newVariant = new Variant(newVariantPrice, productId, variantColor); this.variantPricesStore[this.variantPricesStore.length] = newVariant; this.productStore[productId].addVariant(newVariant); } // enables(shows) all products ProductConsultantManager.prototype.enableAllProducts = function () { //for(i = 0; i < consulManager.productIds.length ; i++) for (key in this.productStore) { this.productStore[key].setEnabled(true); } } // enables or disables all variants ProductConsultantManager.prototype.enableAllVariants = function (enable) { for (key in this.variantPricesStore) { this.variantPricesStore[key].setEnabled(enable); } } /* Variant Class */ function Variant(variantPrice, productId, colorCode) { this.price = parseInt(variantPrice); this.productId = parseInt(productId); this.colorCode = colorCode; this.priceRange = null; this.product = null; this.enabled = true; } /* tests if this variant is in the selected price range */ Variant.prototype.isInPriceRange = function () { if(this.price < consulManager.priceRangeFrom || this.price >= consulManager.priceRangeTo) return false; else return true; } /* enables or disables the variant. that means that the chart table is changed and the product is informed, that one of its variants is enabeld/disabled */ Variant.prototype.setEnabled = function (enabled) { if(enabled && !this.isInPriceRange()) enabled = false; // disable variant if(this.enabled && !enabled) { this.enabled = enabled; this.product.decreaseEnabledVariants(); this.priceRange.decreaseEnabledVariants(); } // enable variant else if(!this.enabled && enabled) { this.enabled = enabled; this.product.increaseEnabledVariants(); this.priceRange.increaseEnabledVariants(); } } /* Product Class Representation of a product. Product knows all its variants and its criterias. */ function Product(productId) { this.productId = productId; this.criterias = new Object(); this.variants = new Array(); this.enabledVariants = 0; this.enabled = true; } /* used to keep track of the enabled variants. if the product was disabled and a variant is enabled so the product is too. */ Product.prototype.increaseEnabledVariants = function () { if(this.enabledVariants < this.variants.length) this.enabledVariants++; if(!this.enabled) this.setEnabled(true); } /* used to keep track of the disabled variants. if all variants ar disabled the product will be disabled too. */ Product.prototype.decreaseEnabledVariants = function () { if(this.enabledVariants > 0) this.enabledVariants--; if(this.enabledVariants == 0) this.setEnabled(false); } /* Adds a variant Object Reference to this product */ Product.prototype.addVariant = function (variant) { variant.product = this; this.variants[this.variants.length] = variant; this.enabledVariants++; } /* set a new cirteria for this product */ Product.prototype.addCriteria = function (criteriaId, criteriaValue) { this.criterias[criteriaId] = criteriaValue; } /* enables or disables a product. disabled: products opacity is set to 25% and links are set to javascript:void(0) enabled: products opacity is set to 0% and links are enabled too. adaption of the chart table is all done before...by the variants */ Product.prototype.setEnabled = function (enabled) { this.enabled = enabled; if(enabled && this.enabledVariants > 0) { // set opacity to 0% this.setOpacity(100) // re-enable the blocked links if(consulManager.linkStore[this.productId]) { document.getElementById("productLink_" + this.productId).href = consulManager.linkStore[this.productId]; document.getElementById("productPictureLink_" + this.productId).href = consulManager.linkStore[this.productId]; document.getElementById("chk_" + this.productId).disabled = false; } } else { // set opacity to 25% this.setOpacity(25); // store the original link if(document.getElementById("productLink_" + this.productId).href != 'javascript:void(0)') consulManager.linkStore[this.productId] = document.getElementById("productLink_" + this.productId).href; // disable the text and the image link for this product document.getElementById("productLink_" + this.productId).href = 'javascript:void(0)'; document.getElementById("productPictureLink_" + this.productId).href = 'javascript:void(0)'; document.getElementById("chk_" + this.productId).disabled = true; // uncheck the products checkbox if it is checked productCheckBox = document.getElementById("chk_" + this.productId); if(productCheckBox.checked) { productCheckBox.checked = false; } } } /* enabled/disables all variants for this product */ Product.prototype.setAllVariantsEnabled = function (enabled) { for(key in this.variants) { this.variants[key].setEnabled(enabled); } } /* sets the opacity for the table that contains this product opacity range is 100 - 0 100 = Product fully visible 0 = Product is invisible */ Product.prototype.setOpacity = function (opacity) { productTable = document.getElementById("productTable_" + this.productId); productTable.style["filter"] = "alpha(opacity=" + opacity + ")"; productTable.style["-moz-opacity"] = opacity/100; productTable.style["opacity"] = opacity/100; } /* manages all the criterias the user can choose */ function CriteriaManager() { this.criteriaStore = new Object(); this.enabledCriterias = new Object(); } /* register a new criteria */ CriteriaManager.prototype.registerCriteria = function (criteriaId, criteriaValue, criteriaType) { this.criteriaStore[criteriaId] = new Criteria(criteriaId, criteriaValue, criteriaType); storedCriteriaValue = readCookie(getCookieName(criteriaId)); if(storedCriteriaValue != null) { if(criteriaType == "LST") { select = document.getElementsByName(criteriaId)[0]; this.criteriaStore[criteriaId].value = storedCriteriaValue; for(key in select.options) { if(select.options[key].value == storedCriteriaValue) { select.selectedIndex = key; } } } else { if((criteriaType == "RNG_MIN") || (criteriaType == "RNG_MAX")) { select = document.getElementsByName(criteriaId)[0]; this.criteriaStore[criteriaId].value = storedCriteriaValue; for(key in select.options) { if(select.options[key].value == storedCriteriaValue) { select.selectedIndex = key; } } } else { document.getElementsByName(criteriaId)[0].checked = true; } } this.criteriaStore[criteriaId].setEnabled(true); } } /* Criteria Id: HashCode of the Text of the Criteria value: In most cases this would be "1" and won't change. But if the criteria is a select field the selected value is stored here type: CHK(bool)/SEL(Select) */ function Criteria(id, value, type) { this.id = id; this.value = value; this.type = type; this.enabled = false; } /* A criteria that has been selected by the user is enabled. */ Criteria.prototype.setEnabled = function (enabled) { this.enabled = enabled; if (this.enabled) criteriaManager.enabledCriterias[this.id] = this; else delete criteriaManager.enabledCriterias[this.id]; } /* tells us if this is a select criteria or not */ Criteria.prototype.isSelect = function () { if (this.type == "LST") return true; return false; } /* tells us if this is a range criteria or not */ Criteria.prototype.isRange = function () { if (( this.type == "RNG_MIN") || (this.type == "RNG_MAX")) return true; return false; } /* manages the colors the user can choose */ function VariantColorManager() { this.variantColors = new Object(); } /* register a new color */ VariantColorManager.prototype.registerVariantColor = function (colorCode) { this.variantColors[colorCode] = new VariantColor(colorCode); } /* disable all colors */ VariantColorManager.prototype.disableAllColors = function () { for(key in this.variantColors) { this.variantColors[key].enable = false; } } /* the variant color */ function VariantColor(code) { this.code = code; this.enabled = false; } /* CHART TABLE CLASS */ function ChartTable() { // set the table width this.width = 260; this.table = document.createElement("table"); //this.table.setAttribute("width", this.width); this.table.setAttribute("cellSpacing", "0"); this.table.setAttribute("cellPadding", "0"); this.table.setAttribute("border", "0"); this.cellWidth = this.width / (rangeManager.realRangeCount + 2) // IE urges DOM Tables to have a tbody element!! // so here we go....sob this.tableBody = document.createElement("tbody"); this.table.appendChild(this.tableBody); } /* draws the chart table the first time */ ChartTable.prototype.draw = function () { var chartDiv = document.getElementById("variantPriceChart"); // add header row var chartTR = document.createElement("tr"); /* var chartTD = document.createElement("td"); chartTD.setAttribute("width", this.cellWidth); chartTD.appendChild(document.createTextNode('\u00A0')); chartTR.appendChild(chartTD); */ var chartTD = document.createElement("td"); chartTD.setAttribute("colSpan", rangeManager.realRangeCount + 2); chartTD.appendChild(document.createTextNode("Anzahl Geräte je Preissegment")); chartTD.appendChild(document.createElement("br")); chartTD.appendChild(document.createElement("br")); chartTR.appendChild(chartTD); this.tableBody.appendChild(chartTR); // add the variantboxes for(j=rangeManager.maxVariantsPerRange;j>=0;j--) { var chartTR = document.createElement("tr"); var chartTD = document.createElement("td"); chartTD.setAttribute("width", this.cellWidth); chartTD.appendChild(document.createTextNode('\u00A0')); chartTR.appendChild(chartTD); for(i = consulManager.minProductPrice; i <= consulManager.maxProductPrice; i += rangeManager.priceRangeStepSize) { var chartTD = document.createElement("td"); chartTD.setAttribute("width", this.cellWidth); chartTD.style.borderLeft = "1px solid white"; chartTD.style.borderTop = "1px solid gray"; if(rangeManager.getPriceRange(i).variants[j]) { chartTD.setAttribute("bgColor", "#6F9FC4"); rangeManager.getPriceRange(i).addVariantTableDataReference(chartTD); } // fill the TD with a space(UTF) chartTD.appendChild(document.createTextNode('\u00A0')); // add the TD to the TR chartTR.appendChild(chartTD); } var chartTD = document.createElement("td"); chartTD.setAttribute("width", this.cellWidth); //chartTD.appendChild(this.getClearPixelImage()); chartTD.appendChild(document.createTextNode('\u00A0')); chartTR.appendChild(chartTD); this.tableBody.appendChild(chartTR); } // create the two footer rows, containing the price range info this.createTableFooter(); chartDiv.innerHTML = ""; chartDiv.appendChild(this.table); } ChartTable.prototype.createTableFooter = function () { // append price range var firstFooTR = document.createElement("tr"); var secondFooTR = document.createElement("tr"); var chartTD = document.createElement("td"); chartTD.setAttribute("width", this.cellWidth); chartTD.appendChild(this.getClearPixelImage()); firstFooTR.appendChild(chartTD); for(i = consulManager.minProductPrice; i <= consulManager.maxProductPrice; i += rangeManager.priceRangeStepSize) { var chartTD = document.createElement("td"); //style = "border-left: ; border-top: 1px solid gray;"; chartTD.style.borderLeft = "1px solid gray"; chartTD.style.borderTop = "1px solid gray"; if((i + rangeManager.priceRangeStepSize) > consulManager.maxProductPrice) chartTD.style.borderRight = "1px solid gray"; chartTD.appendChild(this.getClearPixelImage()); firstFooTR.appendChild(chartTD); } var chartTD = document.createElement("td"); chartTD.appendChild(this.getClearPixelImage()); firstFooTR.appendChild(chartTD); var chartTD = document.createElement("td"); chartTD.setAttribute("colSpan","2"); chartTD.setAttribute("align", "center"); chartTD.appendChild(document.createTextNode(consulManager.minProductPrice + ".-")); secondFooTR.appendChild(chartTD); var chartTD = document.createElement("td"); chartTD.setAttribute("align", "center"); chartTD.setAttribute("colSpan", (rangeManager.realRangeCount - 3)); chartTD.appendChild(document.createTextNode('\u00A0')); secondFooTR.appendChild(chartTD); var chartTD = document.createElement("td"); chartTD.setAttribute("colSpan","2"); chartTD.setAttribute("align", "center"); chartTD.appendChild(document.createTextNode(consulManager.highestSelectablePrice+ ".-")); secondFooTR.appendChild(chartTD); this.tableBody.appendChild(firstFooTR); this.tableBody.appendChild(secondFooTR); } // returns the clearpixel element ChartTable.prototype.getClearPixelImage = function () { var clearPixel = document.createElement("img"); clearPixel.setAttribute("src","/image/clearpixel.gif"); clearPixel.setAttribute("height", "2"); clearPixel.setAttribute("width", this.cellWidth); return clearPixel; } // UTILITIES //###################################################################################### // rounding function for prices. // precision: 100.- function priceRound(price) { var myPrice = parseFloat(price); myPrice = Math.round(price / 100) * 100; return parseInt(myPrice); } // some cookie helpers // #################################################### // create / write / update cookie function createCookie(name,value,days) { if (days) { var date = new Date(); date.setTime(date.getTime()+(days*24*60*60*1000)); var expires = "; expires="+date.toGMTString(); } else var expires = ""; document.cookie = name+"="+value+expires+"; path=/"; } // read a cookie function readCookie(name) { var nameEQ = name + "="; var ca = document.cookie.split(';'); for(var i=0;i < ca.length;i++) { var c = ca[i]; while (c.charAt(0)==' ') c = c.substring(1,c.length); if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length); } return null; } // delete the cookie and its store values function eraseCookie(name) { createCookie(name,"",-1); } // composes a produkteberater compatible cookie name string function getCookieName(name) { return consulManager.brandName + "_" + consulManager.subgroupCode + "_" + name; } // PRODUCT VALIDATION / CHECKER //###################################################################################### /* check for products not matching the selected criteria and disable them */ function recheckProductCritera() { //alert('recheckproductcriteria()'); // updates the selected price range consulManager.setPriceRange(); // check for selected colors checkVariantsColorSelection(); // check for selected criteria checkCriteriaSelection(); } /* goes through all products and checks if they match the selected criteria. if not they are disabled. */ function checkCriteriaSelection() { if( criteriaManager.enabledCriterias.length == 0) return; // ?? // iterate through all products for (productId in consulManager.productStore) { myProduct = consulManager.productStore[productId]; productMatchesCriterias = true; // check for the criteria for(criteriaId in criteriaManager.enabledCriterias) { myCriteria = criteriaManager.enabledCriterias[criteriaId]; if(myCriteria.type == "RNG_MIN") { prodCriteriaId = criteriaId.substring(0, criteriaId.indexOf("_")); if(myProduct.criterias[prodCriteriaId]) { tmpProductValue = parseInt(myProduct.criterias[prodCriteriaId]); tmpCriteriaValue = parseInt(myCriteria.value); if(tmpProductValue < tmpCriteriaValue) productMatchesCriterias = false; } } else { if(myCriteria.type == "RNG_MAX") { prodCriteriaId = criteriaId.substring(0, criteriaId.indexOf("_")); if(myProduct.criterias[prodCriteriaId]) { tmpProductValue = parseInt(myProduct.criterias[prodCriteriaId]); tmpCriteriaValue = parseInt(myCriteria.value); if(tmpProductValue > tmpCriteriaValue) productMatchesCriterias = false; } } else { if(myProduct.criterias[criteriaId]) { if(myProduct.criterias[criteriaId] != myCriteria.value) productMatchesCriterias = false; } else { productMatchesCriterias = false; } } } } if(!productMatchesCriterias) myProduct.setAllVariantsEnabled(false); } } /* goes through all variants and checks if the match the selected colors */ function checkVariantsColorSelection() { // check if there is a color selected at all /*noVariantColorChoosen = true; for(key in variantColorManager.variantColors) { if(variantColorManager.variantColors[key].enabled) { noVariantColorChoosen = false; } }*/ var colorSelector = document.getElementById("color_selector"); var selectedColorCode = ""; // leaf function if no colors are selectable if(colorSelector != null) { selectedColorCode = colorSelector[colorSelector.selectedIndex].value; } // if no color is selected enable all variants if(selectedColorCode == "") { consulManager.enableAllVariants(true); } else { var i=0; // loop through all variants for(i=0;i < consulManager.variantPricesStore.length;i++) { var enableVariant = false; // loop through the available colors //for(key in variantColorManager.variantColors) //{ // if color is checked //if(variantColorManager.variantColors[key].enabled) //{ //noVariantColorChoosen = false; // variant matches the color if(selectedColorCode == consulManager.variantPricesStore[i].colorCode) { enableVariant = true; } //} //} // now what to do with the variant if(enableVariant) consulManager.variantPricesStore[i].setEnabled(true); else consulManager.variantPricesStore[i].setEnabled(false); } } } /* called when a color criteria is selected or unselected. enables/disables the color and runs the criteriacheck */ function variantColorsChanged(changedElement) { newColorValue = changedElement[changedElement.selectedIndex].value; if(newColorValue != "") { createCookie(getCookieName("color_selector"),newColorValue,0); } createCookie(getCookieName("variantColor"), newColorValue); recheckProductCritera(); } /* called when a criteria is selected */ function criteriaSelectionChanged(changedElement) { criteria = criteriaManager.criteriaStore[changedElement.name]; // criteria for LST and CHK // select criteria? if(criteria.isSelect() || criteria.isRange()) { selectedValue = changedElement.options[changedElement.selectedIndex].value; if(selectedValue != "") { criteria.value = selectedValue; createCookie(getCookieName(criteria.id),selectedValue,0); criteria.setEnabled(true); } else { criteria.setEnabled(false); eraseCookie(getCookieName(criteria.id)); } } // bool criteria else { // bool criteria if(changedElement.checked) { criteria.setEnabled(true); createCookie(getCookieName(criteria.id),1,0); } else { criteria.setEnabled(false); eraseCookie(getCookieName(criteria.id)); } } // run recheck recheckProductCritera(); } /* called when a range is changed */ function rangeSelectionChanged(changedElement) { changedElementName = changedElement.name; if(changedElementName.indexOf("_")>0) { changedElementName = changedElementName.substring(0, (changedElementName.indexOf("_")-1)); } criteria = criteriaManager.criteriaStore[changedElementName]; selectedValue = changedElement.options[changedElement.selectedIndex].value; if(selectedValue != "") { criteria.value = selectedValue; createCookie(getCookieName(criteria.id),selectedValue,0); criteria.setEnabled(true); } else { criteria.setEnabled(false); eraseCookie(getCookieName(criteria.id)); } // run recheck recheckProductCritera(); } //################################################################################################# // Comparator Part /* There's a seconde component to the ProdukteBerater, the ProdukteVergleich. In an easy to understand table view you can directly compare products of the same productsubgroup. On the productsubgroup page you can select products(max. 4) to compare. Here are just to minor functions for this page. */ //################################################################################################# /* removes all products from the comparator and reloads the productsubgroup view */ function removeAllProducts() { elements = document.getElementsByName("product_compare[]"); for( key in elements ) { elements[key].value = ''; } document.forms[0].submit(); } /* show the variant selection div for this product and hide all others */ function switchVariantSelectionDivs(productId) { // disable all other divs first products = document.getElementsByName("product_compare[]"); for( i = 0; i < products.length; i++ ) { if(products[i].value != productId) { document.getElementById('vList_' + products[i].value).style.display = 'none'; } } // now enable or disable the selected div productDiv = document.getElementById('vList_' + productId ); if(productDiv.style.display == '') { productDiv.style.display = 'none'; } else { productDiv.style.display = ''; } } // ################################################################################## // Error Message Handling /* As allways users can make mistakes and we have to tell them so. So here is a quite fancy error message feature. Messages popup in a div that is placed next to the mouse cursor so the user can't avoid seeing it ;) The message just shows some seconds and then slowly fades out. */ // ################################################################################## var selectedProductsCount; var activeFading; var activeTimeout; /* Updates and checks the count of the selected products for the comparator. If the user tries to add more than 4 products to the comparator an errormessage is shown */ function updateSelectedProductCount(productCheckbox) { //if(!selectedProductsCount) //{ selectedProductsCount = 0; checkboxes = document.getElementsByName("product_compare[]"); for(i = 0; i < checkboxes.length; i++) { if(checkboxes[i].checked && checkboxes[i] != productCheckbox && !checkboxes[i].disabled) selectedProductsCount++; } //} if(selectedProductsCount < 4) { if(productCheckbox.checked) { selectedProductsCount++; } else { selectedProductsCount--; } } else { productCheckbox.checked = false; showAlert("Es können nicht mehr als 4 Produkte in den Produktevergleich übernommen werden.", findElementPos(productCheckbox)); } } /* used the find the right position for the error message div */ function findElementPos(obj) { var curleft = curtop = 0; if (obj.offsetParent) { curleft = obj.offsetLeft curtop = obj.offsetTop while (obj = obj.offsetParent) { curleft += obj.offsetLeft curtop += obj.offsetTop } } return [curleft,curtop]; } /* show an allert message with the given text on the given position */ function showAlert(message, coords) { // get the alertDiv var alertDiv = document.getElementById("alertBox"); // set the message text alertDiv.innerHTML = message; // un-hide the div alertDiv.style.display = ''; // move the div to the right spot alertDiv.style.left = coords[0]; alertDiv.style.top = coords[1]; // set opacity to 100 => fully visible alertOpacity = 100; alertDiv.style["filter"] = "alpha(opacity=" + alertOpacity + ")"; alertDiv.style["-moz-opacity"] = alertOpacity/100; alertDiv.style["opacity"] = alertOpacity/100; // clear the timer if one is running already if(activeTimeout) window.clearTimeout(activeTimeout); // start the timeout for the fadeout (now 2.5 seconds) activeTimeout = window.setTimeout("StartFadeAlert()", 2500); } var alertOpacity = 100; /* starts the fade out process */ function StartFadeAlert() { var alertDiv = document.getElementById("alertBox"); if(activeFading) window.clearInterval(activeFading); activeFading = window.setInterval("FadeAlert()", 100); window.clearTimeout(activeTimeout); } /* fades out the div one bit more */ function FadeAlert() { var alertDiv = document.getElementById("alertBox"); alertDiv.style["filter"] = "alpha(opacity=" + alertOpacity + ")"; alertDiv.style["-moz-opacity"] = alertOpacity/100; alertDiv.style["opacity"] = alertOpacity/100; alertOpacity -= 5; if(alertOpacity <= 0) { alertDiv.style.display = 'none'; window.clearInterval(activeFading); } } /* If the user wants to enter the comparator and has no products selected an errormessage will be shown. */ function checkForSelectedProducts(selectedLink) { checkboxes = document.getElementsByName("product_compare[]"); for(i = 0; i < checkboxes.length; i++) { if(checkboxes[i].checked && !checkboxes[i].disabled) return true; } showAlert("Wählen Sie mindestens ein Produkt aus um zum Produktevergleich zu gelangen.", findElementPos(selectedLink)); return false; }