var Class = {
  create: function() {
    return function() {
      this.initialize.apply(this, arguments);
    }
  }
}

var quantityMessage = null;
var scOrderLoaded = false;
var scSummaryLoaded = false;
var MSG_INVALID_PACKAGE_QUANTITY = 'Please check a products quantity';

function lookupOnSubmit()
{
    showProgress();
    setTimeout('loadProduct();', 1000);
}

function renderPartNote(product)
{
    var html = '';
    var purch_note = $('purch_note', product).text();
    if (!purch_note.length) {
        return html;
    }
    var title = $('suffix', product).text() + ' - ';
    var short_num = $('short_num', product).text();
    var long_num = $('long_num', product).text();
    if (long_num.length) {
        title += long_num;
    } else {
        title += short_num;
    }
    html += '<a class="part_note_link" href = "#" onclick = "return false" title="hideselects=[off] cssbody=[hintbody] '
         + 'requireclick=[on] cssheader=[hinthdr] header=[' + htmlspecialchars(title) + '] body=['
         + nl2br(htmlspecialchars(purch_note)) + ']">'
         + '<img style="vertical-align:middle" src = "img/note.png" alt = "" class="part_note_icon" />'
         + '</a>';
    return html;
}

function showMessageCatalogAccessDeny()
{
    alert('You do not currently have cataloging privileges.\nPlease contact your salesperson for further details!');
}

function shoppingCartShowMessage() 
{
    if (typeof quantityMessage == 'string' && quantityMessage.length && scSummaryLoaded && scOrderLoaded) {
        alert(quantityMessage);
        quantityMessage = null;
    }
}

var newAddedProducts = new Array(12);
var SCPQMTimers = {};

function scPakageQuantityMessageHide(id_product) 
{
    $('#msg_pkg_qty_' + id_product).remove();
    if (SCPQMTimers[id_product]) {
        SCPQMTimers[id_product] = null;
    }
}

function scQuantityBtnOnFocus(quantityElement)
{
    scIsValidPakageQuantity(quantityElement);
}

function scQuantityBtnOnBlur(quantityElement)
{
    var id_product = scGetProductIdByQuantityElement(quantityElement);
    scPakageQuantityMessageHide(id_product);
}

function scPQMessageClearTimer(id_product)
{
    if (SCPQMTimers[id_product]) {
        clearTimeout(SCPQMTimers[id_product]);
    }
}

function scPQMessageStartTimer(id_product)
{
    scPQMessageClearTimer(id_product);
    SCPQMTimers[id_product] = setTimeout('scPakageQuantityMessageHide(' + id_product + ')', 1000);
}

function scGetProductIdByQuantityElement(quantityElement)
{
    var parts = quantityElement.id.match(/(\d+)/g);
    var id_product = 0;
    if (parts.length) {
        id_product = parts[parts.length - 1];
    }
    return id_product;
}

function scIsValidPakageQuantity(quantityElement)
{
    var id_product = scGetProductIdByQuantityElement(quantityElement);
    var quantity = parseInt(quantityElement.value, 10);
    if (isNaN(quantity)) {
        //return true;
    }
    var inputPakageQuantity = getPreviousTag(quantityElement, 'INPUT');
    var td = getParentTag(quantityElement, 'TD');
    if (!inputPakageQuantity) {
        return true;
    }
    var pkg_qty = parseInt(inputPakageQuantity.value, 10);
    if (pkg_qty == 0 || pkg_qty == 1) {
        return true;
    }
    if (quantity % pkg_qty == 0) {
        scPakageQuantityMessageHide(id_product);
        return true;
    }
    //$(quantityElement).unbind('blur');
    //$(quantityElement).blur();
    //$(quantityElement).blur(function onblur(event) {scIsValidPakageQuantity(this);});
    //$(quantityElement).unbind('focus', scQuantityBtnOnFocus);
    //$(quantityElement).focus(scQuantityBtnOnFocus);
    var timerEvents = 'onmouseout = "scPQMessageStartTimer(' + id_product + ')" onmouseover = "scPQMessageClearTimer(' + id_product + ')" ';
    var left = -210 + Math.floor(td.clientWidth / 2);
    var tipMessage = 'This item must be ordered in multiples of <span class="msg_pkg_qty_value">' + pkg_qty + '</span>';
    if (isNaN(quantity)) {
        //tipMessage = 'Quantity must be numeric';
    }
    var html = '<div id = "msg_pkg_qty_' + id_product + '" '
             + 'onclick = "$(this).remove()" style="position:relative;">'
             + '<div ' + timerEvents + 'style = "cursor:pointer; width:230px; position:absolute; text-align:left; top:-30px; left:' + left + 'px;">'
             + '<div ' + timerEvents + 'style = "background-color:#FAFB89; padding:5px; border:solid red 1px; border-bottom:none;">'
             + tipMessage
             + '</div>'
             + '<div ' + timerEvents + 'style = "background-image:url(\'img/pkg_qty_tip_bottom.png\'); background-repeat:no-repeat; height:5px;"></div>'
             + '</div>'
             + '</div>';
    var container = getParentTag(inputPakageQuantity, 'TD');
    // if (inputPakageQuantity.parentNode && inputPakageQuantity.parentNode.tagName == 'SPAN') {
        // container = inputPakageQuantity.parentNode;
    // } else {
        // container = inputPakageQuantity;
    // }
    $('#msg_pkg_qty_' + id_product).remove();
    $(container).prepend(html);
    return false;
    var productName = $('#product_name_' + id_product).text();
    var message = 'The quantity you entered for part ' + productName + ' is not a multiple \n' 
                + 'of ' + pkg_qty + '. This part has not been added to your cart. Please enter\n'
                + 'a quantity that is a multple of ' + pkg_qty + ' to meet our packaging \n'
                + 'requirements';
    return message;
    alert(message);
    return false;
}

