// Classes
var TableManager = Class.create();
TableManager.prototype = {
    initialize: function() {
        this.mainTables = new Array(); // The array of the names of the tables, which added to a page by table.jsp?tableName=<tableName> 
                                       // - the array of the main tables
        this.contextPath = ""; // The path to an application
        this.url = ""; // The url of a server, where ajax requests have to be sending
        this.okButtonId = "ok_" // The part of the id of an ok button
        this.cancelButtonId = "cancel_" // The part of the id of a cancel button
        this.buttonToChangeId = "buttonToChange_" // The part of the id of the buttons, which enabled/disabled state depends on marked rows
        this.allRowsCheckboxId = "allRowsCheckbox_" // The part of the id of the all-rows-checkbox of a table
        this.tagPrefix = "f_"; // The prefix of the id of the controls for the entering the values parameters
                               // (see ru.spb.iac.web.table2.impl.TableBeen.TAG_PREFIX)
        this.index = "_^" // The part of the id of the controls for the entering the values parameters
        this.debug = true; // The flag, that table workes in the debug mode
        this.enabledButtonImageURLs = $H(); // The hash of the urls to the images for the enabled buttons 
        this.disabledButtonImageURLs = $H(); // The hash of the urls to the images for the disabled buttons 
        this.fileUploadResultTag = "fileUploadResult" // The tag, which marks the string with the file upload result 
                                                      // (because of IE shows XML in an iframe as a HTML, it is impossible to parse a result 
                                                      //  of a file upload operation like a result of a simple AJAX request)
                                                      // (see ru.spb.iac.web.table2.impl.TableBeen.FILE_UPLOAD_RESULT_TAG)
        this.tableDivId = "tableDiv_" // The part of the id of a div, containing a table
        this.editPageTRName = "trToHide" // The part of the id of a table row on the edit page
    },

    addMainTable: function(tableName) {
        // Adds the table name to the array of the main tables
        //  tableName - the name of the table, which it is necessary to add to the array of the main tables
        this.mainTables.push(tableName);
    },
    
    setContextPath: function(contextPath) {
        // Sets the path to an application
        //  contextPath - path to an application
        if (this.contextPath.length == 0) {
            this.contextPath = contextPath;
            this.url = contextPath + "/TableServlet";
        }
    },
    
    setTagPrefix: function(tagPrefix) {
        // Sets the prefix for the names of the request parameters to send the values of the fields and the filter patterns
        if (this.tagPrefix != tagPrefix)
            this.tagPrefix = tagPrefix;
    },
    
    getParameters: function(tableName) {
        // Returns the parameters, that is common for all requests (the debug mode is on or off, the current mode, the name of the table)
        //  tableName - the name of the table, which request is sent
        return "debug=" + this.debug + "&mode=" + $("mode_" + tableName).value + "&tableName=" + tableName;
    },
    
    clear: function() {
        // Clears the array of the main tables
        this.mainTables.clear();
        this.contextPath = "";
        this.tagPrefix = "";
        this.enabledButtonImageURLs.clear();
        this.disabledButtonImageURLs.clear();
    },
    
    addButtonImageURLs: function(buttonName, enabledImageURL, disabledImageURL) {
        // Adds the urls of the images for the button
        //  buttonName - the name of the button, which images urls are added
        //  enabledImageURL - the url to the image for the enabled button
        //  disabledImageURL - the url to the image for the disabled button
        this.enabledButtonImageURLs.merge({buttonName: enabledImageURL});
        this.disabledButtonImageURLs.merge({buttonName: disabledImageURL});
    },
    
    setFileUploadResultTag: function(fileUploadResultTag) {
        // Sets the tag, which marks the string with the file upload result 
        //  fileUploadResultTag - the tag, which marks the string with the file upload result 
        this.fileUploadResultTag = fileUploadResultTag;
    }
};

// Functions

