jenkinsci/hpe-application-automation-tools-plugin

View on GitHub
src/main/resources/lib/octane/configure.js

Summary

Maintainability
B
6 hrs
Test Coverage
/*
 * Certain versions of software accessible here may contain branding from Hewlett-Packard Company (now HP Inc.) and Hewlett Packard Enterprise Company.
 * This software was acquired by Micro Focus on September 1, 2017, and is now offered by OpenText.
 * Any reference to the HP and Hewlett Packard Enterprise/HPE marks is historical in nature, and the HP and Hewlett Packard Enterprise/HPE marks are the property of their respective owners.
 * __________________________________________________________________
 * MIT License
 *
 * Copyright 2012-2024 Open Text
 *
 * The only warranties for products and services of Open Text and
 * its affiliates and licensors ("Open Text") are as may be set forth
 * in the express warranty statements accompanying such products and services.
 * Nothing herein should be construed as constituting an additional warranty.
 * Open Text shall not be liable for technical or editorial errors or
 * omissions contained herein. The information contained herein is subject
 * to change without notice.
 *
 * Except as specifically indicated otherwise, this document contains
 * confidential information and a valid license is required for possession,
 * use or copying. If this work is provided to the U.S. Government,
 * consistent with FAR 12.211 and 12.212, Commercial Computer Software,
 * Computer Software Documentation, and Technical Data for Commercial Items are
 * licensed to the U.S. Government under vendor's standard commercial license.
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ___________________________________________________________________
 */

