<commit_before>var transcript = null;
var sdviewer = null;
var formatter = null;
var polltimeout = -1;

function onBinaryFetchResult(data) {

}

function pollForResults(data, polltime) {
    if (data == null) {
        alert("Return data is null!");
    } else if (data.error) {
        alert(data.error);
    } else if (data.status == "PENDING") {
        $.ajax({
            url: "/ocr/viewer_binarization_results/" + data.task_id + "/",
            dataType: "json",
            beforeSend: function(e) {
                sdviewer.setWaiting(true);
            },
            complete: function(e) {
                sdviewer.setWaiting(false);
            },
            success: function(data) {
                if (polltimeout != -1) {
                    clearTimeout(polltimeout);
                    polltimeout = -1;
                }
                polltimeout = setTimeout(function() {
                    pollForResults(data, polltime);
                }, polltime);
            },
            error: OCRJS.ajaxErrorHandler,
        });
    } else if (data.status == "SUCCESS") {
        $(sdviewer).data("binpath", data.results.out);
        sdviewer.setBufferPath(0, data.results.dst);
        sdviewer.setWaiting(false);
    }
}

function reconvertLines(lines) {
    var linedata = [];
    lines.each(function(i, elem) {
        linedata.push( {
            line: $(elem).data("num"),
            box: $(elem).data("bbox"),
        });
    });

    $.ajax({
        url: "/ocr/reconvert_lines/" + transcript.taskData().pk + "/",
        data: {
            coords: JSON.stringify(linedata),
            engine: $("#reconvert_engine").val(),
        },
        type: "POST",
        beforeSend: function(event) {
            lines.addClass("reconverting");
        },
        complete: function(data) {
        },
        success: function(data) {
            $.each(data.results, function(i, line) {
                var lineelem = document.getElementById("line_" + line.line);
                transcript.replaceLineText(lineelem, $(lineelem).text(), line.text);
                $(lineelem)
                    .removeClass("reconverting")
                    .addClass("reconverted");
                $("#save_data").button({disabled: false});
            });
        },
        error: OCRJS.ajaxErrorHandler,
    });
}

function unsavedPrompt() {
    return confirm("Save changes to transcript?");
}