function encodeHtmlTag(str) {
    var result = "";
    for (var i = 0; i < str.length; i++) {
        if (str.charAt(i) == '<') result += "%3c";
        else if (str.charAt(i) == '>') result += "%3e";
        else if (str.charAt(i) == '&') result += "%26";
        else if (str.charAt(i) == '"') result += "%22";
        else if (str.charAt(i) == ' ') result += "%20";
        else if (str.charAt(i) == '%') result += "%25";
        else if (str.charAt(i) == '#') result += "%23";
        else if (str.charAt(i) == '?') result += "%3f";
        else result += str.charAt(i);
    }
    return result;
}

function changeTablesVisibility(tableName, visible) {
    // Makes the tables on the page visible or hides them
    //  tableName - the name of the table, which visibility should't be changed
    //  visible - the flag, that the tables should be made visible
    tableName = (tableName) ? tableName : "";
    /*$$("div").reject(function(element){
        return element.id.indexOf(tm.tableDivId) != 0 || (element.id.substr(tm.tableDivId.length) == tableName && tableName.length > 0)
               || !tm.mainTables.include(element.id.substr(tm.tableDivId.length));
    }).each(function(item){
        item.style.display = visible ? "inline" : "none";
    });*/
    var mainTableName = "";
    $$("div").reject(function(element) {
        return element.id.indexOf(tm.tableDivId) != 0 || (element.id == (tm.tableDivId + tableName) && tableName.length > 0)
               || !tm.mainTables.include(element.id.substr(tm.tableDivId.length));
    }).each(function(item) {
        var isMainTableDiv = tableName.length > 0 && $$('#' + item.id + ' div').findAll(function(divItem) {
            return divItem.id == tm.tableDivId + tableName;
        }).length > 0;
        if (!isMainTableDiv)
            item.style.display = visible ? "" : "none";
        else mainTableName = item.id.substr(tm.tableDivId.length);
    });
    if (mainTableName.length > 0)
        changeTrsVisibility(tableName, visible, $(tm.tableDivId + mainTableName));
}

function changeTrsVisibility(tableName, visible, div) {
    // Makes the rows of the table visible or hide them
    //  tableName - the name of the table, which rows visibility should't be changed
    //  visible - the flag, that the rows should be made visible
    //  div - the div, the table is contained in
    $$('#' + div.id + ' table').each(function(item) {
        if (item.id.length > 0)
            $$('#' + item.id + ' tr[name="' + tm.editPageTRName + '"]').each(function(trItem) {
                var haveToBeChanged = $$('#' + trItem.id + ' div[id="' + tm.tableDivId + tableName + '"]').length == 0;
                if (haveToBeChanged)
                    trItem.style.display = visible ? "" : "none";
                else {
                    //var tableDiv = trItem.childNodes[3].childNodes[0].childNodes[1].childNodes[0].childNodes[1].childNodes[1];
                    var tableDiv = trItem.getElementsByClassName("value")[0].getElementsByTagName("table")[0].getElementsByTagName("tbody")[0].getElementsByTagName("tr")[0].getElementsByTagName("td")[0].getElementsByTagName("div")[0];
                    changeTrsVisibility(tableName, visible, tableDiv);
                }
            });
    });
}

function isOkButton(buttonId) {
    // Returns true, if the button is ok button
    //  buttonId - the id attribute of the button
    return buttonId.indexOf(tm.okButtonId) == 0;
}

function getTableName(buttonId) {
    // Returns the name of the table, which contains button
    //  buttonId - the id attribute of the button
    return buttonId.indexOf(tm.okButtonId) == 0 ? buttonId.substr(tm.okButtonId.length) : 
        buttonId.substr(tm.cancelButtonId.length);
}

