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';
var MSG_LEAST_ONE_QUANTITY_REQUITED = 'You should fill in at least one "Quantity" field. Please use numbers.';
var qs = {
    backgroundImage: function (inputId, image, cssOptions)
    {
        var input = document.getElementById(inputId);
        if (!input) {
            return false;
        }
        input.style.backgroundRepeat = 'no-repeat';
        if (typeof cssOptions != 'undefined') {
            $(input).css(cssOptions);
        }
        $(input).focus(function() {
            this.style.backgroundImage = '';
        }).blur(function() {
            if (this.value == '') {
                this.style.backgroundImage = "url('" + image +"')";
            } else {
                this.style.backgroundImage = '';
            }
        }).blur();
    }
}

var Qs_Array = {
    get: function (data, field, defaultValue)
    {
        if (typeof field != 'undefined') {
            field = field.replace(/\]/g, '');
            var parts = field.split(/\[/);
            while ((name = array_shift(parts))) {
                if (!array_key_exists(name, data)) {
                    return (typeof defaultValue != 'undefined') ? defaultValue : null;
                }
                data = data[name];
            }
        }
        return data;
    },

    set: function (data, field, value)
    {
        if (typeof field == 'undefined') {
            return false;
        }
        field = field.replace(/\]/g, '');
        var parts = field.split(/\[/);
        var _data = data;
        while ((name = array_shift(parts))) {
            if (parts.length == 0) {
                _data[name] = value;
                break;
            }
            if (!array_key_exists(name, _data)) {
                _data[name] = {};
            }
            _data = _data[name];
        }
        return true;
    },
    
    isAssoc: function (array)
    {
        if (is_array(array)) {
            return false;
        }
        var i = 0;
        for (var j in array) {
            if (parseInt(j, 10) != i++) {
                return true;
            }
        }
        return false;
    }
}