function updateTask(event) {
    var abstaskpk = parseInt($("#task_pk").val());
    var hashoffset = parseInt(window.location.hash.replace(/^#!\//, ""));
    var batchoffset = parseInt($("#batchoffset").val());
    if (!isNaN(hashoffset)) {
        abstaskpk += hashoffset;
        batchoffset += hashoffset;
    }
    transcript.setTaskId(abstaskpk);
    console.log("Setting page slider to: " + batchoffset + " for task: " + abstaskpk);
    $("#page_slider").slider({value: batchoffset});
}

function updateNavButtons() {
    var ismax = $("#page_slider").slider("option", "value")
            == $("#page_slider").slider("option", "max");
    var ismin = $("#page_slider").slider("option", "value") == 0;
    $("#next_page").button({disabled: ismax});
    $("#prev_page").button({disabled: ismin});
    $("#heading").button({disabled: true});
}

$(function() {
    // setup toolbar
    $("#centre").button({
        text: false,
        icons: {
            primary: "ui-icon-link",
        },
    });
    $("#spellcheck").button();
    $("#format").buttonset();
    $("#next_page").button({
        text: false,
        icons: {
            primary: "ui-icon-seek-next",
        },
    });
    $("#prev_page").button({
        disabled: true,
        text: false,
        icons: {
            primary: "ui-icon-seek-prev",
        },
    });
    $("#save_data").button({
        disabled: true,
        text: false,
        icons: {
            primary: "ui-icon-disk",
        },
    });
    $("#save_training_data").button({
        text: false,
        icons: {
            primary: "ui-icon-check",
        },
    });
    $("#heading").button({
        disabled: true,
        text: true,
    });
    $("#reconvert").button({
        text: true,
    });

    $("#vlink").buttonset();

    $("#format_block").click(function(event) {
        formatter.blockLayout($(".transcript_lines"));
    });
    $("#format_line").click(function(event) {
        formatter.lineLayout($(".transcript_lines"));
    });
    $("#format_column").click(function(event) {
        formatter.columnLayout($(".transcript_lines"));
    });
    $("#page_slider").slider({
        min: 0,
        max: $("#batchsize").val() - 1,
        value: $("#batchoffset").val(),
    });

    // initialise the transcript editor
    transcript = new OCRJS.TranscriptEditor(document.getElementById("transcript"));
    transcript.onTaskChange = function() {
        var ismax = $("#page_slider").slider("option", "value")
                == $("#batchsize").val() - 1;
        var ismin = $("#page_slider").slider("option", "value") == 0;
        $("#next_page").button({disabled: ismax});
        $("#prev_page").button({disabled: ismin});
        $("#heading").button({disabled: true});
    }
    transcript.onLineSelected = function(type) {
        $("#heading").button({disabled: false});
        $("#heading").attr("checked", type == "h1")
            .button("refresh");
    }

    $("#reconvert").change(function(event) {
        $("#reconvert_engine").attr("disabled", !$("#reconvert").attr("checked"));
        $("#transcript_toolbar")
            .find("#spellcheck")
            .button({
                disabled: $(this).attr("checked"),
            });
        if (!$(this).attr("checked")) {
            //transcript.enable();
            $(".reconverted")
                .removeClass("reconverted");
            $(".ocr_line")
                .die("click.reconvert")
                .die("mouseover.reconvert")
                .die("mouseout.reconvert");
        } else {
            //transcript.disable();
            $(".ocr_line.hover").removeClass("hover");
            $(".ocr_line").live("click.reconvert", function(e) {
                positionViewer($(this).data("bbox"));
                reconvertLines($(this));
            }).live("mouseover.reconvert", function(e) {
                $(this).addClass("reconvert");
            }).live("mouseout.reconvert", function(e) {
                $(this).removeClass("reconvert");
            });
        }
    });

    $("#reconvert_engine").attr("disabled", !$("#reconvert").attr("checked"));

    $("#heading").change(function() {
        transcript.setCurrentLineType($(this).attr("checked") ? "h1" : "span");
    });

    // When a page loads, read the data and request the source
    // image is rebinarized so we can view it in the viewer
    // This is likely to be horribly inefficient, at least
    // at first...
    transcript.onTaskLoad = function() {
        //var ismax = $("#page_slider").slider("option", "value")
        //        == $("#batchsize").val() - 1;
        //var ismin = $("#page_slider").slider("option", "value") == 0;
        //$("#next_page").button({disabled: ismax});
        //$("#prev_page").button({disabled: ismin});
        //$("#heading").button({disabled: true});
        // get should-be-hidden implementation details
        // i.e. the task id that process the page.  We
        // want to rebinarize with the same params
        var task_pk = transcript.taskData().pk;
        $.ajax({
            url: "/ocr/submit_viewer_binarization/" + task_pk + "/",
            dataType: "json",
            beforeSend: function(e) {
                sdviewer.close();
                sdviewer.setWaiting(true);
            },
            success: function(data) {
                if (polltimeout != -1) {
                    clearTimeout(polltimeout);
                    polltimeout = -1;
                }
                pollForResults(data, 300);
            },
            error: OCRJS.ajaxErrorHandler,
        });
    }

    transcript.onTextChanged = function() {
        $("#save_data").button({
            disabled: false,
        });
    }

    transcript.onSave = function() {
        $("#save_data").button({
            disabled: true,
        });
    }

    transcript.onLinesReady = function() {
        // trigger a reformat
        $("input[name=format]:checked").click();
    }

    var positionViewer = function(position) {
        // ensure the given line is centred in the viewport
        var bounds = transcript.taskData().fields.results.box;
        var fw = bounds[2], fh = bounds[3];
        var x = position[0], y = position[1], w = position[2], h = position[3];
        var rect = new Seadragon.Rect(x / fw, (y - h) / fw, w / fw, h / fw);
        sdviewer.setBufferOverlays({
            "current": [rect],
        });
        if ($("#centre").attr("checked")) {
            sdviewer.fitBounds(rect, true);
        }
    }

    transcript.onHoverPosition = function(position) {
        if (!($("input[name=vlink]:checked").val() == "hover"
                    && sdviewer.isReady()))
            return;
        positionViewer(position);
    }

    transcript.onClickPosition = function(position) {
        if (!($("input[name=vlink]:checked").val() == "click"
                    && sdviewer.isReady()))
            return;
        positionViewer(position);
    }

    $("#spellcheck").change(function(event) {
        if ($(this).attr("checked")) {
            transcript.startSpellcheck();
        } else {
            transcript.endSpellcheck();
        }
    });

    $("#save_data").click(function(event) {
        transcript.save();
    });

    $("#save_training_data").click(function(event) {
        var pk = transcript.taskData().pk;
        var binurl = $(sdviewer).data("binpath");
        $.ajax({
            url: "/reference_pages/create_from_task/" + pk + "/",
            data: {binary_image: binurl},
            dataType: "json",
            type: "POST",
            success: function(data) {
                if (data.error)
                    return alert("Error: " + data.error);
                alert("Saved!");
            },
            complete: function() {
            },
            error: OCRJS.ajaxErrorHandler,
        });
    });

    $("#page_slider").slider({
        change: function(e, ui) {
            var val = $("#page_slider").slider("value");
            var diff = val - parseInt($("#batchoffset").val());
            var batchoffset = parseInt($("#batchoffset").val());
            var hashoffset = parseInt(window.location.hash.replace(/^#!\//, ""));
            if (isNaN(hashoffset))
                hashoffset = 0;
            var diff = val - batchoffset;
            var orig = batchoffset + hashoffset;

            // return if nothing's changed
            if (hashoffset == diff)
                return;

            // check for unsaved changes
            if (transcript.hasUnsavedChanges()) {
                if (!unsavedPrompt()) {
                    $("#page_slider").slider({value: orig});
                    return;
                } else {
                    transcript.save();
                }
            }
            window.location.hash = "#!/" + diff;

            // set the buttons accordingly
            updateNavButtons();
        },
    });

    $("#prev_page").click(function(event) {
        $("#page_slider").slider("option", "value",
            $("#page_slider").slider("option", "value") - 1);
    });

    $("#next_page").click(function(event) {
        $("#page_slider").slider("option", "value",
            $("#page_slider").slider("option", "value") + 1);
    });

    // line formatter object
    formatter = new OCRJS.LineFormatter();

    // viewer object
    sdviewer = new OCRJS.ImageViewer($("#viewer").get(0), {
        numBuffers: 1,
        height: 300,
    });

    updateTask();
    updateNavButtons();
    window.addEventListener("hashchange", updateTask);

    // maximise the height of the transcript page
    maximiseWidgets(transcript, sdviewer);
    $(window).resize(function(event) {
        maximiseWidgets(transcript, sdviewer);
    });
});

<commit_msg>Missed out from last commit<commit_after>var transcript = null;
var sdviewer = null;
var formatter = null;
var polltimeout = -1;

function onBinaryFetchResult(data) {

}

function pollForResults(data, polltime) {
    if (data == null) {
        alert("Return data is null!");
    } else if (data.error) {
        alert(data.error);
    } else if (data.status == "PENDING") {
        $.ajax({
            url: "/ocr/viewer_binarization_results/" + data.task_id + "/",
            dataType: "json",
            beforeSend: function(e) {
                sdviewer.setWaiting(true);
            },
            complete: function(e) {
                sdviewer.setWaiting(false);
            },
            success: function(data) {
                if (polltimeout != -1) {
                    clearTimeout(polltimeout);
                    polltimeout = -1;
                }
                polltimeout = setTimeout(function() {
                    pollForResults(data, polltime);
                }, polltime);
            },
            error: OCRJS.ajaxErrorHandler,
        });
    } else if (data.status == "SUCCESS") {
        $(sdviewer).data("binpath", data.results.out);
        sdviewer.setBufferPath(0, data.results.dst);
        sdviewer.setWaiting(false);
    }
}

function reconvertLines(lines) {
    var linedata = [];
    lines.each(function(i, elem) {
        linedata.push( {
            line: $(elem).data("num"),
            box: $(elem).data("bbox"),
        });
    });

    $.ajax({
        url: "/ocr/reconvert_lines/" + transcript.taskData().pk + "/",
        data: {
            coords: JSON.stringify(linedata),
            engine: $("#reconvert_engine").val(),
        },
        type: "POST",
        beforeSend: function(event) {
            lines.addClass("reconverting");
        },
        complete: function(data) {
        },
        success: function(data) {
            $.each(data.results, function(i, line) {
                var lineelem = document.getElementById("line_" + line.line);
                transcript.replaceLineText(lineelem, $(lineelem).text(), line.text);
                $(lineelem)
                    .removeClass("reconverting")
                    .addClass("reconverted");
                $("#save_data").button({disabled: false});
            });
        },
        error: OCRJS.ajaxErrorHandler,
    });
}

function unsavedPrompt() {
    return confirm("Save changes to transcript?");
}

function updateTask(event) {
    var abstaskpk = parseInt($("#task_pk").val());
    var hashoffset = parseInt(window.location.hash.replace(/^#!\//, ""));
    var batchoffset = parseInt($("#batchoffset").val());
    if (!isNaN(hashoffset)) {
        abstaskpk += hashoffset;
        batchoffset += hashoffset;
    }
    transcript.setTaskId(abstaskpk);
    console.log("Setting page slider to: " + batchoffset + " for task: " + abstaskpk);
    $("#page_slider").slider({value: batchoffset});
}

function updateNavButtons() {
    var ismax = $("#page_slider").slider("option", "value")
            == $("#page_slider").slider("option", "max");
    var ismin = $("#page_slider").slider("option", "value") == 0;
    $("#next_page").button({disabled: ismax});
    $("#prev_page").button({disabled: ismin});
    $("#heading").button({disabled: true});
}

$(function() {
    // setup toolbar
    $("#centre").button({
        text: false,
        icons: {
            primary: "ui-icon-link",
        },
    });
    $("#spellcheck").button();
    $("#format").buttonset();
    $("#next_page").button({
        text: false,
        icons: {
            primary: "ui-icon-seek-next",
        },
    });
    $("#prev_page").button({
        disabled: true,
        text: false,
        icons: {
            primary: "ui-icon-seek-prev",
        },
    });
    $("#save_data").button({
        disabled: true,
        text: false,
        icons: {
            primary: "ui-icon-disk",
        },
    });
    $("#save_training_data").button({
        text: false,
        icons: {
            primary: "ui-icon-check",
        },
    });
    $("#heading").button({
        disabled: true,
        text: true,
    });
    $("#reconvert").button({
        text: true,
    });

    $("#vlink").buttonset();

    $("#format_block").click(function(event) {
        formatter.blockLayout($(".transcript_lines"));
    });
    $("#format_line").click(function(event) {
        formatter.lineLayout($(".transcript_lines"));
    });
    $("#format_column").click(function(event) {
        formatter.columnLayout($(".transcript_lines"));
    });
    $("#page_slider").slider({
        min: 0,
        max: $("#batchsize").val() - 1,
        value: $("#batchoffset").val(),
    });

    // initialise the transcript editor
    transcript = new OCRJS.TranscriptEditor(document.getElementById("transcript"));
    transcript.onTaskChange = function() {
        var ismax = $("#page_slider").slider("option", "value")
                == $("#batchsize").val() - 1;
        var ismin = $("#page_slider").slider("option", "value") == 0;
        $("#next_page").button({disabled: ismax});
        $("#prev_page").button({disabled: ismin});
        $("#heading").button({disabled: true});
    }
    transcript.onLineSelected = function(type) {
        $("#heading").button({disabled: false});
        $("#heading").attr("checked", type == "h1")
            .button("refresh");
    }

    $("#reconvert").change(function(event) {
        $("#reconvert_engine").attr("disabled", !$("#reconvert").attr("checked"));
        $("#transcript_toolbar")
            .find("#spellcheck")
            .button({
                disabled: $(this).attr("checked"),
            });
        if (!$(this).attr("checked")) {
            //transcript.enable();
            $(".reconverted")
                .removeClass("reconverted");
            $(".ocr_line")
                .die("click.reconvert")
                .die("mouseover.reconvert")
                .die("mouseout.reconvert");
        } else {
            //transcript.disable();
            $(".ocr_line.hover").removeClass("hover");
            $(".ocr_line").live("click.reconvert", function(e) {
                positionViewer($(this).data("bbox"));
                reconvertLines($(this));
            }).live("mouseover.reconvert", function(e) {
                $(this).addClass("reconvert");
            }).live("mouseout.reconvert", function(e) {
                $(this).removeClass("reconvert");
            });
        }
    });

    $("#reconvert_engine").attr("disabled", !$("#reconvert").attr("checked"));

    $("#heading").change(function() {
        transcript.setCurrentLineType($(this).attr("checked") ? "h1" : "span");
    });

    // When a page loads, read the data and request the source
    // image is rebinarized so we can view it in the viewer
    // This is likely to be horribly inefficient, at least
    // at first...
    transcript.addListener("onTaskLoad", function() {
        //var ismax = $("#page_slider").slider("option", "value")
        //        == $("#batchsize").val() - 1;
        //var ismin = $("#page_slider").slider("option", "value") == 0;
        //$("#next_page").button({disabled: ismax});
        //$("#prev_page").button({disabled: ismin});
        //$("#heading").button({disabled: true});
        // get should-be-hidden implementation details
        // i.e. the task id that process the page.  We
        // want to rebinarize with the same params
        var task_pk = transcript.taskData().pk;
        $.ajax({
            url: "/ocr/submit_viewer_binarization/" + task_pk + "/",
            dataType: "json",
            beforeSend: function(e) {
                sdviewer.close();
                sdviewer.setWaiting(true);
            },
            success: function(data) {
                if (polltimeout != -1) {
                    clearTimeout(polltimeout);
                    polltimeout = -1;
                }
                pollForResults(data, 300);
            },
            error: OCRJS.ajaxErrorHandler,
        });
    });
    transcript.addListener("onTextChanged", function() {
        $("#save_data").button({
            disabled: false,
        });
    });
    transcript.addListener("onSave", function() {
        $("#save_data").button({
            disabled: true,
        });
    });
    transcript.addListener("onLinesReady", function() {
        // trigger a reformat
        $("input[name=format]:checked").click();
    });

    var positionViewer = function(position) {
        // ensure the given line is centred in the viewport
        var bounds = transcript.taskData().fields.results.box;
        var fw = bounds[2], fh = bounds[3];
        var x = position[0], y = position[1], w = position[2], h = position[3];
        var rect = new Seadragon.Rect(x / fw, (y - h) / fw, w / fw, h / fw);
        sdviewer.setBufferOverlays({
            "current": [rect],
        });
        if ($("#centre").attr("checked")) {
            sdviewer.fitBounds(rect, true);
        }
    }

    transcript.addListener("onHoverPosition", function(position) {
        if (!($("input[name=vlink]:checked").val() == "hover"
                    && sdviewer.isReady()))
            return;
        positionViewer(position);
    });

    transcript.addListener("onClickPosition", function(position) {
        if (!($("input[name=vlink]:checked").val() == "click"
                    && sdviewer.isReady()))
            return;
        positionViewer(position);
    });

    $("#spellcheck").change(function(event) {
        if ($(this).attr("checked")) {
            transcript.startSpellcheck();
        } else {
            transcript.endSpellcheck();
        }
    });

    $("#save_data").click(function(event) {
        transcript.save();
    });

    $("#save_training_data").click(function(event) {
        var pk = transcript.taskData().pk;
        var binurl = $(sdviewer).data("binpath");
        $.ajax({
            url: "/reference_pages/create_from_task/" + pk + "/",
            data: {binary_image: binurl},
            dataType: "json",
            type: "POST",
            success: function(data) {
                if (data.error)
                    return alert("Error: " + data.error);
                alert("Saved!");
            },
            complete: function() {
            },
            error: OCRJS.ajaxErrorHandler,
        });
    });

    $("#page_slider").slider({
        change: function(e, ui) {
            var val = $("#page_slider").slider("value");
            var diff = val - parseInt($("#batchoffset").val());
            var batchoffset = parseInt($("#batchoffset").val());
            var hashoffset = parseInt(window.location.hash.replace(/^#!\//, ""));
            if (isNaN(hashoffset))
                hashoffset = 0;
            var diff = val - batchoffset;
            var orig = batchoffset + hashoffset;

            // return if nothing's changed
            if (hashoffset == diff)
                return;

            // check for unsaved changes
            if (transcript.hasUnsavedChanges()) {
                if (!unsavedPrompt()) {
                    $("#page_slider").slider({value: orig});
                    return;
                } else {
                    transcript.save();
                }
            }
            window.location.hash = "#!/" + diff;

            // set the buttons accordingly
            updateNavButtons();
        },
    });

    $("#prev_page").click(function(event) {
        $("#page_slider").slider("option", "value",
            $("#page_slider").slider("option", "value") - 1);
    });

    $("#next_page").click(function(event) {
        $("#page_slider").slider("option", "value",
            $("#page_slider").slider("option", "value") + 1);
    });

    // line formatter object
    formatter = new OCRJS.LineFormatter();

    // viewer object
    sdviewer = new OCRJS.ImageViewer($("#viewer").get(0), {
        numBuffers: 1,
        height: 300,
    });

    updateTask();
    updateNavButtons();
    window.addEventListener("hashchange", updateTask);

    // maximise the height of the transcript page
    maximiseWidgets(transcript, sdviewer);
    $(window).resize(function(event) {
        maximiseWidgets(transcript, sdviewer);
    });
});

<|endoftext|>


<commit_before>var userid;
//arrays gebruikt voor autocompletes en selecteren waarde hiervan.
var studentLists = [];
var studentListsid = [];
var teachers = []
var teachersid = []

function pageViewModel(gvm) {
    gvm.projecttitle = ko.observable("");
    gvm.userId = -1;
    gvm.coupledCount = 0;

    // Page specific i18n bindings
    gvm.title = ko.computed(function (){i18n.setLocale(gvm.lang()); return gvm.app() + ' - ' + i18n.__("ProjectTitle") + ": " + gvm.projecttitle();}, gvm);
    gvm.pageHeader = ko.observable("Project");
    gvm.projectname = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ProjectName");}, gvm);

    gvm.addBtn = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("AddBtn")}, gvm);

    gvm.addmoduleBtn = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("Addmodule");}, gvm);
    gvm.savePage = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("SaveBtn");}, gvm);

    gvm.getProjectInfo = function() {
        $.getJSON('/api/project/' + $("#projectHeader").data('value'), function(data) {
            gvm.pageHeader(data[0].code + ' - ' + data[0].name);
        });
    };

    gvm.coupledLists = ko.observableArray([]);
    gvm.availableLists = ko.observableArray([]);

    gvm.addCoupledList = function(id, name) {
        // Push data
        var tblOject = {tid: id, tname: name};
        gvm.coupledLists.push(tblOject);

        $("#uncouplebtn-" + id).click(function(){
            showYesNoModal("Bent u zeker dat u dit item wil ontkoppelen?", function(val){
                if(val){
                    $.ajax({
                        url: '/api/project/' + $("#projectHeader").data('value') +'/studentlist/uncouple/' + id,
                        type: "DELETE",
                        success: function() {
                            gvm.coupledCount--;
                            viewModel.coupledLists.remove(tblOject);
                        }
                    });

                }
            });
        });
    }

    gvm.addAvailableLists = function(id, name) {
        var tblOject = {tid: id, tname: name};
        gvm.availableLists.push(tblOject);

        $("#couplebtn-" + id).click(function(){
            if(gvm.coupledCount == 0) {
                $.ajax({
                    url: '/api/project/' + $("#projectHeader").data('value') + '/studentlist/' + id,
                    type: "POST",
                    success: function() {
                        gvm.coupledCount++;
                        viewModel.coupledLists.push(tblOject);
                    }
                });
            } else {
                alert("list already coupled");
            }
        });
    }

    gvm.getCoupledLists = function() {
        $.getJSON('/api/project/' + $("#projectHeader").data('value') + '/coupledlists', function(data) {
            $.each(data, function(i, item) {
                gvm.coupledCount++;
                gvm.addCoupledList(item.id, item.name);
            });
        });
}

    gvm.getAvailableLists = function() {
        $.getJSON('/api/studentlists/' + gvm.userId, function(data) {
            $.each(data, function(i, item) {
                gvm.addAvailableLists(item.id, item.name);
            });
        });
    }

    // The table data observable array
    gvm.tabledata = ko.observableArray([]);

    // Add data to the table
    gvm.addTableData = function(id, studlist, name) {
        console.log("addTableData")
        // Push data
        var tblOject = {tid: id, tstudlist: studlist, tteacher: name};
        gvm.tabledata.push(tblOject);

        // Attach delete handler to delete button
        $('#removebtn-' + id).bind('click', function(event, data){
            // Delete the table item
            deleteTableItem(id, tblOject);
            event.stopPropagation();
        });
            }

    gvm.clearTable = function() {
        gvm.tabledata.removeAll();
    }

 }

 function getAllTeachers() {
     teachers = [];
     teachersid = [];
     $.getJSON('/api/getteacherrapport', function(data) {
        $.each(data, function(i, item) {
            teachers.push(item.firstname + " " + item.lastname);
            teachersid.push(item.id);
        });
    });
    return teachers;
 }

function getAllStudentLists() {
    studentLists = [];
    studentListsid = [];
    $.getJSON('/api/studentlistsrapporten/' + userid, function (data) {
        $.each(data, function (i, item) {
            studentLists.push(item.name);
            studentListsid.push(item.id);
        });
    });
    return studentLists;
}

function getTeacherid() {

    // functie voor het opsplitsen van naam indien gewenst.
    // console.log(getTeacherID($('#teachersComplete').val().substr(0,$('#teachersComplete').val().indexOf(' ')), $('#teachersComplete').val().substr($('#teachersComplete').val().indexOf(' ')+1)));

    var i = 0;
    var teacher = 0;
    teachers.forEach(function(entry) {
        if (new String(entry).valueOf() == new String($('#teachersComplete').val()).valueOf()) {
            teacher = teachersid[i];
        }
        i+= 1;
    });
    return teacher
}

function getGroupid() {
    var i = 0;
    var studlijst;
    studentLists.forEach(function(entry) {
        if (new String(entry).valueOf() == new String($('#studentListComplete').val()).valueOf()) {
            studlijst = (studentListsid[i]);
        }
        i+= 1;
    });
    return studlijst;
}

 function addGroup(courseid, teacherid, studlijstid) {
     //TODO if teacher or studlijst = 0 dan bestaat deze niet!
     //TODO momenteel nog mogelijk om meer als 1 maal zelfde velden in te voeren.

         $.ajax({
            url: "/api/coursecouple/" + courseid + "/" + studlijstid + "/" + teacherid,
             type: "POST",
             data: {'course': courseid, 'teacher': teacherid, 'studentlist': teacherid},
             success: function(data) {
                 //console.log(data);
                 //callback(true);
             },
             error: function(data) {
                 console.log('Failed to add new student');
                 //callback(false);
             }
         });

 }

/*
 * Load page of the table
 */
function loadTablePage(pagenr)
{
    console.log("loadTablePage");
    //$.getJSON('/api/coursesrapport/page/' + pagenr, function(data){
    $.getJSON('/api/getStudentGroupTeacherByCourseID/' + pagenr, function(data){

        /* Clear current table page */
        viewModel.clearTable();

        // Load table data
        $.each(data.data, function(i, item) {
            console.log(data.data);
           //viewModel.addTableData(item.id, item.code, item.name);
        });

        /* Let previous en next buttons work */
        if(data.prev == "none"){
            $('#pager-prev-btn').addClass('disabled');
        } else {
            $('#pager-prev-btn').removeClass('disabled');
            $('#pager-prev-btn a').click(function(){
                loadTablePage(data.prev);
            });
        }

        if(data.next == "none"){
            $('#pager-next-btn').addClass('disabled');
        } else {
            $('#pager-next-btn').removeClass('disabled');
            $('#pager-next-btn a').click(function(){
                loadTablePage(data.next);
            });
        }

        // Number of pager buttons
        var numItems = $('.pager-nr-btn').length;

        /* Calculate for the pager buttons */
        var lowPage = Math.floor(pagenr/numItems) + 1;

        $('.pager-nr-btn').each(function() {
            /* calculate current page number */
            var thispagenr = lowPage++;

            /* Add the page number */
            $(this).html('' + thispagenr + '');

            /* Add active class to current page */
            if(thispagenr == pagenr) {
                $(this).addClass('active');
            } else {
                $(this).removeClass('active');
            }

            /* Disable inactive classes and bind handlers to active classes */
            if(thispagenr > data.pagecount) {
                $(this).addClass('disabled');
            } else {
                /* Add click listener for button */
                $(this).click(function() {
                    loadTablePage(thispagenr);
                });
            }
        });
    });
}

function initPage() {
    viewModel.getProjectInfo();
    viewModel.getCoupledLists();

    //Add Course members

    $('#addGroupForm').hide();

    $('#addCoursemembers').click(function() {
        $("#addGroupForm").show();
        $('#teachersComplete').autocomplete({ source: getAllTeachers() });
        $('#studentListComplete').autocomplete({ source: getAllStudentLists() });
    });

    $('#addGroupBtn').click(function() {
         addGroup($('#projectHeader').attr("data-value"), getTeacherid(), getGroupid());

        $('addGroupForm').hide();
    });

    $.getJSON('/api/currentuser', function(data) {
        viewModel.userId = data.id;
        userid = data.id;
        viewModel.getAvailableLists(data.id);
    });

    loadTablePage(1);

}
<commit_msg>table load functies coursesstudentsrapporten<commit_after>var userid;
//arrays gebruikt voor autocompletes en selecteren waarde hiervan.
var studentLists = [];
var studentListsid = [];
var teachers = []
var teachersid = []

function pageViewModel(gvm) {
    gvm.projecttitle = ko.observable("");
    gvm.userId = -1;
    gvm.coupledCount = 0;

    // Page specific i18n bindings
    gvm.title = ko.computed(function (){i18n.setLocale(gvm.lang()); return gvm.app() + ' - ' + i18n.__("ProjectTitle") + ": " + gvm.projecttitle();}, gvm);
    gvm.pageHeader = ko.observable("Project");
    gvm.projectname = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ProjectName");}, gvm);

    gvm.addBtn = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("AddBtn")}, gvm);

    gvm.addmoduleBtn = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("Addmodule");}, gvm);
    gvm.savePage = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("SaveBtn");}, gvm);

    gvm.getProjectInfo = function() {
        $.getJSON('/api/project/' + $("#projectHeader").data('value'), function(data) {
            gvm.pageHeader(data[0].code + ' - ' + data[0].name);
        });
    };

    gvm.coupledLists = ko.observableArray([]);
    gvm.availableLists = ko.observableArray([]);

    gvm.addCoupledList = function(id, name) {
        // Push data
        var tblOject = {tid: id, tname: name};
        gvm.coupledLists.push(tblOject);

        $("#uncouplebtn-" + id).click(function(){
            showYesNoModal("Bent u zeker dat u dit item wil ontkoppelen?", function(val){
                if(val){
                    $.ajax({
                        url: '/api/project/' + $("#projectHeader").data('value') +'/studentlist/uncouple/' + id,
                        type: "DELETE",
                        success: function() {
                            gvm.coupledCount--;
                            viewModel.coupledLists.remove(tblOject);
                        }
                    });

                }
            });
        });
    }

    gvm.addAvailableLists = function(id, name) {
        var tblOject = {tid: id, tname: name};
        gvm.availableLists.push(tblOject);

        $("#couplebtn-" + id).click(function(){
            if(gvm.coupledCount == 0) {
                $.ajax({
                    url: '/api/project/' + $("#projectHeader").data('value') + '/studentlist/' + id,
                    type: "POST",
                    success: function() {
                        gvm.coupledCount++;
                        viewModel.coupledLists.push(tblOject);
                    }
                });
            } else {
                alert("list already coupled");
            }
        });
    }

    gvm.getCoupledLists = function() {
        $.getJSON('/api/project/' + $("#projectHeader").data('value') + '/coupledlists', function(data) {
            $.each(data, function(i, item) {
                gvm.coupledCount++;
                gvm.addCoupledList(item.id, item.name);
            });
        });
}

    gvm.getAvailableLists = function() {
        $.getJSON('/api/studentlists/' + gvm.userId, function(data) {
            $.each(data, function(i, item) {
                gvm.addAvailableLists(item.id, item.name);
            });
        });
    }

    // The table data observable array
    gvm.tabledata = ko.observableArray([]);

    // Add data to the table
    gvm.addTableData = function(id, studlist, name) {
        console.log("addTableData")
        // Push data
        var tblOject = {tid: id, tstudlist: studlist, tteacher: name};
        gvm.tabledata.push(tblOject);

        // Attach delete handler to delete button
        $('#removebtn-' + id).bind('click', function(event, data){
            // Delete the table item
            deleteTableItem(id, tblOject);
            event.stopPropagation();
        });
            }

    gvm.clearTable = function() {
        gvm.tabledata.removeAll();
    }

 }

 function getAllTeachers() {
     teachers = [];
     teachersid = [];
     $.getJSON('/api/getteacherrapport', function(data) {
        $.each(data, function(i, item) {
            teachers.push(item.firstname + " " + item.lastname);
            teachersid.push(item.id);
        });
    });
    return teachers;
 }

