Source: manage_time_slots.js

(
/**
* Contains the functionalities related to the manage time slots page.
*
* @module manage_time_slots
* @author Joonas Konki
* @author Anu Koskela
* @author Mikko Kuhno
* @author Henrik Paananen
* @author Atte Räty
* @license BSD 3-clause, see LICENSE for more details.
* @copyright 2015 Kepler project authors
*/
function(){

var timeslots = null;
var currentTime = null;
var cal = null;

/**
* Initialises and updates the calendar.
* @memberof module:manage_time_slots
*/
function updateCalendar() {
    if(cal === null) {
        initCalendar();
    }
    cal.fullCalendar('refetchEvents');
}

/**
* Gets the selected calendar timeslot event.
* @memberof module:manage_time_slots
* @param event - Contains the event information.
* @param element - Contains information what kind of.
* element the clicked event is
* @param view - Contains the viewmode in which the element is shown.

*/
function calendarClick (event, element, view) {
    if (event.eventTimeslot !== undefined) {
        selectCalendarTimeslot(event);
    }
}

/**
* Renders the selected timeslots to the calendar.
* @memberof module:manage_time_slots
* @param event - Contains the event information.
* @param element - Contains information what kind of
* element the event is.
* @param view - Contains the viewmode in which the element is shown.

*/
function calendarEventRender(event, element, view) {
    calUtil.defaultEventRender(event, element, view);
}

/**
* Initialises the calendar with the given custom attributes.
* @memberof module:manage_time_slots
* @param timeslot - The timeslots to render to the calendar.
* @param data - The data to initialise the calendar with.
*/
function initCalendar(timeslot, data) {
    cal = calUtil.initCal(calendarClick, calendarEventRender);

    cal.fullCalendar('addEventSource', {
        events: function(start, end, timezone, callback) {

            var params = {
                'start_time': start.toISOString(),
                'end_time': end.toISOString()
            };

            kepler.getCalendarEvents(params, function(data) {
                 var events = [];
                 for (var i = 0; i < data.events.length; i++) {

                    var event = data.events[i];
                    // console.log(event);
                    var color = calUtil.colorPalette.reservedGrey;

                    var pointer = false;
                    var res_id = null;
                    if(event.type === 'reservation'){
                        color = calUtil.colorPalette.lightBlue;
                        res_id = event.reservation.id;
                        pointer = true;
                    }

                    calEvent = calUtil.makeCalendarEvent(event);
                    calEvent.color = color;
                    calEvent.borderColor = calUtil.colorPalette.borderBlue;
                    calEvent.textColor = 'black';
                    calEvent.reservation_id = res_id;
                    calEvent.pointer = pointer;

                    events.push(calEvent);
                }
                callback(events);
            });
        }
    });

}

/**
* Empties the supervisor selector and fills it with those supervisors whose
* user role matches the selected study level.
* @memberof module:manage_time_slots
* @param {object} supervisors - A list of all available users who have the user
* role "supervisor".
* @param {string} studyTypeId - The ID of the selected study level.
*/
function updateSupervisors(supervisors, studyTypeId) {
    $("#instructorSelection").empty();
    for (var i = 0; i < supervisors.length; i++) {
        var supervisor = supervisors[i];
        for (var k = 0; k < supervisor.roles.length; k++) {
            if (supervisor.roles[k].unit_type_id === studyTypeId) {
                var opt = $("<option>").attr("value", supervisor.user_id);
                opt.text(supervisor.first_names + " " + supervisor.last_name);
                $("#instructorSelection").append(opt);
            }
        }
    }
    $("#instructorSelection option").first().prop("selected", true);
}

/**
* Empties all of the editable fields related to creating a new reservation.
* Fetches time slots from the database and updates the study level selection.
* @memberof module:manage_time_slots
*/
function updateNewReservationForm() {
    kepler.getTimeSlotEditInfo(function (data) {
        var unitTypes = data.unit_types;
        var supervisors = data.supervisors;
        $('#maxRes').val(data.max_reservations_default);
        $('#studyLevelSelection').empty();
        $("#instructorSelection").empty();
        $('#studyLevelSelection').change(function (v) {
            var selectedStudyType = parseInt(v.target.value);
            updateSupervisors(supervisors, selectedStudyType);
        });
        for (var i = 0; i < unitTypes.length; i++) {
            var opt = $("<option>").attr("value", unitTypes[i].unit_type_id);
            opt.text(unitTypes[i].unit_type_name);
            $('#studyLevelSelection').append(opt);
        }
        $('#studyLevelSelection option').first().prop("selected", true);
        var sLevelId = parseInt($('#studyLevelSelection').val());
        updateSupervisors(supervisors, sLevelId);
    });
}

/**
* Returns a list of the selected supervisors.
* @memberof module:manage_time_slots
*/
function getSelectedSupervisors() {
    var ins = [];
    ins[0] = $("#instructorSelection").val();
    //TODO (Anu) This needs to work for multiple supervisors.
    return ins;
}

/**
* Gets the information from all editable fields and makes a call to save the
* new time slot with the gathered information.
* @memberof module:manage_time_slots
*/
function saveTimeSlot(){
    var date;
    var startTime;
    var endTime;
    var studyLevel;
    var maxRes;
    var supervisorIds = [];
    var status;

    date = $("#date").val(); //TODO (Anu) formatting
    if($("#timeMorning").is(':checked')) {
        startTime = "08:00:00";
        endTime = "12:00:00";
    }
    else if($("#timeMidday").is(':checked')) {
        startTime = "12:00:00";
        endTime = "16:00:00";
    }
    else if($("#timeEvening").is(':checked')) {
        startTime = "16:00:00";
        endTime = "20:00:00";
    }
    else if($("#timeOther").is(':checked')) {
        startTime = $("#timeStart").val();
        endTime = $("#timeEnd").val();
    }
    studyLevel = $("#studyLevelSelection").val();
    maxRes = $("#maxRes").val();
    supervisorIds = getSelectedSupervisors();
    status = $("#statusSelection").val();

    var params = {
        'start_time': date + "T" + startTime,
        'end_time': date + "T" + endTime,
        'unit_type_id': studyLevel,
        'max_reservations': maxRes,
        'time_slot_supervisor_id_list': supervisorIds,
        'status': status,
    };

    if(startTime === null || endTime === null) {
        alert(kepler.translate('content.manage_time_slots_time_unclear'));
    }
    else {
      kepler.addTimeSlot(params, function (data) {
        kepler.showResult(data.result, "#addTimeSlotNotification");
        if (data.result.success) {
            reloadTimeSlots();
        }
      });
    }
}


/**
* Displays the students and additional information related to the time slot
* in a list element.
* @memberof module:manage_time_slots
* @param {object} timeslot - The time slot for which the information is
* related to.
* @param {number} index - The index of the wanted unit.
* @param {object} parent - The element where the information is added.
*/
function addExperimentsInfo(timeslot, index, parent){
    var studentList = $("<ul>").css("list-style","none");
    var experiment = timeslot.reservations[index];

    parent.append($("<div>").text(experiment.unit_group_name + "/" +
        experiment.unit_qualifier + ", " + experiment.unit_name));

    for (var k = 0; k < experiment.user_group.members.length; k++){
        var listItem = $("<li>");
        var button = $("<button>");
        button.addClass("btn btn-xs btn-default").attr("type", "button");
        button.attr("data-placement", "bottom");
        button.attr("data-toggle", "popover");
        button.attr("title", kepler.translate('label.contactInfo'));
        var emailText = "";
        if (experiment.user_group.members[k].email === ""){
            emailText = kepler.translate('label.noContactInfo');
        }
        else emailText = experiment.user_group.members[k].email;
        button.attr("data-content",emailText);
        button.append($("<span>").addClass("glyphicon glyphicon-info-sign"));
        button.popover();
        listItem.append(button);
        listItem.append($("<span>").text(" " +
            experiment.user_group.members[k].name + " "));
        studentList.append(listItem);
    }
    parent.append(studentList);
}

/**
* An utility function used to cancel the selected time slot.
*
* @function
* @name make_cancelTimeSlot
* @inner
* @memberof module:
* @param {number} tsId - The ID of the selected time slot.
* @param {string} tsName - The header information of the selected
* time slot.
* @returns function
*/
function make_cancelTimeSlot(tsId, tsName) {
    return function () {
        $("#modalButtonYes").off();
        /**
        * Makes a call to the database to cancel the current time
        * slot.
        *
        * @function
        * @name modalButtonYes click
        * @inner
        * @memberof module:manage_time_slots
        */
        $("#modalButtonYes").click(function () {
            console.log("test to remove: " + tsId + ", " + tsName);
            //TODO (Anu) Remove console.log when not needed.
            kepler.deleteTimeSlot(
                { 'time_slot_id': tsId },
                function (data) {
                    kepler.showResult(data.result, "#notification");
                    if (data.result.success) {
                        reloadTimeSlots();
                    }
                }
            );
        });
        $("#modalLabel").text(tsName);
        $("#modalCancel").modal('show');
    };
}

/**
* Creates a table that displays all the time slots and a toggleable area
* for each of the time slot which have reservations. The toggleable area
* includes the information of the reserved unit groups and of the participants.
* Adds a button and a listener for each upcoming time slot, which enables
* cancelling the time slot.
* @memberof module:manage_time_slots
*/
function initTable(data) {
//TODO (Anu) Editing the time slot

    timeslots = data.time_slots;
    var tsList = $('#timeSlotList');

    // console.log(timeslots);

    for (var i = 0; i < timeslots.length; i++) {
        var ts = timeslots[i];
        var tsDiv = $('<li>').addClass('timeSlot').attr('id',"li" + i);
        if (ts.status == 'canceled_id')
            tsDiv.addClass('canceled');
        var hasReservations = ts.reservations.length !== 0;

        var listRow = $('<div>').addClass("list-group-item");
        listRow.attr("id", "header" + i);

        var noOfExperiments = getNumberOfReservations(ts);
        if (ts.status === 'canceled_id') {
            listRow.append($("<span>").addClass(
              "badge").text(noOfExperiments).css(
              "background", "#a94442").css("display", "inline-block"));
            listRow.addClass("list-group-item-danger");
            listRow.attr("data-toggle", "collapse");
            listRow.attr("data-target", "#row" + i).css("cursor","pointer");
            listRow.append($("<span>").addClass(
              "glyphicon glyphicon-chevron-down").css("float","left").css(
              "cursor","pointer"));
        } else if (hasReservations) {
            listRow.append($("<span>").addClass(
              "badge").text(noOfExperiments).css("display", "inline-block"));
            listRow.addClass("list-group-item-info");
            listRow.attr("data-toggle", "collapse");
            listRow.attr("data-target", "#row" + i).css("cursor","pointer");
            listRow.append($("<span>").addClass(
              "glyphicon glyphicon-chevron-down").css("float","left").css(
              "cursor","pointer"));
        } else {
            listRow.append($("<span>").addClass(
              "badge").text(noOfExperiments).css("background", "lightgray").css(
                "display", "inline-block"));
            listRow.addClass("list-group-item-default");
            listRow.attr("data-toggle", "collapse").attr("data-target",
            "#row" + i).css("cursor","pointer");
            listRow.append($("<span>").addClass(
              "glyphicon glyphicon-chevron-down").css("color", "lightgray").css(
                "float","left").css("cursor","pointer"));
        }

        var tsTime = kepler.datetimeIntervalString(ts.start_time, ts.end_time);

        var tm_span = $("<div>").text(tsTime);
        tm_span.css("display", "inline-block").css("min-width", "12em");
        tm_span.css("margin-left", "1em");
        listRow.append(tm_span);

        var tsName = ts.unit_type_name;
        var instructors = "";
        for (var k = 0; k < ts.supervisors.length; k++){
            if (k > 0) instructors += ",";
            if(ts.supervisors[k].call_name !== null)
                instructors += " " + ts.supervisors[k].call_name;
            else instructors += " " + ts.supervisors[k].first_names;
            instructors += " " + ts.supervisors[k].last_name;
        }
        tsName += ", " + instructors;
        listRow.append($("<div>").text(tsName).css("display", "inline-block"));

        if (ts.status == 'canceled_id'){
            var cancelledSpan =
                $("<div>").text(
                  kepler.translate('content.reservations_cancelled_time_slot'));
            cancelledSpan.css("display","inline-block").css("min-width","12em");
            cancelledSpan.css("margin-left", "1em");
            listRow.append(cancelledSpan);
        }

        tsDiv.append(listRow);

        var collapseRow = $('<div>').addClass('collapse').attr('id',"row" + i);
        for (var j = 0; j < ts.reservations.length; j++){
            var colUnitGroup = "";
            var listItem = $("<li>").addClass("list-group-item");
            var helpDiv = $("<div>").addClass("row");
            var listDivInfo = $("<div>").addClass("col-sm-6");
            addExperimentsInfo(ts, j, listDivInfo);
            helpDiv.append(listDivInfo);

            if(ts.reservations[j].notes.length !== 0){
                var infoDiv = $("<div>").append($("<b>").text(kepler.translate(
                  'content.reservations_extra_info') + ":"));
                for (var l = 0; l < ts.reservations[j].notes.length; l++) {

                    infoDiv.append($("<br>")).append($("<i>").text(
                      ts.reservations[j].notes[l].note));
                }
                helpDiv.append(infoDiv);
            }
            listItem.append(helpDiv);
            collapseRow.append(listItem);
        }

        if ((ts.status !== 'canceled_id') && (ts.start_time > currentTime))
        {
            var buttonClass = '';
            buttonClass = "class='btn btn-primary'";
            var editButton = $("<button id=" + ts.id + " " + buttonClass +
                ">" + kepler.translate("button.edit") + "</button>");
            editButton.click(function(){
                notImplementedMsg();
                //window.location = '/edit_time_slot?id=' + this.id;
            });

            var deleteButton = $("<button>").addClass("btn btn-default");
            deleteButton.text(kepler.translate("button.delete"));

            deleteButton.click(make_cancelTimeSlot(ts.id,tsTime +", "+tsName));

            var buttonDiv = $("<div>").attr("align", "right");
            buttonDiv.append(editButton);
            buttonDiv.append(deleteButton);
            collapseRow.append(buttonDiv);
        }
        tsDiv.append(collapseRow);
        tsList.append(tsDiv);
    }
}

/**
* Returns the number of reservations made to the given time slot.
* @memberof module:manage_time_slots
* @param {object} timeslot - The current time slot.
* @returns {number} - The number of reservations the time slot has.
*/
function getNumberOfReservations(timeslot){
    return timeslot.reservations.length;
}


/**
* Shows the wanted time slots based on which options (past, present, future)
* are checked and hides the rest. If no time slots match the selected time
* frame, shows a notification.
* @memberof module:manage_time_slots
*/
function updateTable(){
    var tsList = $('#timeSlotList');
    var howManyDisplayed = 0;
    var showPast = $('#selectionPast').is(':checked');
    var showCurrent = $('#selectionCurrent').is(':checked');
    var showFuture =$('#selectionFuture').is(':checked');

    function timeSlotVisible(timeSlot) {
        return (showPast && timeSlot.end_time < currentTime) ||
            (showCurrent && (timeSlot.start_time < currentTime &&
                timeSlot.end_time > currentTime) ) ||
            (showFuture && timeSlot.start_time > currentTime);
    }

    for (var i = 0; i < timeslots.length; i++) {
        if (timeSlotVisible(timeslots[i])) {
            $("#li" + i).show();
            howManyDisplayed++;
        }
        else $("#li" + i).hide();
    }

    if(howManyDisplayed === 0) {
        $("#noMatches").show();
        $("#timeSlotList").hide();
    }
    else {
      $("#noMatches").hide();
      $("#timeSlotList").show();
    }
}


/**
* Gets the time slots from the database and updates the timeSlotList.
* @memberof module:manage_time_slots
*/
function reloadTimeSlots(){
    var tsTable = $("#timeSlotList");
    tsTable.empty();

    var params = {
        'only_active': true
    };
    kepler.getTimeSlots(params, function (data) {
        currentTime = data.timestamp;
        initTable(data);
        updateTable();
        $('#displayedSelection').change(function (data) {
            updateTable();
        });
    });
}

/**
* This is executed after the HTML page has been loaded.
* This is a common procedure of all of the client-side modules to
* initialise the page content.
* @memberof module:manage_time_slots
*/
function doc_ready() {
    $("#navManageTimeSlots").addClass("active");

    reloadTimeSlots();

    updateNewReservationForm();
    $("#shiftTimeFrame").change(function(v){
        notImplementedMsg();
    });
    $("#saveButton").click(function(){
        saveTimeSlot();
    });

    /**
    * Toggles between the calendar and list views when the button is clicked.
    *
    * @function
    * @name toggleCalendarBtn click
    * @inner
    * @memberof module:manage_time_slots
    */
    $("#toggleCalendarBtn").click(function(v) {
        if($("#calendar").is(":visible")) {
            $("#timeSlotList").show();
            $("#calendar").hide();
            $("#toggleCalendarBtn").text(
                kepler.translate('content.reservation_frame_button_calendar'));
            reloadTimeSlots();
        } else {
            $("#timeSlotList").hide();
            $("#calendar").show();
            updateCalendar();
            $("#toggleCalendarBtn").text(
                kepler.translate('content.reservation_frame_button_list'));
        }
    });

}
$(document).ready(doc_ready);
}());