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 Customer = {
    is_dealer: 'n',
    id_country: 0,
    reward_is: 'n',
    rewards_to_price: 'n',
    hasAdjustPrice: false,
    reward_margine_increase: 0.0,
    reward_amount_put_towards: 0.0,
    reward_matrix_value: 0.0,
    reward_formula_towards: 0.0,

    setOptions: function (options)
    {
        for (var i in options) {
            Customer[i] = options[i];
        }
    },

    isDealer: function ()
    {
        return 'y' == this.is_dealer;
    },

    isCanadian: function ()
    {
        return 40 == this.id_country;
    },

    getRewardPutTowards: function ()
    {
        if ('formula' == Customer.reward_is) {
            return Customer.reward_formula_towards;
        }
        if ('y' == Customer.reward_is) {
            return Customer.reward_amount_put_towards;
        }
        return 0;
    },

    hasRewards: function ()
    {
        return in_array(Customer.reward_is, ['y', 'formula']);
    }
};
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();
    },
    ajaxError: function(jqXHR, textStatus, errorThrown)
    {
        if (DEBUG) {
            alert(jqXHR.responseText);
            return false;
        }
        alert(textStatus + ' ' + errorThrown);
    },
    ajaxSuccess: function (response)
    {
        if (typeof $.fancybox != 'undefined') {
            $.fancybox.hideActivity();
        }
        if (!response.isValid) {
            alert(response.message);
            return false;
        }
        if (typeof response.callbacks != 'undefined') {
            qs.processCallbacks(response.callbacks);
        }
        return true;
    },

    ifNull: function (expr1, expr2)
    {
        if (null === expr1) {
            return expr2;
        }
        return expr1;
    },

    log: function ()
    {
        if (typeof console != 'undefined' && typeof console.log == 'function') {
            console.log(arguments);
        }
    },

    money: function (value, decimals)
    {
        if (typeof decimals == 'undefined') {
            decimals = 2;
        } else {
            decimals = intval(decimals);
        }
        value = qs.round(value, decimals);
        if (0 == value) {
            return value;
        }
        return number_format(value, decimals, '.', ',');
    },

    round: function (value, decimals)
    {
        decimals = intval(decimals);
        value = floatval(value);
        var multiplier = Math.pow(10, decimals);
        value = Math.round(value * multiplier) / multiplier;
        return value;
    },

    processCallbacks: function (callbacks)
    {
        var name;
        for (var i in callbacks) {
            name = callbacks[i];
            qs.callback(name);
        }
    },

    callback: function (spec)
    {
        if (typeof spec == 'function') {
          spec.call();
        } else 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);
            }
        }
    }
};

var Qs_Validate = Class.create();
Qs_Validate.prototype =
{
    _value: null,
    _messages: {},
    _messageTemplates: {},

    initialize: function ()
    {

    },

    _error: function (messageKey, value)
    {
        if (!messageKey) {
            messageKey = array_key(this._messageTemplates);
        }
        this._messages[messageKey] = this._createMessage(messageKey, value || this._value);
    },

    _createMessage: function (messageKey, value)
    {
        if (typeof this._messageTemplates[messageKey] == 'undefined') {
            return null;
        }
        message = this._messageTemplates[messageKey];
        message = message.replace('%value%', value);
        return message;
    },

    getMessages: function ()
    {
        return this._messages;
    }
};


var Qs_Validate_Float = Class.create();

Qs_Validate_Float.prototype = {};
$.extend(Qs_Validate_Float.prototype, Qs_Validate.prototype, {
     NOT_FLOAT: 'notFloat',
    _messageTemplates: {
        'notFloat': "'%value%' does not appear to be a float"
    },
    regexes: [
        /^[++]{0,1}[0-9]{1,3}(\,{0,1}[0-9]{3})*(\.{1}[0-9]{1,}){0,1}$/,
        /^[--]{0,1}[0-9]{1,3}(\,{0,1}[0-9]{3})*(\.{1}[0-9]{1,}){0,1}$/,
        /^[++]{0,1}[0-9]{1,}(\.[0-9]{1,})*[eE][++]{0,1}[0-9]{1,}$/,
        /^[--]{0,1}[0-9]{1,}(\.[0-9]{1,})*[eE][--]{0,1}[0-9]{1,}$/
    ],
    constructor: Qs_Validate,

    isValid: function (value)
    {
        this._value = value;
        if (is_float(value) || is_int(value)) {
            return true;
        }
        if (is_string(value)) {
            for (var i = 0; i < this.regexes.length; i++) {
                if (this.regexes[i].test(value)) {
                    this._error(this.INVALID);
                    return true;
                }
            }
        }
        this._error(this.INVALID);
        return false;
    }
});