function octane_job_configuration(target, progress, proxy) {

    if (typeof jQuery === 'undefined') {
        return {
            configure: function() {
                target.innerHTML = "JQuery plugin must be installed and enabled in <a href=" + rootURL + "/pluginManager>Plugin Manager</a>";
            }
        }
    }

    var originalUnload = window.onbeforeunload;

    var fieldSelectors = [];

    var $ = jQuery;

    function caseInsensitiveStringEquals(left, right) {
        // TODO: janotav: no easy way to do this in JS (?), need to implement this properly
        return left.toLowerCase() === right.toLowerCase();
    }

    function loadJobConfigurationFromServer(sharedspace) {
        progressFunc("Retrieving configuration from server");

        proxy.loadJobConfigurationFromServer(sharedspace.id, function (t) {
            progressFunc();
            var response = t.responseObject();
            if (response.errors) {
                response.errors.forEach(renderError);
            } else {
                renderConfiguration(response, undefined, sharedspace.id);
            }
        });
    }

    function configure() {
        proxy.searchSharedSpaces("", (function (sharedspaces) {
            if(sharedspaces.responseJSON.results.length===1){
                loadJobConfigurationFromServer(sharedspaces.responseJSON.results[0]);
            }else{
                var sharedspaceDiv = $("<div class='mqm'><label><h3>Select ALM Octane server configuration</h3></label><select id='sharedspaceSelect'></select></div>");
                $(target).append(sharedspaceDiv);
                $("#sharedspaceSelect").select2({
                    placeholder: 'Select a configuration',
                    ajax: {
                        dataType: 'json',
                        delay: 250,
                        transport: function (params, success, failure) {
                            var term = "";
                            if (params.data.hasOwnProperty("q") && params.data.q !== undefined) {term = params.data.q;}
                            proxy.searchSharedSpaces(term, (function (data) {
                                queryToMqmCallback(data, success, failure)
                            }));
                        },
                        cache: true
                    },
                    templateResult: formatSelect2Option
                });
                $("#sharedspaceSelect").on("select2:select", function(e) {
                    loadJobConfigurationFromServer(e.params.data);
                });
            }
        }));


    }

    function renderError(error) {
        var errorDiv = $("<div class='error'><font color='red'><b/></font></div>");
        errorDiv.find("b").text(error.message);
        $(target).append(errorDiv);
        if (error.hasOwnProperty("url") && error.hasOwnProperty("label")) {
            var errorLink = $("<div class='error-link'><a></a><div>");
            errorLink.find("a").text(error.label);
            errorLink.find("a").attr("href", rootURL + error.url);
            $(target).append(errorLink);
        }
    }

    function progressFunc(msg) {
        if (typeof msg === 'string') {
            $(progress).find("h3").text(msg);
            $(progress).show();
            $(target).hide();
        } else {
            $(progress).hide();
            $(target).show();
        }
    }

    function renderConfiguration(jobConfiguration, pipelineId, instanceId) {
        var result = $(target);
        result.empty();

        var pipelineDiv = $("<div class='mqm'>");
        result.append(pipelineDiv);

        var buttons = $("<div>");
        var checkbox1 = $("<div>");
        var checkbox2 = $("<div>");
        var status = $("<div>");

        var selectedWorkspaceId;
        var tagTypes = {};
        var allTags = {};
        var tagTypesByName = {};

        var validators = [];
        var apply = [];
        var dirtyFlag;

        var selectedReleaseId;

        function initialize() {
            validators.length = 0;
            apply.length = 0;
            clearDirty();

            pipelineDiv.empty();
        }



        function addCheckbox(currPipeline) {
            checkbox1.empty();
            checkbox1.append(status);
            var ignoreTestCheckbox = $('<input type="checkbox" name="ignoreTestResults" id="ignoreTestResults" />' + 'Ignore test run results (Results are not sent to ALM Octane). ' + '<br /><br />');
            checkbox1.append(ignoreTestCheckbox);
            checkbox2.empty();
            pipelineDiv.append(checkbox1);
            checkbox2.append(status);
            var deleteTestCheckbox = $('<input type="checkbox" name="deleteTestResults" id="deleteTestResults"/>' + 'Delete results previously collected from this step. ' + '<br /><br />');
            checkbox2.append(deleteTestCheckbox);
            checkbox2.hide();
            pipelineDiv.append(checkbox2);

            ignoreTestCheckbox.change(function() {
                if($('#ignoreTestResults').prop('checked')){
                    checkbox2.show();
                }
                else {
                    checkbox2.hide();
                    $('#deleteTestResults').attr('checked', false);
                }



            });
            $('#ignoreTestResults').attr('checked', currPipeline.ignoreTests);
            if(currPipeline.ignoreTests){
                checkbox2.show();
            }
        }

        function addApplyButton(caption, pipeline, func, callback) {
            buttons.empty();
            buttons.append(status);
            var applyButton = $("<button>");
            applyButton.text(caption);
            applyButton.unbind('click').click(function() {
                saveConfiguration(pipeline, func, callback);
            });
            buttons.append(applyButton);
            pipelineDiv.append(buttons);
        }

        function renderPipelineMetadata(pipeline, pipelineSelector) {
            var table = $("<table class='ui-block'><tbody><tr/></tbody></table>");
            pipelineDiv.append(table);

            var releaseMilestoneAlertMessage = "<div id=\"releaseMilestoneAlert\" style=\"color: orange; visibility: hidden; font-style: italic\">Note: When you change a milestone or release, a new set of runs is created on ALM Octane, " +
                "with a new run history.</div>";

            pipelineDiv.append(releaseMilestoneAlertMessage);

            var tbody = table.find("tbody");
            var tr = tbody.find("tr");

            if (pipeline.id === null) { //rendering workspace selector - only for creating new pipeline
                var trWorkspace = $("<tr><td class='setting-name'><label for='pipeline-workspace'>Workspace:</label></td></tr>");
                tbody.append(trWorkspace);

                var tdWorkspaceSelect = $("<td class='setting-main'>");
                trWorkspace.append(tdWorkspaceSelect);
                var workspaceSelect = $("<select>");
                tdWorkspaceSelect.append(workspaceSelect);
                workspaceSelect.prop('id', 'workspaceSelect');

                var area = $("<div class='validation-error-area' id='workspace-selector-validation-area'/>");
                tdWorkspaceSelect.append(area);

                workspaceSelect.change(function () {
                    selectedWorkspaceId = workspaceSelect.val();
                    $(jq("workspace-selector-validation-area")).hide();
                    trRelease.css('visibility', 'visible');
                });

                var isWorkspaceUndefined = function() {
                    if (!workspaceSelect.val()) {
                        return "Workspace must be selected";
                    }
                    return false;
                };

                enableInputValidation(workspaceSelect, "", area, {
                    check: isWorkspaceUndefined
                });
                apply.push(function () {
                    pipeline.workspaceId = workspaceSelect.val();
                });
            } else {
                pipelineSelector.renderIfNecessary(tbody, pipeline.id);
            }

            var tdPipeline = $("<td class='setting-name'><label for='pipeline-name'>Pipeline:</label></td>");
            tr.append(tdPipeline);

            //  NAME
            if (pipeline.isRoot) {
                var tdPipelineInput = $("<td class='setting-main' colspan='2'><input id='pipeline-name' type='text' placeholder='Pipeline name' class='jenkins-input setting-input' maxlength='50'/><div class='validation-error-area'/></td>");
                tr.append(tdPipelineInput);

                var input = tdPipelineInput.find("input");
                input.attr("value", pipeline.name);
                var area = tdPipelineInput.find("div");

                var pipelineNameValidation = function() {
                    if (!input.val()) {
                        return "Pipeline name must be specified";
                    }
                    // based on latest discussions all characters should be allowed
                    //if (!input.val().match(/^[a-z0-9 _-]+$/i)) {
                    //    return "Pipeline name contains invalid characters";
                    //}
                    return false;
                };

                apply.push(function() {
                    pipeline.name = input.val();
                });
                enableDirtyInputCheck(input);
                enableInputValidation(input, "", area, {
                    check: pipelineNameValidation
                });
            } else {
                var tdPipelineName = $("<td class='setting-main' colspan='2'/>");
                tr.append(tdPipelineName);

                tdPipelineName.text(pipeline.name);
            }

            apply.push((function (){
                var ignoreTestsCheck = $('#ignoreTestResults');
                var deleteTestsCheck = $('#deleteTestResults');
                pipeline.ignoreTests = ignoreTestsCheck.prop("checked");
                if(pipeline.ignoreTests){
                    pipeline.deleteTests = deleteTestsCheck.prop("checked");
                }else {
                    pipeline.deleteTests = false;
                }
            }));

            //  RELEASE
            if (pipeline.isRoot) {
                var trRelease = $("<tr><td class='setting-name'><label for='pipeline-release'>Release:</label>");
                tbody.append(trRelease);

                var tdReleaseSelect = $("<td class='setting-main'>");
                trRelease.append(tdReleaseSelect);

                var releaseSelect = $("<select style='width: 360px;'>");
                releaseSelect.append($("<option>").text("-- Not specified --").val(-1).attr('selected', !isReleaseSpecified(pipeline)));
                if (pipeline.id !== null && isReleaseSpecified(pipeline)) {
                    releaseSelect.append($("<option>").text(pipeline.releaseName).val(pipeline.releaseId).attr('selected', 'true'));
                }
                apply.push(function () {
                    pipeline.releaseId = Number(releaseSelect.val());
                });
                enableDirtyChangeCheck(releaseSelect);
                tdReleaseSelect.append(releaseSelect);
                releaseSelect.prop('id', 'releaseSelect');
                trRelease.append($("<td class='setting-new'/>"));
                if (pipeline.id == null) {
                    trRelease.css('visibility', 'hidden');
                }

                var trMilestone = $("<tr><td class='setting-name'><label for='pipeline-release-milestone'>Milestone:</label>");
                tbody.append(trMilestone);

                var tdMilestoneSelect = $("<td class='setting-main'>");
                trMilestone.append(tdMilestoneSelect);

                var milestoneSelect = $("<select style='width: 360px;'>");
                milestoneSelect.append($("<option>").text("-- Not specified --").val(-1).attr('selected', !isMilestoneSpecified(pipeline)));
                if (pipeline.id !== null && isMilestoneSpecified(pipeline)) {
                    milestoneSelect.append($("<option>").text(pipeline.milestoneName).val(pipeline.milestoneId).attr('selected', 'true'));
                }
                apply.push(function () {
                    pipeline.milestoneId = Number(milestoneSelect.val());
                });
                enableDirtyChangeCheck(milestoneSelect);
                tdMilestoneSelect.append(milestoneSelect);
                milestoneSelect.prop('id', 'milestoneSelect');
                trMilestone.append($("<td class='setting-new'/>"));
                if (pipeline.id == null) {
                    trMilestone.css('visibility', 'hidden');
                }

                selectedReleaseId = pipeline.releaseId;

                releaseSelect.change(function () {
                    selectedReleaseId = releaseSelect.val();
                    milestoneSelect.val(-1).trigger("change");
                    trMilestone.css('visibility', 'visible');
                    if(pipeline.id != null){
                        document.getElementById("releaseMilestoneAlert").style.visibility = 'visible';
                    }
                });

                milestoneSelect.change(function () {
                    if(pipeline.id != null) {
                        document.getElementById("releaseMilestoneAlert").style.visibility = 'visible';
                    }
                });
            }
        }

        function renderNewPipeline(pipeline) {

            function createPipelineFunc(pipeline, callback) {
                proxy.createPipelineOnServer(pipeline, callback);
            }

            function createPipelineCallback(pipeline, response) {
                jobConfiguration.currentPipeline = response.pipeline;
                jobConfiguration.fieldsMetadata = response.fieldsMetadata;

                //inserting newly created pipeline properly into workspaces object
                var workspacePipelines = {};
                workspacePipelines[jobConfiguration.currentPipeline.id] = jobConfiguration.currentPipeline;

                jobConfiguration.workspaces = {};
                jobConfiguration.workspaces[jobConfiguration.currentPipeline.workspaceId] = {
                    id: jobConfiguration.currentPipeline.workspaceId,
                    name: jobConfiguration.currentPipeline.workspaceName,
                    pipelines: workspacePipelines
                };
                renderConfiguration(jobConfiguration, jobConfiguration.currentPipeline.id);
            }

            initialize();
            renderPipelineMetadata(pipeline);
            addApplyButton('Create', pipeline, createPipelineFunc, createPipelineCallback);
        }

        function renderExistingPipeline(pipeline, pipelineSelector) {

            function saveFunc(pipeline, callback) {
                if(pipeline.deleteTests){
                    proxy.deleteTests(pipeline, function(response) {
                        console.log(response.responseJSON)
                    });
                }
                proxy.updatePipelineOnSever(pipeline, callback);
            }

            function saveCallback(pipeline, response) {
                jobConfiguration.currentPipeline = response.pipeline;
                jobConfiguration.workspaces[response.pipeline.workspaceId].pipelines[response.pipeline.id] = response.pipeline;

                renderConfiguration(jobConfiguration, pipeline.id);
                if(response.error){
                    renderError(response.error);
                }
            }

            var groupBy = {};

            function addField(field) {
                var tr = $("<tr>");
                fieldsTbody.append(tr);

                var tdName = $("<td class='setting-name'>");
                tr.append(tdName);

                var label = $("<label>");
                label.prop('for', field.logicalListName);
                label.text(field.listName + ":");
                tdName.append(label);

                var tdSelect = $("<td class='setting-main'>");
                tr.append(tdSelect);

                var fieldValueSelect = $("<select>");
                tdSelect.append(fieldValueSelect);

                fieldValueSelect.prop('name', field.logicalListName);
                fieldValueSelect.prop('id', field.logicalListName);
                fieldSelectors.push({name: field.logicalListName, logicalListName: field.logicalListName, multiValue: field.multiValue, extensible: field.extensible});
                if (field.multiValue) {
                    fieldValueSelect.attr('multiple', 'multiple');
                    tdSelect.prop('colspan', 2);
                } else {
                    fieldValueSelect.append($("<option value='-1'>-- Not specified --</option>"));
                }
                // showing pre-selected values, if there are any
                field.values.forEach(function (fieldSelectedValue) {
                    fieldValueSelect.append($("<option>").text(fieldSelectedValue.name).val(fieldSelectedValue.id).attr('selected', 'true'));
                });
                enableDirtyChangeCheck(fieldValueSelect);
                apply.push(function () {
                    field.values = [];
                    fieldValueSelect.find("option:selected").each(function (index, option) {
                        if (option.value < 0) {
                            // not specified
                        } else if (option.value == 0) {
                            // new value
                            field.values.push({
                                name: newValueInput.val()
                            });
                        } else {
                            field.values.push({
                                id: option.value,
                                name: option.text
                            });
                        }
                    });
                });
                if (field.extensible) {
                    var tdAdd = $("<td class='setting-add'>");
                    tr.append(tdAdd);

                    var newValueInput = $("<input type='text' class='jenkins-input setting-input' maxlength='70' style ='width: 150px;'>");
                    tdAdd.append(newValueInput);

                    var trArea = $("<tr><td/></tr>");
                    fieldsTbody.append(trArea);

                    var tdArea = $("<td class='setting-main' colspan='2'>");
                    trArea.append(tdArea);

                    var validationArea = $("<div class='validation-error-area'>");
                    tdArea.append(validationArea);

                    newValueInput.blur(validateInput(validationArea, newFieldValueValidation(newValueInput, fieldValueSelect)));
                    newValueInput.hide();
                    enableInputValidation(newValueInput, "Value must be specified", validationArea, {
                        check: newFieldValueValidation(newValueInput, fieldValueSelect)
                    });

                    fieldValueSelect.change(function () {
                        validationArea.empty();
                        if (fieldValueSelect.val() == 0) {
                            newValueInput.css('display', 'inline');
                            newValueInput.val($(jqClass('select2-search__field')).last().val());
                        } else {
                            newValueInput.hide();
                        }
                    });
                }
            }

            function addTag(tag) {

                var tagTd;
                var tagTr;
                var group = groupBy[tag.tagTypeName];
                if (typeof group !== 'object') {
                    tagTr = $("<tr><td class='setting-name'></td>");
                    tagsTbody.append(tagTr);
                    tagTr.find("td").text(tag.tagTypeName + ":").attr("title", tag.tagTypeName);
                    tagTd = $("<td class='setting-main' colspan='2'/>");
                    tagTr.append(tagTd);
                    group = {
                        tr: tagTr,
                        td: tagTd,
                        count: 0
                    };
                    groupBy[tag.tagTypeName] = group;
                }
                tagTd = group.td;
                tagTr = group.tr;
                group.count++;

                var tagDiv = $("<div class='tag'><span/><a class='remove' href='javascript:void(0)'>X</a></div>");
                tagTd.append(tagDiv).append(" ");
                tagDiv.find("span").text(tag.tagName).attr("title", tag.tagName);
                var anchor = tagDiv.find("a");
                enableDirtyClickCheck(anchor);
                anchor.on("click", function() {
                    var index = pipeline.taxonomyTags.indexOf(tag);
                    pipeline.taxonomyTags.splice(index, 1);
                    tagDiv.remove();
                    if (--group.count == 0) {
                        tagTr.remove();
                        delete groupBy[tag.tagTypeName];
                    }
                    if (tag.tagId) {
                        addSelect.find("option[value='" + tag.tagId + "']").prop('disabled', false);
                    }
                });
            }

            initialize();
            renderPipelineMetadata(pipeline, pipelineSelector);

            //merging fields with fieldsMetadata => fieldTags
            pipeline.fieldTags = [];
            jobConfiguration.fieldsMetadata.forEach(function (fieldMetadata) {
                pipeline.fieldTags.push($.extend(true, {}, fieldMetadata)); //creating deep copy of fieldsMetadata so that the original metadata are not affected by the merge
            });

            pipeline.fieldTags.forEach(function(fieldMetadata) {
                fieldMetadata.values = [];
                if (pipeline.fields[fieldMetadata.name]) {
                    pipeline.fields[fieldMetadata.name].forEach(function (field) {
                        fieldMetadata.values.push({id: field.id, name: field.name});
                    });
                }
            });
            pipelineDiv.append($("<h4>Fields</h4>"));
            var fieldsTable = $("<table class='ui-block'><tbody/></table>");
            pipelineDiv.append(fieldsTable);
            var fieldsTbody = fieldsTable.find("tbody");
            pipeline.fieldTags.sort(function(a, b) {
                return parseInt(a.order) - parseInt(b.order);
            });
            pipeline.fieldTags.forEach(addField);

            pipelineDiv.append($("<h4>Environment</h4>"));
            var tagsTable = $("<table class='ui-block'><tbody/></table>");
            pipelineDiv.append(tagsTable);
            var tagsTbody = tagsTable.find("tbody");
            pipeline.taxonomyTags.forEach(addTag);

            var addTagTable = $("<table class='ui-block'><tbody/></table>");
            pipelineDiv.append(addTagTable);
            var addTagTbody = addTagTable.find("tbody");

            var tagSelectTr = $("<tr>");
            addTagTbody.append(tagSelectTr);
            var tagSelectTd = $("<td>");
            tagSelectTr.append(tagSelectTd);
            var addSelect = $("<select>");
            tagSelectTd.append(addSelect);
            addSelect.css('width', '300px');
            addSelect.prop('id','taxonomySelect');
            var defaultOption = $("<option value='default' selected>Add Environment...</option>");
            defaultOption.prop('disabled', 'disabled');
            addSelect.append(defaultOption);
            var addedTag;
            addSelect.change(function () {
                var val = addSelect.val();
                if (val === null || val === "default") {    //JQuery 1.7.2 receives "default", JQuery 1.11.2 receives null when this is called: addSelect.val("default").trigger("change")
                    return;
                } else if (val < 0) {
                    var tagType = tagTypes[tagTypeValue(val)];
                    addedTag = {
                        tagTypeId: tagType.tagTypeId,
                        tagTypeName: tagType.tagTypeName
                    };
                    tagTypeInput.val(tagType.tagTypeName);
                    tagTypeInput.hide();
                    tagTypeSpan.text(tagType.tagTypeName + ": ");
                    tagTypeSpan.css('display', 'inline');
                    tagInput.val($(jqClass('select2-search__field')).last().val()); //prefilled value for new taxonomy
                    tagInput.attr('title', 'Environment');
                    tagInput.attr('placeholder', 'Environment');
                    tagInput.css('display', 'inline');
                    add.css('display', 'inline');
                } else if (val == 'newTagType') {
                    addedTag = {};
                    tagTypeInput.val("");
                    tagTypeInput.attr('title', 'Environment Type');
                    tagTypeInput.attr('placeholder', 'Environment Type');
                    tagTypeInput.css('display', 'inline');
                    tagTypeSpan.hide();
                    tagInput.val($(jqClass('select2-search__field')).last().val()); //prefilled value for new taxonomy
                    tagInput.attr('title', 'Environment');
                    tagInput.attr('placeholder', 'Environment');
                    tagInput.css('display', 'inline');
                    add.css('display', 'inline');
                } else {
                    addedTag = allTags[val];
                    tagTypeInput.hide();
                    tagTypeSpan.hide();
                    tagInput.hide();
                    add.hide();
                    doAdd();
                }
                validationAreaTagType.empty();
                validationAreaTag.empty();
            });

            var validationAreaTagType = $("<div class='validation-error-area'>");
            var validationAreaTag = $("<div class='validation-error-area'>");

            var tagTypeInputTd = $("<td class='setting-name'>");
            tagSelectTr.append(tagTypeInputTd);
            var tagTypeInput = $("<input type='text' class='jenkins-input setting-input'>");
            tagTypeInputTd.append(tagTypeInput);
            tagTypeInput.hide();
            tagTypeInput.blur(newTagTypeValidation(tagTypeInput, pipeline.instanceId, pipeline.workspaceId, function(error) {
                doValidateTag(error, validationAreaTagType);
            }));
            var tagTypeSpan = $("<span>");
            tagTypeSpan.hide();
            tagTypeInputTd.append(tagTypeSpan);

            var tagInputTd = $("<td>");
            tagSelectTr.append(tagInputTd);
            var tagInput = $("<input type='text' class='jenkins-input setting-input'>");
            tagInputTd.append(tagInput);
            tagInput.hide();
            tagInput.blur(validateInput(validationAreaTag, newTagValidation(tagTypeInput, tagInput, pipeline.taxonomyTags)));

            var add = $("<button>Add</button>");
            add.hide();
            var doFinishValidation = function(){
                pipeline.taxonomyTags.push(addedTag);
                addTag(addedTag);
                if (addedTag.tagId) {
                    addSelect.find("option:selected").prop('disabled', 'disabled');
                }
                addedTag = undefined;
                makeDirty();
                defaultOption.prop('selected', 'selected');
                tagTypeInput.hide();
                tagTypeSpan.hide();
                tagInput.hide();
                add.hide();
                setTimeout(function() {
                    addSelect.val("default").trigger("change"); //set "Add environment..." as selected option
                }, 100);
            };
            var doValidateTag = function(error, target ){
                function showError(message) {
                    var container = $("<div class='error'/>");
                    container.html(message);
                    target.append(container);
                    target.show();
                }

                target.empty();
                if (error) {
                    showError(error);
                } else {
                    target.hide();
                }

            };
            var doAdd = function () {
                var validationTagTypeOk = null;
                var validationTagOk = null;

                if (!addedTag.tagTypeId) {
                    addedTag.tagTypeName = tagTypeInput.val();

                    newTagTypeValidation(tagTypeInput, pipeline.workspaceId, function(error) {

                        doValidateTag(error, validationAreaTagType);
                        validationTagTypeOk = typeof error === 'undefined';
                        if (validationTagOk !== null) {
                            if(validationTagTypeOk && validationTagOk){
                                doFinishValidation();

                            }
                        }
                    })();

                }else{
                    validationTagTypeOk = true;
                }

                if (!addedTag.tagId) {
                    addedTag.tagName = tagInput.val();
                    if (!validateInput(validationAreaTag, newTagValidation(tagTypeInput, tagInput, pipeline.taxonomyTags))()) {
                        validationTagOk = false;
                    }else{
                        validationTagOk = true;
                    }
                    if (validationTagTypeOk !== null) {
                        if(validationTagTypeOk && validationTagOk){
                            doFinishValidation();
                        }
                    }

                }else if(addedTag.tagId && addedTag.tagTypeId){
                    doFinishValidation();
                }

            };
            add.click(doAdd);
            enableDirtyClickCheck(add);
            var tagAddTd = $("<td>");
            tagSelectTr.append(tagAddTd);
            tagAddTd.append(add);

            // put validation area bellow both input fields
            var tagValidationTr = $("<tr>");
            addTagTbody.append(tagValidationTr);
            tagValidationTr.append($("<td>"));
            var tagValidationTd = $("<td colspan='2'>");
            tagValidationTr.append(tagValidationTd);

            tagValidationTd.append(validationAreaTagType);
            tagValidationTd.append(validationAreaTag);

            addCheckbox(jobConfiguration.currentPipeline);

            addApplyButton('Apply', pipeline, saveFunc, saveCallback);
        }

        var CONFIRMATION = "There are unsaved changes, if you continue they will be discarded. Continue?";

        if (!jobConfiguration.hasOwnProperty("currentPipeline")) {
            var createPipelineDiv = $("<div><h2>No Pipeline</h2><div class='mqm'><p>No pipeline is currently defined for this job</p><button>Create Pipeline</button></div></div>");
            pipelineDiv.append(createPipelineDiv);
            var createPipelineButton = createPipelineDiv.find("button");
            createPipelineDiv.find("div.mqm").append(createPipelineButton);
            createPipelineButton.click(function () {
                pipelineDiv.empty();
                var pipeline = {
                    id: null,
                    isRoot: true,
                    fieldTags: [],
                    taxonomyTags: [],
                    instanceId: instanceId
                };

                jobConfiguration.currentPipeline = pipeline;
                result.prepend($("<h2>Create Pipeline</h2>"));
                renderNewPipeline(pipeline);
                covertSelectorsToSelect2(pipeline);
            });
        } else {
            selectedWorkspaceId = jobConfiguration.currentPipeline.workspaceId;
            var pipelineSelector = undefined;

            var selectWorkspaceDiv = $("<div class='mutton rpos'>" +
                "<div id='configuration-div' class='config-label'>ALM Octane : " + jobConfiguration.currentPipeline.instanceCaption + "</div>" +
                "<div id='select-workspace-div'><label for='workspace-select'>Workspace:</label><select/>" +
                "</div></div>");
            var workspaceSelect = selectWorkspaceDiv.find("select");

            //sort workspaces by name to show in UI
            var sortedWorkspaces = sortByName(Object.values(jobConfiguration.workspaces));
            sortedWorkspaces.forEach(function (workspace) {
                workspaceSelect.append($("<option>").text(workspace.name).val(workspace.id).attr('selected', (workspace.id === selectedWorkspaceId)));
            });

            var lastSelectedWorkspace = $(workspaceSelect).find("option:selected");
            pipelineSelector = {
                renderIfNecessary: function (target, selectedPipelineId) {
                    if (Object.keys(jobConfiguration.workspaces[selectedWorkspaceId].pipelines).length > 1) {
                        var selectPipelineRow = $("<tr><td/><td class='setting-main'><select/><br/><br/></td><td class='setting-new'></td></tr>");
                        var pipelineSelect = selectPipelineRow.find("select");

                        //sort pipelines by name to show in UI
                        var sortedPipelines = sortByName(Object.values(jobConfiguration.workspaces[selectedWorkspaceId].pipelines));
                        sortedPipelines.forEach(function (pipeline) {
                            pipelineSelect.append($("<option>").text(pipeline.name).val(pipeline.id).attr('selected', (pipeline.id === selectedPipelineId)));
                        });
                        var lastSelectedPipeline = $(pipelineSelect).find("option:selected");
                        target.prepend(selectPipelineRow);
                        pipelineSelect.change(function () {
                            if (isDirty()) {
                                if (!window.confirm(CONFIRMATION)) {
                                    lastSelectedPipeline.attr("selected", true);
                                    return;
                                }
                            }
                            lastSelectedPipeline = $(pipelineSelect).find("option:selected");
                            var currentPipeline = jobConfiguration.workspaces[selectedWorkspaceId].pipelines[lastSelectedPipeline.val()];

                            progressFunc("Retrieving configuration from server");
                            proxy.enrichPipeline(currentPipeline, function (t) {
                                progressFunc();
                                var response = t.responseObject();
                                if (response.errors) {
                                    response.errors.forEach(renderError);
                                } else {
                                    jobConfiguration.currentPipeline = response.pipeline;
                                    renderExistingPipeline(jobConfiguration.currentPipeline, pipelineSelector);
                                    covertSelectorsToSelect2(jobConfiguration.currentPipeline);
                                }
                            });

                        });
                    }
                }
            };
            workspaceSelect.change(function () {
                if (isDirty()) {
                    if (!window.confirm(CONFIRMATION)) {
                        lastSelectedWorkspace.attr("selected", true);
                        return;
                    }
                }
                selectedWorkspaceId = workspaceSelect.val();
                lastSelectedWorkspace = $(workspaceSelect).find("option:selected");
                var currentPipeline = sortByName(Object.values(jobConfiguration.workspaces[selectedWorkspaceId].pipelines))[0];

                progressFunc("Retrieving configuration from server");
                proxy.loadWorkspaceConfiguration(currentPipeline, function (t) {
                    progressFunc();
                    var response = t.responseObject();
                    if (response.errors) {
                        response.errors.forEach(renderError);
                    } else {
                        jobConfiguration.currentPipeline = response.pipeline;
                        jobConfiguration.fieldsMetadata = response.fieldsMetadata;
                        renderExistingPipeline(jobConfiguration.currentPipeline, pipelineSelector);
                        covertSelectorsToSelect2(jobConfiguration.currentPipeline);
                    }
                });
            });

            result.prepend(selectWorkspaceDiv);
            result.prepend($("<h2>Edit Pipeline</h2>"));
            renderExistingPipeline(jobConfiguration.currentPipeline, pipelineSelector);
            covertSelectorsToSelect2(jobConfiguration.currentPipeline);
        }

        window.onbeforeunload = function() {
            if (isDirty()) {
                return CONFIRMATION;
            } else {
                // keep original check just in case there is another dirty data (shouldn't be)
                if (typeof originalUnload === 'function') {
                    return originalUnload();
                } else {
                    return undefined;
                }
            }
        };

        function sortByName(array) {
            array.sort(function(o1, o2) {
                return o1.name.localeCompare(o2.name);
            });
            return array;
        }

        function tagTypeValue(n) {
            // mapping to ensure negative value (solve the "0" tag type ID)
            return -(Number(n)+1);
        }

        function isReleaseSpecified(pipeline) {
            if (pipeline.hasOwnProperty("releaseId") && pipeline.releaseId !== -1) {
                return true;
            } else {
                return false;
            }
        }

        function isMilestoneSpecified(pipeline) {
            if (pipeline.hasOwnProperty("milestoneId") && pipeline.milestoneId !== -1) {
                return true;
            } else {
                return false;
            }
        }

        function makeDirty() {
            dirtyFlag = true;
        }

        function clearDirty() {
            dirtyFlag = false;
        }

        function isDirty() {
            return dirtyFlag;
        }

        function enableDirtyChangeCheck(select) {
            select.on('change', makeDirty);
        }

        function enableDirtyInputCheck(input) {
            input.on('input', makeDirty);
        }

        function enableDirtyClickCheck(button) {
            button.on('click', makeDirty);
        }

        function validateFields() {
            var valid = true;
            validators.forEach(function (validator) {
                if (!validator()) {
                    valid = false;
                }
            });
            return valid;
        }

        function applyFields() {
            apply.forEach(function (func) {
                func();
            });
        }

        function newTagValidation(tagTypeInput, tagInput, taxonomyTags) {
            return function () {
                var error = undefined;

                function matchTag(tag) {
                    if (caseInsensitiveStringEquals(tag.tagName, tagInput.val())) {
                        error = "Environment " + tagType.tagTypeName + ":" + tag.tagName + " is already defined";
                        return true;
                    } else {
                        return false;
                    }
                }

                function matchAddedTag(tag) {
                    if (caseInsensitiveStringEquals(tag.tagName, tagInput.val()) &&
                      caseInsensitiveStringEquals(tag.tagTypeName, tagTypeInput.val())) {
                        error = "Environment " + tag.tagTypeName + ":" + tag.tagName + " is already added";
                        return true;
                    } else {
                        return false;
                    }
                }

                if (!tagInput.val()) {
                    return "Environment must be specified";
                }

                var tagType = tagTypesByName[tagTypeInput.val()];
                if (tagType) {
                    tagType.values.some(matchTag);
                }

                if (!error) {
                    // could be added as new tag
                    taxonomyTags.some(matchAddedTag);
                }

                return error;
            };
        }

        function newFieldValueValidation(newValueInput, valueSelect) {
            return function () {
                var error = undefined;

                function matchValue(item) {
                    if (caseInsensitiveStringEquals(item, newValueInput.val())) {
                        error = "Value " + item + " is already defined";
                        return true;
                    } else {
                        return false;
                    }
                }

                if (valueSelect.val() != '0') {
                    return;
                }

                if (!newValueInput.val()) {
                    return "Value must be specified";
                }

                var values = [];
                valueSelect.find("option").each(function (index, option) {
                    values.push(option.text);
                });

                values.some(matchValue);
                return error;
            };
        }

        function newTagTypeValidation(tagTypeInput, instanceId, workspaceId, callback) {
            return function () {
                var error = undefined;

                function searchTaxCallback(data) {
                    var tagTypeSearch = [];
                    var response = data.responseObject();

                    function matchTagType(tagType) {
                        if (caseInsensitiveStringEquals(tagType, tagTypeInput.val())) {
                            error = "Environment Type " + tagType + " is already defined";
                            return true;
                        } else {
                            return false;
                        }
                    }

                    if (response.errors) {
                        response.errors.forEach(renderError);
                        failure();
                    } else {
                        Object.keys(response.tagTypes).forEach(function(key) {
                            tagTypeSearch.push(response.tagTypes[key].tagTypeName);
                        });
                        if(tagTypeSearch.length){
                            tagTypeSearch.some(matchTagType);
                        }

                    }
                    callback(error);
                }

                if (!tagTypeInput.val()) {
                    return "Environment type must be specified";
                }
                proxy.searchTaxonomies(tagTypeInput.val(), instanceId, workspaceId, [], searchTaxCallback);

            };
        }


        function enableInputValidation(input, message, validationArea, options_opt) {
            function emptyCheck() {
                if (!input.val()) {
                    return message;
                } else {
                    return false;
                }
            }
            var options = options_opt || {};
            var check = options.check || emptyCheck;
            var validate = validateInput(validationArea, check);
            input.blur(validate);
            validators.push(validate);
            validationArea.hide();
        }

        function validateInput(target, conditionFunc) {

            function showError(message) {
                var container = $("<div class='error'/>");
                container.html(message);
                target.append(container);
                target.show();
            }

            return function() {
                var error = conditionFunc();
                // workaround: prevent the click event from being lost due to onblur event,
                // currently no better solution seems to be available
                setTimeout(function() {
                    target.empty();
                    if (error) {
                        showError(error);
                    } else {
                        target.hide();
                    }
                }, 100);
                return !error;
            };
        }

        function validationError(error) {
            var errorDiv = $("<div class='error'><font color='red'><b/></font></div>");
            errorDiv.find("b").text(error.message);
            status.append(errorDiv);
        }

        function saveConfiguration(pipeline, saveFunc, saveCallback) {
            if (!validateFields()) {
                return;
            }
            applyFields();

            status.empty();

            progressFunc("Storing configuration on server");
            saveFunc(pipeline, function (t) {
                progressFunc();
                var response = t.responseObject();
                if (response.errors) {
                    response.errors.forEach(validationError);
                } else {
                    saveCallback(pipeline, response);
                    clearDirty();
                }
            });
        }

        function covertSelectorsToSelect2(pipeline) {

            function createFieldsSelect2(selector) {
                $(jq(selector.name)).select2({
                    ajax: {
                        dataType: 'json',
                        delay: 250,
                        transport: function (params, success, failure) {
                            var term = "";
                            if (params.data.hasOwnProperty("q") && params.data.q !== undefined) {term = params.data.q;}
                            var listId = 0;
                            proxy.searchListItems(selector.logicalListName, term, pipeline.instanceId, pipeline.workspaceId, selector.multiValue, selector.extensible, (function (data) {
                                queryToMqmCallback(data, success, failure)
                            }));
                        },
                        cache: true
                    },
                    templateResult: formatSelect2Option
                });
            }

            $("document").ready(function() {
                $("#taxonomySelect").select2({
                    ajax: {
                        dataType: 'json',
                        delay: 250,
                        transport: function (params, success, failure) {
                            var term = "";
                            if (params.data.hasOwnProperty("q") && params.data.q !== undefined) {term = params.data.q;}
                            proxy.searchTaxonomies(term, pipeline.instanceId, pipeline.workspaceId, pipeline.taxonomyTags, (function (data) {
                                queryToMqmCallback(data, success, failure)
                            }));
                        },
                        processResults: function (data, page) {
                            //resetting the values of known tags to values that have been retrieved from server
                            allTags = data.allTags;
                            tagTypesByName = data.tagTypesByName;
                            tagTypes = data.tagTypes;
                            return {
                                results: data.select2Input
                            };
                        },
                        cache: true
                    },
                    templateResult: formatSelect2Option
                    //minimumInputLength: 1
                });

                $("#releaseSelect").select2({
                    ajax: {
                        dataType: 'json',
                        delay: 250,
                        transport: function (params, success, failure) {
                            var term = "";
                            var tmpWorkspaceId;
                            if (params.data.hasOwnProperty("q") && params.data.q !== undefined) {term = params.data.q;}
                            if (!pipeline.id) {
                                tmpWorkspaceId = selectedWorkspaceId;
                            } else {
                                tmpWorkspaceId = pipeline.workspaceId;
                            }
                            proxy.searchReleases(term, pipeline.instanceId, tmpWorkspaceId, (function (data) {
                                queryToMqmCallback(data, success, failure)
                            }));
                        },
                        cache: true
                    },
                    templateResult: formatSelect2Option
                });

                $("#milestoneSelect").select2({
                    ajax: {
                        dataType: 'json',
                        delay: 250,
                        transport: function (params, success, failure) {
                            var term = "";
                            var tmpWorkspaceId;
                            if (params.data.hasOwnProperty("q") && params.data.q !== undefined) {term = params.data.q;}
                            if (!pipeline.id) {
                                tmpWorkspaceId = selectedWorkspaceId;
                            } else {
                                tmpWorkspaceId = pipeline.workspaceId;
                            }

                            proxy.searchMilestones(term, pipeline.instanceId, tmpWorkspaceId, selectedReleaseId, (function (data) {
                                queryToMqmCallback(data, success, failure)
                            }));
                        },
                        cache: true
                    },
                    templateResult: formatSelect2Option
                });

                $("#workspaceSelect").select2({
                    placeholder: 'Select a workspace',
                    ajax: {
                        dataType: 'json',
                        delay: 250,
                        transport: function (params, success, failure) {
                            var term = "";
                            if (params.data.hasOwnProperty("q") && params.data.q !== undefined) {term = params.data.q;}
                            var instanceId = jobConfiguration.currentPipeline.instanceId;
                            proxy.searchWorkspaces(term, instanceId, (function (data) {
                                queryToMqmCallback(data, success, failure)
                            }));
                        },
                        cache: true
                    },
                    templateResult: formatSelect2Option
                });
                fieldSelectors.forEach(createFieldsSelect2);
            });
        }
    }

    function queryToMqmCallback(data, success, failure) {
        var response = data.responseObject();
        if (response.errors) {
            response.errors.forEach(renderError);
            failure();
        } else {
            //workaround for IE on Jenkins with Jquery 1.7.2 , not necessary for JQuery 1.11.2-0
            //select2 input box is loosing focus after refreshing items
            setTimeout(function() {
                $(jqClass('select2-search__field')).last().focus();
            }, 100);
            //end of workaround
            success(data.responseJSON);
        }
    }

    //if option includes meta info, it should have another css style
    function formatSelect2Option(option) {
        //if (option.hasOwnProperty('newValue') && option.newValue) {
        //    var ret = $('<span title="Will let you create new environment in MQM"> ' +
        //    '<img src="http://icons.iconarchive.com/icons/rafiqul-hassan/blogger/16/Plus-icon.png"/><span style="margin: 3px"/>' +
        //    option.text + '</span>');
        //    return ret;
        //}
        if (option.hasOwnProperty('warning') && option.warning) {
            var ret = $('<span style="color: orange; font-style: italic">' + option.text + '</span>');
            return ret;
        }
        return option.text;
    }

    //jquery escaping special characters - used for searching elements
    function jq( myid ) {
        return "#" + myid.replace( /(:|\.|\[|\]|,)/g, "\\$1" );
    }

    //jquery escaping special characters - used for searching elements
    function jqClass(className) {
        return "." + className.replace( /(:|\.|\[|\]|,)/g, "\\$1" );
    }

    return {
        configure: configure
    };
}