In my store I have a large amount of products that have no attributes, and when I use the DPUv2 the products that I have specials and no attributes get updated with the Special Price and when the product is Sold Out it shows as "Your Price: 0.00". The product that I'm testing has 3 attributes. The first one is "Add Canopy" and it is a check that adds $91.00. The second one is the finish options with radio buttons. They don't add any amount to the price, and the last attribute is purely a test called Other Option with a "Just Add Me" value and adds the amount of $35.00 to the price. Well, in that order, the canopy Updates the price with the $91.00, but the Just Add Me does not Update the price with its $35.00. If I shuffle the order of the attributes to Add Canopy (first), Just Add Me (second), and the Finish at the end, then it works and both Attributes get to Update the Price.

Using the original DPU, there are no problems with the rest of the products that don't have attributes. The test product with the attributes works exactly the same way that it does with DPUv2 but in IE it shows the Done (with errors) symbol on the lower left corner of the browser and when double clicking the error we get:

Webpage error details

User Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)
Timestamp: Fri, 14 May 2010 13:13:58 UTC


Message: Object required
Line: 158
Char: 4
Code: 0
URI: http://f i s h p e t l o v e.com/index.php?main_page=product_info&cPath=11&products_id=14580


Message: Object required
Line: 158
Char: 4
Code: 0
URI: http://f i s h p e t l o v e.com/index.php?main_page=product_info&cPath=11&products_id=14580

The error only shows in some IE in some computers, I have seen the IE in two different setups in different locations. I talk with my hosting and they had the same version of IE I have (version: 8.0.6001.18702 running Windows XP) and they didn't see the error. Firefox does not show the error. I setted up IE to not show errors, and it is still showing.

My jscript_updater.php looks like:

Code:
<?php
// Small module to dynamically update main price when the product has price altering attributes

// (c) D Parry (Chrome) 2007 ([email protected])
// This module is free to distribute and use as long as the above copyright message is left in tact

// Alterations are permitted but please let me know of any changes you make, specifically where incompatibility is concerned

// some contstant declarations
define('UPDATER_PREFIX_TEXT', 'Price: ');
define('UPDATER_SB_TITLE', 'Price Breakdown'); // the heading that shows in the Updater sidebox
?>
<script language="javascript" type="text/javascript">
// <![CDATA[

var objPrice, origPrice;
var defaultCurrencyLeft, defaultCurrencyRight;
defaultCurrencyLeft = defaultCurrencyRight = '';
var quantity = false; // do not alter this
var showQuantity = false; // show the quantity the customer has requested in the main price header
var showQuantitySB = false; // show the quantity the customer has requested in the sidebox
var prArr = nameArr = new Array(); // holds an array of prices to be adjusted (for multiple price adjustments)
var _oflag = false; // do not alter this

var seeker = new RegExp(/\(\s*([+-]?)([^0-9]*)([0-9,]+\.[0-9]+)([^0-9)]*)\s*\)/);

// Updater sidebox settings
var _sidebox = false; // set this to: false - don't use or the ID of the sidebox you want the display to insert above (must be exact)
var objSB = false; // this holds the sidebox object

// Second price setting
// the following settings allow a second price to be displayed... if a page is very long this allows you to add another price display
// thanks to Ryk on the Zen Cart forums for the idea and pointing out the issue
var _secondPrice = 'false'; // set this to either false for disabled or supply the ID of an element for the price to appear BEFORE... for example, cartAdd
var _SPDisplay = 'update'; // governs the behaviour of the second... 'always' permanently displays and 'update' shows the second price only when an attribute is selected
var objSP = false; // please don't adjust this

// debug settings
var _debug = false; // set to false to disable debug output
var _db = '';
var _dbdiv = false;