function getAllStudentLists() {
    studentLists = [];
    studentListsid = [];
    $.getJSON('/api/studentlistsrapporten/' + userid, function (data) {
        $.each(data, function (i, item) {
            studentLists.push(item.name);
            studentListsid.push(item.id);
        });
    });
    return studentLists;
}

function getTeacherid() {

    // functie voor het opsplitsen van naam indien gewenst.
    // console.log(getTeacherID($('#teachersComplete').val().substr(0,$('#teachersComplete').val().indexOf(' ')), $('#teachersComplete').val().substr($('#teachersComplete').val().indexOf(' ')+1)));

    var i = 0;
    var teacher = 0;
    teachers.forEach(function(entry) {
        if (new String(entry).valueOf() == new String($('#teachersComplete').val()).valueOf()) {
            teacher = teachersid[i];
        }
        i+= 1;
    });
    return teacher
}

function getGroupid() {
    var i = 0;
    var studlijst;
    studentLists.forEach(function(entry) {
        if (new String(entry).valueOf() == new String($('#studentListComplete').val()).valueOf()) {
            studlijst = (studentListsid[i]);
        }
        i+= 1;
    });
    return studlijst;
}

 function addGroup(courseid, teacherid, studlijstid) {
     //TODO if teacher or studlijst = 0 dan bestaat deze niet!
     //TODO momenteel nog mogelijk om meer als 1 maal zelfde velden in te voeren.

         $.ajax({
            url: "/api/coursecouple/" + courseid + "/" + studlijstid + "/" + teacherid,
             type: "POST",
             data: {'course': courseid, 'teacher': teacherid, 'studentlist': teacherid},
             success: function(data) {
                 //console.log(data);
                 //callback(true);
             },
             error: function(data) {
                 console.log('Failed to add new student');
                 //callback(false);
             }
         });

 }

