// Core Taobase Javascript functionality
var tao = {
    form: {
        // Validates a form element to a given rule which can either be a 
        // function or a string corresponding to a library function
        validateElement: function(formEle, validationFn, errorMessage, className) {
            if (null === $(formEle)) return false;
            if (tao.form.isElementValid(formEle, validationFn)) {
                tao.form.removeError(formEle);
                return true;
            } else {
                tao.form.displayError(formEle, errorMessage, className);
                return false;
            }
        },
        // Checks whether an element validates
        isElementValid: function(formEle, validationFn) {
            if (null === $(formEle)) return false;
            if (typeof validationFn == 'function') {
                return validationFn($F(formEle));
            } else if (typeof tao.form.validate[validationFn] == 'function') {
                validationFn = tao.form.validate[validationFn];
                return validationFn($F(formEle));
            }
            return false;
        },
        // Displays an error message beneath a specified element.
        // The class of the error block can be specified.
        displayError: function(ele, message, className) {
            ele = $(ele);
            tao.form.errorClass = className || 'formError';
            if (tao.form.isErrorElementCreated(ele)) {
                ele.next().update(message).highlight();
            } else {
                ele.insert({after: new Element('div', {'class': tao.form.errorClass}).update(message)});
            }
        },
        removeError: function(ele) {
            if (tao.form.isErrorElementCreated(ele)) {
                ele.next().remove();
            }
        },
        removeAllErrors: function(ele) {
            $(ele).select('.'+tao.form.errorClass).invoke('remove');
        },
        isErrorElementCreated: function(ele) {
            return !!(ele.next() && ele.next().className == tao.form.errorClass)
        },
        // Library of validation functions
        validate: {
            regex: function(testString, regEx) {
                return testString.search(regEx) != -1;
            },
            required: function(test) {
                return !test.strip().empty();
            },
            nonzero: function(test) {
                return parseInt(test) > 0;
            },
            emailAddress: function(test) {
                return tao.form.validate.regex(test, /^\w+([.\-+]\w+)*@[A-Za-z0-9]+([.-]\w+)*\.[A-Za-z]+$/);
            },
            password: function(test) {
                return tao.form.validate.regex(test, /^.{6,}$/);
            },
            postcode: function(test) {
                return tao.form.validate.regex(test, /^[a-zA-Z][a-zA-Z0-9]{1,3}\s*\d[a-zA-Z]{2}$/);
            },
            phoneNumber: function(test) {
                return tao.form.validate.regex(test, /^[\d() ]{10,}$/);
            },
            date: function(test) {
                return tao.form.validate.regex(test, /^(0[1-9]|1[012])\/\d{2}$/);
            },
            price: function(test) {
                return tao.form.validate.regex(test, /^\d+\.\d{2}$/);
            },
            // Payment validation functions
            securityNumber: function(test) {
                return tao.form.validate.regex(test, /^\d{3,4}$/);
            },
            issueNumber: function(test) {
                return tao.form.validate.regex(test, /^\d+$/);
            },
            cardNumber: function(test) {
                var length = test.length;
                if (0 == length) return false;
                var total = 0;
                var valid = true;
                var digits = 0 - length;
                var i = -1;
                while (i >= digits) {
                    if ((i % 2) == 0) {
                        var digit = 2 * (test.substring(length+i, length+i+1));
                        digit = digit.toString();
                        total += parseInt(digit.substring(0, 1));
                        if (digit.length > 1) {
                            total += parseInt(digit.substring(1,2));
                        }
                    } else {
                        total += parseInt(test.substring(length+i, length+i+1));
                    }
                    i--;
                }
                if ((total % 10) != 0){
                    valid = false;
                }
                return valid;
            }
        },
        tips: {
			init: function() {
				tao.form.tips.defaultText = new Hash();
				$$('.formTip').each(tao.form.tips.storeDefaultText)
				              .invoke('observe', 'click', tao.form.tips.clearDefaultText)
				              .invoke('observe', 'blur', tao.form.tips.restoreDefaultText);
			},
			storeDefaultText: function(ele) {
				tao.form.tips.defaultText.set(ele.identify(), $F(ele));
			},
			clearDefaultText: function(event) {
				var input = event.findElement('input');
				if ($F(input) == tao.form.tips.defaultText.get(input.identify())) {
					input.clear();
				}
			},
			restoreDefaultText: function(event) {
				var input = event.findElement('input');
				if ($F(input).strip().empty()) {
					input.setValue(tao.form.tips.defaultText.get(input.identify()));
				}
			}
		}
    },
    // Cookie manipulation
    cookie: {
        set: function(key, 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 = key+"="+value+expires+"; path=/";
        },
        get: function(key)
        {
            var keyEquals = key+"=";
            var value = null;
            document.cookie.split(';').each(function(s){
                s = s.replace(/^\s*/, '');
                if (s.startsWith(keyEquals)) {
                    value = s.substring(keyEquals.length, s.length)
                    throw $break;
                }
            });
            return value;
        },
        clear: function(key) {
            tao.cookie.set(key, '', -1);
        }
    },
    // Positioning of elements
    position: {
        centreInViewport: function(ele) {
            var positionLeft = document.viewport.getWidth()/2 - ele.getDimensions().width/2;
            var positionTop = document.viewport.getScrollOffsets().last() + document.viewport.getHeight()/2 - ele.getDimensions().height/2
            ele.setStyle({
                top: String(positionTop)+'px',
                left: String(positionLeft)+'px'
            });
        },
        nextToClick: function(ele, clickX, clickY) {
            // Determine top position
            var eleHeight = ele.getDimensions().height;
            var distanceToViewPortBottom = document.viewport.getScrollOffsets().last() + document.viewport.getHeight() - clickY;
            var distanceToViewPortTop = clickY - document.viewport.getScrollOffsets().last();
            var positionTop;
            if (distanceToViewPortBottom > eleHeight) {
                positionTop = clickY;    
            } else if (distanceToViewPortTop > eleHeight) {
                positionTop = clickY - eleHeight; 
            } else {
                positionTop = clickY - eleHeight/2;
            }
            // Determine left position
            var eleWidth = ele.getDimensions().width;
            var distanceToViewPortRight = document.viewport.getScrollOffsets().first() + document.viewport.getWidth() - clickX;
            var distanceToViewPortLeft = clickX - document.viewport.getScrollOffsets().first();
            var positionLeft;
            if (distanceToViewPortRight > eleWidth) {
                positionLeft = clickX;    
            } else if (distanceToViewPortLeft > eleWidth) {
                positionLeft = clickX - eleWidth; 
            } else {
                positionLeft = clickX - eleWidth/2;
            }
            
            // Set position
            ele.setStyle({
                top: String(positionTop)+'px',
                left: String(positionLeft)+'px'
            });
        }
    },
    // Debugging
    debug: {
        log: function() {
            if (typeof console == 'object') {
                if (arguments.length == 1) {
                    console.log(arguments[0]);
                } else {
                    console.log(arguments);
                }
            }
        },
        timer: function() {
            tao.debug.log('['+tao.debug.getExecutionTime().toFixed(3)+' seconds]');
            tao.debug.log(arguments);
        },
        getExecutionTime: function() {
            date = new Date();
            if (!tao.debug.startTime) {
                tao.debug.startTime = date.getTime();
                return 0;
            } else {
                var totalMilliseconds = date.getTime() - tao.debug.startTime;
                var totalSeconds = totalMilliseconds/1000;
                return totalSeconds;
            }
        }
    },
    analytics: {
        track: function(path) {
            try {
                pageTracker._trackPage(path);
            } catch (error) {
                tao.debug.log(error.message);
            }
        }
    }
}