var App_ShoppingCart = {
    options: {},

    setOptions: function (options)
    {
        if (typeof options == 'undefined') {
            return false;
        }
        App_ShoppingCart.options = options;
    },

    setOption: function (name, value)
    {
        return Qs_Array.set(App_ShoppingCart.options, name, value)
    },

    addOptions: function (options)
    {
        $.extend(true, App_ShoppingCart.options, options);
    },

    getOption: function (name)
    {
        return Qs_Array.get(App_ShoppingCart.options, name);
    },

    xmlProductToObject: function (xml)
    {
        var product = $.xml2json(xml);
        if (typeof product.tools == 'undefined') {
            product.tools = [];
        }
        if (typeof product.install_notes == 'undefined') {
            product.install_notes = [];
        }
        if (Qs_Array.isAssoc(product.tools)) {
            product.tools = [product.tools];
        }
        if (Qs_Array.isAssoc(product.install_notes)) {
            product.install_notes = [product.install_notes];
        }
        return product;
    },

    renderToolsLink: function (product)
    {
        var html = '';
        if (product.tools.length == 0) {
            return html;
        }
        var isList = (product.tools.length > 1);
        if (isList) {
            html += '<ul>';
        }
        for (var i = 0; i<product.tools.length; i++) {
            var tool = product.tools[i];
            if (isList) {
                html += '<li>';
            }
            html += tool.suffix + ' - ';
            html += (tool.long_num.length) ? tool.long_num : tool.short_num ;
            html += htmlspecialchars(truncate(tool.description, 45));
            if (isList) {
                html += '</li>';
            }
        };
        if (isList) {
            html += '</ul>';
        }
        var title = product.suffix + ' - ' + ((product.long_num.length) ? product.long_num : product.short_num) ;
        html = '&nbsp;<a href="#" class="part_note_link product_tools_link" onclick="return App_HingeKit_View.openToolsPopup('
             + product.id + ');" title="hideselects=[off] cssbody=[hintbody] cssheader=[hinthdr] '
             + 'header=[Special tool(s) for '+ htmlspecialchars(title) + '] '
             + 'body=[' + nl2br(html) + ']">'
             + ' Special Tools Recommended'
             + '</a>';
        return html;
    },
    
    renderPaintedLink: function (product)
    {
        var html = '';
        if (parseInt(product.count_painted, 10) > 1) {
            html = '<a href="#" class="part_color_link" onclick="return App_HingeKit_View.openPaintedPopup(\''
                 + product.id + '\');">Painted Options Available (' + product.count_painted + ')</a>';
        }
        return html;
    },
    
    renderPartNote: function (product)
    {
        var html = '';
        if (typeof !product.purch_note.length) {
            return html;
        }
        var title = product.suffix + ' - ';
        if (product.long_num.length) {
            title += product.long_num;
        } else {
            title += product.short_num;
        }
        html += '<a class="part_note_link" href = "#" onclick = "return false" title="hideselects=[off] cssbody=[hintbody] '
             + 'cssheader=[hinthdr] header=[' + htmlspecialchars(title) + '] body=['
             + nl2br(htmlspecialchars(product.purch_note)) + ']">'
             + '<img style="vertical-align:middle" src = "img/note.png" alt = "" class="part_note_icon" />'
             + '</a>';
        return html;
    },
    
    renderInstallNotes: function (product)
    {
        var html = '';
        if (product.install_instructions_file) {
            html += '<a class="install_instructions_link" href="' + product.install_instructions_file + '" '
                  + 'onclick="customerLog(\'CUSTOM\', \'Clicked to open Installation Instructions ' + basename(product.install_instructions_file) + '\'); return true;" '
                  + 'target="_blank" '
                  + 'title = ""> '
                  + 'Installation Instructions (.pdf)'
                  + '</a>';
        }
        if (product.install_notes.length == 0) {
            return html;
        }
        for(var i = 0; i < product.install_notes.length; i++) {
            var note = product.install_notes[i];
            var text = note.link_text;
            text = text.replace(/\\/g, '\\');
            text = text.replace(/"/g, '\'');
            text = text.replace(/'/g, '\\\'');
            text = text.replace(/\x0A/g, ' ');
            text = text.replace(/\x0D/g, ' ');
            html += '<a class="install_note_link" href="__hinge-kit-ajax?action=renderInstallNote&amp;id='
                 + note.id + '" onclick="customerLog(\'CUSTOM\', \'Opened popup with install notes - \\\''
                 + text
                 + '\\\'\'); return false;">'
                 + htmlspecialchars(note.link_text)
                 + '</a>';
        }
        return html;
    },
    
    renderLikeProductsLink: function (product)
    {
        var html = '';
        var message = 'Clicked to show products like ' + product.suffix + ' - ' + product.long_num;
        html +='<a class="like_products_link" '
             + 'onclick="customerLog(\'CUSTOM\', \'' + message  + '\'); return true;" '
             + 'href="nonmenu/prctsearchres?maker_id=' + product.maker_id + '&subset_id=' + product.subset_id + '&subm=Search">';
        html +='Show Like Products';
        html +='</a>';
        return html;
    },
    
    renderCorrespondingPartsLink: function (product)
    {
        var html = '';
        if (!parseInt(product.count_corresponding_parts, 10)) {
            return html;
        }
        html = '<a href="#" class="corresponding_parts_link" onclick="return App_HingeKit_View.openCorrespondingPartsPopup('
             + product.id + ');">'
             + 'Customers who ordered this part also ordered (' + product.count_corresponding_parts + ')'
             + '</a>';
        return html;
    },
    
    afterLoadProduct: function ()
    {
        $('a.install_note_link').fancybox();
    },
    
    quantityOnKeyUp: function (element, event)
    {
        if (event.keyCode == 13) {
            if ($.browser.msie) {
                $(element.form).trigger('submit');
                return false;
            }
        }
        return true;
    },
    
    submitForm: function(form)
    {
        $(form).trigger('submit');
        return false;
    },
    
    updateQuantity: function(form)
    {
        var options = {
            dataType: 'xml',
            success: function(responseXML, status, form) {
                scAfterQuantityUpdate(form.formToArray());
                var data = $('list', responseXML).text();
                $('a#lastAddedProduct').remove();
                quantityMessage = data;
				loadOrder();
				loadSummary();
            },
            beforeSubmit: validQuantityValues
        };
        $(form).ajaxSubmit(options);
        return false;
    }
}

var App_Product_List = {
    id_country: null,
    columns: {},
    
    init: function(options)
    {
        App_Product_List.setOptions(options);
    },
    
    setOptions: function(options)
    {
        for (var name in options) {
            var method = 'set' + ucfirst(name);
            if (typeof App_Product_List[method] == 'function') {
                App_Product_List[method](options[name]);
            } else {
                App_Product_List[name] = options[name];
            }
        }
    },
    
    setColumn: function(name, options)
    {
        App_Product_List.columns[name] = options;
    },
    
    setColumns: function(columns)
    {
        for (var name in columns) {
            App_Product_List.setColumn(name, columns[name]);
        }
    },
    
    renderReferencePrices: function (product)
    {
        if (40 == App_Product_List.id_country) {
			var html = '<table class="ref_price_tbl" border="0" width="100%">';
			html += '<col width="35" />'
				  + '<col width="50" />'
				  + '<col width="50" />'
				  + '<thead><tr>'
                  + '<th>&nbsp;</th>'
                  + '<th class="us_col"><img src="img/flag/usa.jpg" class="flag" alt="USD"/></th>'
                  + '<th><img src="img/flag/canada.jpg" class="flag" alt="CAD"/></th>'
                  + '</tr></thead>';
        } else {
			var html = '<table class="ref_price_us_tbl" border="0" width="100%">';
			html += '<col width="40" />'
				  + '<col width="50" />';
		}
        var values = Qs_Array.get(App_Product_List.columns, 'reference_price[values]', {});
		html += '<tbody>';
        for (var name in values) {
            var options = values[name];
            var value = parseFloat(Qs_Array.get(product, options.field, 0));
            var can_value = parseFloat(Qs_Array.get(product, 'can_' + options.field, 0));
            if (!(value > 0 && (40 != App_Product_List.id_country || 40 == App_Product_List.id_country && can_value > 0))) {
                continue;
            }
            if (name == 'cpp_reference') {
                if (product.cpp_part == 'n') {
                    value = 0;
                    can_value = 0;
                } else {
                    //value = 
                }
            }
            if (40 == App_Product_List.id_country) {
				html += '<tr>'
					 + '<td class="ref_tbl_lbl">' + htmlspecialchars(options.title)  + '&nbsp;</td>'
                	 + '<td class="us_col">&nbsp;' + ((value > 0) ? '$' + number_format(value, 2, '.', ',') : 'N/A') + '</td>'
					 + '<td>&nbsp;' + ((can_value > 0) ? '$' + number_format(can_value, 2, '.', ',') : 'N/A') + '</td>';
            } else {
				html += '<tr>'
					 + '<td class="ref_tbl_lbl">' + htmlspecialchars(options.title) + ' -' + '</td>'
					 + '<td>&nbsp;' + ((value > 0) ? '$' + number_format(value, 2, '.', ',') : 'N/A') + '</td>'
			}
            html += '</tr>';
        }
        html += '</tbody></table>';
        return html;
    },
    
    renderSavingColumn: function (product)
    {
        var html = '';
        var column = Qs_Array.get(App_Product_List.columns, 'saving', {});
        if (40 == App_Product_List.id_country) {
            html += '<span class="price_savngs"><img src="img/flag/canada.jpg" class="flag" alt="CAD"/> '
                 + '$' + number_format(parseFloat(Qs_Array.get(product, 'can_' + column.field, 0)), 2, '.', ',')
                 + '</span><br />'
                 + '<span class="price_savngs"><img src="img/flag/usa.jpg" class="flag" alt="USD"/> '
				 + '$' + number_format(parseFloat(Qs_Array.get(product, column.field, 0)), 2, '.', ',')
				 + '</span><br />' + number_format(parseFloat(Qs_Array.get(product, column.percentField, 0)), 1, '.', ',') + '%';
        } else {
			html += '$' + number_format(parseFloat(Qs_Array.get(product, column.field, 0)), 2, '.', ',')
				 + '<br />' + number_format(parseFloat(Qs_Array.get(product, column.percentField, 0)), 1, '.', ',') + '%';
		}
        return html;
    },
    
    renderYourPrice: function (product)
    {
        var html = '';
        if (40 == App_Product_List.id_country) {
            html += '<img src="img/flag/canada.jpg" class="flag" alt="CAD"/> ';
            html += '$' + number_format(parseFloat(product.can_your_cost), 2, '.', ',');
            html += '<br />';
            html += '<img src="img/flag/usa.jpg" class="flag" alt="USD"/> ';
        }
        html += '$' + number_format(parseFloat(product.your_cost), 2, '.', ',');
        return html;
    }
}

function truncate(string, length, etc, break_words, middle)
{
    if (typeof length == 'undefined') {
        length = 80;
    }
    if (typeof etc == 'undefined') {
        etc = '...';
    }
    if (typeof break_words == 'undefined') {
        break_words = false;
    }
    if (typeof middle == 'undefined') {
        middle = false;
    }
    
    if (length == 0) {
        return '';
    }
    if (string.length > length) {
        length -= etc.length;
        if (!break_words && !middle) {
            string = string.replace(/\s+?(\S+)?$/, '', string.substr(0, length + 1));
        }
        if(!middle) {
            return string.substr(0, length) + etc;
        } else {
            return string.substr(0, Math.ceil(length/2)) + etc + string.substr(- Math.ceil(length/2));
        }
    } else {
        return string;
    }
}

// -------------------------------------------------------

function loadOrder()
{
    scOrderLoaded = false;
    var options = App_ShoppingCart.getOption('listOptions');
    if (null === options) {
        options = {};
    }
    options.action = 'getcart';
    $.post('cart', options, loadOrderSuccess);
}

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

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');
    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;
    }
    var timerEvents = 'onmouseout = "scPQMessageStartTimer(' + id_product + ')" onmouseover = "scPQMessageClearTimer(' + id_product + ')" ';
    var tipMessage = 'This item must be ordered in multiples of <span class="msg_pkg_qty_value">' + pkg_qty + '</span>';
    var html = '<div id = "msg_pkg_qty_' + id_product + '" style="z-index:1103" '
             + '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 pos = getElementPos(quantityElement);
    var top = pos.y - 35;
    var left = pos.x;
    var html = '<div id = "msg_pkg_qty_' + id_product + '" '
             + 'onclick = "$(this).remove()" style="z-index:1103; position:absolute; top: ' + top + 'px; left: ' + left + 'px">'
             + '<div ' + timerEvents + 'style = "cursor:pointer; width:230px; text-align:left;">'
             + '<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_left.png\'); background-repeat:no-repeat; height:5px;"></div>'
             + '</div>'
             + '</div>';
    
    $('#msg_pkg_qty_' + id_product).remove();
    $(document.body).prepend(html);
    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 scGetHingeKitQuantityFromResponce(responce)
{
    var hingeKitQuantity = {};
    $('hinge_kit_list product', responce).each(function(){
        var id = $(this).attr('id');
        if (typeof hingeKitQuantity[id] == 'undefined') {
            hingeKitQuantity[id] = {};
         }
         var id_placement = $('id_placement', this).text();
         var quantity = $('quantity', this).text();
         hingeKitQuantity[id][id_placement] = intval(quantity);
    });
    return hingeKitQuantity;
}