var Qs_Debug =
{
    enabled: DEBUG,

    log: function ()
    {
        if (!Qs_Debug.enabled || typeof console == 'undefined') {
            return false;
        }
        console.log.apply(console, arguments);
    }
};

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)
    {
        var i = 0;
        for (var j in array) {
            if (parseInt(j, 10) != i++) {
                return true;
            }
        }
        return false;
    },

    collapse: function(array, belongsTo)
    {
        if (typeof belongsTo == 'undefined') {
            belongsTo = '';
        }
        result = {};
        for (var key in array) {
            var value = array[key];
            var itemBelongsTo = belongsTo + (empty(belongsTo) ? key : '[' + key + ']');
            if (is_array(value)) {
                result = array_merge(result, Qs_Array.collapse(value, itemBelongsTo));
            } else {
                result[itemBelongsTo] = value;
            }
        }
        return result;
    }
};

var Form_Element_Select =
{
    setOptions: function (element, options, emptyTitle)
    {
        var i;
        for(i = element.options.length - 1; i >= 0; i--) {
            element.remove(i);
        }
        if (typeof emptyTitle == 'string') {
            var option = document.createElement('OPTION');
            option.text = emptyTitle;
            option.value = '';
            element.options.add(option);
        }
        var isAssoc = Qs_Array.isAssoc(options);
        for (i in options) {
            var option = document.createElement('OPTION');
            option.value = isAssoc ? i : options[i].value;
            option.text = isAssoc ? options[i] : options[i].text;
            element.options.add(option);
        }
    },

    getValue: function (element)
    {
        if (element.multiple) {
            var value = [];
            for (var j in element.options) {
                if (
                    element.options[j] != null
                    && typeof element.options[j].tagName == 'string'
                    && element.options[j].tagName == 'OPTION'
                    && element.options[j].selected
                ) {
                    value.push(element.options[j].value);
                }
            }
            return value;
        } else {
            return element.value;
        }
    },

    setValue: function(element, value)
    {
        if (element.multiple) {
            if (!is_array(value)) {
                value = [value];
            }
            for (var j in element.options) {
                if (
                    element.options[j] != null
                    && typeof element.options[j].tagName == 'string'
                    && element.options[j].tagName == 'OPTION'
                ) {
                   element.options[j].selected = in_array(element.options[j].value, value);
                }
            }
        } else {
            element.value = value;
        }
    }
};