function checkResponse(request) {
    // Performed, when a ajax response come from server
    //  request - the reference to the XMLHttpRequest object, which containes response
    var root = request.responseXML.getElementsByTagName("root")[0];
    var tableName = root.getElementsByTagName("tableName")[0].childNodes[0].nodeValue;
    var wasMarked = root.getElementsByTagName("mark").length > 0;

    // if a row was checked, its class has to be changed
    if (wasMarked) {
        var mark = root.getElementsByTagName("mark")[0];
        var rowNumber = mark.getElementsByTagName("rowNumber")[0].childNodes[0].nodeValue;
        var marked = new Boolean(mark.getElementsByTagName("checked")[0].childNodes.length);
        var markedAll = true;
        var rowId = "_row";
        var row = $(tableName + rowId + rowNumber).parentNode.parentNode;
        $$('tr[id="' + row.id + '"]').each(function (item) {
            item.className = (marked.valueOf() ? "checked" : (rowNumber % 2 > 0 ? "odd" : "even"));
        })
        $$('input[type="checkbox"]').findAll(function(element) {
            return element.id.indexOf(tableName + rowId) == 0;
        }).each(function(item) {
            if (item.checked) {
                marked = new Boolean(true);
            } else markedAll = false;
        });
        if ($(tm.allRowsCheckboxId + tableName) != null)
            $(tm.allRowsCheckboxId + tableName).checked = markedAll;
        $$('input[type="button"]').findAll(isButtonToChange).each(function(item) {
            item.className = (marked.valueOf() ? "enableButton" : "disableButton");
            item.disabled = !marked.valueOf();
        });
        $$('a').findAll(isButtonToChange).each(function(item) {
            var imageURL = "";
            if (marked.valueOf()) {
                item.className = "enableButton";
                tm.enabledButtonImageURLs.each(function(pair) {
                    if (pair.key == item.id.substr(tm.buttonToChangeId.length)) {
                        imageURL = pair.value;
                        throw $break;
                    }
                });
            } else {
                item.className = "disableButton";
                tm.disabledButtonImageURLs.each(function(pair) {
                    if (pair.key == item.id.substr(tm.buttonToChangeId.length)) {
                        imageURL = pair.value;
                        throw $break;
                    }
                });
            }
            // TODO проверить!!!
            item.$$("img")[0].src = imageURL;
        });
        tableName = Try.these(
            function() {
                var parentRowId = row.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.id;
                return parentRowId.substring(0, parentRowId.indexOf("_" + tm.editPageTRName));
            },
            function() {return "";}
        );
    }
    
    // columnName tag is empty for the marking row in a main table
    if (root.getElementsByTagName("columnName")[0].childNodes.length > 0) {
        // error tag is empty for the correct result and containes "true" for the error
        var error = new Boolean(root.getElementsByTagName("error")[0].childNodes.length);
        updatePage(tableName, root.getElementsByTagName("columnName")[0].childNodes[0].nodeValue, error);
    }
    
}

function checkIFrameContent(iframe) {
    // Performed, when target frame for file form is reloaded
    //  iframe - target frame for file form
    if (iframe.contentWindow.document.childNodes.length > 0) {
        var resultText = iframe.contentWindow.document.getElementsByTagName("body")[0].innerHTML;
        if (resultText.length > 0) {
            var fileUploadResult = resultText.substring(resultText.indexOf(tm.fileUploadResultTag) + tm.fileUploadResultTag.length, 
                                                        resultText.indexOf("/" + tm.fileUploadResultTag));
            var columnNameTag = "!columnName";
            var errorTag = "!error";
            var columnName = fileUploadResult.substring(fileUploadResult.indexOf(columnNameTag) + columnNameTag.length, 
                                                        fileUploadResult.indexOf(errorTag));
            var error = fileUploadResult.substr(fileUploadResult.indexOf(errorTag) + errorTag.length);
            error = error.length > 1 ? error : "";
            updatePage(iframe.id.substr("fileUploadResult_".length), columnName, error);
            updateTable(iframe.id.substr("fileUploadResult_".length), true);
        }
    }
}