function init() { // discover the selects that are required to adjust the main price
	var centre = document.getElementById('productGeneral');
	var objSel = centre.getElementsByTagName('SELECT');
	var objInp = centre.getElementsByTagName('INPUT');
	var db = '';
	var _flag = false; // flag to decide if a load of attribute information is needed

	if (!_oflag)	{ // get the base price and the quantity box (if it exists)
		// firstly find out if debug messages should be shown
		if (_debug === true) { // build the div that will hold debug messages
			createdb();
		}

		if (_secondPrice !== false && _SPDisplay == 'always') {
			regdb('SP Onload', 'Type: ' + _SPDisplay);
			updSP();
		}

		// quantity box
		var qtemp = document.getElementById('cartAdd');
		if (qtemp) { // got the containing div... go for the quantity!
			var itemp = qtemp.getElementsByTagName('INPUT');
			if (itemp) { // make sure some inputs are available to scan
				for (var i=0; itemp[i]; i++) {
					if (itemp[i].name == 'cart_quantity') { // we have the input we need
						quantity = itemp[i].value;
						regdb('Onload quantity', 'Cart add INPUT discovered (' + quantity + ')');
						itemp[i].onkeyup = function () { adjQuan(this); }
					}
				}
			}
		}
		// if quantity is still false we'll assign it a value of 1
		if (quantity === false)	quantity = 1;

		if (!(objPrice = document.getElementById('productPrices').getElementsByTagName('SPAN')[1])) {
			objPrice = document.getElementById('productPrices');
		} else {
			regdb('Onload sale', 'Looks like an item on sale');
		}

		origPrice = Number(objPrice.innerHTML.match(/([0-9,.]+)/g)[0].replace(/,/g, ''));
		var origHTML = objPrice.innerHTML;
		var db = '';
		if (!origPrice) {
			db = 'Initial phase failure';
			if (objPrice) {
				db += ' - H2 found';
				var temp = objPrice.getElementsByTagName('SPAN');
				for (var i=0; temp[i]; i++) {
					if (temp[i].className = 'productSpecialPrice') {
						origPrice = temp[i].innerHTML.match(/([0-9,.]+)/g)[0].replace(/,/g, '');
						db += ' - price in SPAN';
						origHTML = temp[i].innerHTML;
						if (!origPrice)	return;
					}
				}
			} else {
				db += ' - price not found!';
				return;
			}
		} else {
			db = 'Price found: ' + origPrice;
		}
		regdb('Onload base price', db);

		// try and find the default currency symbols
		var temp = origHTML.match(/s*([^0-9 ]*)([0-9.,]+)(.*)/);
		defaultCurrencyLeft = temp[1];
		db = 'Left: ' + defaultCurrencyLeft;
		defaultCurrencyRight = temp[3];
		db += ' - Right: ' + defaultCurrencyRight;
		regdb('Onload default currency locator', db);
	}

	for (var i=0; objSel[i]; i++) {
		var _this = objSel[i];

		objSel[i].onchange = function () { updatePrice(this); }
		db = 'Name - ' + objSel[i].name + ' : ID - ' + objSel[i].id;

		// scan the attributes to find out if any adjustments are needed
		var matches = objSel[i][objSel[i].selectedIndex].text.match(seeker);

		if (matches) { // yep
			db += ' - Adjusted!';
			prArr[objSel[i].id] = new Array();
			prArr[objSel[i].id]['p'] = Number(matches[3].replace(/,/, '')); // push the value onto the stack
			prArr[objSel[i].id]['n'] = objSel[i][objSel[i].selectedIndex].text.replace(seeker, '');
			prArr[objSel[i].id]['m'] = matches[1]; // mode indicator
			prArr[objSel[i].id]['l'] = matches[2]; // left side currency indeicator
			prArr[objSel[i].id]['r'] = matches[4]; // the right side currency indicator
			_flag = true;
		}

		regdb ('Onload SELECT', db);
	}

	for (var i=0; objInp[i]; i++) {
		if (objInp[i].type == 'radio' || objInp[i].type == 'checkbox') { // make sure we're dealing with radio boxes
			db = 'Name - ' + objInp[i].name + ' : ID - ' + objInp[i].id;
			matches = objInp[i].nextSibling.innerHTML.match(seeker);
			if (matches) {
				db += ' : Adjusted!';
				objInp[i].onclick = function () { updateR(this); }
				if (objInp[i].checked)	updateR(objInp[i]);
			}
		}
		regdb('Onload RAD/CH', db);
	}

	if (_flag  && !_oflag)	{
		updatePriceNow();
	}

	if (_oflag === true)	regdb('Onload', '--- End of loading procedures ---');
	_oflag = true;
}