var App_ShoppingCart =
{
    options: {},
    /**
     * list of one time calling events
     */
    events: {
        onLookupProductsLoad: []
    },

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

    buyButtonOnClick: function ()
    {
        showProgress();
        var t = $.makeArray($('.buy_qut'));
        var x = new Object();
        pattern = /^\d+$/
        var flag = false;
        var is_valid_pkg_qty = true;
        var url = 'cart?action=additem';
        $('div.showcnt>input.buy_qut').each( function () {
            url = $(this.form).attr('action');
            if (this.value.match(pattern)) {
                if (!scIsValidPakageQuantity(this)) {
                    is_valid_pkg_qty = false;
                    return true;
                }
                x[this.id] = this.value;
                var id_product = scGetProductIdByQuantityElement(this);
                if (id_product != ''){
                    newAddedProducts.push(id_product);
                }
                flag = true;
            }
        });
        if (!is_valid_pkg_qty) {
            hideProgress();
            scShowPQAlert();
            return false;
        }
        if(flag) {
            $.ajaxSetup({async: true});
            $.ajax({
                url: url,
                type: 'POST',
                dataType: 'json',
                data: x,
                success: function (response)
                {
                    qs.ajaxSuccess(response);
                    loadOrder();
                    loadSummary();
                }
            });
        } else {
            alert("You should fill in at least one 'Quantity' field. Please use numbers.");
            $('#loadprogress').hide();
        }
        return false;
    },

    addEvent: function (name, callback)
    {
        if (typeof App_ShoppingCart.events[name] == 'undefined') {
            alert('Invalid event name: "' + name + '"');
            return false;
        }
        App_ShoppingCart.events[name].push(callback);
    },

    callEvent: function(name)
    {
        if (typeof App_ShoppingCart.events[name] == 'undefined') {
            alert('Invalid event name: "' + name + '"');
            return false;
        }
        var events = App_ShoppingCart.events[name];
        var callback = null;
        while (callback = events.shift()) {
            Qs_Form.callExternal(callback);
        }
    },

    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<br>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: 'json',
            success: function(response, status, form) {
                qs.ajaxSuccess(response);
                scAfterQuantityUpdate(form.formToArray());
                $('a#lastAddedProduct').remove();
                quantityMessage = response.message;
                loadOrder();
                loadSummary();
            },
            beforeSubmit: validQuantityValues
        };
        $(form).ajaxSubmit(options);
        return false;
    },

    focusLookupField: function ()
    {
        $('#lookup-query').focus();
        $.scrollTo('#lookup-query', {offset: - $(window).height()/2});
    },

    scrollToList: function ()
    {
        $.scrollTo('#cart-lookup-container');
    },

    renderRemoveLink: function (product)
    {
        var html = '';
        if (in_array(product.id, newAddedProducts)) {
            html += 'Item just added.<br />'
                  + '<a href="' + CURR_PAGE + '#anker_' + product.id + '" '
                  + 'onclick="window.location.hash=\'#anker_'+ product.id +'\'; return false;"'
                  + '>Continue shopping</a><br />';
        }
        html += '<a href="#" id="'+ product.cartitem_id + '" '
              + 'title="Delete this item" onclick="return App_ShoppingCart.deleteItemOnClick(this.id, '+ product.id +');">Remove item</a>';
        return html;
    },

    renderExtendedTotalsCell: function (product)
    {
        var html = '';
        var quantity = floatval(product.quantity);
        var points = floatval(product.reward_points_float);

        html += App_Product_List.renderYourPrice(product, {quantity: quantity, renderAdjustLink: false})
              + '<br/>';
        if (Customer.hasRewards()) {
            html += '<em class="c_quantity_points">Points Earned:</em><br/>'
                + '<span class="c_quantity_points">' + qs.money(quantity * points, 2) + '</span>'
                + '<br/>';
        }
        html += '<em>Savings Vs. ' + (Customer.isDealer() ? 'Dealer' : 'List') + ':</em><br/>'
              + App_Product_List.renderSavingColumn(product, {quantity: quantity, showPercent: false});
         return html;
    },

    changeBuyCellMode: function (form, quantity)
    {
        if (intval(quantity) > 0) {
            $('.buy_elements', form).removeClass('showcnt').addClass('hidecnt');
            $('.buy_status', form).removeClass('hidecnt').addClass('showcnt');
            $('div.in_cart', form).html("" + quantity + " in your cart");
        } else {
            $('input.buy_qut', form).val('');
            $('.buy_elements', form).removeClass('hidecnt').addClass('showcnt');
            $('.buy_status', form).removeClass('showcnt').addClass('hidecnt');
        }
    },

    deleteItemOnClick: function (id_item, id_product)
    {
        if (!confirm('Do you really want to delete this product from shopping cart?')) {
            return false;
        }
        $('#div_squantity_' + id_product).removeClass();
        $('#div_squantity_' + id_product).addClass('showcnt');
        $('#s_in_cart_' + id_product).removeClass();
        $('#s_in_cart_' + id_product).addClass('hidecnt');
        $('#squantity_' + id_product).val('');

        $.ajax({
            url     : 'cart',
            type    : 'POST',
            data    : {action: 'del', id: id_item},
            dataType: 'json',
            success : function (response)
            {
                qs.ajaxSuccess(response);
                $('a#lastAddedProduct').remove();
                loadOrder();
                loadSummary();
                return true;
            }
        });
        return false;
    },

    hideQuantityMessageByNode: function(quantityInputNode)
    {
        scPakageQuantityMessageHide(scGetProductIdByQuantityElement(quantityInputNode));
    },

    validatePackageQuantity: function (quantityElement)
    {
        var quantity = intval(quantityElement.value);
        var inputPackageQuantity = getPreviousTag(quantityElement, 'INPUT');
        if (!inputPackageQuantity) {
            return true;
        }
        var pkg_qty = intval(inputPackageQuantity.value);
        if (in_array(pkg_qty, [0, 1])) {
            return true;
        }
        if (0 == quantity % pkg_qty) {
            return true;
        }
        return ['This item must be ordered in multiples of ' + qs.money(pkg_qty, 0)];
    }
};

