nus-mtp/cs-modify

View on GitHub
static/javascripts/csmodify.js

Summary

Maintainability
F
1 wk
Test Coverage
/****************************************************************/
/* =============== CS-MODIFY JAVASCRIPT FILE ================== */
/* This file serves as the central javascript source for all the*/
/* custom-defined animations or interactions on the page.       */
/****************************************************************/

/* VARIABLES USED FOR THE INTERFACE FOR EDITING MODULE PREREQUISITES */
var unitTemplate;
var addUnitConnector = '<tr><td colspan="12">and</td></tr>';
var addModuleTemplate;
var addModuleTemplateConnector = '<td>or</td>';

/* VARIABLES USED FOR THE INTERFACE FOR EDITING MODULE PRECLUSIONS */
var preclusionUnitTemplate;

/*
 * FUNCTIONS FOR SIDEBAR ANIMATIONS
 */

/* Set the width of the side navigation to 400px */
function openSidebar() {
    document.getElementById("sidebar").style.width = "400px";
}

/* Set the width of the side navigation to 0 */
function closeSidebar() {
    if (document.getElementById("sidebar") != null) {
        document.getElementById("sidebar").style.width = "0";
    }
}

$(function () {
    $('[data-toggle="tooltip"]').tooltip();

    $('#sidebar-button').click(function(e) {
        e.stopPropagation();
        openSidebar();
    });

    $('body').click(function(e) {
        if (e.target.id != 'sidebar') {
            closeSidebar();
        }
    });

    unitTemplate = document.getElementById("prereq-unit-template");
    if (unitTemplate != null) {
        unitTemplate.removeAttribute("id");
    }

    addModuleTemplate = document.getElementById("module-template");
    if (addModuleTemplate != null) {
        addModuleTemplate.removeAttribute("id");
    }

    preclusionUnitTemplate = document.getElementById("preclusion-unit-template");
    if (preclusionUnitTemplate != null) {
        preclusionUnitTemplate.removeAttribute("id");
    }
})

/*
 * FUNCTIONS FOR SCROLLING TO TOP OF PAGE
 */

// Collapses the navbar on scroll
$(window).scroll(function() {
    try{
        if ($(".navbar").offset().top > 50) {
            $(".navbar-fixed-top").addClass("top-nav-collapse");
        } else {
            $(".navbar-fixed-top").removeClass("top-nav-collapse");
        }
    }catch(err){
        console.log(err.message, err.name);
    }
});

// jQuery for page scrolling feature - requires jQuery Easing plugin
$(function() {
    $('a.page-scroll').bind('click', function(event) {
        var $anchor = $(this);
        $('html, body').stop().animate({
            scrollTop: $($anchor.attr('href')).offset().top
        }, 1500, 'easeInOutExpo');
        event.preventDefault();
    });
});

/*
 *   FUNCTION FOR SHOWING PROMPT WHEN USER CLICKS ON LOGOUT BUTTON
 */
 function confirmLogout() {
    toLogout = window.confirm("Are you sure you want to logout?");
    if (toLogout) {
        $.ajax({
            type: "POST",
            url: "/logout"
        }).done(function() {
            window.location = "/login";
        })
    }
 }

/*
 * FUNCTIONS FOR CUSTOM VALIDATION MESSAGES FOR LOGIN
 * AND REGISTRATION FORMS
 */
function checkUsername(input) {
    if (input.validity.patternMismatch == true) {
        input.setCustomValidity("User ID should be alphanumeric and within 9 characters.");
    } else if (input.validity.valueMissing == true) {
        input.setCustomValidity("Please fill in your User ID.");
    } else {
        input.setCustomValidity("");
    }
}

function checkPassword(input) {
    if (input.validity.valueMissing == true) {
        input.setCustomValidity("Please enter a password.");
    } else {
        input.setCustomValidity("");
    }
}

/*
 * FUNCTIONS USED FOR THE INTERFACE FOR EDITING MODULE PREREQUISITES
 */