/*
 * Load page of the table
 */
function loadTablePage(pagenr)
{
    console.log("loadTablePage");
    $.getJSON('/api/coursesrapport/page/' + pagenr, function(data){

        /* Clear current table page */
        viewModel.clearTable();

        // Load table data
        $.each(data.data, function(i, item) {
            console.log(data.data);
           //viewModel.addTableData(item.id, item.code, item.name);
        });

        /* Let previous en next buttons work */
        if(data.prev == "none"){
            $('#pager-prev-btn').addClass('disabled');
        } else {
            $('#pager-prev-btn').removeClass('disabled');
            $('#pager-prev-btn a').click(function(){
                loadTablePage(data.prev);
            });
        }

        if(data.next == "none"){
            $('#pager-next-btn').addClass('disabled');
        } else {
            $('#pager-next-btn').removeClass('disabled');
            $('#pager-next-btn a').click(function(){
                loadTablePage(data.next);
            });
        }

        // Number of pager buttons
        var numItems = $('.pager-nr-btn').length;

        /* Calculate for the pager buttons */
        var lowPage = Math.floor(pagenr/numItems) + 1;

        $('.pager-nr-btn').each(function() {
            /* calculate current page number */
            var thispagenr = lowPage++;

            /* Add the page number */
            $(this).html('' + thispagenr + '');

            /* Add active class to current page */
            if(thispagenr == pagenr) {
                $(this).addClass('active');
            } else {
                $(this).removeClass('active');
            }

            /* Disable inactive classes and bind handlers to active classes */
            if(thispagenr > data.pagecount) {
                $(this).addClass('disabled');
            } else {
                /* Add click listener for button */
                $(this).click(function() {
                    loadTablePage(thispagenr);
                });
            }
        });
    });
}

function initPage() {
    viewModel.getProjectInfo();
    viewModel.getCoupledLists();

    //Add Course members

    $('#addGroupForm').hide();

    $('#addCoursemembers').click(function() {
        $("#addGroupForm").show();
        $('#teachersComplete').autocomplete({ source: getAllTeachers() });
        $('#studentListComplete').autocomplete({ source: getAllStudentLists() });
    });

    $('#addGroupBtn').click(function() {
         addGroup($('#projectHeader').attr("data-value"), getTeacherid(), getGroupid());

        $('addGroupForm').hide();
    });

    $.getJSON('/api/currentuser', function(data) {
        viewModel.userId = data.id;
        userid = data.id;
        viewModel.getAvailableLists(data.id);
    });

    loadTablePage(1);

}
<|endoftext|>