function scChangeHingeKitQuantityAtList(hingeKitQuantity)
{
    for (var id_product in hingeKitQuantity) {
        for (var id_placement in hingeKitQuantity[id_product]) {
            var quantity = hingeKitQuantity[id_product][id_placement];
            var id =  id_placement + '_' + id_product;
            $.fn.changeQuantityAtList(id, quantity);
        }
    }
}

$.fn.changeQuantityAtList = function (id_product, quantity)
{
    var element = document.getElementById('quantity_' + id_product);
    if (element) {
        var form = getParentTag(element, 'FORM');
        if ($('.buy_elements', form).size() == 1) {
            $('.buy_elements', form).removeClass('showcnt').addClass('hidecnt');
            $('.buy_status', form).removeClass('hidecnt').addClass('showcnt');
            $('div.in_cart', form).html("" + quantity + " in your cart");
            element.value = quantity;
        }
    }
    // ajax list
	$('#div_squantity_' + id_product).removeClass();
	$('#div_squantity_' + id_product).addClass('hidecnt');
	$('#s_in_cart_' + id_product).removeClass();
	$('#s_in_cart_' + id_product).addClass('showcnt');
	$('input#squantity_' + id_product).val(quantity);
	$('span#txt_quantity_' + id_product).html("" + quantity + " in your cart");
    
    // painted popup
    if (typeof App_HingeKit_View != 'undefined') {
        App_HingeKit_View.setQuantity(id_product, quantity);
    }
}


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(MSG_LEAST_ONE_QUANTITY_REQUITED);
		$('#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;
		if (y > max_y) {
			y = max_y;
			y-=40;
		}
		img.parentNode.style.top = y+'px';
		img.parentNode.style.zIndex = 1102;
		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);
		$('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);
		$(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();
    return false;
}

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 array_key_exists(key, search)
{
    if( !search || (search.constructor !== Array && search.constructor !== Object) ){
        return false;
    }
    return key in search;
}

function array_key (arr) {
    // http://kevin.vanzonneveld.net
    // +   original by: Brett Zamir (http://brett-zamir.me)
    // +   input by: Riddler (http://www.frontierwebdev.com/)
    // +   bugfixed by: Brett Zamir (http://brett-zamir.me)
    // %        note 1: Uses global: php_js to store the array pointer
    // *     example 1: array = {fruit1: 'apple', 'fruit2': 'orange'}
    // *     example 1: key(array);
    // *     returns 1: 'fruit1'

    // BEGIN REDUNDANT
    this.php_js = this.php_js || {};
    this.php_js.pointers = this.php_js.pointers || [];
    var indexOf = function (value) {
        for (var i = 0, length=this.length; i < length; i++) {
            if (this[i] === value) {
                return i;
            }
        }
        return -1;
    };
    // END REDUNDANT

    var pointers = this.php_js.pointers;
    if (!pointers.indexOf) {
        pointers.indexOf = indexOf;
    }

    if (pointers.indexOf(arr) === -1) {
        pointers.push(arr, 0);
    }
    var cursor = pointers[pointers.indexOf(arr)+1];
    if (!(arr instanceof Array)) {
        var ct = 0;
        for (var k in arr) {
            if (ct === cursor) {
                return k;
            }
            ct++;
        }
        return false; // Empty
    }
    if (arr.length === 0) {
        return false;
    }
    return cursor;
}

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
}