function attachListenersToInputs(length) {
    $("input").on("input", function() {
        $("input").each(function() {
            var parentColumn = this.parentElement;
            if (length == 1) {
                parentColumn.style.backgroundColor = "#ffffff";
            } else if (length == 2) {
                parentColumn.style.backgroundColor = "#9bc2e4";
            }

            if (parentColumn.children.length != length) {
                parentColumn.removeChild(parentColumn.lastChild);
            }
        });
    });
}

function addModule(btn) {
    // Get the parent row, to add <td> elements to.
    var parentRow = btn.parentElement.parentElement;

    // Creates a table column <td>, which represents the 'OR'
    // module prerequisite unit.
    var moduleUnit = addModuleTemplate.cloneNode(true);

    // Creates another table column containing the word 'or',
    // as a 'connector' to the 'OR' unit.
    var moduleConnectorUnit = document.createElement("td");
    moduleConnectorUnit.innerHTML = addModuleTemplateConnector;

    // Hide the last 'OR'.
    moduleConnectorUnit.style.display = "none"
    
    // Add the module unit to the row.
    parentRow.appendChild(moduleUnit);
    parentRow.appendChild(moduleConnectorUnit);

    // Reveal the previous 'OR'.
    moduleUnit.previousElementSibling.style.display = "";

    // Re-initialize all tooltips to ensure that even newly added
    // HTML elements receive the tooltip event listener.
    $('[data-toggle="tooltip"]').tooltip();
}

function removeModule(btn) {
    // Gets the target column and the 'OR' column.
    var parentTd = btn.parentElement;
    var nextTd = parentTd.nextElementSibling;

    // Gets the row that the column belongs to.
    var parentRow = parentTd.parentElement;

    // Case 1: Prerequisite unit only contains 1 module.
    // (Options column + [Module column & 'OR' column])
    // Action: Drop the whole unit altogether, and
    // perform hiding + deletion of adjacent 'AND' rows.
    if (parentRow.children.length == 3) {
        // Retrieve the target row's delete button.
        var deleteUnitButton = btn.parentElement.parentElement.firstElementChild.children[1];

        // Call the delete-prerequisite-unit function with it.
        deletePrereqUnit(deleteUnitButton);
    }
    else {
        // Case 2: Module deleted from the end.
        // Action: after deletion of module unit,
        // hide the new last 'OR' column.
        if (parentRow.lastElementChild.previousElementSibling == parentTd) {
            // Hide the new last 'OR' column.
            parentTd.previousElementSibling.style.display = "none";

            // Delete the module unit with its accompanying 'OR' column.
            parentRow.removeChild(parentTd);
            parentRow.removeChild(nextTd);
        }
        else {
            // Delete the module unit with its accompanying 'OR' column.
            parentRow.removeChild(parentTd);
            parentRow.removeChild(nextTd);
        }
    }
}

function addPrereqUnit() {
    // Creates a table row <tr>, which represents the 'AND'
    // module prerequisite unit.
    var prereqUnit = unitTemplate.cloneNode(true);

    // Also create the row containing the 'AND'.
    var prereqUnitConnector = document.createElement("tr");
    prereqUnitConnector.innerHTML = addUnitConnector;
    prereqUnitConnector.children[0].style.display = "none";

    document.getElementsByTagName("tbody")[0].appendChild(prereqUnit);
    document.getElementsByTagName("tbody")[0].appendChild(prereqUnitConnector);

    // Reveal the 'AND' row (when there is more than 1 AND unit).
    if (prereqUnit.previousElementSibling != null) {
        prereqUnit.previousElementSibling.children[0].style.display = ""
    }

    // Creates a table column <td>, which represents the 'OR'
    // module prerequisite unit.
    var moduleUnit = addModuleTemplate.cloneNode(true);

    // Creates another table column containing the word 'or',
    // as a 'connector' to the 'OR' unit.
    var moduleConnectorUnit = document.createElement("td");
    moduleConnectorUnit.innerHTML = addModuleTemplateConnector;

    // Hide the last 'OR'.
    moduleConnectorUnit.style.display = "none"
    
    // Add the module unit to the row.
    prereqUnit.appendChild(moduleUnit);
    prereqUnit.appendChild(moduleConnectorUnit);

    // Re-initialize all tooltips to ensure that even newly added
    // HTML elements receive the tooltip event listener.
    $('[data-toggle="tooltip"]').tooltip();
}