<commit_before>/*    Copyright 2020 Firewalla INC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';

const Sensor = require('./Sensor.js').Sensor;
const sem = require('../sensor/SensorEventManager.js').getInstance();
const rclient = require('../util/redis_manager.js').getRedisClient();
const fs = require('fs');
const util = require('util');
const exec = require('child-process-promise').exec
const writeFileAsync = util.promisify(fs.writeFile);
const readFileAsync = util.promisify(fs.readFile);
const existsAsync = util.promisify(fs.exists);
const extensionManager = require('./ExtensionManager.js')
const systemDNSKey = 'sys:dns:custom';

class SystemDNSSensor extends Sensor {
  constructor() {
    super();
  }

  async run() {
    sem.on('SystemDNSUpdate', async (event) => {
      this.updateSystemDNS();
    });

    this.updateSystemDNS();
  }

  async updateSystemDNS() {
    let oldContent = "";
    let content = await this.getSystemDNS(); // get redis
    const profilePath = "/etc/resolvconf/resolv.conf.d/head";
    const fileExists = await existsAsync(profilePath);
    if (fileExists) {
      oldContent = await readFileAsync(profilePath, {encoding: 'utf8'});
    }
    if (oldContent.trim() == content)
      return;

    if (content != "") {
      content += "\n";
    }
    const tmpfile = "/tmp/customDNS";
    await writeFileAsync(tmpfile, content, 'utf8');
    let cmd = `sudo bash -c 'cat ${tmpfile} > ${profilePath}'`;
    await exec(cmd);
    cmd = `sudo systemctl restart resolvconf`;
    await exec(cmd);
  }

  async apiRun() {
    extensionManager.onSet("systemDNS", async (msg, data) => {
      await this.setSystemDNS(data.content);
      sem.sendEventToFireMain({
        type: 'SystemDNSUpdate'
      });
    });

    extensionManager.onGet("systemDNS", async (msg, data) => {
      return this.getSystemDNS();
    });
  }

  async setSystemDNS(content) {
    if (!content) {
      content = "";
    }
    await rclient.setAsync(systemDNSKey, content);
  }

  async getSystemDNS() {
    const value = await rclient.getAsync(systemDNSKey);
    return value || "";
  }
}

module.exports = SystemDNSSensor;
<commit_msg>update<commit_after>/*    Copyright 2020 Firewalla INC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';

const log = require('../net2/logger.js')(__filename);
const Sensor = require('./Sensor.js').Sensor;
const sem = require('../sensor/SensorEventManager.js').getInstance();
const rclient = require('../util/redis_manager.js').getRedisClient();
const fs = require('fs');
const util = require('util');
const exec = require('child-process-promise').exec
const writeFileAsync = util.promisify(fs.writeFile);
const readFileAsync = util.promisify(fs.readFile);
const existsAsync = util.promisify(fs.exists);
const extensionManager = require('./ExtensionManager.js')
const systemDNSKey = 'sys:dns:custom';
const platformLoader = require('../platform/PlatformLoader.js');
const platform = platformLoader.getPlatform();

class SystemDNSSensor extends Sensor {
  constructor() {
    super();
  }

  async run() {
    sem.on('SystemDNSUpdate', async (event) => {
      this.updateSystemDNS();
    });

    this.updateSystemDNS();
  }

  async updateSystemDNS() {
    if (platform.getName() !== 'blue' && platform.getName() !== 'red') {
      return;
    }

    let oldContent = "";
    let content = await this.getSystemDNS(); // get redis
    const profilePath = "/etc/resolvconf/resolv.conf.d/head";
    const fileExists = await existsAsync(profilePath);
    if (fileExists) {
      oldContent = await readFileAsync(profilePath, {encoding: 'utf8'});
    }
    if (oldContent.trim() == content)
      return;

    if (content != "") {
      content += "\n";
    }
    const tmpfile = "/tmp/customDNS";
    await writeFileAsync(tmpfile, content, 'utf8');
    let cmd = `sudo bash -c 'cat ${tmpfile} > ${profilePath}'`;
    await exec(cmd);
    cmd = `sudo systemctl restart resolvconf`;
    await exec(cmd);
  }

  async apiRun() {
    extensionManager.onSet("systemDNS", async (msg, data) => {
      await this.setSystemDNS(data.content);
      sem.sendEventToFireMain({
        type: 'SystemDNSUpdate'
      });
    });

    extensionManager.onGet("systemDNS", async (msg, data) => {
      return this.getSystemDNS();
    });
  }

  async setSystemDNS(content) {
    if (!content) {
      content = "";
    }
    await rclient.setAsync(systemDNSKey, content);
  }

  async getSystemDNS() {
    const value = await rclient.getAsync(systemDNSKey);
    return value || "";
  }
}

module.exports = SystemDNSSensor;
<|endoftext|>


<commit_before>import { createStore, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
import reducer from '../reducers'

const middleware = [ thunk ]
if (process.env.NODE_ENV !== 'production') {
  const createLogger = require('redux-logger');
  const logger = createLogger();
  middleware.push(logger);
}

const composeEnhancers =
  process.env.NODE_ENV !=='production' &&
  typeof window === 'object' &&
  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
      name: "in1box"
    }) : compose;

// const composeEnhancers = compose;

const configureStore = (preloadedState = {}) => createStore(
  reducer, preloadedState, composeEnhancers(
    applyMiddleware(...middleware)
  )
)

export default configureStore;
<commit_msg>forgot to comment out code for production<commit_after>import { createStore, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
import reducer from '../reducers'

const middleware = [ thunk ]
// if (process.env.NODE_ENV !== 'production') {
//   const createLogger = require('redux-logger');
//   const logger = createLogger();
//   middleware.push(logger);
// }
//
// const composeEnhancers =
//   process.env.NODE_ENV !=='production' &&
//   typeof window === 'object' &&
//   window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
//     window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
//       name: "in1box"
//     }) : compose;

const composeEnhancers = compose;

const configureStore = (preloadedState = {}) => createStore(
  reducer, preloadedState, composeEnhancers(
    applyMiddleware(...middleware)
  )
)

export default configureStore;
<|endoftext|>


<commit_before>var format = require('string-template');

function findNextIndexFit(arr, spaceAvailable) {
  var spaceUsed = arr[0].length;
  var index = 1;
  while (index < arr.length) {
    //+1 because of space
    newSpace = arr[index].length + 1;
    if (spaceUsed + newSpace > spaceAvailable) break;
    spaceUsed += newSpace;
    index++;
  }
  return index;
}

function generateLongTweets(maxSpace, fields) {
  var tweets = [];
  var times = fields.msg.split(' ');
  var firstTweetTemplate = '@{to} movie:{movie} theater:{theater} -> {msg} ' +
                           '({count}) {uniqueField}';

  var spaceAvailable = getSpaceWithoutMsg(maxSpace, fields,
                                          firstTweetTemplate);
  var last = findNextIndexFit(times, spaceAvailable);
  fields.msg = times.slice(0, last).join(' ');
  tweets.push(format(firstTweetTemplate, fields));

  var initial;
  var nextTweetsTemplate = '@{to} {msg} ({count}) {uniqueField}';
  spaceAvailable = getSpaceWithoutMsg(maxSpace, fields, nextTweetsTemplate);

  while(last < times.length) {
    fields.count++;
    initial = last;
    last += findNextIndexFit(times.slice(initial), spaceAvailable);
    fields.msg = times.slice(initial, last).join(' ');
    tweets.push(format(nextTweetsTemplate, fields));
  }

  return tweets;
}

function shouldUseOneTweet(max, tweetSize) {
  return tweetSize < max;
}

function getSpaceWithoutMsg(max, fields, template) {
  fields.msg = '';
  return max - format(template, fields).length;
}

function createPosts(to, queryInfo) {
  var fields = {
    to: to,
    msg: queryInfo.schedule,
    count: 1,
    movie: queryInfo.movie,
    theater: queryInfo.theater,
    uniqueField: '*' + (new Date()).getMilliseconds()
  }

  var maxSpace = 140;
  var tweets = [];

  var oneTweetTemplate = '@{to} movie:{movie} theater:{theater} -> {msg} ' +
                         '{uniqueField}';
  var oneTweet = format(oneTweetTemplate, fields);

  if (shouldUseOneTweet(maxSpace, oneTweet.length)) {
    tweets.push(oneTweet);
  } else {
    tweets = generateLongTweets(maxSpace, fields);
  }
  return tweets;
}

function createHelpMsg(to) {
  var fields = {
    to: to,
    msg: 'Hey, tweet movie and cinema to get the schedule',
    uniqueField: '*' + (new Date()).getMilliseconds()
  }

  var template = '@{to} {msg} {uniqueField}';
  return format(template, fields);
}

module.exports = {
  createPosts: createPosts,
  createHelpMsg: createHelpMsg
}<commit_msg>refactor create long tweets<commit_after>var format = require('string-template');

var getSpace = function(max, fields, template) {
  fields.content = '';
  return max - format(template, fields).length;
};

var splitMsg = function(fields, spaceAvailable) {
  var msgTemplate = 'movie: {movie} theater: {theater} -> {msg}';
  var message = format(msgTemplate, fields);
  var msgs = [];

  var len = message.length;
  var i = Math.min(spaceAvailable, len);
  var last = 0;

  while (last < len) {
    var car = message.charAt(i);
    if (i >= len || car === ' ') {
      msgs.push(message.substring(last, i))
      last = i;
      i = Math.min(last + spaceAvailable, len);
    } else {
      i--;
    }
  }

  return msgs;
};

function generateLongTweets(maxSpace, fields) {
  var tweets = [];
  var mainTemplate = '@{to} {content} {uniqueField}';
  var spaceAvailable = getSpace(maxSpace, fields, mainTemplate);
  var arrMsg = splitMsg(fields, spaceAvailable);

  arrMsg.forEach(function(msg) {
    fields.content = msg;
    tweets.push(format(mainTemplate, fields));
  })

  return tweets;
}

function createPosts(to, queryInfo) {
  var fields = {
    to: to,
    msg: queryInfo.schedule,
    count: 1,
    movie: queryInfo.movie,
    theater: queryInfo.theater,
    uniqueField: '*' + (new Date()).getMilliseconds()
  }

  var maxSpace = 140;
  var tweets = [];

  var oneTweetTemplate = '@{to} movie:{movie} theater:{theater} -> {msg} ' +
                         '{uniqueField}';
  var oneTweet = format(oneTweetTemplate, fields);

  if (maxSpace >= oneTweet.length) {
    tweets.push(oneTweet);
  } else {
    tweets = generateLongTweets(maxSpace, fields);
  }
  return tweets;
}

function createHelpMsg(to) {
  var fields = {
    to: to,
    msg: 'Hey, tweet movie and cinema to get the schedule',
    uniqueField: '*' + (new Date()).getMilliseconds()
  }

  var template = '@{to} {msg} {uniqueField}';
  return format(template, fields);
}

module.exports = {
  createPosts: createPosts,
  createHelpMsg: createHelpMsg
}
<|endoftext|>


<commit_before>//viewmodel for the assess page
function pageViewModel(gvm) {
    gvm.app  = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ProjectRapportName")}, gvm);
    gvm.title = ko.computed(function(){i18n.setLocale(gvm.lang()); return gvm.app() + ' - ' + i18n. __("AssessTitle2");}, gvm);
    gvm.userId = -1;

    // Page specific i18n bindings
    gvm.pageHeader = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("AssessTitle2");}, gvm);
    gvm.projectname = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ProjectRapportName");}, gvm);
    gvm.homeManual = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("HomeRapportManual");}, gvm);
    gvm.foundProjects = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("FoundProjects");}, gvm);
    gvm.selectCourse = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("SelectCourse");}, gvm);

    // Pagination i18n bindings
    gvm.addBtn = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("AddBtn")}, gvm);

    // Table i18n bindings
    gvm.codeTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("CodeTableTitle");}, gvm);
    gvm.nameTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("NameTableTitle");}, gvm);
    gvm.descTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("DescTableTitle");}, gvm);
    gvm.actionTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ActionTableTitle");}, gvm);

    gvm.availableLocations = ko.observableArray([]);
    gvm.availableTrainings = ko.observableArray([]);
    gvm.availableCourses = ko.observableArray([]);
    gvm.availableGoals = ko.observableArray([]);

    gvm.currentGoalId = null;
    gvm.currentCourseId = null;
    gvm.currentLocationId = null;
    gvm.currentTrainingid = null;

    gvm.updateDropdowns = function() {
        $.getJSON('api/lastdropdownchoice/' + gvm.userId, function(data) {
            if(!$.isEmptyObject(data)) {
                $.each(data, function(i, item) {
                    $(".btn-location span:first").text(item.location);
                    $(".btn-training span:first").text(item.training);
                    $(".btn-course span:first").text(item.course);
                    $(".btn-goal span:first").text(item.goal);
                    gvm.currentLocationId = item.locationid;
                    gvm.currentTrainingid = item.trainingid;
                    gvm.currentCourseId = item.courseid;
                    gvm.currentGoalId = item.goalid;
                    gvm.updateLocations();
                    gvm.updateTrainings(item.locationid);
                    gvm.updateCourses(item.trainingid);
                    gvm.updateGoals(item.courseid);
                    loadTablePage(item.courseid, 1);
                });
            } else {
                gvm.updateLocations();
            }
        });
    }

    gvm.saveLastSelectedDropdowns = function() {
        data = {};
        data["location"] = $(".btn-location span:first").text();
        data["locationid"] = gvm.currentLocationId;
        data["training"] = $(".btn-training span:first").text();
        data["trainingid"] = gvm.currentTrainingid;
        data["course"] = $(".btn-course span:first").text();
        data["courseid"] = gvm.currentCourseId;
        data["goal"] = $(".btn-goal span:first").text();
        data["goalid"] = gvm.currentGoalId;
        data["user"] = gvm.userId;
        console.log(data);
        $.ajax({
            type: "POST",
            url: "/api/savedropdowns",
            data: data,
            success: function() {
                console.log("success");
            }
        })
    }

    gvm.updateLocations = function() {
        $.getJSON('/api/courserapportdrop', function(data) {
            gvm.availableLocations.removeAll();
            $.each(data, function(i, item) {
              //  Put item in list
                gvm.availableLocations.push(item);

                // Add listener to listitem
                $("#locbtn-" + item.id).click(function(){
                    gvm.currentLocationId = item.id;
                    gvm.currentTrainingid = null;
                    gvm.currentCourseId = null;
                    gvm.updateTrainings(item.id);
                    $(".btn-location span:first").text($(this).text());
                    $(".btn-training span:first").text("module");
                    $(".btn-course span:first").text("sub-module");
                });
            });
        });
    };

    /*
     * Update the course data
     */
    gvm.updateTrainings = function(id) {
        $.getJSON('/api/coursesrapport/' + id, function(data) {
            gvm.availableTrainings.removeAll();
            $.each(data, function(i, item) {
                gvm.availableTrainings.push(item);

                /* Add listener to listitem */
                $("#trainingbtn-" + item.id).click(function(){
                    gvm.currentTrainingid = item.id;
                    gvm.currentCourseId = null;
                    gvm.updateCourses(item.id);
                    $(".btn-training span:first").text($(this).text());
                    $(".btn-course span:first").text("course");
                });
            });
        });
    }

    /*
     * Update sub-module
     */
    gvm.updateCourses = function(id) {
        $.getJSON('/api/submodulerapport/' + id, function(data) {
            gvm.availableCourses.removeAll();
            $.each(data, function(i, item) {
                gvm.availableCourses.push(item);

                /* Add listener to listitem */
                $("#coursebtn-" + item.id).click(function(){
                    $(".btn-course span:first").text($(this).text());
                    gvm.currentCourseId = item.id;
                    gvm.saveLastSelectedDropdowns();
                    loadTablePage(item.id, 1);
                });
            });
        });
    }

    /*
     * Update goals
     */
    gvm.updateGoals = function(id) {
        $.getJSON('/api/goalrapport/' + id, function(data) {
            gvm.availableGoals.removeAll();
            $.each(data, function(i, item) {
                gvm.availableGoals.push(item);

                /* Add listener to listitem */
                $("#goalbtn-" + item.id).click(function(){
                    $(".btn-goal span:first").text($(this).text());
                    gvm.currentGoalId = item.id;
                    gvm.saveLastSelectedDropdowns();
                    loadTablePage(item.id, 1);
                });
            });
        });
    }

    // The table data observable array
    gvm.tabledata = ko.observableArray([]);

    // Add data to the table
    gvm.addTableData = function(id, code, name, desc) {
        // Push data
        var tblOject = {tid: id, tcode: code, tname: name, tdesc: desc};
        gvm.tabledata.push(tblOject);
    }

    gvm.clearTable = function() {
        gvm.tabledata.removeAll();
    }
}