var App_Product_List = {
    id_country: null,
    columns: {},
    adjustResetNote: {title: '', body: ''},
    adjustedNote: {title: '', body: ''},

    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="65" />'
                  + '<col width="65" />'
                  + '<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="65" />';
        }
        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) ? '$' + qs.money(value) : 'N/A') + '</td>'
                     + '<td>&nbsp;' + ((can_value > 0) ? '$' + qs.money(can_value) : 'N/A') + '</td>';
            } else {
                html += '<tr>'
                     + '<td class="ref_tbl_lbl">' + htmlspecialchars(options.title) + ' -' + '</td>'
                     + '<td>&nbsp;' + ((value > 0) ? '$' + qs.money(value) : 'N/A') + '</td>'
            }
            html += '</tr>';
        }
        html += '</tbody></table>';
        return html;
    },

    renderSavingColumn: function (product, options)
    {
        var html = '';
        if (typeof options == 'undefined') {
            options = {quantity: 1, showPercent: true}
        }
        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"/> '
                 + '$' + qs.money(options.quantity * parseFloat(Qs_Array.get(product, 'can_' + column.field, 0)))
                 + '</span>'
                 + '<br /><span class="price_savngs"><img src="img/flag/usa.jpg" class="flag" alt="USD"/> '
                 + '$' + qs.money(options.quantity * parseFloat(Qs_Array.get(product, column.field, 0))) + '</span>';
        } else {
            html += '$' + qs.money(options.quantity * parseFloat(Qs_Array.get(product, column.field, 0)));
        }
        if (options.showPercent) {
            var percent = options.quantity * parseFloat(Qs_Array.get(product, column.percentField, 0));
            html += '<br />' + qs.money(percent, 1) + '%';
            html += App_Product_List.renderAdjustResetNote(product, options);
        }
        return html;
    },

    renderYourPrice: function (product, options)
    {
        if (typeof options == 'undefined') {
            options = {quantity: 1, renderAdjustLink: true}
        }
        var html = '<span class="pricetype">';
        var adjustResetNote = App_Product_List.renderAdjustResetNote(product, options);
        var adjustedNote = App_Product_List.renderAdjustedNote(product, options);
        if (40 == App_Product_List.id_country) {
            html += '<img src="img/flag/canada.jpg" class="flag" alt="CAD"/> ';
            html += '$' + qs.money(options.quantity * parseFloat(product.can_your_cost));
            html += adjustedNote;
            html += adjustResetNote;
            html += '<br />';
            html += '<img src="img/flag/usa.jpg" class="flag" alt="USD"/> ';
        }
        html += '$' + qs.money(options.quantity * parseFloat(product.your_cost));
        if (!Customer.isCanadian()) {
             html += adjustedNote;
             html += adjustResetNote;
        }
        if (options.renderAdjustLink && Customer.hasAdjustPrice) {
            html += '<div>';
            var className = 'adjust_price_link';
            if (!intval(product.hasAdjustPrice)) {
                className += ' disabled';
                html += '<a class="' + className + '" '
                      + 'onclick="return App_AdjustPrice_Popup.prototype.adjustLinkOnClick(this);" '
                      + 'href="__product-points?action=showAdjustPopup&amp;idProduct=' + product.id + '"'
                      + '><img src="img/btn_adj_price_disabled.png" width="84" height="39" alt="Disabled" /></a>';
                html += '</div>';
            } else {
                html += '<a class="' + className + '" '
                      + 'onclick="return App_AdjustPrice_Popup.prototype.adjustLinkOnClick(this);" '
                      + 'href="__product-points?action=showAdjustPopup&amp;idProduct=' + product.id + '"'
                      + '><img src="img/btn_adj_price.png" width="84" height="39" alt="Adjust Price" /></a>';
                html += '</div>';
            }
        }
        html += '</span>';
        return html;
    },

    renderAdjustResetNote: function (product, options)
    {
        var html = '';
        if (Customer.hasRewards() && '' != product.real_adjust_rate
            && floatval(product.real_adjust_rate) < floatval(product.min_adjust_rate)
        ) {
            html += ' <a href = "#" class="adjust_reset_note" onclick="return false;" '
                  + 'title="hideselects=[off] cssbody=[hintbody hintbodyalert] cssheader=[hinthdr hinthdrred] '
                  + 'header=[' + htmlspecialchars(App_Product_List.adjustResetNote.title) + '] '
                  + 'body=[' + htmlspecialchars(App_Product_List.adjustResetNote.body) + ']"'
                  + '><img src="img/alert.gif" width="15" height="14" alt=""></a>';
        }
        return html;
    },

    renderAdjustedNote: function (product, options)
    {
        var html = '';
        if (Customer.hasRewards() && '' != product.real_adjust_rate) {
            html += ' <a href = "#" class="adjust_reset_note" onclick="return false;" '
                  + 'title="hideselects=[off] cssbody=[hintbodyalert_blue] cssheader=[hinthdr hinthdrred] '
                  + 'header=[' + htmlspecialchars(App_Product_List.adjustedNote.title) + '] '
                  + 'body=[' + htmlspecialchars(App_Product_List.adjustedNote.body) + ']"'
                  + '><img src="img/ico_alert.png" width="19" height="19" alt="" /></a>';
        }
        return html;
    },

    applyPointsToPrice: function(checkbox, request)
    {
        request.query = {};
        var ajaxOptions = {
            url     : '__product-points',
            type    : 'GET',
            dataType: 'json',
            data    : {
                action : 'applyPoints',
                checked: checkbox.checked ? 'y' : 'n'

            },
            error: qs.ajaxError,
            success: function(response)
            {
                if (!response.isValid) {
                    alert(response.message);
                    return false;
                }
                App_Product_List.renderCells(request, response.cells);
                if (typeof loadSummary != 'undefined') {
                    loadSummary();
                }
            }
        };
        $.extend(ajaxOptions.data, Qs_Array.collapse(request));
        $.ajax(ajaxOptions);
    },

    applyAllPointsToPrice: function(checkbox)
    {
        var ajaxOptions = {
            url     : BASE_URL_REAL + '/' + CURR_PAGE,
            type    : 'GET',
            dataType: 'json',
            data    : {
                action : 'applyAllPoints',
                checked: checkbox.checked ? 'y' : 'n'
            },
            error: qs.ajaxError,
            success: function(response)
            {
                if (!response.isValid) {
                    alert(response.message);
                    return false;
                }
                $('#shopping-cart-preview').html($(response.listHtml).html());
                $('#summary').html($(response.summaryHtml).html());
            }
        };
        $.ajax(ajaxOptions);
    },

    renderCells: function (request, cells)
    {
        for (var name in cells) {
            var cell = cells[name];
            $('tr.p_' + request.id_product + ' .c_' + name).each(function(index, element){
                var obj = $(cell.html);
                var id = $('input', obj).attr('id');
                id += '-' + index;
                $('input', obj).attr('id', id);
                $('label', obj).attr('for', id);
                $(this).html(obj.html());
            });
        }
    },

    renderCellRewardPoints: function (product)
    {
        var html = rewardPoints(product.reward_points_float);
        var checkboxId = uniqid('rpo-');
        html += '<div>'
              + '<input type="checkbox" id="' + checkboxId + '" '
              + ((product.apply_points == 'y') ? 'checked="checked" ' : '')
              + 'onchange="App_Product_List.applyPointsToPrice(this, {\'id_product\':\'' + product.id + '\'})" '
              + 'onclick="this.blur()" /><br/>'
              + '<label for="'+ checkboxId + '">Apply points to price</label>'
              + '</div>';
        return html;
    },

    updateList: function (products)
    {
        var cells;
        for (var id_product in products) {
            cells = products[id_product];
            App_Product_List.renderCells({id_product:id_product}, cells);
        }
    }
};

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 shoppingCartShowMessage()
{
    if (typeof quantityMessage == 'string' && quantityMessage.length && scSummaryLoaded && scOrderLoaded) {
        alert(quantityMessage);
        quantityMessage = null;
    }
}