function deletePrereqUnit(btn) {
    var parentTr = btn.parentElement.parentElement;
    var tableBody = parentTr.parentElement;

    // Case 1: Only 1 'AND' unit present.
    // Action: Delete whole unit.
    if (tableBody.children.length == 2) {
        tableBody.removeChild(parentTr.nextElementSibling);
        tableBody.removeChild(parentTr);
    }
    else {
        // Case 2: Last prerequisite unit is deleted.
        // Action: delete the last prerequisite unit,
        // and hide the 'AND' row.
        if (tableBody.lastElementChild.previousElementSibling == parentTr) {
            parentTr.previousElementSibling.children[0].style.display = "none";
            tableBody.removeChild(parentTr.nextElementSibling);
            tableBody.removeChild(parentTr);
        }
        else {
            tableBody.removeChild(parentTr.nextElementSibling);
            tableBody.removeChild(parentTr);
        }
    }
}

function saveChangesPrerequisite() {
    attachListenersToInputs(2);

    // Submit data to backend for updating the prerequisites for module.
    var modulePrerequisites = convertToDataPrerequisite();
    var modulePrereqsJSON = JSON.stringify(modulePrerequisites);

    // Retrieve the module code to pass to the handler backend.
    var moduleCode = document.getElementsByTagName("h1")[0].children[0].children[0].textContent;

    var toSave = window.confirm("Are you sure you want to save your changes?");
    if (toSave) {
        $.ajax({
            type: "POST",
            url: "/editModulePrerequisites",
            data: {
                'code': moduleCode,
                'prerequisites': modulePrereqsJSON,
            }
        }).success(function(data) {
            var parsedData = JSON.parse(data);
            if (parsedData[0][0] == true) {
                window.alert("Your changes have been saved.");
                if (window.opener != null) {
                    var new_prerequisites = parsedData[1];
                    new_prerequisites = '<p id="prereq-display">' + new_prerequisites + ' <b>(saved)</b></p>';
                    window.opener.document.getElementById("prereq-display").innerHTML = new_prerequisites;
                    window.close();
                } else {
                    window.location.href = ("/editModule?code=" + moduleCode);
                }
            } else {
                highlightErrorFieldsPrerequisite(parsedData[0][1]);
            }
        }).fail(function() {
            window.alert("There was an error processing your request.");
        })
    }
}

function convertToDataPrerequisite() {
    var prerequisites = [];
    // For iterating through units comprising of
    // [[Module-columns],[AND-column]]
    var rows = document.getElementsByTagName("tbody")[0].children;

    for (i = 0; i < rows.length; i+=2) {
        // Reads the prerequisite modules for each unit
        var modulesInUnit = [];
        var columns = rows[i].children;

        for (j = 1; j < columns.length; j+=2) {
            // Reads off the module code in the input fields for each unit.
            var moduleCode = columns[j].children[0].value;

            if (moduleCode != "") {
                modulesInUnit.push(columns[j].children[0].value.toUpperCase());
            }
        }

        if (modulesInUnit.length != 0) {
            prerequisites.push(modulesInUnit);
        }
    }

    return prerequisites;
}

function revertChangesPrerequisite() {
    var toRevert = window.confirm("Are you sure you want to revert your changes?");
    if (toRevert) {
        var moduleCode = document.getElementsByTagName("h1")[0].children[0].children[0].textContent;
        window.location.href = ('/editModulePrerequisites?code=' + moduleCode);
    }
}

/*
 * FUNCTIONS USED FOR THE INTERFACE FOR EDITING MODULE PRECLUSIONS
 */
function addPreclusionModule(btn) {
    // Get the <tbody>, to add the <tr> elements to.
    var tableBody = document.getElementsByTagName("tbody")[0];

    // Creates the table row <tr>
    var preclusionModuleUnit = preclusionUnitTemplate.cloneNode(true);
    
    // Add the row to the table body
    tableBody.appendChild(preclusionModuleUnit);

    // Re-initialize all tooltips to ensure that even newly added
    // HTML elements receive the tooltip event listener.
    $('[data-toggle="tooltip"]').tooltip();
}