function loadTablePage(courseid, pagenr)
{
    $.getJSON('/api/projects/' + courseid + '/page/' + pagenr, function(data){

        /* Clear current table page */
        viewModel.clearTable()

        // Load table data
        $.each(data.data, function(i, item) {
            viewModel.addTableData(item.id, item.code, item.name, item.description);
        });

        /* Let previous en next buttons work */
        if(data.prev == "none"){
            $('#pager-prev-btn').addClass('disabled');
        } else {
            $('#pager-prev-btn').removeClass('disabled');
            $('#pager-prev-btn a').click(function(){
                loadTablePage(data.prev);
            });
        }

        if(data.next == "none"){
            $('#pager-next-btn').addClass('disabled');
        } else {
            $('#pager-next-btn').removeClass('disabled');
            $('#pager-next-btn a').click(function(){
                loadTablePage(data.next);
            });
        }

        // Number of pager buttons
        var numItems = $('.pager-nr-btn').length;

        /* Calculate for the pager buttons */
        var lowPage = Math.floor(pagenr/numItems) + 1;

        $('.pager-nr-btn').each(function() {
            /* calculate current page number */
            var thispagenr = lowPage++;

            /* Add the page number */
            $(this).html('' + thispagenr + '');

            /* Add active class to current page */
            if(thispagenr == pagenr) {
                $(this).addClass('active');
            } else {
                $(this).removeClass('active');
            }

            /* Disable inactive classes and bind handlers to active classes */
            if(thispagenr > data.pagecount) {
                $(this).addClass('disabled');
            } else {
                /* Add click listener for button */
                $(this).click(function() {
                    loadTablePage(courseid, thispagenr);
                });
            }
        });
    });
}

function initPage() {
    $.getJSON('/api/currentuser', function(data) {
        viewModel.userId = data.id;
        viewModel.updateDropdowns();
    });
}<commit_msg>4e dropdownlist<commit_after>//viewmodel for the assess page
function pageViewModel(gvm) {
    gvm.app  = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ProjectRapportName")}, gvm);
    gvm.title = ko.computed(function(){i18n.setLocale(gvm.lang()); return gvm.app() + ' - ' + i18n. __("AssessTitle2");}, gvm);
    gvm.userId = -1;

    // Page specific i18n bindings
    gvm.pageHeader = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("AssessTitle2");}, gvm);
    gvm.projectname = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ProjectRapportName");}, gvm);
    gvm.homeManual = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("HomeRapportManual");}, gvm);
    gvm.foundProjects = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("FoundProjects");}, gvm);
    gvm.selectCourse = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("SelectCourse");}, gvm);

    // Pagination i18n bindings
    gvm.addBtn = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("AddBtn")}, gvm);

    // Table i18n bindings
    gvm.codeTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("CodeTableTitle");}, gvm);
    gvm.nameTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("NameTableTitle");}, gvm);
    gvm.descTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("DescTableTitle");}, gvm);
    gvm.actionTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ActionTableTitle");}, gvm);

    gvm.availableLocations = ko.observableArray([]);
    gvm.availableTrainings = ko.observableArray([]);
    gvm.availableCourses = ko.observableArray([]);
    gvm.availableGoals = ko.observableArray([]);

    gvm.currentGoalId = null;
    gvm.currentCourseId = null;
    gvm.currentLocationId = null;
    gvm.currentTrainingid = null;

    gvm.updateDropdowns = function() {
        $.getJSON('api/lastdropdownchoice/' + gvm.userId, function(data) {
            if(!$.isEmptyObject(data)) {
                $.each(data, function(i, item) {
                    $(".btn-location span:first").text(item.location);
                    $(".btn-training span:first").text(item.training);
                    $(".btn-course span:first").text(item.course);
                    $(".btn-goal span:first").text(item.goal);
                    gvm.currentLocationId = item.locationid;
                    gvm.currentTrainingid = item.trainingid;
                    gvm.currentCourseId = item.courseid;
                    gvm.currentGoalId = item.goalid;
                    gvm.updateLocations();
                    gvm.updateTrainings(item.locationid);
                    gvm.updateCourses(item.trainingid);
                    gvm.updateGoals(item.courseid);
                    loadTablePage(item.courseid, 1);
                });
            } else {
                gvm.updateLocations();
            }
        });
    }

    gvm.saveLastSelectedDropdowns = function() {
        data = {};
        data["location"] = $(".btn-location span:first").text();
        data["locationid"] = gvm.currentLocationId;
        data["training"] = $(".btn-training span:first").text();
        data["trainingid"] = gvm.currentTrainingid;
        data["course"] = $(".btn-course span:first").text();
        data["courseid"] = gvm.currentCourseId;
        data["goal"] = $(".btn-goal span:first").text();
        data["goalid"] = gvm.currentGoalId;
        data["user"] = gvm.userId;
        console.log(data);
        $.ajax({
            type: "POST",
            url: "/api/savedropdowns",
            data: data,
            success: function() {
                console.log("success");
            }
        })
    }

    gvm.updateLocations = function() {
        $.getJSON('/api/courserapportdrop', function(data) {
            gvm.availableLocations.removeAll();
            $.each(data, function(i, item) {
              //  Put item in list
                gvm.availableLocations.push(item);

                // Add listener to listitem
                $("#locbtn-" + item.id).click(function(){
                    gvm.currentLocationId = item.id;
                    gvm.currentTrainingid = null;
                    gvm.currentCourseId = null;
                    gvm.updateTrainings(item.id);
                    $(".btn-location span:first").text($(this).text());
                    $(".btn-training span:first").text("module");
                    $(".btn-course span:first").text("sub-module");
                });
            });
        });
    };

    /*
     * Update the course data
     */
    gvm.updateTrainings = function(id) {
        $.getJSON('/api/coursesrapport/' + id, function(data) {
            gvm.availableTrainings.removeAll();
            $.each(data, function(i, item) {
                gvm.availableTrainings.push(item);

                /* Add listener to listitem */
                $("#trainingbtn-" + item.id).click(function(){
                    gvm.currentTrainingid = item.id;
                    gvm.currentCourseId = null;
                    gvm.updateCourses(item.id);
                    $(".btn-training span:first").text($(this).text());
                    $(".btn-course span:first").text("course");
                });
            });
        });
    }

    /*
     * Update sub-module
     */
    gvm.updateCourses = function(id) {
        $.getJSON('/api/submodulerapport/' + id, function(data) {
            gvm.availableCourses.removeAll();
            $.each(data, function(i, item) {
                gvm.availableCourses.push(item);

                /* Add listener to listitem */
                $("#coursebtn-" + item.id).click(function(){
                    $(".btn-course span:first").text($(this).text());
                    gvm.currentCourseId = item.id;
                    gvm.saveLastSelectedDropdowns();
                    loadTablePage(item.id, 1);
                });
            });
        });
    }

    /*
     * Update goals
     */
    gvm.updateGoals = function(id) {
        $.getJSON('/api/submodulerapport/' + id, function(data) {
            gvm.availableGoals.removeAll();
            $.each(data, function(i, item) {
                gvm.availableGoals.push(item);

                /* Add listener to listitem */
                $("#goalbtn-" + item.id).click(function(){
                    $(".btn-goal span:first").text($(this).text());
                    gvm.currentGoalId = item.id;
                    gvm.saveLastSelectedDropdowns();
                    loadTablePage(item.id, 1);
                });
            });
        });
    }

    // The table data observable array
    gvm.tabledata = ko.observableArray([]);

    // Add data to the table
    gvm.addTableData = function(id, code, name, desc) {
        // Push data
        var tblOject = {tid: id, tcode: code, tname: name, tdesc: desc};
        gvm.tabledata.push(tblOject);
    }

    gvm.clearTable = function() {
        gvm.tabledata.removeAll();
    }
}

function loadTablePage(courseid, pagenr)
{
    $.getJSON('/api/projects/' + courseid + '/page/' + pagenr, function(data){

        /* Clear current table page */
        viewModel.clearTable()

        // Load table data
        $.each(data.data, function(i, item) {
            viewModel.addTableData(item.id, item.code, item.name, item.description);
        });

        /* Let previous en next buttons work */
        if(data.prev == "none"){
            $('#pager-prev-btn').addClass('disabled');
        } else {
            $('#pager-prev-btn').removeClass('disabled');
            $('#pager-prev-btn a').click(function(){
                loadTablePage(data.prev);
            });
        }

        if(data.next == "none"){
            $('#pager-next-btn').addClass('disabled');
        } else {
            $('#pager-next-btn').removeClass('disabled');
            $('#pager-next-btn a').click(function(){
                loadTablePage(data.next);
            });
        }

        // Number of pager buttons
        var numItems = $('.pager-nr-btn').length;

        /* Calculate for the pager buttons */
        var lowPage = Math.floor(pagenr/numItems) + 1;

        $('.pager-nr-btn').each(function() {
            /* calculate current page number */
            var thispagenr = lowPage++;

            /* Add the page number */
            $(this).html('' + thispagenr + '');

            /* Add active class to current page */
            if(thispagenr == pagenr) {
                $(this).addClass('active');
            } else {
                $(this).removeClass('active');
            }

            /* Disable inactive classes and bind handlers to active classes */
            if(thispagenr > data.pagecount) {
                $(this).addClass('disabled');
            } else {
                /* Add click listener for button */
                $(this).click(function() {
                    loadTablePage(courseid, thispagenr);
                });
            }
        });
    });
}