function updatePage(tableName, columnName, error) {
    // Updates a page with a table acording to the results of the entered data validation
    //  tableName - the name of the table, which data has been changed
    //  colunmName - the name of the table column, which data has been changed
    //  error - the result of the data validation
    if (columnName != "") {
        var spanId = "_label_";
        $(tableName + spanId + columnName).className = (error.valueOf() ? "error" : "");
    }

    // if result is negagive and edited field is captcha, all captchas have to be updated
    if (error.valueOf()) {
        var captchaId = "captcha_";
        $$("td").findAll(function(element) {
            return element.id.indexOf(captchaId) == 0 && element.id.indexOf(columnName) > 0;
        }).each(function(item) {
            $$('#' + item.id + ' img').each(function(img) {
                var src = img.src;
                img.src = src + "&" + Math.random();
            });
        });
    }

    // ok button have to be enable, when all fields filled in correct
    error = new Boolean($$('span[class="error"]').reject(function(element) {
        return element.id.indexOf(tableName + spanId) != 0;
    }).length > 0);
    if ($(tm.okButtonId + tableName)) {
        $(tm.okButtonId + tableName).className = (error.valueOf() ? "disableButton" : "enableButton");
        $(tm.okButtonId + tableName).disabled = error.valueOf();
    }
}

function isButtonToChange(element) {
    // Checkes, if the button makes action with marked rows
    //  element - the element (<input type="button"> or <a>), which has to be checked
    return element.id != "undefine" && element.id.indexOf(tm.buttonToChangeId) == 0;    
}

function errorReport(request) {
    // Takes out error message window, if the error has been generated during ajax request
    //  request - the reference to the XMLHttpRequest object, which containes error code
    alert("Error! " + request.status + " " + request.statusText);
}

function buttonClick(tableName, buttonName, question, windowParameters, constitutiveExclusiveMode, rowNumber, link) {
    // Performed, when any table or row button is clicked
    //  tableName                 - the name of the table, the button of which was clicked
    //  buttonName                - the name of the button, which was clicked
    //  question                  - the question, after the positive answer on which, action have to continue
    //  windowParameters          - the parameters of the window, where the result of the button action have to appear
    //  constitutiveExclusiveMode - flag, that only table with name tableName have to state visible on a page
    //  rowNumber                 - the number of the row, which button was clicked
    //  link                      - the link, if the button is rendered as a link
    if (!(link) || link.className == "enableButton")
        if (question.length == 0 || confirm(question)) {
            var parameters = tm.getParameters(tableName) + "&action=BUTTON_CLICK&buttonName=" + buttonName 
                             + (rowNumber > -1 ? "&rowNumber=" + rowNumber : "");
            if (windowParameters.length > 0)
                window.open(tm.url + "?" + parameters, "_blank", windowParameters);
            else {
                new Ajax.Updater({success: tm.tableDivId + tableName}, tm.url, 
                                 {method: "post", parameters: parameters, onFailure: errorReport});
                if (constitutiveExclusiveMode) {
                    changeTablesVisibility(tableName, false);
                }
            }
        }
}

function checkAllRows(tableName, columnName) {
    // Performed, when all-rows-checkbox is checked or unchecked
    //  tableName - the name of the table, where all-rows-checkbox is checked or unchecked
    //  columnName - the name of the column, which containes the table, which all-rows-checkbox is checked or unchecked
    var checkboxId = "_row";
    $$('input[type="checkbox"]').reject(function(element) {
        return element.id.indexOf(tableName + checkboxId) != 0;
    }).each(function(item){
        if (item.checked != $(tm.allRowsCheckboxId + tableName).checked) {
            item.checked = $(tm.allRowsCheckboxId + tableName).checked;
            checkRow(tableName, columnName, item.id.substr((tableName + checkboxId).length));
        }
    });
}

function checkRow(tableName, columnName, rowNumber) {
    // Performed, when the row of the table is marked by the clicking the checkbox in this row
    //  tableName - the name of the table, which row is marked
    //  columnName - the name of the column, which containes the table, which row is marked
    //  rowNumber - the number of the row, which is marked
    var parameters = tm.getParameters(tableName) + "&action=MARK&rowNumber=" + rowNumber + "&columnName=" + columnName;
    new Ajax.Request(tm.url, {method: "post", parameters: parameters, onComplete: checkResponse, onFailure: errorReport});
}