function removePreclusionModule(btn) {
    // Get the <tbody>, to add the <tr> elements to.
    var tableBody = document.getElementsByTagName("tbody")[0];

    // Gets the target row
    var parentRow = btn.parentElement.parentElement;

    // Remove target row from table body
    tableBody.removeChild(parentRow);
}

function saveChangesPreclusion() {
    attachListenersToInputs(1);

    // Submit data to backend for updating the preclusions for module.
    var modulePreclusions = convertToDataPreclusion();
    var modulePreclusionsJSON = JSON.stringify(modulePreclusions);

    var moduleCode = document.getElementsByTagName("h1")[0].children[0].children[0].textContent;

    var toSave = window.confirm("Are you sure you want to save your changes?");
    if (toSave) {
        $.ajax({
            type: "POST",
            url: "/editModulePreclusions",
            data: {
                'code': moduleCode,
                'preclusions': modulePreclusionsJSON,
            }
        }).success(function(data) {
            var parsedData = JSON.parse(data);
            if (parsedData[0][0] == true) {
                window.alert("Your changes have been saved.");
                if (window.opener != null) {
                    var new_preclusions = parsedData[1];
                    new_preclusions = '<p id="preclusion-display">' + new_preclusions + ' <b>(saved)</b></p>';
                    window.opener.document.getElementById("preclusion-display").innerHTML = new_preclusions;
                    window.close();
                } else {
                    window.location.href = ("/editModule?code=" + moduleCode);
                }
            } else {
                highlightErrorFieldsPreclusion(parsedData[0][1]);
            }
        }).fail(function() {
            window.alert("There was an error processing your request.");
        })
    }
}

function convertToDataPreclusion() {
    var preclusions = [];

    // For iterating through all the preclusion modules
    var rows = document.getElementsByTagName("tbody")[0].children;

    for (i = 0; i < rows.length; i++) {
        var moduleInput = rows[i].children[1].children[0];

        if (moduleInput.value != "") {
            preclusions.push(moduleInput.value.toUpperCase());
        }
    }

    return preclusions;
}

function revertChangesPreclusion() {
    var toRevert = window.confirm("Are you sure you want to revert your changes?");
    if (toRevert) {
        var moduleCode = document.getElementsByTagName("h1")[0].children[0].children[0].textContent;
        window.location.href = ('/editModulePreclusions?code=' + moduleCode);
    }
}

function highlightErrorFieldsPrerequisite(data) {
    var modulesWithErrors = data;

    var rows = document.getElementsByTagName("tbody")[0].children;
    var message_start = "<p><b>";
    var message_end = "</b></p>";

    for (i = 0; i < rows.length; i+=2) {
        // Reads the prerequisite modules for each unit
        var columns = rows[i].children;

        for (j = 1; j < columns.length; j+=2) {
            // Reads off the module code in the input fields for each unit.
            var targetColumn = columns[j];
            var moduleCode = columns[j].children[0].value;

            if (targetColumn.children.length != 2) {
                // Removal of message in preparation of adding a new message (if necessary)
                targetColumn.removeChild(targetColumn.lastChild);
            }

            for (k = 0; k < modulesWithErrors.length; k++) {
                if (modulesWithErrors[k][0].toUpperCase() == moduleCode.toUpperCase()) {
                    var messageElement = document.createElement("p");
                    messageElement.innerHTML = (message_start + modulesWithErrors[k][1] + message_end);
                    targetColumn.appendChild(messageElement);
                    targetColumn.style.backgroundColor = "#d9534f";
                    break;
                }
            }
        }
    }
}

function highlightErrorFieldsPreclusion(data) {
    var modulesWithErrors = data;

    var rows = document.getElementsByTagName("tbody")[0].children;
    var message_start = "<p><b>";
    var message_end = "</b></p>";

    // Locates the element containing the module code,
    // and attaches a message there.
    for (i = 0; i < rows.length; i++) {
        var targetRow = rows[i];
        var moduleCodeColumn = targetRow.children[1];
        var moduleCode = moduleCodeColumn.children[0].value;

        for (k = 0; k < data.length; k++) {
            if (modulesWithErrors[k][0].toUpperCase() == moduleCode.toUpperCase()) {
                var messageElement = document.createElement("p");
                messageElement.innerHTML = (message_start + modulesWithErrors[k][1] + message_end);
                moduleCodeColumn.appendChild(messageElement);
                moduleCodeColumn.style.backgroundColor = "#d9534f";
                break;
            }
        }
    }
}