function updSP() {
	// adjust the second price display; create the div if necessary
	var flag = false; // error tracking flag

	if (_secondPrice !== false) { // second price is active
		var centre = document.getElementById('productGeneral');
		var temp = document.getElementById('productPrices');
		var itemp = document.getElementById(_secondPrice);

		if (objSP === false) { // create the second price object
			if (!temp || !itemp)	flag = true;

			if (!flag) {
				objSP = temp.cloneNode(true);
				objSP.id = temp.id + 'Second';
				regdb('updSP', 'Price node cloned!');

				if (!itemp.parentNode.insertBefore(objSP, itemp.nextSibling)) {
					regdb('updSP', 'Unable to insert node at point ' + _secondPrice);
				} else {
					regdb('updSP', 'Node inserted successfully');
				}
			} else {
				regdb('updSP', 'Unable to clone price node!');
			}
		}

		regdb('updSP', 'Duplicating price, by jove!');
		objSP.innerHTML = temp.innerHTML;
	} else { // second price inactive
		regdb('updSP', 'Cancelled');
	}
}

function adjQuan(objInp) {
	// adjust the global cart quantity for multiplication
	var newVal = Number(objInp.value.match(/[0-9]+/g));

	quantity = newVal;
	regdb('Quantity change', newVal);
	updatePriceNow();
	if (_sidebox !== false && objSB === false)	createSB();
	if (objSB !== false)	updateSB(); // update the sidebox
}

function updateR(objInp) {
	var matches = objInp.nextSibling.innerHTML.match(seeker);
	var priceAdj, totalAdj = 0;
	var flag = false;
	var db = '';

	if (matches) { // make sure this attribute is price-adjust-worthy
		db += '*Adj* - ';
		priceAdj = Number(matches[3].replace(/,/g, '')); // Number(matches[0].match(/[0-9.]+/)[0]);
	} else {
		db += '*No adj* - ';
		priceAdj = 0;
	}

	if (objInp.type == 'radio') {
		// the radio type input can be inserted into the array using its name as a reference as radio boxes are mutually
		// exclusive in their group
		db += 'Radio - Name: ' + objInp.name + ' - ';
		prArr[objInp.name] = new Array();
		prArr[objInp.name]['p'] = priceAdj; // push the price adjustment into the array referenced by the ID of the calling select
		prArr[objInp.name]['n'] = objInp.nextSibling.innerHTML.replace(seeker, '');
		prArr[objInp.name]['m'] = matches[1];
		prArr[objInp.name]['l'] = matches[2]; // left side currency indeicator
		prArr[objInp.name]['r'] = matches[4]; // the right side currency indicator
		db += 'Price adjust: ' + priceAdj + ' - Mode: ' + matches[1];
	} else {
		// checkboxes are always autonomous so can have multiple selections from a group so use the ID as before
		if (objInp.checked) {
			db += 'Checkbox - ID: ' + objInp.id + ' - ';
			prArr[objInp.id] = new Array();
			prArr[objInp.id]['p'] = priceAdj; // push the price adjustment into the array referenced by the ID of the calling select
			prArr[objInp.id]['n'] = objInp.nextSibling.innerHTML.replace(seeker, ''); // attribute name, price removed
			prArr[objInp.id]['m'] = matches[1]; // the mode (+, - or base) of the attribute
			prArr[objInp.id]['l'] = matches[2]; // left side currency indeicator
			prArr[objInp.id]['r'] = matches[4]; // the right side currency indicator
			db += 'Price adjust: ' + priceAdj + ' - Mode: ' + matches[1];
		} else {
			prArr[objInp.id] = null;
			db = 'Checkbox ID ' + objInp.id + ' is now NULL';
		}
	}

	regdb('updateR', db);
	updatePriceNow();
}