function setFilterParameters(control) {
    // Sets the parameters for the filtration of the table
    //  element - the element, the parameters are taken from
    var controlId = "_field_";
    var parameters = tm.getParameters(control.id.substring(0, control.id.indexOf("_"))) + "&action=EDIT_FILTER_PARAMETER&columnName=" 
                     + control.id.substring(control.id.indexOf(controlId) + controlId.length, 
                                (control.id.indexOf(tm.index) > 0 ? control.id.indexOf(tm.index) : control.id.length));
    if (control.type.indexOf("select") == 0) {
        $$("#" + control.id + " option").each(function(item){
            if (item.selected)
                parameters += "&value=" + item.value; 
        });
    } else {
        $$('input[type="text"]').findAll(function(element) {
            return element.id == control.id || 
                   (control.id.indexOf("^") > 0 && element.id.indexOf(control.id.substring(0, control.id.length - 2)) == 0);
        }).each(function(item){
            parameters += "&value=" + (item.value.length > 0 ? encodeHtmlTag(item.value) : null); 
        });
    }
    new Ajax.Request(tm.url, {method: "post", parameters: parameters, onComplete: checkResponse, onFailure: errorReport});
}

function setFilter(button) {
    // Performed, when one of the filter buttones is clicked
    //  button - the button, which is clicked
    var tableName = getTableName(button.id);
    var parameters = tm.getParameters(tableName) + "&action=" + (isOkButton(button.id) ? "FILTER_ON" : "FILTER_OFF");
    new Ajax.Updater({success: tm.tableDivId + tableName}, tm.url, {method: "post", parameters: parameters, onFailure: errorReport});
}

function sortRows(tableName, columnName) {
    // Performed, when one of the coluns headers is clicked to sort the rows of a table by the values of this column
    //  tableName - the name of the table, which rows have to be sorted
    //  columnName - the name of the column, which values the rows have to be sorted by
    var parameters = tm.getParameters(tableName) + "&action=SORT&columnName=" + columnName;
    new Ajax.Updater({success: tm.tableDivId + tableName}, tm.url, {method: "post", parameters: parameters, onFailure: errorReport});
}

function changePage(tableName, pageNumber) {
    // Performed, when new page number is clicked
    //  tableName - the name of the table, which page have to be changed
    //  pageNumber - the number of the new page
    var parameters = tm.getParameters(tableName) + "&action=CHANGE_PAGE&pageNumber=" + pageNumber;
    new Ajax.Updater({success: tm.tableDivId + tableName}, tm.url, {method: "post", parameters: parameters, onFailure: errorReport});
}

function changeRowsPerPage(tableName, rowsPerPage) {
    // Changes the count of the rows per page
    //  tableName - the name of the table, where changes have to be made
    //  rowsPerPage - the new count of the rows per page
    var parameters = tm.getParameters(tableName) + "&action=CHANGE_ROWS_PER_PAGE&rowsPerPage=" + rowsPerPage;
    new Ajax.Updater({success: tm.tableDivId + tableName}, tm.url, {method: "post", parameters: parameters, onFailure: errorReport});
}

function genPassword(tableName, columnName, length, index) {
    // Generates a password
    //  tableName - the name of the table, the passwor have to be generated for
    //  columnName - the name of the column, the passwor have to be generated for
    //  length - the length of the string of the password
    //  index - the index of the value, the passwor have to be generated for
        var newPassword = "";
        do {
            var code = Math.round(Math.random() * 1000);
            if (code > 47 && code < 123 && code != 60 && code != 62)
                newPassword += String.fromCharCode(code);
        } while (newPassword.length < length);
        $("showPasswordField_" + columnName + tm.index + index).innerHTML = newPassword;
        $$("input[type='password']").reject(function(element) {
            return element.id.indexOf(columnName + tm.index + index) == -1;
        }).each(function(item) {
            item.value = newPassword;
        });
        doCheck(tableName, $("passwordField_" + columnName + tm.index + index));
}

function saveChanges(tableName, button) {
    // Performed, when the Ok or Cancel button on the edit page is clicked
    //  tableName - the name of the table, which edit page is open
    //  button - the button, which is clicked
    updatePage(tableName, "", new Boolean());
    if (!button.disabled) {
        var parameters = tm.getParameters(tableName) + "&action=" + (isOkButton(button.id) ? "SAVE_CHANGES" : "CANCEL_CHANGES");
        new Ajax.Updater({success: tm.tableDivId + tableName}, tm.url, {method: "post", parameters: parameters, onFailure: errorReport});
        changeTablesVisibility(tableName, true);
    }
}