/* ========== DOCUMENT-READY FUNCTIONS ========== */
$(document).ready(function() {
    /*
     * READY FUNCTION:FUNCTIONS FOR ENABLING SORTING FOR CERTAIN TABLES
     */

    /*
     * order: [column #, asc/desc],
     * where column # uses 0-based indexing
     * from left to right
    */
    var table = $('#module-listing-table').DataTable( {
        "aaSorting": [],
    } );

    // Apply the search
    table.columns().every( function () {
        var col = this;
 
        $('input', this.header()).on('keyup change', function () {
            if (col.search() !== this.value) {
                col
                    .search(this.value)
                    .draw();
            }
        } );

        $('#toggleButton').on('click', function () {
            if (col.search() !== this.value) {
                col
                    .search(this.value)
                    .draw();
            }
        } );
    } );

    $('#delete-module-table').DataTable( {
        "aaSorting": []
    } );

    $('#fixed-mounting-table').DataTable( {
        "aaSorting": [],
        "columnDefs": [
            { type: 'num-html', width: "12%", targets: 3 },
            { type: 'num-html', width: "12%", targets: 4 },
            { type: 'num-html', width: "12%", targets: 6 },
            { type: 'num-html', width: "12%", targets: 7 }
        ],
        "initComplete": function(settings, json) {
            $('#fixed-mounting-table').show();
            $('.toggle-columns-display').show();
            $('.loading-div').hide();
            $('#fixed-mounting-table').DataTable().columns.adjust().draw();
            setTimeout(function() {
                $('.dataTable').width("100%");
            }, 200);
        }
    } );

    $('#tentative-mounting-table').DataTable( {
        "aaSorting": [],
        "columnDefs": [
            { type: 'num-html', width: "12%", targets: 3 },
            { type: 'num-html', width: "12%", targets: 4 },
            { type: 'num-html', width: "12%", targets: 6 },
            { type: 'num-html', width: "12%", targets: 7 }
        ],
        "initComplete": function(settings, json) {
            $('#tentative-mounting-table').show();
            $('.toggle-columns-display').show();
            $('.loading-div').hide();
            $('#tentative-mounting-table').DataTable().columns.adjust().draw();
            setTimeout(function() {
                $('.dataTable').width("100%");
            }, 200);
        }
    } );

    $('#student-year-table').DataTable( {
        "aaSorting": [],
        "bPaginate": false,
        "searching": false
    } );

    $('#modified-modules-summary-table').DataTable( {
        "aaSorting": [ 0, "asc" ],
        "autoWidth": false,
        "columnDefs": [
            { "targets": 0, "width": "15%" },
            { "targets": 1, "width": "35%" }
        ]
    } );

    $('#modified-modules-mounting-table').DataTable( {
        "aaSorting": [ 0, "asc" ],
        "autoWidth": false,
        "columnDefs": [
            { "targets": 0, "width": "15%" },
            { "targets": 1, "width": "35%" }
        ]
    } );

    $('#modified-modules-quota-table').DataTable( {
        "aaSorting": [ 0, "asc" ],
        "autoWidth": false,
        "columnDefs": [
            { "targets": 0, "width": "15%" },
            { "targets": 1, "width": "35%" },
            { "targets": [2, 3, 4], "width": "13%", type: 'num-html' }
        ]
    } );

    var modifiedModuleDetailsTable = $('#modified-modules-details-table').DataTable( {
        "aaSorting": [ 0, "asc" ],
        "autoWidth": false,
        "columnDefs": [
            { "targets": 0, "width": "15%" },
            { "targets": 1, "width": "35%" },
        ]
    } );

    // Add event listener for opening and closing details
    $('#modified-modules-details-table tbody').on('click', 'td.details-control', function () {
        var cell_data = modifiedModuleDetailsTable.cell(this).data();
        var tr = $(this).closest('tr');
        var row = modifiedModuleDetailsTable.row( tr );
 
        if ( row.child.isShown() ) {
            // This row is already open - close it
            row.child.hide();
            tr.removeClass('shown');
        }
        else {
            var formattedData = format(cell_data);
            if (formattedData != '') {
                // Open this row
                row.child(formattedData).show();
                tr.addClass('shown');
            }
        }
    } );

    $('#specific-modified-module').DataTable( {
        "autoWidth": false,
        "columnDefs": [
            { "targets": [0, 1], "width": "25%" }
        ]
    } );

    $('#student-focus-area-table').DataTable( {
        "aaSorting": [],
        "pageLength": 25,
        "bPaginate": false,
        "searching": false
    } );

    $('#oversubscribed-modules-table').DataTable( {
        "order": [[ 4, "desc" ]],
        "columnDefs": [
            { "targets": 0, "width": "8%" },
            { "targets": 3, "type": 'num-html', "width": "12%" },
            { "targets": 4, "width": "12%" },
            { "targets": 5, "width": "12%" }
        ],
        "initComplete": function(settings, json) {
            $('#oversubscribed-modules-table').show();
            $('.loading-div').hide();
            $('#oversubscribed-modules-table').DataTable().columns.adjust().draw();
            setTimeout(function() {
                $('.dataTable').width("100%");
            }, 200);
        }
    } );

    $('#modules-taken-prior-table').DataTable( {
        "order": [[ 6, "desc" ], [ 0, "asc" ], [ 3, "asc" ]],
        "columnDefs": [
            { "targets": [0, 3], "width": "8%" },
            { "targets": [1, 4], "width": "20%" },
            { "targets": 6, "width": "20%" }
        ],
        "initComplete": function(settings, json) {
            $('#modules-taken-prior-table').show();
            $('.loading-div').hide();
            $('#modules-taken-prior-table').DataTable().columns.adjust().draw();
            setTimeout(function() {
                $('.dataTable').width("100%");
            }, 200);
        }
    } );

    $('#modules-taken-prior-intern-table').DataTable( {
        "order": [[ 2, "desc" ]],
        "columnDefs": [
            { "targets": 0, "width": "15%" },
            { "targets": 1, "width": "60%" },
        ]
    } );
     
    $('#common-module-table').DataTable( {
        "aaSorting": [],
        "initComplete": function(settings, json) {
            $('#common-module-table').show();
            $('.loading-div').hide();
            $('#common-module-table').DataTable().columns.adjust().draw();
            setTimeout(function() {
                $('.dataTable').width("100%");
            }, 200);
        }
    } );

    $('#non-overlap-table').DataTable( {
        "aaSorting": [],
        "deferRender": true,
        "initComplete": function(settings, json) {
            $('#non-overlap-table').show();
            $('.loading-div').hide();
            $('#non-overlap-table').DataTable().columns.adjust().draw();
            setTimeout(function() {
                $('.dataTable').width("100%");
            }, 200);
        }
    } );
     
    $('#students-taking-module-table').DataTable({
       aaSorting: [],
        "deferRender": true
    });

    $('#mod-specific-size-table').DataTable( {
        "order": [[ 2, "asc"]]
    } );
});

function format(data) {
    var split = data.split("<!--p>");
    if (split.length > 1) {
        var d1 = split[1].trim();
        var d2 = split[2].split("</p-->")[0];

        var originalDescription = d1.substring(0, d1.length - 6);
        var modifiedDescription = d2;

        return '<div class="row">' +
               '<div class="col-md-5 text-justify">' +
               '<b>From</b><br>' +
               '<i>' + originalDescription + '</i>' +
               '</div>' +
               '<div class="col-md-2">' +
               '<h3>&#8594;</h3>' +
               '</div>' +
               '<div class="col-md-5 text-justify">' +
               '<b>To</b><br>' +
               '<i>' + modifiedDescription + '</i><br><br>' +
               '</div>' +
               '</div>';
    } else {
        return '';
    }    
}

window.onresize = function() {
    $('.dataTable').width("100%");
}