function is_object (mixed_var)
{
    if(mixed_var instanceof Array) {
        return false;
    } else {
        return (mixed_var !== null) && (typeof( mixed_var ) == 'object');
    }
}

function intval (mixed_var, base) {
    // http://kevin.vanzonneveld.net
    // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   improved by: stensi
    // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   input by: Matteo
    // +   bugfixed by: Brett Zamir (http://brett-zamir.me)
    // *     example 1: intval('Kevin van Zonneveld');
    // *     returns 1: 0
    // *     example 2: intval(4.2);
    // *     returns 2: 4
    // *     example 3: intval(42, 8);
    // *     returns 3: 42
    // *     example 4: intval('09');
    // *     returns 4: 9
    // *     example 5: intval('1e', 16);
    // *     returns 5: 30

    var tmp;

    var type = typeof( mixed_var );

    if (type === 'boolean') {
        return (mixed_var) ? 1 : 0;
    } else if (type === 'string') {
        tmp = parseInt(mixed_var, base || 10);
        return (isNaN(tmp) || !isFinite(tmp)) ? 0 : tmp;
    } else if (type === 'number' && isFinite(mixed_var) ) {
        return Math.floor(mixed_var);
    } else {
        return 0;
    }
}

var Qs_Form = {
    message: new Qs_Message({
        'eng': {
            'invalidInformationEntered': 'Please review the comments marked in red and make appropriate corrections.',
            'pleaseCorrectTheseFields': ''
        }
    }),
    options: {
        errorDisplayMethod: 'HTML'
    },

    formOptions: {},

    init: function (idForm, options)
    {
        var form = document.getElementById(idForm);
        if (!form) {
            alert('Form (id = ' + idForm + ') is not available');
            return false;
        }
        if (!form.tagName || form.tagName != 'FORM') {
            alert('Element (id = ' + idForm + ') is not a form - tagName = ' + form.tagName);
            return false;
        }
        if (options) {
            Qs_Form.formOptions[idForm] = options;
        }
        $(form).unbind('submit.form-plugin').bind('submit.form-plugin', Qs_Form.onSubmit);
        $(':submit,input:image', form).unbind('click.form-plugin').bind('click.form-plugin', Qs_Form.buttonOnClick);
    },

    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);
    },

    getOption: function (name)
    {
        return Qs_Form.options[name];
    },

    setErrorDisplayMethod: function (idForm, value)
    {
        Qs_Form.setFormOption(idForm, 'errorDisplayMethod', value);
    },

    getErrorDisplayMethod: function (idForm)
    {
        return Qs_Form.getFormOption(idForm, 'errorDisplayMethod');
    },

    setFormOption: function (idForm, name, value)
    {
        if (typeof Qs_Form.formOptions[idForm] == 'undefined') {
            Qs_Form.formOptions[idForm] = {};
        }
        Qs_Form.formOptions[idForm][name] = value;
    },

    getFormOption: function (idForm, name)
    {
        if (typeof Qs_Form.formOptions[idForm] == 'undefined' || typeof Qs_Form.formOptions[idForm][name] == 'undefined') {
            return Qs_Form.getOption(name);
        }
        return Qs_Form.formOptions[idForm][name];
    },

    callExternal: function (spec)
    {
        if (typeof spec == 'string') {
            $.globalEval(spec + '.call(this)');
        } else if (typeof spec == 'object' && spec != null) {
            var scriptHtml = '';
            for (var callback in spec) {
                var params = new Array();
                if (is_numeric(callback)) {
                    if (typeof spec[callback] == 'string') {
                        callback = spec[callback]
                    } else if (typeof spec[callback] == 'object'
                               && typeof spec[callback] != null
                    ) {
                        var key = array_key(spec[callback]);
                        params = spec[callback][key]
                        callback = key;
                    } else {
                        continue;
                    }
                } else {
                    params = spec[callback];
                }
                scriptHtml += callback;
                if (params.length) {
                    scriptHtml += '.apply(this, ' + json_encode(params) + ');\n';
                } else {
                    scriptHtml += '.call(this);\n';
                }
            }
            if (scriptHtml.length) {
                $.globalEval(scriptHtml);
            }
        }
    },

    onSubmitSuccess: function (response, form)
    {
        var id = $(form).attr('id');
        Qs_Form.setFormOption(id, 'response', response)
        if (response.isValid) {
            if (form) {
                var onSuccessCallback = Qs_Form.getFormOption($(form).attr('id'), 'onSuccessCallback');
                if (typeof onSuccessCallback == 'undefined') {
                    form.submit();
                } else {
                    Qs_Form.callExternal(onSuccessCallback);
                }
            }
        } else {
            var onErrorCallback = Qs_Form.getFormOption($(form).attr('id'), 'onErrorCallback');
            if (typeof onErrorCallback != 'undefined') {
                Qs_Form.callExternal(onErrorCallback);
                return true;
            }
            Qs_Form.displayErrors(id);
        }
        return response;
    },

    displayErrors: function (id)
    {
        var response = Qs_Form.getFormOption(id, 'response');
        var form = document.getElementById(id);
        var displayMethod = Qs_Form.getErrorDisplayMethod(id);
        switch (displayMethod) {
            case 'ALERT':
                var message = Qs_Form.message.get('invalidInformationEntered') + '\n';
                message += Qs_Form.prepareErrorsAlert(response.errors, response.elements);
                message += '\n\n' + Qs_Form.message.get('pleaseCorrectTheseFields');
                var name = array_key(response.errors);
                if (form[name] && form[name].focus) {
                    form[name].focus();
                } else if (form[name + '[input]'] && form[name + '[input]'].focus) { // встановлення фокуса для каптчі
                    form[name + '[input]'].focus();
                }
                $.scrollTo('#' + name + '-label');
                alert(message);
                break;
            case 'HTML':
                Qs_Form.displayErrorsHtml(form, response.errors);
                var elementName = array_key(response.errors);
                var idElement = Qs_Array.get(response.elements, elementName + '[id]');
                if (idElement == null) {
                    idElement = elementName;
                }
                var name = array_key(response.errors);
                var focusKey = null;
                if (form[name] && form[name].focus) {
                    focusKey = name;
                } else if (form[name + '[input]'] && form[name + '[input]'].focus) { // встановлення фокуса для каптчі
                    focusKey = name + '[input]';
                }
                if (focusKey != null) {
                    if ($('[name="' + focusKey + '"]:visible', form).size()) {
                        form[focusKey].focus();
                    }
                }
                var formPrependId = Qs_Form.getFormOption(id, 'prependId');
                if (formPrependId) {
                    name = id + '-' + name;
                }
                //$.scrollTo('#' + idElement + '-label');
                alert(
                    Qs_Form.message.get('invalidInformationEntered')
                    + '\n'
                    + Qs_Form.message.get('pleaseCorrectTheseFields')
                );
                break;
            default:
                alert('Qs_Form. Unknown errorDisplayMethod "' + displayMethod + '"');
        }

        if (typeof response.captcha == 'object' && response.captcha != null) {
            for (var name in response.captcha) {
                var captcha = $('#' + name + '-element');
                if ($(captcha).size() != 0) {
                    $('img:first', captcha).attr('src', response.captcha[name].src);
                    $('#' + name + '-id', captcha).attr('value', response.captcha[name].id);
                    $('#' + name + '-input', captcha).attr('value', '');
                }
            }
        }
    },

    prepareErrorsAlert: function (errors, titles)
    {
        var messages = '';
        for (var element in errors) {
            var message = '';
            var isTitle = true;
            for (var errorIndex in errors[element]) {
                if (typeof errors[element][errorIndex] == 'string') {
                    message += '\n     - ' + errors[element][errorIndex];
                } else {
                    messages += Qs_Form.prepareErrorsAlert(errors[element], titles[element]);
                    isTitle = false;
                    break;
                }
            }
            if (isTitle && message != '') {
                messages += '\n' + titles[element] + ':' + message;
            }
        }
        return messages;
    },

    displayErrorsHtml: function (form, errors, belongsTo)
    {
        for (var element in errors) {
            var html = '';
            var elementName;
            if (typeof belongsTo != 'undefined') {
                elementName = belongsTo + '[' + element + ']';
            } else {
                elementName = element;
            }
            for (var errorIndex in errors[element]) {
                if (typeof errors[element][errorIndex] == 'string') {
                    html += '<li>' + errors[element][errorIndex] + '</li>';
                } else {
                    Qs_Form.displayErrorsHtml(form, errors[element], elementName);
                    break;
                }
            }
            if (html != '') {
                html = '<ul class="errors">' + html + '</ul>';
                var idForm = $(form).attr('id');
                var response = Qs_Form.getFormOption(idForm, 'response')
                var id = Qs_Array.get(response.elements, elementName + '[id]');
                if (null == id) {
                    id = elementName.replace(/\[\]$/, '').replace(/\]/g, '').replace(/\[/g, '-');
                }
                var formPrependId = Qs_Form.getFormOption(idForm, 'prependId');
                if (formPrependId) {
                    id = idForm + '-' + id;
                }
                $('#' + id + '-element').append(html);
            }
        }
    },

    onSubmitError: function (request, textStatus, errorThrown)
    {
        alert(textStatus + ' ' + errorThrown);
    },

    onSubmitComplete: function (XMLHttpRequest, textStatus, form)
    {
        var idForm = $(form).attr('id');
        Qs_Form.formOptions[idForm]['processRequest'] = false;
    },

    removeElementsErrors: function (form)
    {
        $('ul.errors', form).remove();
    },

    editorsTriggerSave: function (form)
    {
        if (typeof tinyMCE != 'undefined') {
            tinyMCE.triggerSave();
        }

        if (typeof FCKeditorAPI != 'undefined') {
            for (var i = 0; i < form.elements.length; i++) {
                if (form.elements[i].tagName == 'TEXTAREA') {
                    var fckObject = FCKeditorAPI.GetInstance(form.elements[i].name);
                    if (typeof fckObject != 'undefined') {
                        if (fckObject.IsDirty()) {
                            fckObject.UpdateLinkedField();
                        }
                    }
                }
            }
        }
    },

    onSubmit: function (spec)
    {
        var form;
        if (this.tagName == 'FORM') {
            form = this;
        } else {
            form = spec;
        }
        var idForm = $(form).attr('id');

        Qs_Form.editorsTriggerSave(form);
        Qs_Form.removeElementsErrors(form);

        if (typeof Qs_Form.formOptions[idForm]['processRequest'] == 'undefined'
            || Qs_Form.formOptions[idForm]['processRequest'] != true) {
            Qs_Form.formOptions[idForm]['processRequest'] = true;
            var options = {
                url: $(form).attr('action'),
                type: 'POST',
                dataType: 'json',
                data: Qs_Form.toObject(form),
                success: function (data)
                {
                    Qs_Form.onSubmitSuccess(data, form);
                },

                error: Qs_Form.onSubmitError,

                complete: function (XMLHttpRequest, textStatus)
                {
                    Qs_Form.onSubmitComplete(XMLHttpRequest, textStatus, form)
                }
            };
            $.ajax(options);
        }
        return false;
    },

    setValue: function(/*Object*/obj, /*String*/name, /*String*/value)
    {
        var val = obj[name];
        if(is_string(val)) {
            if (name.substr(-2) == '[]') {
                obj[name] = [val, value];
            } else {
                obj[name] = 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');
}
function echo () {
    // http://kevin.vanzonneveld.net
    // +   original by: Philip Peterson
    // +   improved by: echo is bad
    // +   improved by: Nate
    // +    revised by: Der Simon (http://innerdom.sourceforge.net/)
    // +   improved by: Brett Zamir (http://brett-zamir.me)
    // +   bugfixed by: Eugene Bulkin (http://doubleaw.com/)
    // +   input by: JB
    // +   improved by: Brett Zamir (http://brett-zamir.me)
    // +   bugfixed by: Brett Zamir (http://brett-zamir.me)
    // +   bugfixed by: Brett Zamir (http://brett-zamir.me)
    // %        note 1: If browsers start to support DOM Level 3 Load and Save (parsing/serializing),
    // %        note 1: we wouldn't need any such long code (even most of the code below). See
    // %        note 1: link below for a cross-browser implementation in JavaScript. HTML5 might
    // %        note 1: possibly support DOMParser, but that is not presently a standard.
    // %        note 2: Although innerHTML is widely used and may become standard as of HTML5, it is also not ideal for
    // %        note 2: use with a temporary holder before appending to the DOM (as is our last resort below),
    // %        note 2: since it may not work in an XML context
    // %        note 3: Using innerHTML to directly add to the BODY is very dangerous because it will
    // %        note 3: break all pre-existing references to HTMLElements.
    // *     example 1: echo('<div><p>abc</p><p>abc</p></div>');
    // *     returns 1: undefined

    var arg = '', argc = arguments.length, argv = arguments, i = 0;
    var win = this.window;
    var d = win.document;
    var ns_xhtml = 'http://www.w3.org/1999/xhtml';
    var ns_xul = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'; // If we're in a XUL context

    var holder;

    var stringToDOM = function (str, parent, ns, container) {
        var extraNSs = '';
        if (ns === ns_xul) {
            extraNSs = ' xmlns:html="'+ns_xhtml+'"';
        }
        var stringContainer = '<'+container+' xmlns="'+ns+'"'+extraNSs+'>'+str+'</'+container+'>';
        if (win.DOMImplementationLS &&
            win.DOMImplementationLS.createLSInput &&
            win.DOMImplementationLS.createLSParser) { // Follows the DOM 3 Load and Save standard, but not
            // implemented in browsers at present; HTML5 is to standardize on innerHTML, but not for XML (though
            // possibly will also standardize with DOMParser); in the meantime, to ensure fullest browser support, could
            // attach http://svn2.assembla.com/svn/brettz9/DOMToString/DOM3.js (see http://svn2.assembla.com/svn/brettz9/DOMToString/DOM3.xhtml for a simple test file)
            var lsInput = DOMImplementationLS.createLSInput();
            // If we're in XHTML, we'll try to allow the XHTML namespace to be available by default
            lsInput.stringData = stringContainer;
            var lsParser = DOMImplementationLS.createLSParser(1, null); // synchronous, no schema type
            return lsParser.parse(lsInput).firstChild;
        }
        else if (win.DOMParser) {
            // If we're in XHTML, we'll try to allow the XHTML namespace to be available by default
            try {
                var fc = new DOMParser().parseFromString(stringContainer, 'text/xml');
                if (!fc || !fc.documentElement ||
                        fc.documentElement.localName !== 'parsererror' ||
                        fc.documentElement.namespaceURI !== 'http://www.mozilla.org/newlayout/xml/parsererror.xml') {
                    return fc.documentElement.firstChild;
                }
                // If there's a parsing error, we just continue on
            }
            catch(e) {
                // If there's a parsing error, we just continue on
            }
        }
        else if (win.ActiveXObject) { // We don't bother with a holder in Explorer as it doesn't support namespaces
            var axo = new ActiveXObject('MSXML2.DOMDocument');
            axo.loadXML(str);
            return axo.documentElement;
        }
        /*else if (win.XMLHttpRequest) { // Supposed to work in older Safari
            var req = new win.XMLHttpRequest;
            req.open('GET', 'data:application/xml;charset=utf-8,'+encodeURIComponent(str), false);
            if (req.overrideMimeType) {
                req.overrideMimeType('application/xml');
            }
            req.send(null);
            return req.responseXML;
        }*/
        // Document fragment did not work with innerHTML, so we create a temporary element holder
        // If we're in XHTML, we'll try to allow the XHTML namespace to be available by default
        //if (d.createElementNS && (d.contentType && d.contentType !== 'text/html')) { // Don't create namespaced elements if we're being served as HTML (currently only Mozilla supports this detection in true XHTML-supporting browsers, but Safari and Opera should work with the above DOMParser anyways, and IE doesn't support createElementNS anyways)
        if (d.createElementNS &&  // Browser supports the method
            d.documentElement.namespaceURI && (d.documentElement.namespaceURI !== null || // We can use if the document is using a namespace
            d.documentElement.nodeName.toLowerCase() !== 'html' || // We know it's not HTML4 or less, if the tag is not HTML (even if the root namespace is null)
            (d.contentType && d.contentType !== 'text/html') // We know it's not regular HTML4 or less if this is Mozilla (only browser supporting the attribute) and the content type is something other than text/html; other HTML5 roots (like svg) still have a namespace
        )) { // Don't create namespaced elements if we're being served as HTML (currently only Mozilla supports this detection in true XHTML-supporting browsers, but Safari and Opera should work with the above DOMParser anyways, and IE doesn't support createElementNS anyways); last test is for the sake of being in a pure XML document
            holder = d.createElementNS(ns, container);
        }
        else {
            holder = d.createElement(container); // Document fragment did not work with innerHTML
        }
        holder.innerHTML = str;
        while (holder.firstChild) {
            parent.appendChild(holder.firstChild);
        }
        return false;
        // throw 'Your browser does not support DOM parsing as required by echo()';
    };


    var ieFix = function (node) {
        if (node.nodeType === 1) {
            var newNode = d.createElement(node.nodeName);
            var i, len;
            if (node.attributes && node.attributes.length > 0) {
                for (i = 0, len = node.attributes.length; i < len; i++) {
                    newNode.setAttribute(node.attributes[i].nodeName, node.getAttribute(node.attributes[i].nodeName));
                }
            }
            if (node.childNodes && node.childNodes.length > 0) {
                for (i = 0, len = node.childNodes.length; i < len; i++) {
                    newNode.appendChild(ieFix(node.childNodes[i]));
                }
            }
            return newNode;
        }
        else {
            return d.createTextNode(node.nodeValue);
        }
    };

    for (i = 0; i < argc; i++ ) {
        arg = argv[i];
        if (this.php_js && this.php_js.ini && this.php_js.ini['phpjs.echo_embedded_vars']) {
            arg = arg.replace(/(.?)\{\$(.*?)\}/g, function (s, m1, m2) {
                // We assume for now that embedded variables do not have dollar sign; to add a dollar sign, you currently must use {$$var} (We might change this, however.)
                // Doesn't cover all cases yet: see http://php.net/manual/en/language.types.string.php#language.types.string.syntax.double
                if (m1 !== '\\') {
                    return m1+eval(m2);
                }
                else {
                    return s;
                }
            });
        }
        if (d.appendChild) {
            if (d.body) {
                if (win.navigator.appName == 'Microsoft Internet Explorer') { // We unfortunately cannot use feature detection, since this is an IE bug with cloneNode nodes being appended
                    d.body.appendChild(stringToDOM(ieFix(arg)));
                }
                else {
                    var unappendedLeft = stringToDOM(arg, d.body, ns_xhtml, 'div').cloneNode(true); // We will not actually append the div tag (just using for providing XHTML namespace by default)
                    if (unappendedLeft) {
                        d.body.appendChild(unappendedLeft);
                    }
                }
            } else {
                d.documentElement.appendChild(stringToDOM(arg, d.documentElement, ns_xul, 'description')); // We will not actually append the description tag (just using for providing XUL namespace by default)
            }
        } else if (d.write) {
            d.write(arg);
        }/* else { // This could recurse if we ever add print!
            print(arg);
        }*/
    }
}


function print_r (array, return_val) {
    // http://kevin.vanzonneveld.net
    // +   original by: Michael White (http://getsprink.com)
    // +   improved by: Ben Bryan
    // +      input by: Brett Zamir (http://brett-zamir.me)
    // +      improved by: Brett Zamir (http://brett-zamir.me)
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // -    depends on: echo
    // *     example 1: print_r(1, true);
    // *     returns 1: 1

    var output = "", pad_char = " ", pad_val = 4, d = this.window.document;
    var getFuncName = function (fn) {
        var name = (/\W*function\s+([\w\$]+)\s*\(/).exec(fn);
        if (!name) {
            return '(Anonymous)';
        }
        return name[1];
    };

    var repeat_char = function (len, pad_char) {
        var str = "";
        for (var i=0; i < len; i++) {
            str += pad_char;
        }
        return str;
    };

    var formatArray = function (obj, cur_depth, pad_val, pad_char) {
        if (cur_depth > 0) {
            cur_depth++;
        }

        var base_pad = repeat_char(pad_val*cur_depth, pad_char);
        var thick_pad = repeat_char(pad_val*(cur_depth+1), pad_char);
        var str = "";

        if (typeof obj === 'object' && obj !== null && obj.constructor && getFuncName(obj.constructor) !== 'PHPJS_Resource') {
            str += "Array\n" + base_pad + "(\n";
            for (var key in obj) {
                if (obj[key] instanceof Array) {
                    str += thick_pad + "["+key+"] => "+formatArray(obj[key], cur_depth+1, pad_val, pad_char);
                } else {
                    str += thick_pad + "["+key+"] => " + obj[key] + "\n";
                }
            }
            str += base_pad + ")\n";
        } else if (obj === null || obj === undefined) {
            str = '';
        } else { // for our "resource" class
            str = obj.toString();
        }

        return str;
    };

    output = formatArray(array, 0, pad_val, pad_char);

    if (return_val !== true) {
        if (d.body) {
            this.echo(output);
        }
        else {
            try {
                d = XULDocument; // We're in XUL, so appending as plain text won't work; trigger an error out of XUL
                this.echo('<pre xmlns="http://www.w3.org/1999/xhtml" style="white-space:pre;">'+output+'</pre>');
            }
            catch (e) {
                this.echo(output); // Outputting as plain text may work in some plain XML
            }
        }
        return true;
    } else {
        return output;
    }
}

function ucfirst(str) 
{
    str += '';
    var f = str.charAt(0).toUpperCase();
    return f + str.substr(1);
}

function basename(path, suffix) 
{
    var b = path.replace(/^.*[\/\\]/g, '');
    if (typeof(suffix) == 'string' && b.substr(b.length-suffix.length) == suffix) {
        b = b.substr(0, b.length-suffix.length);
    }
    return b;
}

function vdie()
{
    var __getType = function( inp ) {
        var type = typeof inp, match;
        if (type == 'object' && !inp) {
            return 'null';
        }
        if (type == "object") {
            if (!inp.constructor) {
                return 'object';
            }
            var cons = inp.constructor.toString();
            if (match = cons.match(/(\w+)\(/)) {
                cons = match[1].toLowerCase();
            }
            var types = ["boolean", "number", "string", "array"];
            for (key in types) {
                if (cons == types[key]) {
                    type = types[key];
                    break;
                }
            }
        }
        return type;
    };
    var content = '';
    for (var i =0; i < arguments.length; i++) {
        content += (i + 1) + ') [' + __getType(arguments[i]) + '] ' + print_r(arguments[i], true) + '\n';
    }
    alert(content);
}