function scShowPQAlert()
{
    var message = 'This item must be ordered in specific multiples. \n'
                + 'See the yellow pop-up next to the "Order Qty" field.';
    alert(message);
}


function scQuantityOnBlur()
{
    scIsValidPakageQuantity.call(this);
}

function addProduct(id)
{
    //alert('addProduct');
    showProgress();
    var t = $.makeArray($('.buy_qut'));
    var x = new Object();
    pattern = /^\d+$/
    var flag = false;
    var is_valid_pkg_qty = true;
    
    $('div.showcnt>form>span.showcnt>input.buy_qut').each( function () {
        if (this.value.match(pattern)) {
            if (!scIsValidPakageQuantity(this)) {
                is_valid_pkg_qty = false;
                return true;
            }
            x[this.id] = this.value;
            if (this.id) {
                str = this.id;
                newAddedProducts.push(str.substring(10));
            }
            flag = true;
        }
    });
    if (!is_valid_pkg_qty) {
        hideProgress();
        scShowPQAlert();
        return false;
    }
    if(flag){
        $.post(
             "cart?action=additem&redir=shopping/wizard", 
            x,
            addProductSuccess
            );
    } else {
        alert("You should fill in at least one 'Quantity' field. Please use numbers.");
		$('#loadprogress').hide();
    }
    return false;
}

function proceedToCheckoutOnSubmit()
{
    var err = [];
    var validQuantity = true;
    $('#orderForm input.quantity').each(function(){
        if (!scIsValidPakageQuantity(this)) {
            validQuantity  = false;
        }
    });
    if (!validQuantity) {
        err.push(MSG_INVALID_PACKAGE_QUANTITY);
    }
    if (err.length) {
        var msgError = '';
		for (var i = 0; i < err.length; i++) {
			msgError += err[i] + "\n";
		}
		alert(msgError);
		return false;
	}
    return confirmExit();
}

function confirmExit()
{
    var cnf_msg = "You changed quantities for parts on this page, but you have not added them to your cart.\nThese items will be added to your order in the quantities you specified if you click OK.";

    if (isQuantityChanged) {
        if (confirm(cnf_msg)) {
            if (!validQuantityValues($('#orderForm').formToArray())) {
                return false;
            }
            var options = {
                dataType: 'xml',
                success: function(responseXML, status, form) {
                    var data = $('list', responseXML).text();
                    $('a#lastAddedProduct').remove();
                    alert(data);
                },
                beforeSubmit: validQuantityValues
            };
            $('#orderForm').ajaxSubmit(options);
        }
    }
    return true;
}

function addProductSuccess()
{
	loadOrder();
	loadSummary();
}

function shoppingCartRenderNotFoundNotice(isSearchResults)
{
    var search_notice_box = '<div style="padding: 5px 5px 10px 10px;">';
    if (isSearchResults) {
        search_notice_box += '<strong>If the results above are still not what you are looking for, please use one of the options below:</strong> '
    }
    search_notice_box += '<div><img src="img/li.png" align="top">&nbsp;&nbsp; Please start a new search below or <a href="info/contact">contact us</a> for more information.</div>'
        + '<div><img src="img/li.png" align="top">&nbsp;&nbsp; <a href="service/request_a_product?part_number=' + num_desc + '">Click here</a> '
        + 'if you would like this part to be researched by Total Auto.</div>'
        + '</div>';
    $('#search_notice_box').html(search_notice_box);
}