var newAddedProducts = new Array();
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 errors = App_ShoppingCart.validatePackageQuantity(quantityElement);
    var id_product = scGetProductIdByQuantityElement(quantityElement);
    if (true === errors) {
        scPakageQuantityMessageHide(id_product);
        return true;
    }
    var timerEvents = 'onmouseout = "scPQMessageStartTimer(' + id_product + ')" onmouseover = "scPQMessageClearTimer(' + id_product + ')" ';
    var tipMessage = errors.join('\n');
    if (false === $(quantityElement).is(':visible')) {
        // check if this is stock order item to click 'modify' link
        var modifyLink = $('a.modify_link:visible', quantityElement.parentNode);
        if (modifyLink.size()) {
            $(modifyLink).click();
        }
    }

    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:250px; 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) {
            App_ShoppingCart.changeBuyCellMode(form, quantity);
            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);
    }
};

$.fn.chngQ = function(element) {
    $('.buy_elements', element.form).removeClass('hidecnt').addClass('showcnt');
    $('.buy_status', element.form).removeClass('showcnt').addClass('hidecnt');
    return false;
};

function addProduct(id)
{
    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) {
        App_ShoppingCart.addEvent('onLookupProductsLoad', App_ShoppingCart.focusLookupField);
        $.ajax({
            url: 'cart?action=additem',
            type: 'POST',
            data: x,
            dataType: 'json',
            success: 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(response, textStatus, jqXHR)
{
    App_ShoppingCart.callEvent('onLookupProductsLoad');
    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 mfg = $("cross_reference>mfg:eq("+n+")", products).text();
        var style = "";
        var quant_msg = '';
        var id_product = $("cross_reference>id_product:eq("+n+")", products).text();
        var resNumber = s_num.replace(/'"/, "\'");
        var searchNumber = mfg.replace(/'"/, "\'");
        $('<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?id=' + id_product + '&subm=Search" '
                + 'onclick="customerLog(\'CLICKED_CROSS-REFERENCE_LINK\', '
                + '[\'' + resNumber + "', '" + searchNumber + '\']); '
                + 'loadProduct(\'' + resNumber + '\', \'cross-reference\', 1, {crossReferenceNumber: \'' + searchNumber + '\'}); 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';
}



function in_array(needle, haystack, argStrict)
{
    var key = '', strict = !!argStrict;
    if (strict) {
        for (key in haystack) {
            if (haystack[key] === needle) {
                return true;
            }
        }
    } else {
        for (key in haystack) {
            if (haystack[key] == needle) {
                return true;
            }
        }
    }
    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 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);
            }
        }
        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 = in_array(_suffix, RED_SUFFIXES);
        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 = parseFloat(points);
    points = Math.round(points * 1000) / 1000;
    var qty = parseFloat(qty);
    if (qty) {
        points *= qty;
    }
    return qs.money(points, 3);
}

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_int (mixed_var)
{
    if (typeof mixed_var !== 'number') {
        return false;
    }
    return !(mixed_var % 1);
}

function is_numeric (mixed_var)
{
    return (typeof(mixed_var) === 'number' || typeof(mixed_var) === 'string') && mixed_var !== '' && !isNaN(mixed_var);
}

function is_float (mixed_var)
{
    if (typeof mixed_var !== 'number') {
        return false;
    }
    return !!(mixed_var % 1);
}

function is_array (mixed_var)
{
    var _getFuncName = function (fn) {
        var name = (/\W*function\s+([\w\$]+)\s*\(/).exec(fn);
        if (!name) {
            return '(Anonymous)';
        }
        return name[1];
    },
        _isArray = function (mixed_var) {
            return Object.prototype.toString.call(mixed_var) === '[object Array]';
            // Other approaches:
            // && mixed_var.hasOwnProperty('length') && // Not non-enumerable because of being on parent class
            // !mixed_var.propertyIsEnumerable('length') && // Since is own property, if not enumerable, it must be a built-in function
            //   _getFuncName(mixed_var.constructor) !== 'String'; // exclude String(), but not another function returning String()
        };

    if (!mixed_var || typeof mixed_var !== 'object') {
        return false;
    }

    // BEGIN REDUNDANT
    this.php_js = this.php_js || {};
    this.php_js.ini = this.php_js.ini || {};
    // END REDUNDANT
    var ini = this.php_js.ini['phpjs.objectsAsArrays'];

    return _isArray(mixed_var) ||
    // Allow returning true unless user has called
    // ini_set('phpjs.objectsAsArrays', 0) to disallow objects as arrays
    (!ini || ( // if it's not set to 0 and it's not 'off', check for objects as arrays
    (parseInt(ini.local_value, 10) !== 0 && (!ini.local_value.toLowerCase || ini.local_value.toLowerCase() !== 'off')))) && (
    Object.prototype.toString.call(mixed_var) === '[object Object]' && _getFuncName(mixed_var.constructor) === 'Object' // Most likely a literal and intended as assoc. array
    );
}

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

function floatval (mixed_var)
{
    return (parseFloat(mixed_var) || 0);
}

function json_encode (mixed_val)
{
    var retVal, json = this.window.JSON;
    try {
        if (typeof json === 'object' && typeof json.stringify === 'function') {
            retVal = json.stringify(mixed_val); // Errors will not be caught here if our own equivalent to resource
            //  (an instance of PHPJS_Resource) is used
            if (retVal === undefined) {
                throw new SyntaxError('json_encode');
            }
            return retVal;
        }

        var value = mixed_val;

        var quote = function (string) {
            var escapable = /[\\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
            var meta = { // table of character substitutions
                '\b': '\\b',
                '\t': '\\t',
                '\n': '\\n',
                '\f': '\\f',
                '\r': '\\r',
                '"': '\\"',
                '\\': '\\\\'
            };

            escapable.lastIndex = 0;
            return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
                var c = meta[a];
                return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
            }) + '"' : '"' + string + '"';
        };

        var str = function (key, holder) {
            var gap = '';
            var indent = '    ';
            var i = 0; // The loop counter.
            var k = ''; // The member key.
            var v = ''; // The member value.
            var length = 0;
            var mind = gap;
            var partial = [];
            var value = holder[key];

            // If the value has a toJSON method, call it to obtain a replacement value.
            if (value && typeof value === 'object' && typeof value.toJSON === 'function') {
                value = value.toJSON(key);
            }

            // What happens next depends on the value's type.
            switch (typeof value) {
            case 'string':
                return quote(value);

            case 'number':
                // JSON numbers must be finite. Encode non-finite numbers as null.
                return isFinite(value) ? String(value) : 'null';

            case 'boolean':
            case 'null':
                // If the value is a boolean or null, convert it to a string. Note:
                // typeof null does not produce 'null'. The case is included here in
                // the remote chance that this gets fixed someday.
                return String(value);

            case 'object':
                // If the type is 'object', we might be dealing with an object or an array or
                // null.
                // Due to a specification blunder in ECMAScript, typeof null is 'object',
                // so watch out for that case.
                if (!value) {
                    return 'null';
                }
                if ((this.PHPJS_Resource && value instanceof this.PHPJS_Resource) || (window.PHPJS_Resource && value instanceof window.PHPJS_Resource)) {
                    throw new SyntaxError('json_encode');
                }

                // Make an array to hold the partial results of stringifying this object value.
                gap += indent;
                partial = [];

                // Is the value an array?
                if (Object.prototype.toString.apply(value) === '[object Array]') {
                    // The value is an array. Stringify every element. Use null as a placeholder
                    // for non-JSON values.
                    length = value.length;
                    for (i = 0; i < length; i += 1) {
                        partial[i] = str(i, value) || 'null';
                    }

                    // Join all of the elements together, separated with commas, and wrap them in
                    // brackets.
                    v = partial.length === 0 ? '[]' : gap ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : '[' + partial.join(',') + ']';
                    gap = mind;
                    return v;
                }

                // Iterate through all of the keys in the object.
                for (k in value) {
                    if (Object.hasOwnProperty.call(value, k)) {
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                        }
                    }
                }

                // Join all of the member texts together, separated with commas,
                // and wrap them in braces.
                v = partial.length === 0 ? '{}' : gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' : '{' + partial.join(',') + '}';
                gap = mind;
                return v;
            case 'undefined':
                // Fall-through
            case 'function':
                // Fall-through
            default:
                throw new SyntaxError('json_encode');
            }
        };

        // Make a fake root object containing our value under the key of ''.
        // Return the result of stringifying the value.
        return str('', {
            '': value
        });

    } catch (err) { // Todo: ensure error handling above throws a SyntaxError in all cases where it could
        // (i.e., when the JSON global is not available and there is an error)
        if (!(err instanceof SyntaxError)) {
            throw new Error('Unexpected error type in json_encode()');
        }
        this.php_js = this.php_js || {};
        this.php_js.last_error_json = 4; // usable by json_last_error()
        return null;
    }
}

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)
    {
        return qs.callback(spec);
    },

    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 rtrim(str, charlist)
{
    charlist = !charlist ? ' \\s\u00A0' : (charlist+'').replace(/([\[\]\(\)\.\?\/\*\{\}\+\$\^\:])/g, '\\$1');
    var re = new RegExp('[' + charlist + ']+$', 'g');
    return (str + '').replace(re, '');
}

function addslashes(str)
{
    return (str + '').replace(/[\\"']/g, '\\$&').replace(/\u0000/g, '\\0');
}


function urldecode (str)
{
    return decodeURIComponent((str + '').replace(/\+/g, '%20'));
}

function parse_str(str, array)
{
    var glue1 = '=',
        glue2 = '&',
        array2 = String(str).replace(/^&?([\s\S]*?)&?$/, '$1').split(glue2),
        i, j, chr, tmp, key, value, bracket, keys, evalStr, that = this,
        fixStr = function (str) {
            return that.urldecode(str).replace(/([\\"'])/g, '\\$1').replace(/\n/g, '\\n').replace(/\r/g, '\\r');
        };

    if (!array) {
        array = this.window;
    }

    for (i = 0; i < array2.length; i++) {
        tmp = array2[i].split(glue1);
        if (tmp.length < 2) {
            tmp = [tmp, ''];
        }
        key = fixStr(tmp[0]);
        value = fixStr(tmp[1]);
        while (key.charAt(0) === ' ') {
            key = key.substr(1);
        }
        if (key.indexOf('\0') !== -1) {
            key = key.substr(0, key.indexOf('\0'));
        }
        if (key && key.charAt(0) !== '[') {
            keys = [];
            bracket = 0;
            for (j = 0; j < key.length; j++) {
                if (key.charAt(j) === '[' && !bracket) {
                    bracket = j + 1;
                } else if (key.charAt(j) === ']') {
                    if (bracket) {
                        if (!keys.length) {
                            keys.push(key.substr(0, bracket - 1));
                        }
                        keys.push(key.substr(bracket, j - bracket));
                        bracket = 0;
                        if (key.charAt(j + 1) !== '[') {
                            break;
                        }
                    }
                }
            }
            if (!keys.length) {
                keys = [key];
            }
            for (j = 0; j < keys[0].length; j++) {
                chr = keys[0].charAt(j);
                if (chr === ' ' || chr === '.' || chr === '[') {
                    keys[0] = keys[0].substr(0, j) + '_' + keys[0].substr(j + 1);
                }
                if (chr === '[') {
                    break;
                }
            }
            evalStr = 'array';
            for (j = 0; j < keys.length; j++) {
                key = keys[j];
                if ((key !== '' && key !== ' ') || j === 0) {
                    key = "'" + key + "'";
                } else {
                    key = eval(evalStr + '.push([]);') - 1;
                }
                evalStr += '[' + key + ']';
                if (j !== keys.length - 1 && eval('typeof ' + evalStr) === 'undefined') {
                    eval(evalStr + ' = [];');
                }
            }
            evalStr += " = '" + value + "';\n";
            eval(evalStr);
        }
    }
}

function empty(mixed_var)
{
    var key;

    if (mixed_var === "" || mixed_var === 0 || mixed_var === "0" || mixed_var === null || mixed_var === false || typeof mixed_var === 'undefined') {
        return true;
    }

    if (typeof mixed_var == 'object') {
        for (key in mixed_var) {
            return false;
        }
        return true;
    }

    return false;
}

function array_merge()
{
    var args = Array.prototype.slice.call(arguments),
        retObj = {},
        k, j = 0,
        i = 0,
        retArr = true;

    for (i = 0; i < args.length; i++) {
        if (!(args[i] instanceof Array)) {
            retArr = false;
            break;
        }
    }

    if (retArr) {
        retArr = [];
        for (i = 0; i < args.length; i++) {
            retArr = retArr.concat(args[i]);
        }
        return retArr;
    }
    var ct = 0;

    for (i = 0, ct = 0; i < args.length; i++) {
        if (args[i] instanceof Array) {
            for (j = 0; j < args[i].length; j++) {
                retObj[ct++] = args[i][j];
            }
        } else {
            for (k in args[i]) {
                if (args[i].hasOwnProperty(k)) {
                    if (parseInt(k, 10) + '' === k) {
                        retObj[ct++] = args[i][k];
                    } else {
                        retObj[k] = args[i][k];
                    }
                }
            }
        }
    }
    return retObj;
}


function uniqid(prefix, more_entropy)
{
    if (typeof prefix == 'undefined') {
        prefix = "";
    }

    var retId;
    var formatSeed = function (seed, reqWidth) {
        seed = parseInt(seed, 10).toString(16); // to hex str
        if (reqWidth < seed.length) { // so long we split
            return seed.slice(seed.length - reqWidth);
        }
        if (reqWidth > seed.length) { // so short we pad
            return Array(1 + (reqWidth - seed.length)).join('0') + seed;
        }
        return seed;
    };

    // BEGIN REDUNDANT
    if (!this.php_js) {
        this.php_js = {};
    }
    // END REDUNDANT
    if (!this.php_js.uniqidSeed) { // init seed with big random int
        this.php_js.uniqidSeed = Math.floor(Math.random() * 0x75bcd15);
    }
    this.php_js.uniqidSeed++;

    retId = prefix; // start with prefix, add current milliseconds hex string
    retId += formatSeed(parseInt(new Date().getTime() / 1000, 10), 8);
    retId += formatSeed(this.php_js.uniqidSeed, 5); // add seed hex string
    if (more_entropy) {
        // for more entropy we add a float lower to 10
        retId += (Math.random() * 10).toFixed(8).toString();
    }

    return retId;
}


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