function updatePrice(objSel) { // update the main price from the value extracted by the regex
	var matches = objSel[objSel.selectedIndex].text.match(seeker);
	var priceAdj, totalAdj = 0;
	var db = '';

	if (matches) { // make sure this attribute is price-adjust-worthy
		db = '*Adj* - ';
		priceAdj = Number(matches[3].replace(/,/g, ''));
	} else {
		db = '*No adj* - ';
		priceAdj = 0;
	}

	if (matches)	{
		prArr[objSel.id] = new Array();
		prArr[objSel.id]['p'] = priceAdj; // push the price adjustment into the array referenced by the ID of the calling select
		prArr[objSel.id]['n'] = objSel[objSel.selectedIndex].text.replace(seeker, '');
		prArr[objSel.id]['m'] = matches[1];
		prArr[objSel.id]['l'] = matches[2]; // left side currency indeicator
		prArr[objSel.id]['r'] = matches[4]; // the right side currency indicator
		db += 'ID: ' + objSel.id + ' - Price adjust: ' + priceAdj + ' - Mode: ' + matches[1];
	} else {
		prArr[objSel.id] = null;
		db = 'SELECT ID ' + objSel.id + ' is now NULL';
	}

	regdb('updatePrice', db);
	updatePriceNow();
}

function updatePriceNow() { // update the price display
	var totalAdj = 0;
	var db = '';
	var l = defaultCurrencyLeft;
	var r = defaultCurrencyRight;

	for (var i in prArr) {
		if (prArr[i] == '') {
			l = (prArr[i]['l'] == '' || typeof(prArr[i]['l']) == 'undefined' ? defaultCurrencyLeft : prArr[i]['l']);
			r = (prArr[i]['r'] == '' ? defaultCurrencyRight : prArr[i]['r']);
			db = 'Item: ' + prArr[i]['n'] + ' - ';
			switch (true) { // adjust the price according to its given mode
				case prArr[i]['m'] == '+': // add the attribute price to the base price
					db += 'Mode: Add';
					db += ' - totalAdj: ' + totalAdj + ' - Adding ' + prArr[i]['p'];
					totalAdj += prArr[i]['p'];
					break;
				case prArr[i]['m'] == '-': // subtract the attribute price from the base
					db += 'Mode: Subtract';
					db += ' - totalAdj: ' + totalAdj + ' - Subtracting ' + prArr[i]['p'];
					totalAdj -= prArr[i]['p'];
					break;
				case prArr[i]['m'] == '': // this means the attribute actually replaces the base price
					db += 'Mode: Base';
					db += ' - Altering base to ' + prArr[i]['p'];
					origPrice = prArr[i]['p'];
					break;
			}
			regdb('updatePriceNow', db);
		}
	}

	var newPrice = ((origPrice + totalAdj) * quantity).toFixed(2);
	document.getElementById('productPrices').innerHTML = '<?php echo UPDATER_PREFIX_TEXT; ?>' + l + addCommas(newPrice) + r + (showQuantity ? ' (' + quantity + ')' : '');
	if (_sidebox !== false && objSB === false)	createSB();
	if (objSB !== false)	updateSB(); // update the sidebox
	updSP();
}

function createSB() { // create the sidebox for the attributes info display
	if (_sidebox !== false) {
		var temp = document.getElementById(_sidebox); // get a handle to the sidebox to insertBefore
		if (temp) {
			objSB = document.createElement('DIV'); // create the sidebox wrapper
			objSB.id = 'updaterSB';
			objSB.className = 'leftBoxContainer'; // set the CSS reference
			// create the heading bit
			var tempH = document.createElement('H3');
			tempH.id = 'updateSBHeading';
			tempH.className = 'leftBoxHeading';
			tempH.innerHTML = '<?php echo UPDATER_SB_TITLE; ?>';
			objSB.appendChild(tempH);
			// create the content div
			var tempC = document.createElement('DIV');
			tempC.id = 'updaterSBContent';
			tempC.className = 'sideBoxContent';
			tempC.innerHTML = 'If you can read this Chrome has broken something';
			objSB.appendChild(tempC);

			temp.parentNode.insertBefore(objSB, temp);
			regdb('createSB', 'Sidebox created!');
		} else {
			regdb('createSB', 'Sidebox could not be created!');
		}
	}
}