function shoppingCartRenderClossReference(products, num_desc)
{
    var search_message_box = '<img align="middle" src="img/important.png"><span style="color:#FE0000;"><strong>' + num_desc + ' is not one of our current part numbers, listed below are any possible cross-references for ' + num_desc + '</strong></span>';
    $('#search_message_box').html(search_message_box);
    shoppingCartRenderNotFoundNotice((($("product", products).size() + $("cross_reference", products).size()) > 0));
    
    $('#cross_reference tr:gt(0)').remove();
 
     //amount of table columns
     var table_cols = $('#searched tr:eq(0) td').size();
         
     $("cross_reference", products).each(function(n){
        
        var s_num = $("cross_reference>long_num:eq("+n+")", products).text() ? $("cross_reference>long_num:eq("+n+")", products).text() : $("cross_reference>short_num:eq("+n+")", products).text;
        var style = "";
        var quant_msg = '';
        
        $('<tr id="tr_' + $("cross_reference>id:eq("+n+")", products).text()+'" class="light_yellow_row"><td>'
                            + '<a target="_blank" href="' + $("cross_reference>pop_image:eq("+n+")", products).text()
                            + '" onclick="return pop_up(this);" onmouseover="return pop_up(this);" onmouseout="return close_pop_up(this);"><img src="' + $("cross_reference>image:eq("+n+")", products).text() + '"></a>'
                + '</td><td>' + $("cross_reference>mfg:eq("+n+")", products).text()
                + '</td><td>' + $("cross_reference>maker:eq("+n+")", products).text()
                + '</td><td>' + $("cross_reference>description:eq("+n+")", products).text()
                + '</td><td><a title="click to use" href="nonmenu/prctsearchres?part=number&num_desc=' + s_num + '&subm=Search" '
				+ 'onclick="customerLog(\'CLICKED_CROSS-REFERENCE_LINK\', [\'' 
				+ (s_num.replace(/'"/, "\'")) + "', '" + (num_desc.replace(/'"/, "\'"))
				+ '\']); loadProduct(\''+s_num+'\', \'number\', 1); return false;">' + s_num + "</a>"
                + '</td>' +
          '</tr>')
        .appendTo('#cross_reference');
    });
}

function shoppingCartShowCrossReference()
{
    $('#cross_reference_available_msg').remove();
    $('#cross_reference').removeClass().addClass('showcnt');
    $('#cross_reference_header').before('<tr id="title_search_results_cross_reference" class="yellow_box"><td colspan="5"><div style="padding:5px; color:red; text-align:left;"><strong>Search results from the cross reference database:</strong></div></td></tr>');
    $('#cross_reference_header').addClass('yellow_row');
    document.location.hash = 'crossReferenceAnchor';
}

Array.prototype.inArray = function ( search_phrase )
{
  for( var i = 0; i < this.length; i++ )
  {
    if( search_phrase == this[i] )
    {
      return i;
    }
  }
  return false;
}

function getElementPos(obj){
	var l = 0;
	var t = 0;
	var w = obj.offsetWidth;
	var h = obj.offsetHeight;
	while (obj) {
		l += obj.offsetLeft;
		t += obj.offsetTop;
		if ((obj.tagName != "TABLE") && (obj.tagName != "BODY")) {
			l += (obj.clientLeft)?obj.clientLeft:0;
			t += (obj.clientTop)?obj.clientTop:0;
		}
		obj = obj.offsetParent;
	}
	var res = new Object();
	res.x = l;
	res.y = t;
	res.left = l;
	res.top = t;
	res.w = w;
	res.h = h;
	res.width = w;
	res.height = h;
	return res;
}

function getPreviousTag(obj, tag)
{
	var tmp = obj;
	while (tmp = tmp.previousSibling) {
		if (tmp.nodeName == tag) {
			return tmp;
		}
	}
	return null;
}

function getNextTag(obj, tag)
{
	var tmp = obj;
	while (tmp = tmp.nextSibling) {
		if (tmp.nodeName == tag) {
			return tmp;
		}
	}
	return null;
}

function getParentTag(obj, tag)
{
	var tmp = obj;
	while (tmp = tmp.parentNode) {
		if (tmp.nodeName == tag) {
			return tmp;
		}
	}
	return null;
}

	function getScrollY() 
	{
		scrollY = 0;    
		if (typeof window.pageYOffset == "number") {
			scrollY = window.pageYOffset;
		} else if (document.documentElement && document.documentElement.scrollTop) {
			scrollY = document.documentElement.scrollTop;
		}  else if (document.body && document.body.scrollTop) {
			scrollY = document.body.scrollTop; 
		} else if (window.scrollY) {
			scrollY = window.scrollY;
		}
		return scrollY;
	}
  
	function getInnerHeight() 
	{
	    height = 0;
	    if (window.innerHeight) {
			height = window.innerHeight - 18;
	  	} else if (document.documentElement && document.documentElement.clientHeight) {
	  		height = document.documentElement.clientHeight;
	  	} else if (document.body && document.body.clientHeight) {
	  		height = document.body.clientHeight;
		}
		return height;
	}

	function popUpDivImage_correctPosition(img, y)
	{
		var scrollY = getScrollY();
		var innerHeight = getInnerHeight();
		var imgW = img.width;
		var imgH = img.height;
		
		var max_y = scrollY + innerHeight - imgH;
		var msg = 'ScrollY:'+getScrollY()+"\n"+
			'y='+y+"\n"+
			'MAX Y:'+max_y+"\n"+
			imgW + 'x' +imgH+"\n"+
			'window.innerHeight = '+innerHeight;
		//alert(msg);
		//document.title = scrollY + ' ' + innerHeight;
		if (y > max_y) {
			y = max_y;
			y-=40;
		}
		img.parentNode.style.top = y+'px';
		img.parentNode.style.zIndex = 199;
		return;
	}
	
	function initLinkShowImg()
	{
		$('a.linkShowImg').each(function (n) {
			var current = this;
			this.onclick = showPopupImage;
			this.onmouseover = showPopupImage;
			this.onmouseout = function(event){
				$('div.popUpDivImage').remove();
				return false;
			}
		});
	}
		
	function showPopupImage(event)
	{
		$('div.popUpDivImage').remove();
		var pos = getElementPos(this.parentNode);
//        alert(pos.x + ' ' + this.parentNode);
		$('td.tdShowImg').removeClass('tdShowImg');
		$('<div class="popUpDivImage" style="top:-800px; left:' + (pos.x + pos.w) + 'px;"><img onload = "popUpDivImage_correctPosition(this, '+pos.y+')" src="'+this.href+'"></div>').prependTo(document.body);
		//$('<div class="popUpDivImage" style="left:'+window.event.offsetX+'px;"><img onload = "popUpDivImage_correctPosition(this, '+pos.y+')" src="'+this.href+'"></div>').prependTo(document.body);
		$(this.parentNode).addClass('tdShowImg');
		return false;
	}

	function addCommas(nStr)
	{
	    nStr += '';
	    x = nStr.split('.');
	    x1 = x[0];
	    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 outMoney(amount)
	{
		var val = 0;
		if (typeof amount == 'string') {
			val = parseFloat(amount);
		} else if (typeof amount == 'number') { 
			val = amount;
		} else {
			return '';
		}
		return addCommas(val.toFixed(2));
	}

	function validQuantityValues(data, set, options) 
	{
        //alert('validQuantityValues');
        inputNamePattern = /^quantity\[\d+\]$/
        pattern = /^\d+$/
        var is_valid_pkg_qty = true;
        var is_valid_qty = true;
        for (i = 0; i < data.length - 1; i++) {
            var validName = inputNamePattern.test(data[i]['name']);
            var validValue = pattern.test(data[i]['value']);
            if (validName && !validValue) {
                alert('All fields of quantity must be numeric');
                return false;
            }
        }
        for (i = 0; i < data.length - 1; i++) {
            var validName = inputNamePattern.test(data[i]['name']);
            if (validName) {
                var element = $('input[name="' + data[i]['name'] + '"]').get(0);
                if (element) {
                    if (!scIsValidPakageQuantity(element)) {
                        is_valid_pkg_qty = false;
                    };
                }
            }
        }
        if (!is_valid_pkg_qty) {
            scShowPQAlert();
            return false;
        }
        return true;
	}
    
    function scAfterQuantityUpdate(elements) 
    {
        inputNamePattern = /^quantity\[\d+\]$/
		pattern = /^\d+$/
		for (var i in elements) {
			var validName = inputNamePattern.test(elements[i].name);
			if (validName) {
                var value = parseInt(elements[i].value, 10);
                if (value == 0) {
                    var parts = $('input[name="' + elements[i].name + '"]').attr('id').split(/[\]\[]/, 2);
                    var id_product = parts[1];
                    $('#squantity_' + id_product).val('');
                }
            }
        }
    }
	
	function confirmBuy()
	{
	    var requestData = new Object();
	    pattern = /^\d+$/
	    var isEmptyRequest = true;
        var is_valid_pkg_qty = true;
        
	    $('form.formBuyProd div.showcnt input.buy_qut').each( function () {
			if (this.value.match(pattern)) {
				requestData[this.id] = this.value;
				isEmptyRequest = false;
                if (!scIsValidPakageQuantity(this)) {
                    is_valid_pkg_qty = false;
                }
			}
		});
		$('div.showcnt>form>span.showcnt>input.buy_qut').each( function () {
			if (this.value.match(pattern)) {
				requestData[this.id] = this.value;
				isEmptyRequest = false;
                if (!scIsValidPakageQuantity(this)) {
                    is_valid_pkg_qty = false;
                }
			}
		});
		
	    if(!isEmptyRequest) {
			var cnf_msg = "You entered quantities for parts on this page, but you have not added them to your cart.\nThese items will be added to your order in the quantities you specified if you click OK.";
			if (confirm(cnf_msg)) {
                if (!is_valid_pkg_qty) {
                    hideProgress();
                    scShowPQAlert();
                    return false;
                }
				$.ajaxSetup({async: false});
				//$.post('cart?action=additem&redir=shopping/wizard', requestData, addProductSuccess);
				$.post('cart?action=additem&redir=shopping/wizard', requestData);
			}
	    }
		return true;
	}
	
	// this array alwo exists in Lib/smarty.func.php
	var RED_SUFFIXES = ['GMX', 'CHX', 'FDX', 'HAX', 'NSX', 'TAX'];
	function is_red_suffix(_suffix)
	{
		var ret = RED_SUFFIXES.inArray(_suffix);
		if (typeof ret == 'boolean' && ret == false) {
			return false;
		}
		return true;
	}
	
function openPopupByLocation(location, target, pW, pH)
{
	if (typeof pW != 'number') {
		pW = screen.width/2;
	}
	if (typeof pH != 'number') {
		pH = screen.height - screen.height/3;
	}
	var top = screen.height/2-pH/2;
	var left = screen.width/2-pW/2; 
	var params = 'toolbar=0,location=0,menubar=0,resizable=1,status=0,scrollbars=yes,screenX='
		+left+',screenY='+top+',top='+top+',left='+left
		+',width='+pW+',height='+pH;
	var wnd = window.open(location, target, params);
		wnd.opener = self;
		wnd.focus();
}

function number_format( number, decimals, dec_point, thousands_sep ) 
{
    var n = number, c = isNaN(decimals = Math.abs(decimals)) ? 2 : decimals;
    var d = dec_point == undefined ? "." : dec_point;
    var t = thousands_sep == undefined ? "," : thousands_sep, s = n < 0 ? "-" : "";
    var i = parseInt(n = Math.abs(+n || 0).toFixed(c)) + "", j = (j = i.length) > 3 ? j % 3 : 0;
    return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : "");
}

function rewardPoints(points, qty)
{
    var points = number_format(parseFloat(points), 2, '.', ',');
    var qty = parseFloat(qty);
    if (qty) {
        points *= qty;
    }
    var result = number_format(points, 2, '.', ',');
    if (result == '0.00') {
        return '0';
    }
    return result;
}

function formErrorOnClick(elementName)
{
    document.location.hash = 'anchor_' + elementName;
    // var anchor = $('a[name="anchor_' + elementName + '"]').get(0);
    // if (anchor) {
        // var td = getParentTag(anchor, 'TD');
        // if (td) {
            // $('[name]:eq(1)', td).focus();
        // }
    // }
    return false;
}

function array_shift( array ) 
{
    if (array.length > 0) {
        return array.shift();
    }
    return null;
}

function customerLog(id_event, params, skip)
{
    
    if (typeof params == 'string') {
        params = [params];
    } else if (typeof params == 'undefined') {
        params = [];
    }
    
    if (typeof skip == 'undefined') {
        skip = 0;
    }
    var requestData = {id_event: id_event, skip: skip};
    for (var i = 0; i < params.length; i++) {
        requestData['params[' + i + ']'] = params[i];
    }
    $.ajax({
        url: '__customer_log',
        type: 'POST',
        async: false,
        data: requestData
    });
    return true;
}

function trim (str, charlist) 
{
    var whitespace, l = 0, i = 0;
    str += '';
	if (!charlist) {
        // default list
        whitespace = " \n\r\t\f\x0b\xa0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000";
    } else {
        // preg_quote custom list
        charlist += '';
        whitespace = charlist.replace(/([\[\]\(\)\.\?\/\*\{\}\+\$\^\:])/g, '\$1');
    }
    l = str.length;
    for (i = 0; i < l; i++) {
        if (whitespace.indexOf(str.charAt(i)) === -1) {
            str = str.substring(i);
            break;
        }
    }
    
    l = str.length;
    for (i = l - 1; i >= 0; i--) {
        if (whitespace.indexOf(str.charAt(i)) === -1) {
            str = str.substring(0, i + 1);
            break;
        }
    }
    return whitespace.indexOf(str.charAt(0)) === -1 ? str : '';
}

function sprintf( ) 
{
    var regex = /%%|%(\d+\$)?([-+#0 ]*)(\*\d+\$|\*|\d+)?(\.(\*\d+\$|\*|\d+))?([scboxXuidfegEG])/g;
    var a = arguments, i = 0, format = a[i++];

    // pad()
    var pad = function(str, len, chr, leftJustify) {
        var padding = (str.length >= len) ? '' : Array(1 + len - str.length >>> 0).join(chr);
        return leftJustify ? str + padding : padding + str;
    };

    // justify()
    var justify = function(value, prefix, leftJustify, minWidth, zeroPad) {
        var diff = minWidth - value.length;
        if (diff > 0) {
            if (leftJustify || !zeroPad) {
                value = pad(value, minWidth, ' ', leftJustify);
            } else {
                value = value.slice(0, prefix.length) + pad('', diff, '0', true) + value.slice(prefix.length);
            }
        }
        return value;
    };

    // formatBaseX()
    var formatBaseX = function(value, base, prefix, leftJustify, minWidth, precision, zeroPad) {
        // Note: casts negative numbers to positive ones
        var number = value >>> 0;
        prefix = prefix && number && {'2': '0b', '8': '0', '16': '0x'}[base] || '';
        value = prefix + pad(number.toString(base), precision || 0, '0', false);
        return justify(value, prefix, leftJustify, minWidth, zeroPad);
    };

    // formatString()
    var formatString = function(value, leftJustify, minWidth, precision, zeroPad) {
        if (precision != null) {
            value = value.slice(0, precision);
        }
        return justify(value, '', leftJustify, minWidth, zeroPad);
    };

    // finalFormat()
    var doFormat = function(substring, valueIndex, flags, minWidth, _, precision, type) {
        if (substring == '%%') return '%';

        // parse flags
        var leftJustify = false, positivePrefix = '', zeroPad = false, prefixBaseX = false;
        var flagsl = flags.length;
        for (var j = 0; flags && j < flagsl; j++) switch (flags.charAt(j)) {
            case ' ': positivePrefix = ' '; break;
            case '+': positivePrefix = '+'; break;
            case '-': leftJustify = true; break;
            case '0': zeroPad = true; break;
            case '#': prefixBaseX = true; break;
        }

        // parameters may be null, undefined, empty-string or real valued
        // we want to ignore null, undefined and empty-string values
        if (!minWidth) {
            minWidth = 0;
        } else if (minWidth == '*') {
            minWidth = +a[i++];
        } else if (minWidth.charAt(0) == '*') {
            minWidth = +a[minWidth.slice(1, -1)];
        } else {
            minWidth = +minWidth;
        }

        // Note: undocumented perl feature:
        if (minWidth < 0) {
            minWidth = -minWidth;
            leftJustify = true;
        }

        if (!isFinite(minWidth)) {
            throw new Error('sprintf: (minimum-)width must be finite');
        }

        if (!precision) {
            precision = 'fFeE'.indexOf(type) > -1 ? 6 : (type == 'd') ? 0 : void(0);
        } else if (precision == '*') {
            precision = +a[i++];
        } else if (precision.charAt(0) == '*') {
            precision = +a[precision.slice(1, -1)];
        } else {
            precision = +precision;
        }

        // grab value using valueIndex if required?
        var value = valueIndex ? a[valueIndex.slice(0, -1)] : a[i++];

        switch (type) {
            case 's': return formatString(String(value), leftJustify, minWidth, precision, zeroPad);
            case 'c': return formatString(String.fromCharCode(+value), leftJustify, minWidth, precision, zeroPad);
            case 'b': return formatBaseX(value, 2, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
            case 'o': return formatBaseX(value, 8, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
            case 'x': return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
            case 'X': return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad).toUpperCase();
            case 'u': return formatBaseX(value, 10, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
            case 'i':
            case 'd': {
                        var number = parseInt(+value);
                        var prefix = number < 0 ? '-' : positivePrefix;
                        value = prefix + pad(String(Math.abs(number)), precision, '0', false);
                        return justify(value, prefix, leftJustify, minWidth, zeroPad);
                    }
            case 'e':
            case 'E':
            case 'f':
            case 'F':
            case 'g':
            case 'G':
                        {
                        var number = +value;
                        var prefix = number < 0 ? '-' : positivePrefix;
                        var method = ['toExponential', 'toFixed', 'toPrecision']['efg'.indexOf(type.toLowerCase())];
                        var textTransform = ['toString', 'toUpperCase']['eEfFgG'.indexOf(type) % 2];
                        value = prefix + Math.abs(number)[method](precision);
                        return justify(value, prefix, leftJustify, minWidth, zeroPad)[textTransform]();
                    }
            default: return substring;
        }
    };

    return format.replace(regex, doFormat);
}


var Qs_Message = Class.create();

Qs_Message.prototype = {
    
    initialize: function (messages)
    {
        this.messages = messages;
    },
    
    get: function (name, language)
    {
        if (typeof language == 'undefined') {
            language = CURR_LANG;
        }
        if (typeof this.messages[language] == 'undefined') {
            return '';
        }
        if (typeof this.messages[language][name] == 'string') {
            return this.messages[language][name];
        }
        if (typeof this.messages[DEFAULT_LANGUAGE][name] == 'string') {
            return this.messages[DEFAULT_LANGUAGE][name];
        }
        return '';
    }
}

function is_string (/*anything*/ it)
{
    return !!arguments.length && it != null && (typeof it == "string" || it instanceof String); // Boolean
}

function is_array (/*anything*/ it)
{
    return it && (it instanceof Array || typeof it == "array"); // Boolean
}

var Qs_Form = {

    buttonOnClick: function (e)
    {
        var form = this.form;
        form.clk = this;
        if (this.type == 'image') {
            if (e.offsetX != undefined) {
                form.clk_x = e.offsetX;
                form.clk_y = e.offsetY;
            } else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin
                var offset = $(this).offset();
                form.clk_x = e.pageX - offset.left;
                form.clk_y = e.pageY - offset.top;
            } else {
                form.clk_x = e.pageX - this.offsetLeft;
                form.clk_y = e.pageY - this.offsetTop;
            }
        }
        setTimeout(function() {
            form.clk = form.clk_x = form.clk_y = null;
        }, 10);
    },

    setValue: function(/*Object*/obj, /*String*/name, /*String*/value)
    {
        var val = obj[name];
        if(is_string(val)) {
            obj[name] = [val, value];
        } else if (is_array(val)) {
            val.push(value);
        } else {
            obj[name] = value;
        }
    },

    toObject: function (/*DOMNode||String*/ formNode)
    {
        var ret = {};
        var exclude = 'file|submit|image|reset|button|';
        if (is_string(formNode)) {
            formNode = document.getElementById(formNode);
        }
        for (var i = 0; i<formNode.elements.length; i++) {
            var item = formNode.elements[i];
            if (!item) {
                continue;
            }
            var _in = item.name;
            var type = (item.type||"").toLowerCase();
            if(_in && type && exclude.indexOf(type) == -1 && !item.disabled){
                if (type == "radio" || type == 'checkbox') {
                    if (item.checked) {
                        Qs_Form.setValue(ret, _in, item.value);
                    }
                } else if (item.multiple) {
                    ret[_in] = [];
                    for (var j in item.options) {
                        if (
                            item.options[j] != null
                            && typeof item.options[j].tagName == 'string'
                            && item.options[j].tagName == 'OPTION'
                            && item.options[j].selected
                        ) {
                            Qs_Form.setValue(ret, _in, item.options[j].value);
                        }
                    }
                } else {
                    Qs_Form.setValue(ret, _in, item.value);
                    if(type == 'image'){
                        ret[_in+".x"] = ret[_in+".y"] = ret[_in].x = ret[_in].y = 0;
                    }
                }
            }
        }
        if (formNode.clk && !formNode.clk.disabled && formNode.clk.name) {
            if (formNode.clk.type == 'image') {
                ret[formNode.clk.name + '.x'] = formNode.clk_x;
                ret[formNode.clk.name + '.y'] = formNode.clk_y;
            } else {
                ret[formNode.clk.name] = formNode.clk.value;
            }
        }
        return ret; // Object
    }
}

function get_html_translation_table(table, quote_style) 
{
    var entities = {}, histogram = {}, decimal = 0, symbol = '';
    var constMappingTable = {}, constMappingQuoteStyle = {};
    var useTable = {}, useQuoteStyle = {};
    
    useTable      = (table ? table.toUpperCase() : 'HTML_SPECIALCHARS');
    useQuoteStyle = (quote_style ? quote_style.toUpperCase() : 'ENT_COMPAT');
    
    // Translate arguments
    constMappingTable[0]      = 'HTML_SPECIALCHARS';
    constMappingTable[1]      = 'HTML_ENTITIES';
    constMappingQuoteStyle[0] = 'ENT_NOQUOTES';
    constMappingQuoteStyle[2] = 'ENT_COMPAT';
    constMappingQuoteStyle[3] = 'ENT_QUOTES';
    
    // Map numbers to strings for compatibilty with PHP constants
    if (!isNaN(useTable)) {
        useTable = constMappingTable[useTable];
    }
    if (!isNaN(useQuoteStyle)) {
        useQuoteStyle = constMappingQuoteStyle[useQuoteStyle];
    }
    if (useTable == 'HTML_SPECIALCHARS') {
        // ascii decimals for better compatibility
        entities['38'] = '&amp;';
        entities['60'] = '&lt;';
        entities['62'] = '&gt;';
    } else if (useTable == 'HTML_ENTITIES') {
        // ascii decimals for better compatibility
	    entities['38'] = '&amp;';
	    entities['60'] = '&lt;';
	    entities['62'] = '&gt;';
	    entities['160'] = '&nbsp;';
	    entities['161'] = '&iexcl;';
	    entities['162'] = '&cent;';
	    entities['163'] = '&pound;';
	    entities['164'] = '&curren;';
	    entities['165'] = '&yen;';
	    entities['166'] = '&brvbar;';
	    entities['167'] = '&sect;';
	    entities['168'] = '&uml;';
	    entities['169'] = '&copy;';
	    entities['170'] = '&ordf;';
	    entities['171'] = '&laquo;';
	    entities['172'] = '&not;';
	    entities['173'] = '&shy;';
	    entities['174'] = '&reg;';
	    entities['175'] = '&macr;';
	    entities['176'] = '&deg;';
	    entities['177'] = '&plusmn;';
	    entities['178'] = '&sup2;';
	    entities['179'] = '&sup3;';
	    entities['180'] = '&acute;';
	    entities['181'] = '&micro;';
	    entities['182'] = '&para;';
	    entities['183'] = '&middot;';
	    entities['184'] = '&cedil;';
	    entities['185'] = '&sup1;';
	    entities['186'] = '&ordm;';
	    entities['187'] = '&raquo;';
	    entities['188'] = '&frac14;';
	    entities['189'] = '&frac12;';
	    entities['190'] = '&frac34;';
	    entities['191'] = '&iquest;';
	    entities['192'] = '&Agrave;';
	    entities['193'] = '&Aacute;';
	    entities['194'] = '&Acirc;';
	    entities['195'] = '&Atilde;';
	    entities['196'] = '&Auml;';
	    entities['197'] = '&Aring;';
	    entities['198'] = '&AElig;';
	    entities['199'] = '&Ccedil;';
	    entities['200'] = '&Egrave;';
	    entities['201'] = '&Eacute;';
	    entities['202'] = '&Ecirc;';
	    entities['203'] = '&Euml;';
	    entities['204'] = '&Igrave;';
	    entities['205'] = '&Iacute;';
	    entities['206'] = '&Icirc;';
	    entities['207'] = '&Iuml;';
	    entities['208'] = '&ETH;';
	    entities['209'] = '&Ntilde;';
	    entities['210'] = '&Ograve;';
	    entities['211'] = '&Oacute;';
	    entities['212'] = '&Ocirc;';
	    entities['213'] = '&Otilde;';
	    entities['214'] = '&Ouml;';
	    entities['215'] = '&times;';
	    entities['216'] = '&Oslash;';
	    entities['217'] = '&Ugrave;';
	    entities['218'] = '&Uacute;';
	    entities['219'] = '&Ucirc;';
	    entities['220'] = '&Uuml;';
	    entities['221'] = '&Yacute;';
	    entities['222'] = '&THORN;';
	    entities['223'] = '&szlig;';
	    entities['224'] = '&agrave;';
	    entities['225'] = '&aacute;';
	    entities['226'] = '&acirc;';
	    entities['227'] = '&atilde;';
	    entities['228'] = '&auml;';
	    entities['229'] = '&aring;';
	    entities['230'] = '&aelig;';
	    entities['231'] = '&ccedil;';
	    entities['232'] = '&egrave;';
	    entities['233'] = '&eacute;';
	    entities['234'] = '&ecirc;';
	    entities['235'] = '&euml;';
	    entities['236'] = '&igrave;';
	    entities['237'] = '&iacute;';
	    entities['238'] = '&icirc;';
	    entities['239'] = '&iuml;';
	    entities['240'] = '&eth;';
	    entities['241'] = '&ntilde;';
	    entities['242'] = '&ograve;';
	    entities['243'] = '&oacute;';
	    entities['244'] = '&ocirc;';
	    entities['245'] = '&otilde;';
	    entities['246'] = '&ouml;';
	    entities['247'] = '&divide;';
	    entities['248'] = '&oslash;';
	    entities['249'] = '&ugrave;';
	    entities['250'] = '&uacute;';
	    entities['251'] = '&ucirc;';
	    entities['252'] = '&uuml;';
	    entities['253'] = '&yacute;';
	    entities['254'] = '&thorn;';
	    entities['255'] = '&yuml;';
    } else {
        throw Error("Table: "+useTable+' not supported');
        return false;
    }
    if (useQuoteStyle != 'ENT_NOQUOTES') {
        entities['34'] = '&quot;';
    }
    if (useQuoteStyle == 'ENT_QUOTES') {
        entities['39'] = '&#039;';
    }
    // ascii decimals to real symbols
    for (decimal in entities) {
        symbol = String.fromCharCode(decimal)
        histogram[symbol] = entities[decimal];
    }
    return histogram;
}

function htmlspecialchars (string, quote_style) 
{
    var hash_map = {}, symbol = '', tmp_str = '', entity = '';
    tmp_str = string.toString();
    if (false === (hash_map = this.get_html_translation_table('HTML_SPECIALCHARS', quote_style))) {
        return false;
    }
    hash_map["'"] = '&#039;';
    for (symbol in hash_map) {
        entity = hash_map[symbol];
        tmp_str = tmp_str.split(symbol).join(entity);
    }
    return tmp_str;
}

function nl2br (str, is_xhtml) 
{
    var breakTag = '';
    breakTag = '<br />';
    if (typeof is_xhtml != 'undefined' && !is_xhtml) {
        breakTag = '<br>';
    }
    return (str + '').replace(/([^>]?)\n/g, '$1'+ breakTag +'\n');
}