function initPage() {
    $.getJSON('/api/currentuser', function(data) {
        viewModel.userId = data.id;
        viewModel.updateDropdowns();
    });
}<|endoftext|>


<commit_before>var xlsx = require('node-xlsx');
var _ = require('lodash');
var Excel = require("exceljs");
var json2csv = require('json2csv');
var fs = require('fs');

// Parse xlsx file to generate [{title:'',dec:''},...] format output
var parseXlsx = function(file){
	var obj = xlsx.parse(file); // parses a file

	var result = _.map(obj[0].data, function(row) {
		return {
			title: row[0],
			dec: row[1],
		};
	});
	result = _.drop(result);
	return result;
}

var tSet = parseXlsx('T.xlsx');
var ntSet = parseXlsx('NT.xlsx');

// Walk over all records from NT and add to results
var results = _.map(ntSet, function(row) {
	return {
		ntTitle: row.title,
		ntDec: row.dec,
	};
});

// Walk over all T records and try to match against NT record
_.forEach(tSet, function(row) {
	var existingNt = _.find(results, {ntTitle: row.title});
	if (existingNt) { // Result set already exists in tSet
		results.tTitle = row.title;
		results.tDec = row.dec;
	} else { // Not found - create new right-hand-side record
		results.push({
			tTitle: row.title,
			tDec: row.dec,
		});
	}
});

// Compute the result
results = _.map(results, function(row) {
	if (_.isUndefined(row.tDec) || _.isUndefined(row.ntDec)) { // Left or right Dec is missing
		row.result = '';
	} else if (row.tDec == row.ntDec) { // Decisions agree
		row.result = '';
	} else { // Decisions disagree
		row.result = 'CONFLICT';
	}
	return row;
});

// Transer results to output.csv
var fields = ['ntTitle', 'ntDec', 'tTitle', 'tDec', 'result'];
json2csv({ data: results, fields: fields }, function(err, csv) {
	if (err) console.log(err);
	fs.writeFile('output.csv', csv, function(err) {
		if (err) throw err;
		console.log('file saved');
	});
});
<commit_msg>Updated input files' paths<commit_after>var xlsx = require('node-xlsx');
var _ = require('lodash');
var Excel = require("exceljs");
var json2csv = require('json2csv');
var fs = require('fs');

// Parse xlsx file to generate [{title:'',dec:''},...] format output
var parseXlsx = function(file){
	var obj = xlsx.parse(file); // parses a file

	var result = _.map(obj[0].data, function(row) {
		return {
			title: row[0],
			dec: row[1],
		};
	});
	result = _.drop(result);
	return result;
}

var tSet = parseXlsx('data/T.xlsx');
var ntSet = parseXlsx('data/NT.xlsx');

// Walk over all records from NT and add to results
var results = _.map(ntSet, function(row) {
	return {
		ntTitle: row.title,
		ntDec: row.dec,
	};
});

// Walk over all T records and try to match against NT record
_.forEach(tSet, function(row) {
	var existingNt = _.find(results, {ntTitle: row.title});
	if (existingNt) { // Result set already exists in tSet
		results.tTitle = row.title;
		results.tDec = row.dec;
	} else { // Not found - create new right-hand-side record
		results.push({
			tTitle: row.title,
			tDec: row.dec,
		});
	}
});

// Compute the result
results = _.map(results, function(row) {
	if (_.isUndefined(row.tDec) || _.isUndefined(row.ntDec)) { // Left or right Dec is missing
		row.result = '';
	} else if (row.tDec == row.ntDec) { // Decisions agree
		row.result = '';
	} else { // Decisions disagree
		row.result = 'CONFLICT';
	}
	return row;
});

// Transer results to output.csv
var fields = ['ntTitle', 'ntDec', 'tTitle', 'tDec', 'result'];
json2csv({ data: results, fields: fields }, function(err, csv) {
	if (err) console.log(err);
	fs.writeFile('output.csv', csv, function(err) {
		if (err) throw err;
		console.log('file saved');
	});
});
<|endoftext|>


<commit_before>import PropTypes from 'prop-types'
import React, { Component } from 'react'
import {
  Button,
  ButtonToolbar,
  ControlLabel,
  FormControl,
  FormGroup,
  Glyphicon,
  HelpBlock,
  Modal,
  ToggleButton,
  ToggleButtonGroup
} from 'react-bootstrap'
import styled from 'styled-components'

const allowedNotificationChannels = [
  {
    type: 'email',
    text: 'Email'
  },
  {
    type: 'sms',
    text: 'SMS'
  },
  {
    type: 'none',
    text: 'Don\'t notify me'
  }
]

// Styles
// HACK: Preverve container height.
const Details = styled.div`
  min-height: 150px;
  margin-bottom: 15px;
`
const ButtonStrip = styled.div`
  > * {
    margin-right: 4px;
  }
`

/**
 * User notification preferences pane.
 */
class NotificationPrefsPane extends Component {
  static propTypes = { // TODO: update prop types.
    onRequestPhoneVerificationCode: PropTypes.func.isRequired,
    onRevertUserPhoneNumber: PropTypes.func.isRequired,
    onSendPhoneVerificationCode: PropTypes.func.isRequired,
    onUserDataChange: PropTypes.func.isRequired,
    userData: PropTypes.object.isRequired
  }

  constructor (props) {
    super(props)

    this.state = {
      // Holds the initial phone number or the last confirmed phone number
      initialPhoneNumber: props.userData.phoneNumber,
      // Set to true when the field is changed, to display validation errors subsequently.
      isPhoneFieldModified: false,
      // If true, a phone verification request has been sent and the UI is awaiting the user sending the code.
      isVerificationPending: false,
      // Holds the validation code.
      phoneValidationCode: ''
    }
  }

  _handleNotificationChannelChange = e => {
    const { onUserDataChange } = this.props
    onUserDataChange({ notificationChannel: e })
  }

  _handlePhoneNumberChange = e => {
    const { onUserDataChange } = this.props
    onUserDataChange({ phoneNumber: e.target.value })

    // Mark field as modified.
    this.setState({
      isPhoneFieldModified: true
    })
  }

  _handlePhoneValidationCodeChange = e => {
    // Update validation code state.
    this.setState({
      phoneValidationCode: e.target.value
    })
  }

  _handlePhoneValidationCancel = () => {
    // Exit the phone verification process.
    this.setState({
      isVerificationPending: false,
      phoneValidationCode: ''
    })
  }

  _handleRevertPhoneNumber = () => {
    // Reset the modified and pending states.
    this.setState({
      isPhoneFieldModified: false,
      isVerificationPending: false
    })

    this.props.onRevertUserPhoneNumber()
  }

  _handleRequestPhoneVerificationCode = () => {
    // Send phone verification request.
    this.props.onRequestPhoneVerificationCode()

    // Prompt for code.
    this.setState({
      isVerificationPending: true,
      phoneValidationCode: ''
    })
  }

  _handleSendPhoneVerificationCode = async () => {
    await this.props.onSendPhoneVerificationCode(this.state.phoneValidationCode)

    // Exit verification modal and update "initialPhoneNumber" if isPhoneNumberVerified was changed to true.
    // Erase the verification code in all cases.
    if (this.props.userData.isPhoneNumberVerified) {
      this.setState({
        initialPhoneNumber: this.props.userData.phoneNumber,
        isVerificationPending: false,
        phoneValidationCode: ''
      })
    } else {
      this.setState({
        phoneValidationCode: ''
      })
    }
  }

  render () {
    const { userData } = this.props
    const {
      initialPhoneNumber,
      isPhoneFieldModified,
      isVerificationPending,
      phoneValidationCode
    } = this.state
    const {
      email,
      isPhoneNumberVerified,
      notificationChannel,
      phoneNumber
    } = userData

    // Here are the states we are dealing with:
    // - First time entering a phone number (phoneNumber blank, not modified)
    //   => no color, no feedback indication.
    // - Typing backspace all the way to erase a number (phoneNumber blank, modified)
    //   => red, "[X] Please provide a number" indication.
    // - Viewing a verified phone number (phoneNumber non-blank, same as initial, verified)
    //   => green, "[V] Verified" indication.
    // - Editing a phone number (phoneNumber non-blank, different than initial or not verified)
    //   => yellow, "[!] Verification required" indication.

    const isPhoneNumberBlank = !(phoneNumber && phoneNumber.length)
    const isPhoneNumberSameAsInitial = phoneNumber === initialPhoneNumber

    let phoneStatusGlyph // one of the Bootstrap glyphs.
    let phoneStatusText
    let phoneFieldValidationState // one of the Bootstrap validationState values.
    let shouldVerifyPhone = false

    if (isPhoneNumberBlank) {
      if (!isPhoneFieldModified) {
        // Do not show an indication.
      } else {
        phoneStatusGlyph = 'remove'
        phoneStatusText = 'Please provide a number'
        phoneFieldValidationState = 'error'
      }
    } else if (isPhoneNumberSameAsInitial && isPhoneNumberVerified) {
      phoneStatusGlyph = 'ok'
      phoneStatusText = 'Verified'
      phoneFieldValidationState = 'success'
    } else {
      phoneStatusGlyph = 'warning-sign'
      phoneStatusText = 'Verification required'
      phoneFieldValidationState = 'warning'
      shouldVerifyPhone = true
    }

    return (

          You can receive notifications about trips you frequently take.

        <FormGroup>
          <ControlLabel>How would you like to receive notifications?</ControlLabel>
          <ButtonToolbar>
            <ToggleButtonGroup
              name='notificationChannels'
              onChange={this._handleNotificationChannelChange}
              type='radio'
              value={notificationChannel}
            >
              {allowedNotificationChannels.map(({ type, text }, index) => (
                <ToggleButton
                  bsStyle={notificationChannel === type ? 'primary' : 'default'}
                  key={index}
                  value={type}
                >
                  {text}
                </ToggleButton>
              ))}
            </ToggleButtonGroup>
          </ButtonToolbar>
        </FormGroup>

          {notificationChannel === 'email' && (
            <FormGroup>
              <ControlLabel>Notification emails will be sent out to:</ControlLabel>
              <FormControl disabled type='text' value={email} />
            </FormGroup>
          )}
          {notificationChannel === 'sms' && (

              <FormGroup validationState={phoneFieldValidationState}>
                <ControlLabel>Enter your phone number for SMS notifications:</ControlLabel>
                {/* TODO: Add field validation. */}

                <FormControl
                  onChange={this._handlePhoneNumberChange}
                  // Fake phone number, see https://en.wikipedia.org/wiki/555_(telephone_number)
                  placeholder='+15555550123'
                  type='tel'
                  value={phoneNumber}
                />
                {/* Show glyphs underneath the input control with <HelpBlock>
                   (there are some alignment issues with <FormControl.Feedback /> in mobile view). */}
                <HelpBlock>
                  {phoneStatusGlyph && <Glyphicon glyph={phoneStatusGlyph} />} {phoneStatusText}
                </HelpBlock>
              </FormGroup>

              {shouldVerifyPhone && (
                <ButtonStrip>
                  <Button bsStyle='primary' onClick={this._handleRequestPhoneVerificationCode}>Verify my phone</Button>
                  <Button onClick={this._handleRevertPhoneNumber}>Revert number</Button>
                </ButtonStrip>
              )}

          )}

        {/* The dialog prompt for validation code. */}
        <Modal onHide={this._handlePhoneValidationCancel} show={isVerificationPending}>
          <Modal.Header>
            <Modal.Title>Enter verification code</Modal.Title>
          </Modal.Header>

          <Modal.Body>

              Please check the SMS messaging app on your mobile phone
              for a text message with a verification code, and enter the code below.

            <FormGroup>
              <ControlLabel>Verification code:</ControlLabel>
              <FormControl onChange={this._handlePhoneValidationCodeChange} value={phoneValidationCode} />
            </FormGroup>

              <Button bsStyle='link' onClick={this._handleRequestPhoneVerificationCode}>Request a new code</Button>

          </Modal.Body>

          <Modal.Footer>
            <Button onClick={this._handlePhoneValidationCancel}>Cancel</Button>
            <Button bsStyle='primary' onClick={this._handleSendPhoneVerificationCode}>Verify my phone</Button>
          </Modal.Footer>
        </Modal>

    )
  }
}