function doCheck(tableName, control) {
    // Performed, when the value of the field on the edit page is changed
    //  tableName - the name of the table, where changes have to be made
    //  control - the element, where the value was changed
    var columnKey = "_column_";
    var checkableControls = ["radio", "checkbox"];
    var parameters = tm.getParameters(tableName) + "&columnName=" + control.name.substring(control.name.indexOf(columnKey) 
                     + columnKey.length, control.name.indexOf(tm.index)) + "&objectNumber=" 
                     + control.name.substr(control.name.indexOf(tm.index) + tm.index.length);
    if (control.type != "button") {
        parameters += "&action=EDIT";
        $$(getControlTag(control.type)).reject(function(element) {
            return element.name.substr(element.name.indexOf(columnKey)) != control.name.substr(control.name.indexOf(columnKey));
        }).each(function(item) {
            if (checkableControls.indexOf(item.type) > -1 && item.checked || checkableControls.indexOf(item.type) < 0) {
                parameters += "&" + tm.tagPrefix + item.name.substring(0, item.name.indexOf(columnKey)) + "=" 
                              + encodeHtmlTag(item.value);
            }
        });
        new Ajax.Request(tm.url, {method: "post", parameters: parameters, onComplete: checkResponse, onFailure: errorReport});
    } else {
        parameters += "&action=EDIT_VECTOR";
        new Ajax.Updater({success: tm.tableDivId + tableName}, tm.url, {method: "post", parameters: parameters, onFailure: errorReport});
    }
}

function getControlTag(type) {
    // Returns the tag of the control of the defined type
    //  type - the type of the control, which tag has to be gotten
    //  return the tag of the control
    var result = "input";
    if (type.indexOf("select") > -1)
        result = "select";
    else if (type == "textarea")
        result = type;
    return result;
}

function submitFile(form, isAdded, isImage) {
    // Submits a form with a file-upload control
    //  form - the form, which has to be submited
    //  isAdded - the flag, that file is uploaded with file-upload control (not deleted)
    //  isImage - the flag, that the file-upload control uploads image file
    var submit = true;
    if (!isAdded) {
        form.value.value = "";
        form.fileAction.value = "delete";
    } else if (isImage) {
        var extentions = /(\.(gif|jpg|png|bmp))$/i
        if (!extentions.test(form.value.value))
            submit = false;
    }
    if (submit) {
        form.debug.value = tm.debug;
        form.submit();
    } else {
        alert("\u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439\u0020\u0442\u0438\u043f\u0020\u0444\u0430\u0439\u043b\u0430!");
        form.value.value = "";
    }
    //form.fileName.value = form.value.value;
    return true;
}

function changeFilterState(tableName, filterOn) {
    // Changes a state of the filter div from invisible to visible and vice versa
    //  tableName - the name of the table, which filter state is changed
    //  filterOn - the flag of the filter state
    var filterDivId = "_filterDiv";
    if (!filterOn)
        $(tableName + filterDivId).className = $(tableName + filterDivId).className == 'filterOn' ? 'off' : 'filterOn';
}

function updateTable(tableName, currentMode) {
    // Updates table view
    //  tableName - the name of the table, which has to be updated
    //  currentMode - the flag, that the mode of the table doesn't have to be changed
    var parameters = tm.getParameters(tableName) + "&action=UPDATE" + ((currentMode) ? "&currentMode=true" : "");
    new Ajax.Updater({success: tm.tableDivId + tableName}, tm.url, {method: "post", parameters: parameters, onFailure: errorReport});
}

function viewRow(tableName, rowNumber) {
    // Shows the row of the table
    //  tableName - the name of the table, which row has to be shown
    //  rowNumber - the number of the table row, which row has to be shown
    var parameters = tm.getParameters(tableName) + "&action=VIEW&rowNumber=" + rowNumber;
    new Ajax.Updater({success: tm.tableDivId + tableName}, tm.url, {method: "post", parameters: parameters, onFailure: errorReport});
}