//  Taoshop functionality
var shop = {
    name: 'Taoshop',
    forbiddenPath: '/pages/forbidden.html',
    init: function() {
        if ('string' == typeof applicationName) {
            shop.name = applicationName;
        } 
        shop.security.init();
        shop.form.init();
        shop.search.init();
        shop.synopsis.init();
        shop.tags.init();
        shop.basket.init();
        shop.tabs.init();
        shop.deliveryInfo.init();
        shop.expandingList.init();
        shop.cms.init();
        shop.checkout.init();
        shop.countdown.init();
    },
    security: {
        init: function() {
            if (top != self) {
                self.location.pathname = shop.forbiddenPath;
            }
        }  
    },
    display: {
        getGrowler: function() {
            if (typeof growlerOptions == 'undefined') {
                growlerOptions = {location: 'br'};
            }
            if (!shop.display.growler) {
                shop.display.growler = new k.Growler(growlerOptions);
            }
            return shop.display.growler;
        },
        notify: function(message) {
            shop.display.getGrowler().growl(message, {header: shop.name, className: 'taoshop-notify'});
        },
        info: function(message) {
            shop.display.getGrowler().growl(message, {header: shop.name, className: 'taoshop-info'});
        },
        error: function(message) {
            shop.display.getGrowler().growl(message, {header: shop.name, sticky: true, className: 'taoshop-error'});
        },
        warn: function(message) {
            shop.display.getGrowler().growl(message, {header: shop.name, className: 'taoshop-warning'});
        },
        special: function(message) {
            var growler = shop.display.getGrowler();
            growler.growl(message, {
                header: shop.name, 
                className: 'taoshop-special', 
                created: function(){$$('#Growler div.taoshop-special').last().highlight()}
            });
        },
        validationWarn: function() {
        	shop.display.warn('There are errors in your submitted data');
        }
    },
    tabs: {
        init: function() {
            var tabs = $$('.tab a');
            if (!tabs) return;
            tabs.invoke('observe', 'focus', shop.tabs.select);
        },
        select: function(event) {
            var selectedElement = event.element();
            var tabList = selectedElement.up('ul');
            tabList.select('a').invoke('removeClassName', 'selected');
            selectedElement.addClassName('selected');
        }
    },
    synopsis: {
        init: function() {
            $$('img.product_image').invoke('observe', 'click', shop.synopsis.show);
        },
        show: function(event) {
            // Load synopsis HTML into pop-up
            var clickedEle = event.element();
            var synopsisContentContainer = clickedEle.previous('.product-synopsis');
            
            if (!synopsisContentContainer) return;
            var synopsisPopUp = $('divSitePopup');
            synopsisPopUp.update(synopsisContentContainer.innerHTML);
            
            // Position pop-up
            tao.position.nextToClick(synopsisPopUp, event.pointerX(), event.pointerY()); 
            if (typeof synopsisOptions != 'undefined' && synopsisOptions.showDimmer == true){
                var Dimmer = 'divDimmer';
                if (typeof(window.innerWidth) == 'number') { 
                	// Non-IE
                    Dimmer.style.position = 'fixed';
                    height = document.body.parentNode.scrollHeight;
                    width = document.body.parentNode.scrollWidth;
                } else if (document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight)) { 
                	// IE 6+ in 'standards compliant mode'
                    height = Math.max(document.body.parentNode.scrollHeight, document.documentElement.clientHeight);
                    width = Math.max(document.body.parentNode.scrollWidth, document.documentElement.clientWidth);
                } else if (document.body && ( document.body.clientWidth || document.body.clientHeight)) { 
                	// IE 4 compatible (quirk mode)
                    height = document.body.parentNode.scrollHeight - 20;
                    width = document.body.parentNode.scrollWidth - 20;
                }
                Dimmer.setStyle({
                	height: height+'px',
                	width: width+'px',
                	display: 'block'
                }).fade({duration: .5, from: 0, to: 1});	
            }
            synopsisPopUp.show();
        },
        lazyload: function() {
            $$('img').each(function(ele){
                if (ele.hasAttribute('_src')) {
                    ele.writeAttribute({'src': ele.readAttribute('_src')});
                }
            });
        }    
    },
    search: {
        defaultText: 'Search by keyword, title, author or ISBN...',
        init: function() {
            if (!$('main_search_input')) return;
            if ($F('main_search_input') == '') $('main_search_input').setValue(shop.search.defaultText);
            $('main_search_input').observe('blur', shop.search.ensureDefaultTextIsPresent);
            $('main_search_input').observe('click', shop.search.removeDefaultText);
            var autocompleter = new Ajax.Autocompleter(
                'main_search_input',
                'main_search_suggestions',
                '/autocomplete/search.tao',
                {
                    updateElement:   shop.search.handleSelect,
                    frequency:       0.4
                }
            );
        },
        ensureDefaultTextIsPresent: function(event) {
            var elementText = $F(event.element());
            if ('' == elementText) event.element().setValue(shop.search.defaultText);
        },
        removeDefaultText: function(event) {
            var elementText = $F(event.element());
            if (elementText == shop.search.defaultText) event.element().setValue('');
        },
        handleSelect: function(){
            var url = $A(arguments).first().select('span.autocomplete_url').first().innerHTML;
            if (url) {
                window.location.href = url;
            } else {
                $('main_search_form').submit();
            }
        }
    },
    basket: {
        init: function() {
            var button = $('shopping_cart_lines_btn');
            if (!button) return;
            button.observe('click', shop.basket.toggleFolding);
        },
        add: function(productId) {
            xajax_addProductToBasket(productId);
            shop.basket.addSupplement(productId);
        },
        addQuietly: function (productId) {
            xajax_addProductToBasketQuietly(productId);
            shop.basket.addSupplement(productId);
        },
        // supplementary action to execute after adding item to basket
        addSupplement: function(productId) {
            // Update google analytics
            tao.analytics.track('/product/added-to-basket/'+productId+'/');
            
            // Alter checkout button
            var checkoutButton = $('checkout-button');
            if (!checkoutButton) return;
            var redButtonSrc = '/images/buttons/checkout_button.gif';
            if (checkoutButton.src != redButtonSrc) {
                checkoutButton.src = redButtonSrc;
                checkoutButton.alt = 'Proceed to checkout to process your order';
            }
        },
        toggleFolding: function() {
            var element = $('shopping_cart_lines');
            if (!element.visible()) {
                Effect.BlindDown(element);
                element.setOpacity(0).appear();
                if ($('shopping_cart_lines_btn')) {
                	$('shopping_cart_lines_btn').update('hide contents');
                }
            } else {
                Effect.BlindUp(element);
                element.fade(); 
                if ($('shopping_cart_lines_btn')) {
                    $('shopping_cart_lines_btn').update('view contents');
                }
            }
        }
    },
    form: {
        init: function() {
            // Bind element focus behaviour to all forms with class 'value_hints'
            $$('form.value_hints input[type=text]').invoke('observe', 'focus', shop.form.element.focus);
        },
        element: {
            focus: function(event) {
                var ele = event.element();
                if (!ele.present()) ele.clear();
                ele.highlight();
            }
        }
    },
    tags: {
        init: function() {
            // Extract all product ids page
            var productIds = $$('.tag-product').collect(function(ele){return ele.identify().match(/\d+$/).first()});
            var cloudContainer = $('sidebar-tagcloud-container');
            if (productIds.length > 0 && cloudContainer) {
                cloudContainer.show();
                xajax_displaySidebarTagCloud(productIds);
            } else {
                var cloudBlock = $('sidebar-tagcloud-block');
                if (cloudBlock) cloudBlock.hide();
            }
        }  
    },
    user: {
        register: function(destinationUrl) {
            var validName = tao.form.validateElement($('register_first_name'), 'required', 'Please enter a first name');
            validName &= tao.form.validateElement($('register_last_name'), 'required', 'Please enter a surname');
            // User names are optional for taoshop instances
            var userName = "";
            var validUserName = true;
            var storeId = 0;
            
            if ($('register_user_name')) {
                validUserName = tao.form.validateElement($('register_user_name'), 'required', 'Please enter a user name');
                userName = $F('register_user_name');
            }
            if ($('register_store_id')) {
            	storeId = $F('register_store_id');
            }
            var validEmailAddress = tao.form.validateElement($('register_email_address'), 'emailAddress', 'Please enter a valid email address');
            validEmailAddress &= tao.form.validateElement($('register_confirm_email_address'), function(){return $F('register_email_address') == $F('register_confirm_email_address');}, 'Please ensure your confirmation email address matches your main address');
            var validPassword = tao.form.validateElement($('register_password'), 'password', 'Please choose a password of 6 characters or more');
            if (true == validPassword) validPassword &= tao.form.validateElement($('register_password'), function(password){return !['password', 'password123'].member(password)}, 'This password is too obvious - please choose another');
            validPassword &= tao.form.validateElement($('register_confirm_password'), function(){return $F('register_password') == $F('register_confirm_password');}, 'Please ensure your confirmation password matches your main password');
            var validForm = (validName && validUserName && validEmailAddress && validPassword);
            if (validForm) {
                var optedIn = $$('input:checked[name=SignedUpForEmail]').pluck('value').first();
                destinationUrl  = destinationUrl || '/';
                xajax_userRegister($F('register_first_name'), $F('register_last_name'), userName , $F('register_email_address'), $F('register_password'), optedIn, storeId, destinationUrl);
                tao.analytics.track('/customer/register/');
            } else {
                shop.display.validationWarn();
            }
        },
        signin: function(errorMessageClass) {
            var validEmailAddress = tao.form.validateElement($('sign_in_email_address'), 'emailAddress', 'Please enter a valid email address', errorMessageClass);
            var validPassword = tao.form.validateElement($('sign_in_password'), 'password', 'Please enter a password of 6 characters or more', errorMessageClass);
            if (validEmailAddress && validPassword) {
                xajax_userSignIn($F('sign_in_email_address'), $F('sign_in_password'));
            } else {
            	shop.display.validationWarn();
            }

        },
        saveProfile: function() {
            var validName = tao.form.validateElement($('profile_first_name'), 'required', 'Please enter a first name');
            validName &= tao.form.validateElement($('profile_last_name'), 'required', 'Please enter a surname');
            //User names are optional for taoshop instances
            var validUserName = true;
            var userName = "";
            var storeId = 0;
            
            if ($('profile_user_name')) {
                validUserName = tao.form.validateElement($('profile_user_name'), 'required', 'Please enter a user name');
                userName = $F('profile_user_name');
            }
            if ($('profile_store_id')) {
            	storeId = $F('profile_store_id');
            }
            var validEmailAddress =tao.form.validateElement($('profile_email_address'), 'emailAddress', 'Please enter a valid email address');
            var validForm = (validName && validUserName && validEmailAddress);
            if (validForm) {
                xajax_saveUserProfile(xajax.getFormValues('profile_form'));
            } else {
            	shop.display.validationWarn();
            }

        },
        newPassword: function() {
            var validPassword = tao.form.validateElement($('password_new'), 'password', 'Please choose a password of 6 characters or more');
            validPassword &= tao.form.validateElement($('password_confirm'), function(){return $F('password_new') == $F('password_confirm');}, 'Please ensure your confirmation password matches your main password');
            if (validPassword) {
                xajax_saveUserPassword($F('password_new'));
            } else {
            	shop.display.validationWarn();
            }

        },
        passwordReminder: function() {
            var validEmailAddress = tao.form.validateElement($('password_reminder_email_address'), 'emailAddress', 'Please enter a valid email address');  
            if (validEmailAddress) {
                xajax_sendPasswordReminder($F('password_reminder_email_address'));
            } else {
            	shop.display.validationWarn();
            }
        },
        passwordReset: function() {
            var validPassword = tao.form.validateElement($('reset_password'), 'password', 'Please choose a password of 6 characters or more');
            validPassword &= tao.form.validateElement($('reset_confirm_password'), function(){return $F('reset_password') == $F('reset_confirm_password');}, 'Please ensure your confirmation password matches your main password'); 
            if (validPassword) {
                xajax_userResetPassword($F('user-id'), $F('hash'), $F('reset_password'));
            }
        },
        saveDeliveryAddress: function() {
            var validName = tao.form.validateElement($('da_first_name'), 'required', 'Please enter a first name');
            validName &= tao.form.validateElement($('da_last_name'), 'required', 'Please enter a last name');
            var validLine1  = tao.form.validateElement($('da_line1'), 'required', 'Please enter the first line of your address');
            var validSuburb = tao.form.validateElement($('da_line3'), 'required', 'Please enter your suburb');
            var validCity = true;
            if ($('da_line4')) {
                // City isn't always present but is required when it is
                validCity = tao.form.validateElement($('da_line4'), 'required', 'Please enter your city');
            }
            var validCode = tao.form.validateElement($('da_postcode'), 'required', 'Please enter a valid post/zip code');
            var validState = tao.form.validateElement($('da_state'), 'required', 'Please choose a state');
            var validForm = (validName && validLine1 && validSuburb && validCity && validCode && validState);
            if (validForm) {
                xajax_saveDeliveryAddress(xajax.getFormValues('delivery_address_form'))
            } else {
            	shop.display.validationWarn();
            }
        },
        saveBillingAddress: function() {
            var validName   = tao.form.validateElement($('ba_first_name'), 'required', 'Please enter a first name');
            validName      &= tao.form.validateElement($('ba_last_name'), 'required', 'Please enter a last name');
            var validLine1  = tao.form.validateElement($('ba_line1'), 'required', 'Please enter the first line of your address');
            var validSuburb = tao.form.validateElement($('ba_line3'), 'required', 'Please enter suburb');
            var validCity = true;
            if ($('ba_line4')) {
                // City isn't always present but is required when it is
                validCity = tao.form.validateElement($('ba_line4'), 'required', 'Please enter your city');
            }
            var validCode   = tao.form.validateElement($('ba_postcode'), 'required', 'Please enter a valid post/zip code');
            var validState  = tao.form.validateElement($('ba_state'), 'required', 'Please choose a state');
            var validForm   = (validName && validLine1 && validSuburb && validCity && validCode && validState);
            if (validForm) {
                xajax_saveBillingAddress(xajax.getFormValues('billing_address_form'))
            } else {
            	shop.display.validationWarn();
            }
        }
    },
    checkout: {
    	init: function() {
    		shop.checkout.voucher.init();
    	    var deliveryAddress = $('delivery_address');
    		if (deliveryAddress) deliveryAddress.observe('change', shop.checkout.updateDeliveryOptions);
    		$$('input[name=DeliveryOption]').invoke('observe', 'click', shop.checkout.updateDeliveryMethod);
    	},
    	voucher: {
			init: function() {
				if (!$('PromotionalCode')) return;
				shop.checkout.voucher.inputText = $F('PromotionalCode');
				$('PromotionalCode').observe('click', shop.checkout.voucher.clear)
									.observe('blur', shop.checkout.voucher.restore);
			},
			clear: function() {
				if ($F('PromotionalCode') == shop.checkout.voucher.inputText) {
					$('PromotionalCode').clear()
				}
			},
			restore: function() {
				if ($F('PromotionalCode').strip().empty()) {
					$('PromotionalCode').setValue(shop.checkout.voucher.inputText);
				}
			}
    	},
    	updateDeliveryOptions: function() {
    	    xajax_reloadDeliveryOptions($F('delivery_address'));
    	},
    	updateDeliveryMethod: function(event) {
    		xajax_updateDeliveryMethod($F(event.element()));
    	},
        saveBankcard: function() {
            if (shop.checkout.validateBankcard()) {
            	var startDate = null;
                if ($('start_month') && $('start_year')) {
                    startDate = $F('start_month')+'/'+$F('start_year');
                }
                var expiryDate = $F('expiry_month')+'/'+$F('expiry_year');
                var saveBankcard = ($('save_details') && $F('save_details') != null);
                shop.checkout.storeBankcard($F('card_type'), $F('card_number'), $F('card_holder_name'), $F('security_number'), $F('issue_number'), startDate, expiryDate, saveBankcard);
                shop.checkout.reloadCardDetails();
                return true;
            }
         	return false;
        },
        validateBankcard: function () {
        	$('card_number').setValue($F('card_number').gsub(/\D/, ''));
            var validName = tao.form.validateElement($('card_holder_name'), 'required', 'Please enter the card holder name');
            var validNumber = tao.form.validateElement($('card_number'), 'cardNumber', 'Please enter a valid card number');
            var validSecurityCode = tao.form.validateElement($('security_number'), 'securityNumber', 'Please enter a valid security number');
            var validOptionalData = tao.form.validateElement($('issue_number'), shop.checkout.validateIssueNumber, 'Please enter an issue number');
            return (validName && validNumber && validSecurityCode && validOptionalData);
        },
        reloadCardDetails: function() {
        	// Passes safe card number back to server to reload card details
        	xajax_displayCardDetails(shop.checkout.getObscuredCardNumber(shop.checkout.newbankcard.get('type'), shop.checkout.newbankcard.get('number')));
        },
        validateIssueNumber: function() {
        	if (!$('card_type') || !$('issue_number')) return true;
        	if ($F('card_type').toLowerCase() == 'switch') { 
        		return tao.form.isElementValid($('issue_number'), 'issueNumber');
        	}
        	return true;
        },
        storeBankcard: function(type, number, name, securityNumber, issueNumber, startDate, expiryDate, saveBankcard) {
            // Saves the card details to a Hash
            shop.checkout.newbankcard = new Hash({
                'type': type,
                'name': name.strip(),
                'number': number.gsub(/\D/, ''),
                'securityNumber': securityNumber.gsub(/\D/, ''),
                'issueNumber': (issueNumber) ? issueNumber : null,
                'startDate': (startDate) ? startDate : null,
                'expiryDate': expiryDate,
                'saveCardDetails': saveBankcard
            });
        },
        getObscuredCardNumber: function(type, number) {
            var partToObscure = number.substring(0, number.length-4);
            var visiblePart = number.substring(number.length-4);
            return type+' - '+partToObscure.gsub(/./, 'x') + visiblePart;;
        },
        submitOrder: function() {
            if (!shop.checkout.isCheckoutComplete()) {
            	shop.display.error('There are errors in your submitted data');
            	return;
            }
            shop.display.notify('Processing order...');
            shop.checkout.registerGuestEmailAddress();
            var orderData = shop.checkout.getOrderDataForSubmission();
            xajax_submitOrder(orderData.toJSON());
            tao.analytics.track('/checkout/completed/');

        },
        isCheckoutComplete: function() {
            return (shop.checkout.isGuestEmailAddressValid() && shop.checkout.isDeliveryDataValid() && shop.checkout.isPaymentDataValid());
        },
        isDeliveryDataValid: function() {
        	return tao.form.validateElement($('delivery_address'), 'nonzero', 'Please choose a delivery address');	
        },
        isGuestEmailAddressValid: function() {
            // this only applies where guest customers can place orders (see also: checkout_view_GuestEmailAddress)
            if ($('guest_email_address') == null) return true;
            if (tao.form.validateElement($('guest_email_address'), 'emailAddress', 'Please provide valid email address')) {
                if (!shop.checkout.isGuestUserAllowed && $('guest_password')) {
                    tao.form.displayError($('guest_password'), 'Please sign-in or use different email address for your order');
                }
                return shop.checkout.isGuestUserAllowed;
            }
            return true;
        },
        isGuestEmailAddressAleadyRegistered: function() {
            // this only applies where guest customers can place orders (see also: checkout_view_GuestEmailAddress)
            if ($('guest_email_address')) {
                //$('guest_password_input').hide();
                //shop.checkout.isGuestUserAllowed = true;
                xajax_checkIsGuestEmailAddressAlreadyRegistered($('guest_email_address').value);
            }
        },
        isGuestUserAllowed: true,
        isPaymentRequired: function() {
        	return (shop.checkout.getOrderTotal() > 0);
        },
        isPaymentDataValid: function() {
            if (!shop.checkout.isPaymentRequired()) return true;
        	if (!shop.checkout.hasPaymentMethodBeenSelected()) {
                shop.display.error('Please choose a payment method');
                return false;
            }
        	return tao.form.validateElement($('billing_address'), 'nonzero', 'Please choose a billing address');	
        },
        isExistingBankcardSelected: function() {
        	return ($('bankcard') && $F('bankcard') > 0);
        },
        isNewBankcardSelected: function() {
        	return ($('bankcard') && 'new' == $F('bankcard'));
        },
        isABankcardSelected: function() {
        	return (shop.checkout.isExistingBankcardSelected() || shop.checkout.isNewBankcardSelected());
        },
        isGiftcardSelected: function() {
        	return !!($('giftcard_id') && $('giftcard_allocation') && $F('giftcard_allocation') > 0);
        },
        hasPaymentMethodBeenSelected: function() {
        	return (shop.checkout.isABankcardSelected() || shop.checkout.isGiftcardSelected());
        },
        getOrderTotal: function() {
        	var orderTotal = $('order_totals_container').select('td.total').first().innerHTML.match(/\d+.\d+/).first();
        	return parseFloat(orderTotal);
        },
        getOrderDataForSubmission: function() {
            var orderData = new Hash();
            if ($('delivery_address')) orderData.set('deliveryAddressId', $F('delivery_address'));
            if (shop.checkout.isPaymentRequired()) {
            	orderData.set('billingAddressId', $F('billing_address'));
            	if (shop.checkout.isExistingBankcardSelected()) {
                    orderData.set('existingbankcard', {bankcardId: $F('bankcard')});
                } else if (shop.checkout.isNewBankcardSelected()) {
                    orderData.set('newbankcard', shop.checkout.newbankcard);
                }
                if (shop.checkout.isGiftcardSelected()) {
                   orderData.set('giftcard', {giftcardId: $F('giftcard_id'), allocation: $F('giftcard_allocation')}); 
                }
            }
            return orderData;
        },
        registerGuestEmailAddress: function() {
            if ($('guest_email_address')) {
                xajax_registerGuestEmailAddress($F('guest_email_address'));
            }
            return true;
        },
        showTermsAndConditions: function() {
            var popUp = $('divSitePopup');
            popUp.update($('terms-and-conditions').innerHTML)
                 .setStyle({height: '400px', overflow: 'auto'});
            tao.position.centreInViewport(popUp.show());
        },
        showPolicy: function(e){
            var popUp = $('divSitePopup');
            popUp.update($(e).innerHTML)
                 .setStyle({height: '400px', overflow: 'auto'});
            tao.position.centreInViewport(popUp.show());
        },
        signinGuest: function(errorMessageClass) {
            var validEmailAddress = tao.form.validateElement($('guest_email_address'), 'emailAddress', 'Please enter a valid email address', errorMessageClass);
            var validPassword = tao.form.validateElement($('guest_password'), 'password', 'Please enter a password of 6 characters or more', errorMessageClass);
            if (validEmailAddress && validPassword) {
                xajax_guestSignIn($F('guest_email_address'), $F('guest_password'));
            } else {
                shop.display.validationWarn();
            }
        }
    },
    deliveryInfo: {
        init: function() {
            $$('.info_pop_up_link').invoke('observe', 'click', shop.deliveryInfo.show);
        },
        show: function(event) {
            try {
                var clickedEle = event.element();
                var infoContentContainer = clickedEle.previous('.info_pop_up_content');
                var infoPopUp = $('divSitePopup');
                infoPopUp.update(infoContentContainer.innerHTML);
                tao.position.nextToClick(infoPopUp.show(), event.pointerX(), event.pointerY());  
            } catch (error) {}
        }
    },
    // Show/hide function for <dl>
    expandingList: {
        init: function() {
            if ($$('dl.expanding_list')) {
                $$('dl.expanding_list').each(function (elem) {
                    elem.select('dd').invoke('hide');
                    elem.select('dt').invoke('observe', 'click', shop.expandingList.expand);
                    if(elem.select('dd .hide_btn')) {
                        elem.select('dd .hide_btn').invoke('observe', 'click', shop.expandingList.contract);
                    }
                });
            }
        },
        expand: function(event) {
            var element = event.element();
            var hiddenElement = element.next(0);
            if (!hiddenElement || !hiddenElement.visible()) {
                Effect.BlindDown(hiddenElement);
                hiddenElement.setOpacity(0);
                hiddenElement.appear(); 
            }
        },
        contract : function(event) {
            var element = event.element();
            var visibleElement = element.ancestors()[0];
            if (visibleElement && visibleElement.visible()) {
                Effect.BlindUp(visibleElement);
                visibleElement.fade();
            }
        }
    },
    pod: {
        init : function() {
    		try {
    			Sortable.create('PodListLeft',{handle:'DragHandle',
    			onUpdate:function() {
    			xajax_reorderPods(Sortable.serialize('PodListLeft'),'Left')
    			}});
    		} catch (error) {}
    			
    		try {
    			Sortable.create('PodListRight',{handle:'DragHandle',
    			onUpdate:function() {
    			xajax_reorderPods(Sortable.serialize('PodListRight'),'Right')
    			}});
    		} catch (error) {}
        }
    },
    collection: {
    	remove:	function(productId) {
    		xajax_removeProductFromCollection(productId);
    	},
    	toggleFavourite: function(productId) {
    		xajax_toggleCollectionProductStatus(productId);
    	},
    	writeReview: function(productId) {
    		
    	}
    },
    countdown: {
    	init: function() {
    		sourceProductIds = "";
    	},
    	addProducts: function() {
    		sourceProductIds = sourceProductIds + "\d" + $F('SourceProductIds'); 
    		xajax_addCountdownProducts($F('CountdownId'), sourceProductIds);
    	}
    },
    cms: {
    	defaultText: 'Search by product Id or source product id',
    	init: function() {
    	    if (!$('product_catalogue_form_productId')) return;
            if ($F('product_catalogue_form_productId') == '') $('product_catalogue_form_productId').setValue(shop.cms.defaultText);
            $('product_catalogue_form_productId').observe('blur', shop.cms.ensureDefaultTextIsPresent);
            $('product_catalogue_form_productId').observe('click', shop.cms.removeDefaultText);
        },
        productOverviewInit: function () {
        	$('product_form_CategoryDropdown').observe('change', shop.cms.updateCategoryId);
        	$('product_form_PublisherDropdown').observe('change', shop.cms.updatePublisherId);
        },
        ensureDefaultTextIsPresent: function(event) {
            var elementText = $F(event.element());
            if ('' == elementText) event.element().setValue(shop.cms.defaultText);
        },
        removeDefaultText: function(event) {
            var elementText = $F(event.element());
            if (elementText == shop.cms.defaultText) event.element().setValue('');
        },
        saveProduct: function() {
    		xajax_saveProductDetails(xajax.getFormValues($('product_catalogue_product_form')));
    	},
    	saveProductAttributes: function() {
    		xajax_saveProductAttributes(xajax.getFormValues($('product_catalogue_product_attributes_form')));
    	},
    	saveProductStockDetails: function() {
    		xajax_saveProductStockDetails(xajax.getFormValues($('product_catalogue_product_stock_form')));
    	},
    	updateCategoryId: function() {
    		$('product_form_CategoryId').setValue($F('product_form_CategoryDropdown'));
    	},
    	updatePublisherId: function() {
    		$('product_form_PublisherId').setValue($F('product_form_PublisherDropdown'));
    	},
    	productContributorsInit: function() {
    		$$('select.cmsContributor').invoke('observe','change', shop.cms.updateContributors);	
    	},
    	updateContributors: function(event) {
    		var ele = event.findElement('.cmsContributor');
    		source = $(ele).identify();
    		id = source.substring(source.lastIndexOf("_")+1);
    		elementId = "product_form_ContributorListContributor_"+id;
    		$(elementId).setValue($F(ele));
    	},
    	saveProductContributors: function () {
    		xajax_saveProductContributors(xajax.getFormValues($('product_catalogue_contributors_form')));
    	},
    	removeProductContributor: function (id) {
    		divId = 'product_form_ContributorListContainer_'+id;
    		$(divId).remove();
    		xajax_saveProductContributors(xajax.getFormValues($('product_catalogue_contributors_form')));
    	},
    	addProductContributor: function (contributorId) {
    		if (contributorId === undefined) {
    			contributorId = 1;
    		}
    		$('product_catalogue_contributors_form').insert('<input type="text" size="50" id="product_form_ContributorListContributor_new" name="ContributorId_new" value="'+contributorId+'" readonly/>')
    		xajax_saveProductContributors(xajax.getFormValues($('product_catalogue_contributors_form')));
    	},
    	createPublisher: function (publisherId) {
    		$('product_form_PublisherId').setValue(publisherId);
    		$('product_form_PublisherDropdownContainer').hide();
    	}
    }
}
try {
    document.observe('dom:loaded', shop.init);
    Event.observe(window, 'load', shop.synopsis.lazyload);
} catch (error) {
    tao.debug(error.name+': '+error.message);
}