function updateSB() { // update the contents of the sidebox with the updated info from the attributes selector
	var newText = hText = '';
	var l = defaultCurrencyLeft;
	var r = defaultCurrencyRight;
	var totalAdj = origPrice;

	for (var i in prArr) {
		if (prArr[i] == '') {
			l = (prArr[i]['l'] == '' || typeof(prArr[i]['l']) == 'undefined' ? defaultCurrencyLeft : prArr[i]['l']);
			r = (prArr[i]['r'] == '' ? defaultCurrencyRight : prArr[i]['r']);

			if (prArr[i]['m'] !== null && prArr[i]['m'] != '')	{
				if (prArr[i]['m'] == '-')	newText += '<span style="color: red;">';
				newText += prArr[i]['n'] + (prArr[i]['p'] != 0 ? ' - ' + (showQuantitySB ? quantity + 'x ' : '') + prArr[i]['l'] + prArr[i]['p'].toFixed(2) + prArr[i]['r']: '') + '<br/>';
				if (prArr[i]['m'] == '-')	newText += '<\/span>';
				switch (true) { // adjust the price according to its given mode
					case prArr[i]['m'] == '+': // add the attribute price to the base price
						totalAdj += prArr[i]['p'];
						break;
					case prArr[i]['m'] == '-': // subtract the attribute price from the base
						totalAdj -= prArr[i]['p'];
						break;
					case prArr[i]['m'] == '': // this means the attribute actually replaces the base price
						origPrice = prArr[i]['p'];
						break;
				}
			}
		}
	}

	hText += 'Product price - ' + (showQuantitySB ? quantity + 'x ' : '') + l + addCommas(origPrice.toFixed(2)) + r + '<br/>';
	newText += '<hr />Total: ' + l + addCommas((totalAdj * quantity).toFixed(2)) + r;

	// I know innerHTML is cheating but I careth not :)
	objSB.getElementsByTagName('DIV')[0].innerHTML = hText + newText;
}

function addCommas(nStr)
{ // this function can be found at http://www.mredkj.com/javascript/numberFormat.html#addcommas
	nStr += '';
	var x = nStr.split('.');
	var x1 = x[0];
	var x2 = x.length > 1 ? '.' + x[1] : '';
	var rgx = /(\d+)(\d{3})/;
	while (rgx.test(x1)) {
		x1 = x1.replace(rgx, '$1' + ',' + '$2');
	}
	return x1 + x2;
}

function createdb () {
	var centre = document.getElementById('productGeneral');

	if (_dbdiv === false) {
		_dbdiv = document.createElement('DIV');
		_dbdiv.style.border = '2px dashed #666';
		_dbdiv.style.padding = '0.1em';
		centre.appendChild(_dbdiv)
	}
	_dbdiv.innerHTML = '<div style="cursor: pointer; width: 100%; text-align: center; margin-bottom: 5px; background-color: #aaa; padding: 1px; font-size: 110%; font-weight: bold;" onclick="createdb();">Debug messages<\/div>';
}

function regdb(strTitle, strText) { // simple routine to format and output the debug messages
	if (_debug === true) { // make sure debug messages should be displayed
		_dbdiv.innerHTML += '<div style="margin: 2px 0; background-color: #ddd; border-top: 1px solid #aaa; border-bottom: 1px solid #aaa;"><span style="font-weight: bold;">' + strTitle + ':<\/span> ' + strText + '<\/div>';
	}
}

// the following statements should allow multiple onload handlers to be applied
// I know this type of event registration is technically deprecated but I decided to use it because I haven't before
// There shouldn't be any fallout from the downsides of this method as only a single function is registered (and in the bubbling phase of each model)
// For backwards compatibility I've included the traditional DOM registration method
try { // the IE event registration model
	window.attachEvent('onload', init);
} catch (e) { // W3C event registration model
	window.addEventListener('load', init, false);
} finally {
	window.onload = init;
}

// ]]></script>
Help Please.

Fred