export default NotificationPrefsPane
<commit_msg>refactor(NotificationPrefsPane): Hide Revert Number if no phone number was saved before.<commit_after>import PropTypes from 'prop-types'
import React, { Component } from 'react'
import {
  Button,
  ButtonToolbar,
  ControlLabel,
  FormControl,
  FormGroup,
  Glyphicon,
  HelpBlock,
  Modal,
  ToggleButton,
  ToggleButtonGroup
} from 'react-bootstrap'
import styled from 'styled-components'

const allowedNotificationChannels = [
  {
    type: 'email',
    text: 'Email'
  },
  {
    type: 'sms',
    text: 'SMS'
  },
  {
    type: 'none',
    text: 'Don\'t notify me'
  }
]

// Styles
// HACK: Preverve container height.
const Details = styled.div`
  min-height: 150px;
  margin-bottom: 15px;
`
const ButtonStrip = styled.div`
  > * {
    margin-right: 4px;
  }
`

/**
 * User notification preferences pane.
 */
class NotificationPrefsPane extends Component {
  static propTypes = {
    onRequestPhoneVerificationCode: PropTypes.func.isRequired,
    onRevertUserPhoneNumber: PropTypes.func.isRequired,
    onSendPhoneVerificationCode: PropTypes.func.isRequired,
    onUserDataChange: PropTypes.func.isRequired,
    userData: PropTypes.object.isRequired
  }

  constructor (props) {
    super(props)

    this.state = {
      // Holds the initial phone number or the last confirmed phone number
      initialPhoneNumber: props.userData.phoneNumber,
      // Set to true when the field is changed, to display validation errors subsequently.
      isPhoneFieldModified: false,
      // If true, a phone verification request has been sent and the UI is awaiting the user sending the code.
      isVerificationPending: false,
      // Holds the validation code.
      phoneValidationCode: ''
    }
  }

  _handleNotificationChannelChange = e => {
    const { onUserDataChange } = this.props
    onUserDataChange({ notificationChannel: e })
  }

  _handlePhoneNumberChange = e => {
    const { onUserDataChange } = this.props
    onUserDataChange({ phoneNumber: e.target.value })

    // Mark field as modified.
    this.setState({
      isPhoneFieldModified: true
    })
  }

  _handlePhoneValidationCodeChange = e => {
    // Update validation code state.
    this.setState({
      phoneValidationCode: e.target.value
    })
  }

  _handlePhoneValidationCancel = () => {
    // Exit the phone verification process.
    this.setState({
      isVerificationPending: false,
      phoneValidationCode: ''
    })
  }

  _handleRevertPhoneNumber = () => {
    // Reset the modified and pending states.
    this.setState({
      isPhoneFieldModified: false,
      isVerificationPending: false
    })

    this.props.onRevertUserPhoneNumber()
  }

  _handleRequestPhoneVerificationCode = () => {
    // Send phone verification request.
    this.props.onRequestPhoneVerificationCode()

    // Prompt for code.
    this.setState({
      isVerificationPending: true,
      phoneValidationCode: ''
    })
  }

  _handleSendPhoneVerificationCode = async () => {
    await this.props.onSendPhoneVerificationCode(this.state.phoneValidationCode)

    // Exit verification modal and update "initialPhoneNumber" if isPhoneNumberVerified was changed to true.
    // Erase the verification code in all cases.
    if (this.props.userData.isPhoneNumberVerified) {
      this.setState({
        initialPhoneNumber: this.props.userData.phoneNumber,
        isVerificationPending: false,
        phoneValidationCode: ''
      })
    } else {
      this.setState({
        phoneValidationCode: ''
      })
    }
  }

  render () {
    const { userData } = this.props
    const {
      initialPhoneNumber,
      isPhoneFieldModified,
      isVerificationPending,
      phoneValidationCode
    } = this.state
    const {
      email,
      isPhoneNumberVerified,
      notificationChannel,
      phoneNumber
    } = userData

    // Here are the states we are dealing with:
    // - First time entering a phone number (phoneNumber blank, not modified)
    //   => no color, no feedback indication.
    // - Typing backspace all the way to erase a number (phoneNumber blank, modified)
    //   => red, "[X] Please provide a number" indication.
    // - Viewing a verified phone number (phoneNumber non-blank, same as initial, verified)
    //   => green, "[V] Verified" indication.
    // - Editing a phone number (phoneNumber non-blank, different than initial or not verified)
    //   => yellow, "[!] Verification required" indication.

    const isPhoneNumberBlank = !(phoneNumber && phoneNumber.length)
    const isPhoneNumberSameAsInitial = phoneNumber === initialPhoneNumber

    let phoneStatusGlyph // one of the Bootstrap glyphs.
    let phoneStatusText
    let phoneFieldValidationState // one of the Bootstrap validationState values.
    let shouldVerifyPhone = false

    if (isPhoneNumberBlank) {
      if (!isPhoneFieldModified) {
        // Do not show an indication.
      } else {
        phoneStatusGlyph = 'remove'
        phoneStatusText = 'Please provide a number'
        phoneFieldValidationState = 'error'
      }
    } else if (isPhoneNumberSameAsInitial && isPhoneNumberVerified) {
      phoneStatusGlyph = 'ok'
      phoneStatusText = 'Verified'
      phoneFieldValidationState = 'success'
    } else {
      phoneStatusGlyph = 'warning-sign'
      phoneStatusText = 'Verification required'
      phoneFieldValidationState = 'warning'
      shouldVerifyPhone = true
    }

    return (

          You can receive notifications about trips you frequently take.

        <FormGroup>
          <ControlLabel>How would you like to receive notifications?</ControlLabel>
          <ButtonToolbar>
            <ToggleButtonGroup
              name='notificationChannels'
              onChange={this._handleNotificationChannelChange}
              type='radio'
              value={notificationChannel}
            >
              {allowedNotificationChannels.map(({ type, text }, index) => (
                <ToggleButton
                  bsStyle={notificationChannel === type ? 'primary' : 'default'}
                  key={index}
                  value={type}
                >
                  {text}
                </ToggleButton>
              ))}
            </ToggleButtonGroup>
          </ButtonToolbar>
        </FormGroup>

          {notificationChannel === 'email' && (
            <FormGroup>
              <ControlLabel>Notification emails will be sent out to:</ControlLabel>
              <FormControl disabled type='text' value={email} />
            </FormGroup>
          )}
          {notificationChannel === 'sms' && (

              <FormGroup validationState={phoneFieldValidationState}>
                <ControlLabel>Enter your phone number for SMS notifications:</ControlLabel>
                {/* TODO: Add field validation. */}

                <FormControl
                  onChange={this._handlePhoneNumberChange}
                  // Fake phone number, see https://en.wikipedia.org/wiki/555_(telephone_number)
                  placeholder='+15555550123'
                  type='tel'
                  value={phoneNumber}
                />
                {/* Show glyphs underneath the input control with <HelpBlock>
                   (there are some alignment issues with <FormControl.Feedback /> in mobile view). */}
                <HelpBlock>
                  {phoneStatusGlyph && <Glyphicon glyph={phoneStatusGlyph} />} {phoneStatusText}
                </HelpBlock>
              </FormGroup>

              {shouldVerifyPhone && (
                <ButtonStrip>
                  <Button bsStyle='primary' onClick={this._handleRequestPhoneVerificationCode}>Verify my phone</Button>
                  {initialPhoneNumber.length !== 0 && <Button onClick={this._handleRevertPhoneNumber}>Revert number</Button>}
                </ButtonStrip>
              )}

          )}

        {/* The dialog prompt for validation code. */}
        <Modal onHide={this._handlePhoneValidationCancel} show={isVerificationPending}>
          <Modal.Header>
            <Modal.Title>Enter verification code</Modal.Title>
          </Modal.Header>

          <Modal.Body>

              Please check the SMS messaging app on your mobile phone
              for a text message with a verification code, and enter the code below.

            <FormGroup>
              <ControlLabel>Verification code:</ControlLabel>
              <FormControl onChange={this._handlePhoneValidationCodeChange} value={phoneValidationCode} />
            </FormGroup>

              <Button bsStyle='link' onClick={this._handleRequestPhoneVerificationCode}>Request a new code</Button>

          </Modal.Body>

          <Modal.Footer>
            <Button onClick={this._handlePhoneValidationCancel}>Cancel</Button>
            <Button bsStyle='primary' onClick={this._handleSendPhoneVerificationCode}>Verify my phone</Button>
          </Modal.Footer>
        </Modal>

    )
  }
}

export default NotificationPrefsPane
<|endoftext|>