<commit_before>// See the file COPYRIGHT for copyright information.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//
// Initialize UI
//

function initIncidentPage() {
    function loadedBody() {
        addLocationAddressOptions();
        disableEditing();
        loadAndDisplayIncident(function() {
            loadPersonnel(function() {drawRangersToAdd()});
        });
    }

    loadBody(loadedBody);
}

//
// Load incident
//

var incident = null;

function loadIncident(success) {
    var url = incidentsURL + "/" + incidentNumber;

    function ok(data, status, xhr) {
        incident = data;

        if (success != undefined) {
            success();
        }
    }

    function fail(xhr, status, error) {
        disableEditing();
        var message = "Failed to load incident:\n" + error
        console.error(message);
        window.alert(message);
    }

    $.ajax({
        "url": url,
        "method": "GET",
        "dataType": "json",
        "success": ok,
        "error": fail,
    });
}

function loadAndDisplayIncident(success) {
    function loaded() {
        if (incident == null) {
            var message = "Incident failed to load"
            console.log(message);
            alert(message);
            return;
        }

        drawIncidentFields();
        enableEditing();

        if (success != undefined) {
            success();
        }
    }

    loadIncident(loaded);
}

//
// Load personnel
//

var personnel = null;

function loadPersonnel(success) {
    var url = personnelURL;

    function ok(data, status, xhr) {
        var _personnel = {};
        for (var i in data) {
            var record = data[i];
            _personnel[record.handle] = record;
        }
        personnel = _personnel

        if (success != undefined) {
            success();
        }
    }

    function fail(xhr, status, error) {
        var message = "Failed to load personnel:\n" + error
        console.error(message);
        window.alert(message);
    }

    $.ajax({
        "url": url,
        "method": "GET",
        "dataType": "json",
        "success": ok,
        "error": fail,
    });
}

//
// Draw all fields
//

function drawIncidentFields() {
    drawNumber();
    drawState();
    drawPriority();
    drawSummary();
    drawRangers();
    drawIncidentTypes();
    drawLocationName();
    drawLocationAddressRadialHour();
    drawLocationAddressRadialMinute();
    drawLocationAddressConcentric();
    drawLocationDescription();
    drawReportEntries();

    $("#incident_report_add").on("input", reportEntryEdited);
}

//
// Add option elements to location address select elements
//

function addLocationAddressOptions() {
    var hours = range(1, 13);
    for (var i in hours) {
        var hour = padTwo(hours[i]);
        $("#incident_location_address_radial_hour")
            .append($("<option />", { "value": hour, "text": hour }))
            ;
    }

    var minutes = range(0, 12, 5);
    for (var i in minutes) {
        var minute = padTwo(minutes[i]);
        $("#incident_location_address_radial_minute")
            .append($("<option />", { "value": minute, "text": minute }))
            ;
    }

    for (var id in concentricStreetNameByID) {
        var name = concentricStreetNameByID[id];
        $("#incident_location_address_concentric")
            .append($("<option />", { "value": id, "text": name }))
            ;
    }
}

//
// Populate incident number
//

function drawNumber() {
    $("#incident_number").text(incident.number);
}

//
// Populate incident state
//

function drawState() {
    selectOptionWithValue(
        $("#incident_state"), stateForIncident(incident)
    );
}

//
// Populate incident priority
//

function drawPriority() {
    selectOptionWithValue(
        $("#incident_priority"), incident.priority
    );
}

//
// Populate incident summary
//

function drawSummary() {
    $("#incident_summary").attr("value", summarizeIncident(incident));
}

//
// Populate Rangers list
//

var _rangerItem = null;

function drawRangers() {
    if (_rangerItem == null) {
        _rangerItem = $("#incident_rangers_list")
            .children(".list-group-item:first")
            ;
    }

    var items = [];

    for (var i in incident.ranger_handles) {
        var item = _rangerItem.clone();
        item.append(incident.ranger_handles[i]);
        items.push(item);
    }

    var container = $("#incident_rangers_list");
    container.empty();
    container.append(items);
}

function drawRangersToAdd() {
    console.log("Draw Rangers to add...");

    var select = $("#ranger_add");

    var handles = [];
    for (var handle in personnel) {
        handles.push(handle);
    }
    handles = handles.sort();

    for (var i in handles) {
        handle = handles[i];

        var option = $("<option />");
        option.attr("value", handle);
        option.text(rangerAsString(personnel[handle]));

        select.append(option);
    }
}

function rangerAsString(ranger) {
    var result = ranger.handle;

    if (ranger.name != undefined && ranger.name != null && ranger.name != "") {
        result += " (" + ranger.name + ")";
    }

    if (ranger.status == "vintage") {
        result += "*";
    }

    return result;
}

//
// Populate incident types list
//

var _typesItem = null;

function drawIncidentTypes() {
    if (_typesItem == null) {
        _typesItem = $("#incident_types_list")
            .children(".list-group-item:first")
            ;
    }

    var items = [];

    for (var i in incident.incident_types) {
        var item = _typesItem.clone();
        item.append(incident.incident_types[i]);
        items.push(item);
    }

    var container = $("#incident_types_list");
    container.empty();
    container.append(items);
}

//
// Populate location
//

function drawLocationName() {
    if (incident.location != undefined) {
        if (incident.location.name != undefined) {
            $("#incident_location_name").attr("value", incident.location.name);
        }
    }
}

function drawLocationAddressRadialHour() {
    var hour = null;
    if (incident.location != undefined) {
        if (incident.location.radial_hour != undefined) {
            hour = padTwo(incident.location.radial_hour);
        }
    }
    selectOptionWithValue(
        $("#incident_location_address_radial_hour"), hour
    );
}

function drawLocationAddressRadialMinute() {
    var minute = null;
    if (incident.location != undefined) {
        if (incident.location.radial_minute != undefined) {
            minute = normalizeMinute(incident.location.radial_minute);
        }
    }
    selectOptionWithValue(
        $("#incident_location_address_radial_minute"), minute
    );
}

function drawLocationAddressConcentric() {
    var concentric = null;
    if (incident.location != undefined) {
        if (incident.location.concentric != undefined) {
            concentric = incident.location.concentric;
        }
    }
    selectOptionWithValue(
        $("#incident_location_address_concentric"), concentric
    );
}

function drawLocationDescription() {
    if (incident.location != undefined) {
        if (incident.location.description != undefined) {
            $("#incident_location_description")
                .attr("value", incident.location.description)
                ;
        }
    }
}

//
// Populate report entry text
//

function reportEntryElement(entry) {
    // Build a container for the entry

    var entryContainer = $("<div />", {"class": "report_entry"});

    if (entry.system_entry) {
        entryContainer.addClass("report_entry_system");
    } else {
        entryContainer.addClass("report_entry_user");
    }

    // Add the timestamp and author

    metaDataContainer = $("<p />", {"class": "report_entry_metadata"})

    var timeStampContainer = timeElement(new Date(entry.created));
    timeStampContainer.addClass("report_entry_timestamp");

    metaDataContainer.append([timeStampContainer, ", "]);

    var author = entry.author;
    if (author == undefined) {
        author = "(unknown)";
    }
    var authorContainer = $("<span />");
    authorContainer.text(entry.author);
    authorContainer.addClass("report_entry_author");

    metaDataContainer.append(author);
    metaDataContainer.append(":");

    entryContainer.append(metaDataContainer);

    // Add report text

    var lines = entry.text.split("\n");
    for (var i in lines) {
        var textContainer = $("<p />", {"class": "report_entry_text"});
        textContainer.text(lines[i]);

        entryContainer.append(textContainer);
    }

    // Return container

    return entryContainer;
}

function drawReportEntries() {
    var container = $("#incident_report");
    container.empty();

    var entries = incident.report_entries;

    if (entries != undefined) {
        for (var i in entries) {
            container.append(reportEntryElement(entries[i]));
        }
    }
}

//
// Editing
//

function sendEdits(edits, success, error) {
    edits.number = incident.number;

    var jsonText = JSON.stringify(edits);

    console.log("Sending edit: " + jsonText);

    var url = incidentsURL + "/" + incident.number;

    function ok(data, status, xhr) {
        success();
        loadAndDisplayIncident();
    }

    function fail(xhr, status, e) {
        var message = "Failed to apply edit:\n" + e
        console.log(message);
        error();
        loadAndDisplayIncident();
        window.alert(message);
    }

    $.ajax({
        "url": url,
        "method": "POST",
        "contentType": "application/json",
        "data": jsonText,
        "success": ok,
        "error": fail,
    })
}

function editFromElement(element, jsonKey, transform) {
    var value = element.val();

    if (transform != undefined) {
        value = transform(value);
    }

    // Build a JSON object representing the requested edits

    var edits = {};

    var keyPath = jsonKey.split(".");
    var lastKey = keyPath.pop();

    var current = edits;
    for (var i in keyPath) {
        var next = {};
        current[keyPath[i]] = next;
        current = next;
    }
    current[lastKey] = value;

    // Location must include type

    if (edits.location != undefined) {
        edits.location.type = "garett";  // UI only supports one type
    }

    // Send request to server

    function ok() {
        controlHasSuccess(element);
        // Clear success state after a 1s delay
        element.delay("1000").queue(function() {controlClear(element)});
    }

    function fail() {
        controlHasError(element);
    }

    sendEdits(edits, element, ok, fail);
}

function editState() {
    editFromElement($("#incident_state"), "state");
}

function editPriority() {
    editFromElement($("#incident_priority"), "priority", Number);
}

function editSummary() {
    editFromElement($("#incident_summary"), "summary");
}

function editLocationName() {
    editFromElement($("#incident_location_name"), "location.name");
}

function editLocationAddressRadialHour() {
    editFromElement(
        $("#incident_location_address_radial_hour"),
        "location.radial_hour",
        Number
    );
}

function editLocationAddressRadialMinute() {
    editFromElement(
        $("#incident_location_address_radial_minute"),
        "location.radial_minute",
        Number
    );
}

function editLocationAddressConcentric() {
    editFromElement(
        $("#incident_location_address_concentric"),
        "location.concentric",
        Number
    );
}

function editLocationDescription() {
    editFromElement($("#incident_location_description"), "location.description");
}

function removeRanger(sender) {
    sender = $(sender);

    var rangerHandle = sender.parent().text().trim();

    function ok() {
        controlHasSuccess(sender);
    }

    function fail() {
        controlHasError(sender);
    }

    sendEdits(
        {
            "ranger_handles": incident.ranger_handles.filter(
                function(h) { return h != rangerHandle }
            ),
        },
        ok, fail
    );
}

function removeIncidentType(sender) {
    sender = $(sender);

    var incidentType = sender.parent().text().trim();

    function ok() {
        controlHasSuccess(sender);
    }

    function fail() {
        controlHasError(sender);
    }

    sendEdits(
        {
            "incident_types": incident.incident_types.filter(
                function(t) { return t != incidentType }
            ),
        },
        ok, fail
    );
}

function addRanger(sender) {
    rangerHandle = $(sender).val().trim();

    console.log("Add Ranger: " + rangerHandle);
}

function addIncidentType(sender) {
    incidentType = $(sender).val().trim();

    console.log("Add incident type: " + incidentType);
}

function reportEntryEdited(event) {
    var text = $("#incident_report_add").val().trim();
    var submitButton = $("#report_entry_submit");

    submitButton.removeClass("btn-default");
    submitButton.removeClass("btn-warning");
    submitButton.removeClass("btn-danger");

    if (text == "") {
        submitButton.addClass("disabled");
        submitButton.addClass("btn-default");
    } else {
        submitButton.removeClass("disabled");
        submitButton.addClass("btn-warning");
    }
}

function submitReportEntry() {
    var text = $("#incident_report_add").val().trim();

    if (text == "") {
        return;
    }

    console.log("New report entry:\n" + text);

    function ok() {
        // Clear the report entry
        $("#incident_report_add").val("");
        // Reset the submit button
        reportEntryEdited();
    }

    function fail() {
        var submitButton = $("#report_entry_submit");
        submitButton.removeClass("btn-default");
        submitButton.removeClass("btn-warning");
        submitButton.addClass("btn-danger");
    }

    sendEdits({"report_entries": [{"text": text}]}, ok, fail);
}
<commit_msg>Filter out Rangers that are not active or vintage.<commit_after>// See the file COPYRIGHT for copyright information.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//
// Initialize UI
//

function initIncidentPage() {
    function loadedBody() {
        addLocationAddressOptions();
        disableEditing();
        loadAndDisplayIncident(function() {
            loadPersonnel(function() {drawRangersToAdd()});
        });
    }

    loadBody(loadedBody);
}

//
// Load incident
//

var incident = null;

function loadIncident(success) {
    var url = incidentsURL + "/" + incidentNumber;

    function ok(data, status, xhr) {
        incident = data;

        if (success != undefined) {
            success();
        }
    }

    function fail(xhr, status, error) {
        disableEditing();
        var message = "Failed to load incident:\n" + error
        console.error(message);
        window.alert(message);
    }

    $.ajax({
        "url": url,
        "method": "GET",
        "dataType": "json",
        "success": ok,
        "error": fail,
    });
}

function loadAndDisplayIncident(success) {
    function loaded() {
        if (incident == null) {
            var message = "Incident failed to load"
            console.log(message);
            alert(message);
            return;
        }

        drawIncidentFields();
        enableEditing();

        if (success != undefined) {
            success();
        }
    }

    loadIncident(loaded);
}

//
// Load personnel
//

var personnel = null;

function loadPersonnel(success) {
    var url = personnelURL;

    function ok(data, status, xhr) {
        var _personnel = {};
        for (var i in data) {
            var record = data[i];
            _personnel[record.handle] = record;
        }
        personnel = _personnel

        if (success != undefined) {
            success();
        }
    }

    function fail(xhr, status, error) {
        var message = "Failed to load personnel:\n" + error
        console.error(message);
        window.alert(message);
    }

    $.ajax({
        "url": url,
        "method": "GET",
        "dataType": "json",
        "success": ok,
        "error": fail,
    });
}

//
// Draw all fields
//

function drawIncidentFields() {
    drawNumber();
    drawState();
    drawPriority();
    drawSummary();
    drawRangers();
    drawIncidentTypes();
    drawLocationName();
    drawLocationAddressRadialHour();
    drawLocationAddressRadialMinute();
    drawLocationAddressConcentric();
    drawLocationDescription();
    drawReportEntries();

    $("#incident_report_add").on("input", reportEntryEdited);
}

//
// Add option elements to location address select elements
//

function addLocationAddressOptions() {
    var hours = range(1, 13);
    for (var i in hours) {
        var hour = padTwo(hours[i]);
        $("#incident_location_address_radial_hour")
            .append($("<option />", { "value": hour, "text": hour }))
            ;
    }

    var minutes = range(0, 12, 5);
    for (var i in minutes) {
        var minute = padTwo(minutes[i]);
        $("#incident_location_address_radial_minute")
            .append($("<option />", { "value": minute, "text": minute }))
            ;
    }

    for (var id in concentricStreetNameByID) {
        var name = concentricStreetNameByID[id];
        $("#incident_location_address_concentric")
            .append($("<option />", { "value": id, "text": name }))
            ;
    }
}

//
// Populate incident number
//

function drawNumber() {
    $("#incident_number").text(incident.number);
}

//
// Populate incident state
//

function drawState() {
    selectOptionWithValue(
        $("#incident_state"), stateForIncident(incident)
    );
}

//
// Populate incident priority
//

function drawPriority() {
    selectOptionWithValue(
        $("#incident_priority"), incident.priority
    );
}

//
// Populate incident summary
//

function drawSummary() {
    $("#incident_summary").attr("value", summarizeIncident(incident));
}

//
// Populate Rangers list
//

var _rangerItem = null;

function drawRangers() {
    if (_rangerItem == null) {
        _rangerItem = $("#incident_rangers_list")
            .children(".list-group-item:first")
            ;
    }

    var items = [];

    for (var i in incident.ranger_handles) {
        var item = _rangerItem.clone();
        item.append(incident.ranger_handles[i]);
        items.push(item);
    }

    var container = $("#incident_rangers_list");
    container.empty();
    container.append(items);
}

function drawRangersToAdd() {
    console.log("Draw Rangers to add...");

    var select = $("#ranger_add");

    var handles = [];
    for (var handle in personnel) {
        handles.push(handle);
    }
    handles = handles.sort();

    for (var i in handles) {
        var handle = handles[i];
        var ranger = personnel[handle];

        // Filter inactive Rangers out
        // FIXME: better yet: filter based on on-playa state
        switch (ranger.status) {
            case "active":
            case "vintage":
                break;
            default:
                continue;
        }

        var option = $("<option />");
        option.attr("value", handle);
        option.text(rangerAsString(ranger));

        select.append(option);
    }
}

function rangerAsString(ranger) {
    var result = ranger.handle;

    if (ranger.name != undefined && ranger.name != null && ranger.name != "") {
        result += " (" + ranger.name + ")";
    }

    if (ranger.status == "vintage") {
        result += "*";
    }

    return result;
}

//
// Populate incident types list
//

var _typesItem = null;

function drawIncidentTypes() {
    if (_typesItem == null) {
        _typesItem = $("#incident_types_list")
            .children(".list-group-item:first")
            ;
    }

    var items = [];

    for (var i in incident.incident_types) {
        var item = _typesItem.clone();
        item.append(incident.incident_types[i]);
        items.push(item);
    }

    var container = $("#incident_types_list");
    container.empty();
    container.append(items);
}

//
// Populate location
//

function drawLocationName() {
    if (incident.location != undefined) {
        if (incident.location.name != undefined) {
            $("#incident_location_name").attr("value", incident.location.name);
        }
    }
}

function drawLocationAddressRadialHour() {
    var hour = null;
    if (incident.location != undefined) {
        if (incident.location.radial_hour != undefined) {
            hour = padTwo(incident.location.radial_hour);
        }
    }
    selectOptionWithValue(
        $("#incident_location_address_radial_hour"), hour
    );
}

function drawLocationAddressRadialMinute() {
    var minute = null;
    if (incident.location != undefined) {
        if (incident.location.radial_minute != undefined) {
            minute = normalizeMinute(incident.location.radial_minute);
        }
    }
    selectOptionWithValue(
        $("#incident_location_address_radial_minute"), minute
    );
}

function drawLocationAddressConcentric() {
    var concentric = null;
    if (incident.location != undefined) {
        if (incident.location.concentric != undefined) {
            concentric = incident.location.concentric;
        }
    }
    selectOptionWithValue(
        $("#incident_location_address_concentric"), concentric
    );
}

function drawLocationDescription() {
    if (incident.location != undefined) {
        if (incident.location.description != undefined) {
            $("#incident_location_description")
                .attr("value", incident.location.description)
                ;
        }
    }
}

//
// Populate report entry text
//

function reportEntryElement(entry) {
    // Build a container for the entry

    var entryContainer = $("<div />", {"class": "report_entry"});

    if (entry.system_entry) {
        entryContainer.addClass("report_entry_system");
    } else {
        entryContainer.addClass("report_entry_user");
    }

    // Add the timestamp and author

    metaDataContainer = $("<p />", {"class": "report_entry_metadata"})

    var timeStampContainer = timeElement(new Date(entry.created));
    timeStampContainer.addClass("report_entry_timestamp");

    metaDataContainer.append([timeStampContainer, ", "]);

    var author = entry.author;
    if (author == undefined) {
        author = "(unknown)";
    }
    var authorContainer = $("<span />");
    authorContainer.text(entry.author);
    authorContainer.addClass("report_entry_author");

    metaDataContainer.append(author);
    metaDataContainer.append(":");

    entryContainer.append(metaDataContainer);

    // Add report text

    var lines = entry.text.split("\n");
    for (var i in lines) {
        var textContainer = $("<p />", {"class": "report_entry_text"});
        textContainer.text(lines[i]);

        entryContainer.append(textContainer);
    }

    // Return container

    return entryContainer;
}

function drawReportEntries() {
    var container = $("#incident_report");
    container.empty();

    var entries = incident.report_entries;

    if (entries != undefined) {
        for (var i in entries) {
            container.append(reportEntryElement(entries[i]));
        }
    }
}

//
// Editing
//

function sendEdits(edits, success, error) {
    edits.number = incident.number;

    var jsonText = JSON.stringify(edits);

    console.log("Sending edit: " + jsonText);

    var url = incidentsURL + "/" + incident.number;

    function ok(data, status, xhr) {
        success();
        loadAndDisplayIncident();
    }

    function fail(xhr, status, e) {
        var message = "Failed to apply edit:\n" + e
        console.log(message);
        error();
        loadAndDisplayIncident();
        window.alert(message);
    }

    $.ajax({
        "url": url,
        "method": "POST",
        "contentType": "application/json",
        "data": jsonText,
        "success": ok,
        "error": fail,
    })
}

function editFromElement(element, jsonKey, transform) {
    var value = element.val();

    if (transform != undefined) {
        value = transform(value);
    }

    // Build a JSON object representing the requested edits

    var edits = {};

    var keyPath = jsonKey.split(".");
    var lastKey = keyPath.pop();

    var current = edits;
    for (var i in keyPath) {
        var next = {};
        current[keyPath[i]] = next;
        current = next;
    }
    current[lastKey] = value;

    // Location must include type

    if (edits.location != undefined) {
        edits.location.type = "garett";  // UI only supports one type
    }

    // Send request to server

    function ok() {
        controlHasSuccess(element);
        // Clear success state after a 1s delay
        element.delay("1000").queue(function() {controlClear(element)});
    }

    function fail() {
        controlHasError(element);
    }

    sendEdits(edits, element, ok, fail);
}

function editState() {
    editFromElement($("#incident_state"), "state");
}

function editPriority() {
    editFromElement($("#incident_priority"), "priority", Number);
}

function editSummary() {
    editFromElement($("#incident_summary"), "summary");
}

function editLocationName() {
    editFromElement($("#incident_location_name"), "location.name");
}

function editLocationAddressRadialHour() {
    editFromElement(
        $("#incident_location_address_radial_hour"),
        "location.radial_hour",
        Number
    );
}

function editLocationAddressRadialMinute() {
    editFromElement(
        $("#incident_location_address_radial_minute"),
        "location.radial_minute",
        Number
    );
}

function editLocationAddressConcentric() {
    editFromElement(
        $("#incident_location_address_concentric"),
        "location.concentric",
        Number
    );
}

function editLocationDescription() {
    editFromElement($("#incident_location_description"), "location.description");
}

function removeRanger(sender) {
    sender = $(sender);

    var rangerHandle = sender.parent().text().trim();

    function ok() {
        controlHasSuccess(sender);
    }

    function fail() {
        controlHasError(sender);
    }

    sendEdits(
        {
            "ranger_handles": incident.ranger_handles.filter(
                function(h) { return h != rangerHandle }
            ),
        },
        ok, fail
    );
}

function removeIncidentType(sender) {
    sender = $(sender);

    var incidentType = sender.parent().text().trim();

    function ok() {
        controlHasSuccess(sender);
    }

    function fail() {
        controlHasError(sender);
    }

    sendEdits(
        {
            "incident_types": incident.incident_types.filter(
                function(t) { return t != incidentType }
            ),
        },
        ok, fail
    );
}

function addRanger(sender) {
    rangerHandle = $(sender).val().trim();

    console.log("Add Ranger: " + rangerHandle);
}

function addIncidentType(sender) {
    incidentType = $(sender).val().trim();

    console.log("Add incident type: " + incidentType);
}

function reportEntryEdited(event) {
    var text = $("#incident_report_add").val().trim();
    var submitButton = $("#report_entry_submit");

    submitButton.removeClass("btn-default");
    submitButton.removeClass("btn-warning");
    submitButton.removeClass("btn-danger");

    if (text == "") {
        submitButton.addClass("disabled");
        submitButton.addClass("btn-default");
    } else {
        submitButton.removeClass("disabled");
        submitButton.addClass("btn-warning");
    }
}

function submitReportEntry() {
    var text = $("#incident_report_add").val().trim();

    if (text == "") {
        return;
    }

    console.log("New report entry:\n" + text);

    function ok() {
        // Clear the report entry
        $("#incident_report_add").val("");
        // Reset the submit button
        reportEntryEdited();
    }

    function fail() {
        var submitButton = $("#report_entry_submit");
        submitButton.removeClass("btn-default");
        submitButton.removeClass("btn-warning");
        submitButton.addClass("btn-danger");
    }

    sendEdits({"report_entries": [{"text": text}]}, ok, fail);
}
<|endoftext|>


<commit_before>module.directive('timePickerRbs', function($compile){
	return {
		scope: {
			ngModel: '=',
			ngBegin: '=',
			ngEnd: '=',
			ngLimit: '='
		},
		transclude: true,
		replace: true,
		restrict: 'E',
		template: "<input type='text' />",
		link: function($scope, $element, $attributes){
			$('body, lightbox').on('click', function(e){
				var timepicker = $element.data('timepicker');
				if(!timepicker || $element[0] === e.originalEvent.target || $('.bootstrap-timepicker-widget').find(e.originalEvent.target).length !== 0){
					return;
				}
				timepicker.hideWidget();
			});
			loader.asyncLoad('/' + infraPrefix + '/public/js/bootstrap-timepicker.js', function(){
				$element.timepicker({
					showMeridian: false,
					defaultTime: 'current',
					minuteStep: model.timeConfig.interval,
					minHour: model.timeConfig.start_hour,
					maxHour: model.timeConfig.end_hour
				});
			});

			$scope.$watch('ngModel', function(newVal){
				$scope.ngModel = newVal;
				$element.val($scope.ngModel.format("HH:mm"));
				if( ($scope.ngLimit !== undefined && !newVal.isSame($scope.ngLimit))
						&& ( ($scope.ngBegin === true && newVal.isAfter($scope.ngLimit))
								|| ($scope.ngEnd === true && newVal.isBefore($scope.ngLimit)) )
				){
					$scope.ngLimit = moment(newVal);
				}
			});

			$element.on('change', function(){
				var time = $element.val().split(':');
				$scope.ngModel = moment($scope.ngLimit);
				$scope.ngModel.set('hour', time[0]);
				$scope.ngModel.set('minute', time[1]);
				$scope.$apply('ngModel');
				$scope.$parent.$eval($scope.ngChange);
				$scope.$parent.$apply();
			});

			$element.on('focus', function() {
				$element.timepicker('updateFromElementVal');
			});

			$element.on('show.timepicker', function() {
				var timepicker = $element.data('timepicker');
				if (! timepicker){
					return;
				}
				var lightbox = $element.parents().find('lightbox');
				if(lightbox) {
					_.each(lightbox.find('.lightbox-view, .lightbox-background'), function(zone) {
						$(zone).on('mousedown.timepicker, touchend.timepicker', function(e) {
							if (!($element.parent().find(e.target).length ||
								timepicker.$widget.is(e.target) ||
								timepicker.$widget.find(e.target).length)) {
								timepicker.hideWidget();
							}
						});
					});
				}
			});

			$element.on('hide.timepicker', function() {
				var lightbox = $element.parents().find('lightbox');
				if(lightbox) {
					_.each(lightbox.find('.lightbox-view, .lightbox-background'), function(zone) {
						$(zone).off('mousedown.timepicker, touchend.timepicker');
					});
				}
			});

			$element.on('$destroy', function(){
				$element.timepicker('remove');
			});
		}
	}
});<commit_msg>[Ano] #4660 : Hiding the time picker on 'focusin'.<commit_after>module.directive('timePickerRbs', function($compile){
	return {
		scope: {
			ngModel: '=',
			ngBegin: '=',
			ngEnd: '=',
			ngLimit: '='
		},
		transclude: true,
		replace: true,
		restrict: 'E',
		template: "<input type='text' />",
		link: function($scope, $element, $attributes){
			var hideFunction = function(e){
				var timepicker = $element.data('timepicker');
				if(!timepicker || $element[0] === e.originalEvent.target || $('.bootstrap-timepicker-widget').find(e.originalEvent.target).length !== 0){
					return;
				}
				timepicker.hideWidget();
			};
			$('body, lightbox').on('click', hideFunction);
			$('body, lightbox').on('focusin', hideFunction);
			loader.asyncLoad('/' + infraPrefix + '/public/js/bootstrap-timepicker.js', function(){
				$element.timepicker({
					showMeridian: false,
					defaultTime: 'current',
					minuteStep: model.timeConfig.interval,
					minHour: model.timeConfig.start_hour,
					maxHour: model.timeConfig.end_hour
				});
			});

			$scope.$watch('ngModel', function(newVal){
				$scope.ngModel = newVal;
				$element.val($scope.ngModel.format("HH:mm"));
				if( ($scope.ngLimit !== undefined && !newVal.isSame($scope.ngLimit))
						&& ( ($scope.ngBegin === true && newVal.isAfter($scope.ngLimit))
								|| ($scope.ngEnd === true && newVal.isBefore($scope.ngLimit)) )
				){
					$scope.ngLimit = moment(newVal);
				}
			});

			$element.on('change', function(){
				var time = $element.val().split(':');
				$scope.ngModel = moment($scope.ngLimit);
				$scope.ngModel.set('hour', time[0]);
				$scope.ngModel.set('minute', time[1]);
				$scope.$apply('ngModel');
				$scope.$parent.$eval($scope.ngChange);
				$scope.$parent.$apply();
			});

			$element.on('focus', function() {
				$element.timepicker('updateFromElementVal');
			});

			$element.on('show.timepicker', function() {
				var timepicker = $element.data('timepicker');
				if (! timepicker){
					return;
				}
				var lightbox = $element.parents().find('lightbox');
				if(lightbox) {
					_.each(lightbox.find('.lightbox-view, .lightbox-background'), function(zone) {
						$(zone).on('mousedown.timepicker, touchend.timepicker', function(e) {
							if (!($element.parent().find(e.target).length ||
								timepicker.$widget.is(e.target) ||
								timepicker.$widget.find(e.target).length)) {
								timepicker.hideWidget();
							}
						});
					});
				}
			});

			$element.on('hide.timepicker', function() {
				var lightbox = $element.parents().find('lightbox');
				if(lightbox) {
					_.each(lightbox.find('.lightbox-view, .lightbox-background'), function(zone) {
						$(zone).off('mousedown.timepicker, touchend.timepicker');
					});
				}
			});

			$element.on('$destroy', function(){
				$element.timepicker('remove');
			});
		}
	}
});
<|endoftext|>


<commit_before>/**
 * @author Pedro Sanders
 * @since v1
 */
import AccountManagerService from 'core/account_manager_service'

const SipFactory = Packages.javax.sip.SipFactory
const FromHeader = Packages.javax.sip.header.FromHeader
const ViaHeader = Packages.javax.sip.header.ViaHeader
const CSeqHeader = Packages.javax.sip.header.CSeqHeader
const ExpiresHeader = Packages.javax.sip.header.ExpiresHeader
const Request = Packages.javax.sip.message.Request
const Response = Packages.javax.sip.message.Response
const LogManager = Packages.org.apache.logging.log4j.LogManager
const LOG = LogManager.getLogger()

export default class ResponseProcessor {

    constructor(sipProvider, locator, registry, registrar, dataAPIs, contextStorage) {
        this.sipProvider = sipProvider
        this.registry = registry
        this.contextStorage = contextStorage
        this.headerFactory = SipFactory.getInstance().createHeaderFactory()
        this.accountManagerService = new AccountManagerService(dataAPIs)
    }

    process(event) {
        const response = event.getResponse()

        // The stack takes care of this cases
        if (ResponseProcessor.isStackJob(response)) {
            return
        }

        if (ResponseProcessor.isRegisterOk(response)) {
            const viaHeader = response.getHeader(ViaHeader.NAME)
            if (ResponseProcessor.isBehindNat(viaHeader)) {
                return this.reRegister(event)
            }
        } else if(ResponseProcessor.isRegisterNok(response)) {
            this.removeFromRegistry(response)
        }

        ResponseProcessor.mustAuthenticate(response)? this.handleAuthChallenge(event)
            : this.sendResponse(event)
    }

    static mustAuthenticate(response) {
        if(response.getStatusCode() == Response.PROXY_AUTHENTICATION_REQUIRED ||
          response.getStatusCode() == Response.UNAUTHORIZED) {
            return true
        }
        return false
    }

    static isStackJob(response) {
        if(response.getStatusCode() == Response.TRYING              ||
            response.getStatusCode() == Response.REQUEST_TERMINATED ||
            response.getHeader(CSeqHeader.NAME).getMethod()
              .equals(Request.CANCEL)) {
              return true
        }
        return false
    }

    static isBehindNat(viaHeader) {
        const rPort = viaHeader.getRPort()
        const port = viaHeader.getPort()
        const host = viaHeader.getHost()
        const received = viaHeader.getReceived()
        return (!!received && !host.equals(received)) || port != rPort? true : false
    }

    static isRegister(response) {
        const cseq = response.getHeader(CSeqHeader.NAME)
        return cseq.getMethod().equals(Request.REGISTER)? true : false
    }

    static isRegisterOk(response) {
        if(ResponseProcessor.isRegister(response) && response.getStatusCode() == Response.OK) {
            return true
        }
        return false
    }

    static isRegisterNok(response) {
        if(response.getStatusCode() != Response.OK && ResponseProcessor.isRegister(response)) {
            return true
        }
        return false
    }

    static isInviteWithCT(event) {
        const clientTransaction = event.getClientTransaction()
        const cseq = event.getResponse().getHeader(CSeqHeader.NAME)
        return cseq.getMethod().equals(Request.INVITE) && clientTransaction != null? true : false
    }

    storeInRegistry(response) {
        const fromURI = response.getHeader(FromHeader.NAME).getAddress().getURI()
        const expiresHeader = response.getHeader(ExpiresHeader.NAME)
        const expires  = expiresHeader != null? expiresHeader.getExpires() : 300
        this.registry.storeRegistry(fromURI.getUser(), fromURI.getHost(), expires)
    }

    removeFromRegistry(response) {
        const fromURI = response.getHeader(FromHeader.NAME).getAddress().getURI()
        this.registry.removeRegistry(fromURI.getHost())
    }

    handleAuthChallenge(event) {
        const authHelper = this.sipProvider
            .getSipStack()
                .getAuthenticationHelper(this.accountManagerService
                    .getAccountManager(), this.headerFactory)
        authHelper.handleChallenge(
            event.getResponse(), event.getClientTransaction(),
              event.getSource(), 5).sendRequest()
    }

    reRegister(event) {
        const response = event.getResponse()
        const clientTransaction = event.getClientTransaction()
        const viaHeader = response.getHeader(ViaHeader.NAME)

        LOG.debug('Sip I/O is behind a NAT. Re-registering using Received and RPort')

        try {
            const fromURI = response.getHeader(FromHeader.NAME).getAddress().getURI()
            const gwRef = clientTransaction.getRequest().getHeader('X-Gateway-Ref').value
            this.registry.requestChallenge(fromURI.getUser(),
                gwRef,
                fromURI.getHost(),
                viaHeader.getTransport().toLowerCase(),
                viaHeader.getReceived(),
                viaHeader.getRPort())
        } catch(e) {
            LOG.error(e)
        }
    }

    sendResponse(event) {
        const responseOut = event.getResponse().clone()

        // Strip the topmost via header
        responseOut.removeFirst(ViaHeader.NAME)

        if (ResponseProcessor.isInviteWithCT(event)) {
            // In theory we should be able to obtain the ServerTransaction casting the ApplicationData.
            // However, I'm unable to find the way to cast this object.
            //let st = clientTransaction.getApplicationData()'
            const context = this.contextStorage.findContext(event.getClientTransaction())

            if (context != null && context.serverTransaction != null) {
                context.serverTransaction.sendResponse(responseOut)
            } else if (responseOut.getHeader(ViaHeader.NAME) != null) {
                this.sipProvider.sendResponse(responseOut)
            }
        } else if(responseOut.getHeader(ViaHeader.NAME) != null) {
            // Could be a BYE due to Record-Route
            // There is no more Via headers; the response was intended for the proxy.
            this.sipProvider.sendResponse(responseOut)
        }
        LOG.debug(responseOut)
    }
}
<commit_msg>Fixed not storing registration<commit_after>/**
 * @author Pedro Sanders
 * @since v1
 */
import AccountManagerService from 'core/account_manager_service'

const SipFactory = Packages.javax.sip.SipFactory
const FromHeader = Packages.javax.sip.header.FromHeader
const ViaHeader = Packages.javax.sip.header.ViaHeader
const CSeqHeader = Packages.javax.sip.header.CSeqHeader
const ExpiresHeader = Packages.javax.sip.header.ExpiresHeader
const Request = Packages.javax.sip.message.Request
const Response = Packages.javax.sip.message.Response
const LogManager = Packages.org.apache.logging.log4j.LogManager
const LOG = LogManager.getLogger()

export default class ResponseProcessor {

    constructor(sipProvider, locator, registry, registrar, dataAPIs, contextStorage) {
        this.sipProvider = sipProvider
        this.registry = registry
        this.contextStorage = contextStorage
        this.headerFactory = SipFactory.getInstance().createHeaderFactory()
        this.accountManagerService = new AccountManagerService(dataAPIs)
    }

    process(event) {
        const response = event.getResponse()

        // The stack takes care of this cases
        if (ResponseProcessor.isStackJob(response)) {
            return
        }

        if (ResponseProcessor.isRegisterOk(response)) {
            const viaHeader = response.getHeader(ViaHeader.NAME)
            if (ResponseProcessor.isBehindNat(viaHeader)) {
                return this.reRegister(event)
            }
            this.storeInRegistry(response)
        } else if(ResponseProcessor.isRegisterNok(response)) {
            this.removeFromRegistry(response)
        }

        ResponseProcessor.mustAuthenticate(response)? this.handleAuthChallenge(event)
            : this.sendResponse(event)
    }

    static mustAuthenticate(response) {
        if(response.getStatusCode() == Response.PROXY_AUTHENTICATION_REQUIRED ||
          response.getStatusCode() == Response.UNAUTHORIZED) {
            return true
        }
        return false
    }

    static isStackJob(response) {
        if(response.getStatusCode() == Response.TRYING              ||
            response.getStatusCode() == Response.REQUEST_TERMINATED ||
            response.getHeader(CSeqHeader.NAME).getMethod()
              .equals(Request.CANCEL)) {
              return true
        }
        return false
    }

    static isBehindNat(viaHeader) {
        const rPort = viaHeader.getRPort()
        const port = viaHeader.getPort()
        const host = viaHeader.getHost()
        const received = viaHeader.getReceived()
        return (!!received && !host.equals(received)) || port != rPort? true : false
    }

    static isRegister(response) {
        const cseq = response.getHeader(CSeqHeader.NAME)
        return cseq.getMethod().equals(Request.REGISTER)? true : false
    }

    static isRegisterOk(response) {
        if(ResponseProcessor.isRegister(response) && response.getStatusCode() == Response.OK) {
            return true
        }
        return false
    }

    static isRegisterNok(response) {
        if(response.getStatusCode() != Response.OK && ResponseProcessor.isRegister(response)) {
            return true
        }
        return false
    }

    static isInviteWithCT(event) {
        const clientTransaction = event.getClientTransaction()
        const cseq = event.getResponse().getHeader(CSeqHeader.NAME)
        return cseq.getMethod().equals(Request.INVITE) && clientTransaction != null? true : false
    }

    storeInRegistry(response) {
        const fromURI = response.getHeader(FromHeader.NAME).getAddress().getURI()
        const expiresHeader = response.getHeader(ExpiresHeader.NAME)
        const expires  = expiresHeader != null? expiresHeader.getExpires() : 300
        this.registry.storeRegistry(fromURI.getUser(), fromURI.getHost(), expires)
    }

    removeFromRegistry(response) {
        const fromURI = response.getHeader(FromHeader.NAME).getAddress().getURI()
        this.registry.removeRegistry(fromURI.getHost())
    }

    handleAuthChallenge(event) {
        const authHelper = this.sipProvider
            .getSipStack()
                .getAuthenticationHelper(this.accountManagerService
                    .getAccountManager(), this.headerFactory)
        authHelper.handleChallenge(
            event.getResponse(), event.getClientTransaction(),
              event.getSource(), 5).sendRequest()
    }

    reRegister(event) {
        const response = event.getResponse()
        const clientTransaction = event.getClientTransaction()
        const viaHeader = response.getHeader(ViaHeader.NAME)

        LOG.debug('Sip I/O is behind a NAT. Re-registering using Received and RPort')

        try {
            const fromURI = response.getHeader(FromHeader.NAME).getAddress().getURI()
            const gwRef = clientTransaction.getRequest().getHeader('X-Gateway-Ref').value
            this.registry.requestChallenge(fromURI.getUser(),
                gwRef,
                fromURI.getHost(),
                viaHeader.getTransport().toLowerCase(),
                viaHeader.getReceived(),
                viaHeader.getRPort())
        } catch(e) {
            LOG.error(e)
        }
    }

    sendResponse(event) {
        const responseOut = event.getResponse().clone()

        // Strip the topmost via header
        responseOut.removeFirst(ViaHeader.NAME)

        if (ResponseProcessor.isInviteWithCT(event)) {
            // In theory we should be able to obtain the ServerTransaction casting the ApplicationData.
            // However, I'm unable to find the way to cast this object.
            //let st = clientTransaction.getApplicationData()'
            const context = this.contextStorage.findContext(event.getClientTransaction())

            if (context != null && context.serverTransaction != null) {
                context.serverTransaction.sendResponse(responseOut)
            } else if (responseOut.getHeader(ViaHeader.NAME) != null) {
                this.sipProvider.sendResponse(responseOut)
            }
        } else if(responseOut.getHeader(ViaHeader.NAME) != null) {
            // Could be a BYE due to Record-Route
            // There is no more Via headers; the response was intended for the proxy.
            this.sipProvider.sendResponse(responseOut)
        }
        LOG.debug(responseOut)
    }
}
<|endoftext|>


<commit_before>/*
options = {
    onPlayerConnect: function()
    onPlayerDisconnect: function (),
    onPlayerReconnect: function (),
    onPlayerUpdate: function (state),
    onPlayerRemove: function (id),
    onGameState: function (state),
    onPing: function (pingDelay),
    onUpdatePlayerPhysics: function (id, state, inputs, deltaTime),
    onUpdate: function (),
    onInterpolation: function(id, previousState, targetState, amount)
    logging: true,
    clientSidePrediction: true,
    interpolation: true,
    interpolationDelay: 100,
    pingInterval: 2000
}
*/

window.GarageServerIO = (function (window, socketio) {

    function StateController() {
        this.state = {};
        this.clientTime;
        this.renderTime;
        this.physicsDelta;
        this.physicsIntervalId;
        this.currentTime;
        this.accumulator = 0.0;
        this.playerId;
        this.pingDelay = 100;
        this.interpolationDelay = 100;
    }
    StateController.prototype = {
        setTime: function (serverTime) {
            this.clientTime = serverTime;
            this.renderTime = this.clientTime - this.interpolationDelay;
        },
        accumulate: function () {
            var newTime = new Date().getTime(),
                frameTime = newTime - this.currentTime;
            if (frameTime  > 250) {
                frameTime = 250;
            }
            this.currentTime = newTime;
            this.accumulator += frameTime;
        }
    };

    function Input(input, seq) {
        this.input = input;
        this.seq = seq;
    }

    function InputController() {
        this.inputs = [];
        this.sequenceNumber = 1;
    }
    InputController.prototype = {
        any: function () {
            return this.inputs.length > 0;
        },
        addInput: function (input) {
            this.sequenceNumber += 1;
            this.inputs.push(new Input(input, this.sequenceNumber));
        },
        removeUpToSequence: function (seq) {
            for (var i = 0; i < this.inputs.length; i ++) {
                if (this.inputs[i].seq === seq) {
                    this.inputs.splice(0, i + 1);
                    break;
                }
            }
        }
    };

    function Update(state, seq, time) {
        this.state = state;
        this.seq = seq;
        this.time = time;
    }

    function Player(id) {
        this.updates = [];
        this.id = id;
        this.currentState = {};
    }
    Player.prototype = {
        anyUpdates: function () {
            return this.updates.length > 0;
        },
        addUpate: function (state, seq, time) {
            var newUpdate = new Update(state, seq, time);
            if (this.updates.length === 0) {
                this.currentState = newUpdate.state;
            }
            this.updates.push(newUpdate);
            if (this.updates.length > 120) {
                this.updates.splice(0, 1);
            }
        },
        getLatestUpdate: function () {
            return this.updates[this.updates.length - 1];
        },
        processState: function (state, seq, time) {
            var updateFound = false;
            this.updates.some(function (update) {
               if (update.seq === seq) {
                   update.state = state;
                   updateFound = true;
                   return true;
               }
            });
            if (!updateFound) {
                this.addUpate(state, seq, time);
            }
        },
        getSurroundingPositions: function (time) {
            var positions = {};
            for (var i = 0; i < this.updates.length; i ++) {
                var previous = this.updates[i];
                var target = this.updates[i + 1];

                if(previous && target && time > previous.time && time < target.time) {
                    positions.previous = previous;
                    positions.target = target;
                    break;
                }
            }
            return positions;
        }
    };

    function PlayerController() {
        this.players = [];
    }
    PlayerController.prototype = {
        addPlayer: function (id) {
            var player = new Player(id);
            this.players.push(player);
            return player;
        },
        removePlayer: function (id) {
            for (var i = 0; i < this.players.length; i ++) {
                this.players.splice(i, 1);
                return;
            }
        }
    };

    var _io = socketio,
        _socket = null,
        _options = null,
        _stateController = new StateController(),
        _inputController = new InputController(),
        _playerController = new PlayerController(),

        initializeGarageServer = function (path, opts) {
            _options = opts;
            _socket = _io.connect(path + '/garageserver.io');
            registerSocketEvents();
            registerPinger();
        },

        registerSocketEvents = function () {
            _socket.on('connect', function () {
                _stateController.playerId = _socket.id;
                _stateController.interpolationDelay = _options.interpolationDelay ? _options.interpolationDelay : 100;
                if (_options.onPlayerConnect) {
                    _options.onPlayerConnect();
                }
                if (_options.logging) {
                    console.log('garageserver.io:: socket connect');
                }
            });
            _socket.on('state', function(data) {
                if (_options.onGameState) {
                    _options.onGameState(data);
                }
                _stateController.physicsDelta = data.physicsDelta;
            });
            _socket.on('disconnect', function () {
                if (_options.onPlayerDisconnect) {
                    _options.onPlayerDisconnect();
                }
                if (_options.logging) {
                    console.log('garageserver.io:: socket disconnect');
                }
            });
            _socket.on('reconnect', function () {
                if (_options.onPlayerReconnect) {
                    _options.onPlayerReconnect();
                }
                if (_options.logging) {
                    console.log('garageserver.io:: socket reconnect');
                }
            });
            _socket.on('update', function(data) {
                updateState(data);
            });
            _socket.on('ping', function(data) {
                _stateController.pingDelay = new Date().getTime() - data;
                if (_options.onPing) {
                    _options.onPing(_stateController.pingDelay);
                }
                if (_options.logging) {
                    console.log('garageserver.io:: socket ping delay ' + _stateController.pingDelay);
                }
            });
            _socket.on('removePlayer', function(id) {
                removePlayer(id);
                if (_options.onPlayerRemove) {
                    _options.onPlayerRemove(id);
                }
                if (_options.logging) {
                    console.log('garageserver.io:: socket removePlayer ' + id);
                }
            });
        },

        registerPinger = function () {
            var interval = 2000;
            if (_options.pingInterval) {
                interval = _options.pingInterval;
            }
            setInterval(function (){
                _socket.emit('ping', new Date().getTime());
            }, interval);
        },

        start = function () {
            var self = this;
            _stateController.currentTime = new Date().getTime();
            _stateController.physicsIntervalId = setInterval(function () { self.update(); }, this.physicsDelta * 1000);
        },

        getPlayerId = function () {
            return _stateController.playerId;
        },

        setPlayerState = function (state) {
            _socket.emit('playerState', state);
        },

        removePlayer = function (id) {
            _playerController.removePlayer(id);
        },

        update = function () {
            if (_options.onUpdate) {
                _stateController.accumulate();
                while (_stateController.accumulator >= (_stateController.physicsDelta * 1000))
                {
                    _options.onUpdate();
                    _stateController.accumulator -= (_stateController.physicsDelta * 1000);
                }
            }
        },

        addPlayerInput = function (clientInput) {
            _inputController.addInput(clientInput);
            if (_options.clientSidePrediction && _options.onUpdatePlayerPhysics) {
                _stateController.state = _options.onUpdatePlayerPhysics(_stateController.playerId, _stateController.state, [{ input: clientInput }], _stateController.physicsDelta);
            }
            _socket.emit('input', [ clientInput, _inputController.sequenceNumber, _stateController.renderTime ]);
        },

        updateState = function (data) {
            _stateController.setTime(data.time);

            updatePlayersState(data);
            updateEntitiesState(data);
        },

        updatePlayersState = function (data) {
            data.playerStates.forEach(function (playerState) {
                if (_socket.socket.sessionid === playerState[0]) {
                    updatePlayerState(playerState);
                } else {
                    updateOtherPlayersState(playerState, data.time);
                }

                if (_options.onPlayerUpdate) {
                    _options.onPlayerUpdate(playerState[1]);
                }
            });
        },

        updatePlayerState = function (playerState) {
            _stateController.state = playerState[1];
            _inputController.removeUpToSequence(playerState[2]);

            if (_options.clientSidePrediction && _inputController.any()) {
                _stateController.state = _options.onUpdatePlayerPhysics(_stateController.playerId, _stateController.state, _inputController.inputs, _stateController.physicsDelta);
            }
        },

        updateOtherPlayersState = function (playerState, time) {
            var playerFound = false;
            _playerController.players.some(function (player) {
                if (player.id === playerState[0]) {
                    playerFound = true;
                    player.processState(playerState[1], playerState[2], time);
                    return true;
                }
            });
            if (!playerFound) {
                var newPlayer = _playerController.addPlayer(playerState[0]);
                newPlayer.addUpate(playerState[1], playerState[2], time);
            }
        },

        updateEntitiesState = function (data) {

        },

        getPlayerStates = function (stateCallback) {
            if(_options.interpolation && _options.onInterpolation) {
                getPlayerStatesInterpolated(stateCallback);
            }
            else {
                getPlayerStatesCurrent(stateCallback);
            }
            stateCallback(_stateController.state);
        },

        getPlayerStatesCurrent = function (stateCallback) {
            _playerController.players.forEach(function (player) {
                if (player.anyUpdates()) {
                    stateCallback(player.getLatestUpdate().state);
                }
            });
        },

        getPlayerStatesInterpolated = function (stateCallback) {
            var positions, amount, newState;
            _playerController.players.forEach(function (player) {
                if (player.anyUpdates()) {
                    positions = player.getSurroundingPositions(_stateController.renderTime);
                    if (positions.previous && positions.target) {
                        amount = getInterpolatedAmount(positions.previous.time, positions.target.time);
                        newState = _options.onInterpolation(player.id, positions.previous.state, positions.target.state, amount);
                        player.currentState = newState = _options.onInterpolation(player.id, player.currentState, newState, _stateController.physicsDelta * 20);
                        stateCallback(player.currentState);
                    }
                    else {
                        stateCallback(player.currentState);
                    }
                }
            });
        },

        getInterpolatedAmount = function (previousTime, targetTime) {
            var range = targetTime - previousTime,
                difference = _stateController.renderTime - previousTime,
                amount = parseFloat((difference / range).toFixed(3));

            return amount;
        };

    return {
        initializeGarageServer: initializeGarageServer,
        start: start,
        update: update,
        addPlayerInput: addPlayerInput,
        getPlayerStates: getPlayerStates,
        getPlayerId: getPlayerId,
        setPlayerState: setPlayerState
    };

}) (window, io);<commit_msg>Progress:  #3<commit_after>/*
options = {
    onPlayerConnect: function()
    onPlayerDisconnect: function (),
    onPlayerReconnect: function (),
    onPlayerUpdate: function (state),
    onPlayerRemove: function (id),
    onGameState: function (state),
    onPing: function (pingDelay),
    onUpdatePlayerPhysics: function (id, state, inputs, deltaTime),
    onUpdate: function (),
    onInterpolation: function(id, previousState, targetState, amount)
    logging: true,
    clientSidePrediction: true,
    interpolation: true,
    interpolationDelay: 100,
    pingInterval: 2000
}
*/

window.GarageServerIO = (function (window, socketio) {

    function StateController() {
        this.state = {};
        this.clientTime;
        this.renderTime;
        this.physicsDelta;
        this.physicsIntervalId;
        this.currentTime;
        this.accumulator = 0.0;
        this.playerId;
        this.pingDelay = 100;
        this.interpolationDelay = 100;
    }
    StateController.prototype = {
        setTime: function (serverTime) {
            this.clientTime = serverTime;
            this.renderTime = this.clientTime - this.interpolationDelay;
        },
        accumulate: function () {
            var newTime = new Date().getTime(),
                frameTime = newTime - this.currentTime;
            if (frameTime  > 250) {
                frameTime = 250;
            }
            this.currentTime = newTime;
            this.accumulator += frameTime;
        }
    };

    function Input(input, seq) {
        this.input = input;
        this.seq = seq;
    }

    function InputController() {
        this.inputs = [];
        this.sequenceNumber = 1;
    }
    InputController.prototype = {
        any: function () {
            return this.inputs.length > 0;
        },
        addInput: function (input) {
            this.sequenceNumber += 1;
            this.inputs.push(new Input(input, this.sequenceNumber));
        },
        removeUpToSequence: function (seq) {
            for (var i = 0; i < this.inputs.length; i ++) {
                if (this.inputs[i].seq === seq) {
                    this.inputs.splice(0, i + 1);
                    break;
                }
            }
        }
    };

    function Update(state, seq, time) {
        this.state = state;
        this.seq = seq;
        this.time = time;
    }

    function Entity(id) {
        this.updates = [];
        this.id = id;
        this.currentState = {};
    }
    Entity.prototype = {
        anyUpdates: function () {
            return this.updates.length > 0;
        },
        addUpate: function (state, seq, time) {
            var newUpdate = new Update(state, seq, time);
            if (this.updates.length === 0) {
                this.currentState = newUpdate.state;
            }
            this.updates.push(newUpdate);
            if (this.updates.length > 120) {
                this.updates.splice(0, 1);
            }
        },
        getLatestUpdate: function () {
            return this.updates[this.updates.length - 1];
        },
        processState: function (state, seq, time) {
            var updateFound = false;
            this.updates.some(function (update) {
               if (update.seq === seq) {
                   update.state = state;
                   updateFound = true;
                   return true;
               }
            });
            if (!updateFound) {
                this.addUpate(state, seq, time);
            }
        },
        getSurroundingPositions: function (time) {
            var positions = {};
            for (var i = 0; i < this.updates.length; i ++) {
                var previous = this.updates[i];
                var target = this.updates[i + 1];

                if(previous && target && time > previous.time && time < target.time) {
                    positions.previous = previous;
                    positions.target = target;
                    break;
                }
            }
            return positions;
        }
    };

    function Player(id) {
        this.updates = [];
        this.id = id;
        this.currentState = {};
    }
    Player.prototype = Object.create(Entity.prototype);

    function PlayerController() {
        this.players = [];
    }
    PlayerController.prototype = {
        addPlayer: function (id) {
            var player = new Player(id);
            this.players.push(player);
            return player;
        },
        removePlayer: function (id) {
            for (var i = 0; i < this.players.length; i ++) {
                this.players.splice(i, 1);
                return;
            }
        }
    };

    var _io = socketio,
        _socket = null,
        _options = null,
        _stateController = new StateController(),
        _inputController = new InputController(),
        _playerController = new PlayerController(),

        initializeGarageServer = function (path, opts) {
            _options = opts;
            _socket = _io.connect(path + '/garageserver.io');
            registerSocketEvents();
            registerPinger();
        },

        registerSocketEvents = function () {
            _socket.on('connect', function () {
                _stateController.playerId = _socket.id;
                _stateController.interpolationDelay = _options.interpolationDelay ? _options.interpolationDelay : 100;
                if (_options.onPlayerConnect) {
                    _options.onPlayerConnect();
                }
                if (_options.logging) {
                    console.log('garageserver.io:: socket connect');
                }
            });
            _socket.on('state', function(data) {
                if (_options.onGameState) {
                    _options.onGameState(data);
                }
                _stateController.physicsDelta = data.physicsDelta;
            });
            _socket.on('disconnect', function () {
                if (_options.onPlayerDisconnect) {
                    _options.onPlayerDisconnect();
                }
                if (_options.logging) {
                    console.log('garageserver.io:: socket disconnect');
                }
            });
            _socket.on('reconnect', function () {
                if (_options.onPlayerReconnect) {
                    _options.onPlayerReconnect();
                }
                if (_options.logging) {
                    console.log('garageserver.io:: socket reconnect');
                }
            });
            _socket.on('update', function(data) {
                updateState(data);
            });
            _socket.on('ping', function(data) {
                _stateController.pingDelay = new Date().getTime() - data;
                if (_options.onPing) {
                    _options.onPing(_stateController.pingDelay);
                }
                if (_options.logging) {
                    console.log('garageserver.io:: socket ping delay ' + _stateController.pingDelay);
                }
            });
            _socket.on('removePlayer', function(id) {
                removePlayer(id);
                if (_options.onPlayerRemove) {
                    _options.onPlayerRemove(id);
                }
                if (_options.logging) {
                    console.log('garageserver.io:: socket removePlayer ' + id);
                }
            });
        },

        registerPinger = function () {
            var interval = 2000;
            if (_options.pingInterval) {
                interval = _options.pingInterval;
            }
            setInterval(function (){
                _socket.emit('ping', new Date().getTime());
            }, interval);
        },

        start = function () {
            var self = this;
            _stateController.currentTime = new Date().getTime();
            _stateController.physicsIntervalId = setInterval(function () { self.update(); }, this.physicsDelta * 1000);
        },

        getPlayerId = function () {
            return _stateController.playerId;
        },

        setPlayerState = function (state) {
            _socket.emit('playerState', state);
        },

        removePlayer = function (id) {
            _playerController.removePlayer(id);
        },

        update = function () {
            if (_options.onUpdate) {
                _stateController.accumulate();
                while (_stateController.accumulator >= (_stateController.physicsDelta * 1000))
                {
                    _options.onUpdate();
                    _stateController.accumulator -= (_stateController.physicsDelta * 1000);
                }
            }
        },

        addPlayerInput = function (clientInput) {
            _inputController.addInput(clientInput);
            if (_options.clientSidePrediction && _options.onUpdatePlayerPhysics) {
                _stateController.state = _options.onUpdatePlayerPhysics(_stateController.playerId, _stateController.state, [{ input: clientInput }], _stateController.physicsDelta);
            }
            _socket.emit('input', [ clientInput, _inputController.sequenceNumber, _stateController.renderTime ]);
        },

        updateState = function (data) {
            _stateController.setTime(data.time);

            updatePlayersState(data);
            updateEntitiesState(data);
        },

        updatePlayersState = function (data) {
            data.playerStates.forEach(function (playerState) {
                if (_socket.socket.sessionid === playerState[0]) {
                    updatePlayerState(playerState);
                } else {
                    updateOtherPlayersState(playerState, data.time);
                }

                if (_options.onPlayerUpdate) {
                    _options.onPlayerUpdate(playerState[1]);
                }
            });
        },

        updatePlayerState = function (playerState) {
            _stateController.state = playerState[1];
            _inputController.removeUpToSequence(playerState[2]);

            if (_options.clientSidePrediction && _inputController.any()) {
                _stateController.state = _options.onUpdatePlayerPhysics(_stateController.playerId, _stateController.state, _inputController.inputs, _stateController.physicsDelta);
            }
        },

        updateOtherPlayersState = function (playerState, time) {
            var playerFound = false;
            _playerController.players.some(function (player) {
                if (player.id === playerState[0]) {
                    playerFound = true;
                    player.processState(playerState[1], playerState[2], time);
                    return true;
                }
            });
            if (!playerFound) {
                var newPlayer = _playerController.addPlayer(playerState[0]);
                newPlayer.addUpate(playerState[1], playerState[2], time);
            }
        },

        updateEntitiesState = function (data) {

        },

        getPlayerStates = function (stateCallback) {
            if(_options.interpolation && _options.onInterpolation) {
                getPlayerStatesInterpolated(stateCallback);
            }
            else {
                getPlayerStatesCurrent(stateCallback);
            }
            stateCallback(_stateController.state);
        },

        getPlayerStatesCurrent = function (stateCallback) {
            _playerController.players.forEach(function (player) {
                if (player.anyUpdates()) {
                    stateCallback(player.getLatestUpdate().state);
                }
            });
        },

        getPlayerStatesInterpolated = function (stateCallback) {
            var positions, amount, newState;
            _playerController.players.forEach(function (player) {
                if (player.anyUpdates()) {
                    positions = player.getSurroundingPositions(_stateController.renderTime);
                    if (positions.previous && positions.target) {
                        amount = getInterpolatedAmount(positions.previous.time, positions.target.time);
                        newState = _options.onInterpolation(player.id, positions.previous.state, positions.target.state, amount);
                        player.currentState = newState = _options.onInterpolation(player.id, player.currentState, newState, _stateController.physicsDelta * 20);
                        stateCallback(player.currentState);
                    }
                    else {
                        stateCallback(player.currentState);
                    }
                }
            });
        },

        getInterpolatedAmount = function (previousTime, targetTime) {
            var range = targetTime - previousTime,
                difference = _stateController.renderTime - previousTime,
                amount = parseFloat((difference / range).toFixed(3));

            return amount;
        };

    return {
        initializeGarageServer: initializeGarageServer,
        start: start,
        update: update,
        addPlayerInput: addPlayerInput,
        getPlayerStates: getPlayerStates,
        getPlayerId: getPlayerId,
        setPlayerState: setPlayerState
    };

}) (window, io);<|endoftext|>


<commit_before>/* global d3 */
/* exported Calendar */

function Calendar(div) {
  "use strict";
  var calendar = {};

  var colorMap = d3.scale.ordinal()
      .range(['#2ca02c', '#ffbb78', '#ff9896', '#c5b0d5', '#e377c2',
        '#f7b6d2', '#7f7f7f', '#c7c7c7 ', '#bcbd22', '#cbcb8d', '#17be6f',
        '#9edae5', '#393b79', '#5254a3', '#6b6ecf', '#9c9ede', '#637939',
        '#8ca252', '#b5cf6b', '#cedb9c', '#8c6d31', '#bd9e39', '#e7ba52',
        '#e7cb94', '#843c39', '#ad494a', '#d6616b', '#e7969c', '#7b4173',
        '#a55194', '#ce6dbd', '#de9ed6', '#3182bd', '#6baed6', '#9ecae1',
        '#c6dbef', '#e6550d', '#fd8d3c', '#fdae6b', '#fdd0a2', '#31a354',
        '#74c476', '#a1d99b', '#c7e9c0', '#756bb1', '#9e9ac8', '#bcbddc',
        '#dadaeb', '#636363', '#969696', '#bdbdbd', '#d9d9d9', 'darkblue',
        'darkgreen', 'crimson', 'darkmagenta', 'darkorange', 'darkorchid',
        'darkturquoise', 'darkviolet']);

  function color(place) {
    switch (place) {
      case 'Unknown':
        return '#eee';
      case 'Washington, DC, USA':
        return '#98df8a';
      case 'Richmond, VA, USA':
        return '#aec7e8';
      case 'Chevy Chase, MD, USA':
        return '#ff7f0e';
      case 'Oakton, VA, USA':
        return '#1fb7b4';
      case 'Chevy Chase Village, MD, USA':
        return 'gold';
      case 'Howard, MD, USA':
        return '#9467bd';
      case 'Vienna, VA, USA':
        return '#C76';
      case 'Corolla, NC, USA':
        return '#c49c94';
      case 'College Park, MD, USA':
        return 'lemonchiffon';
      default:
        return colorMap(place);
    }
  }

  var day = d3.time.format('%w'),
      week = d3.time.format('%U'),
      year = d3.time.format('%Y'),
      format = d3.time.format('%Y-%m-%d');
  var width = 960,
      height = 136,
      cellSize = 15;

  calendar.render = function(data, params, cb) {

    // ensure div is not hidden
    div.style('display', 'block');

    var year_range = d3.extent(data, function(d) {return +year(d.day);});

    var svg = div.selectAll('svg')
        .data(d3.range(year_range[0],year_range[1] + 1), function(d) {return d;})
      .enter().insert('svg', ':first-child')
        .attr('width', width)
        .attr('height', height)
        .attr('class', 'year')
      .append('g')
        .attr('transform', 'translate(' + ((width - cellSize * 53) / 2) + ',' + (height - cellSize * 7 - 1) + ')');

    svg.append('text')
      .attr('transform', 'translate(-28,' + cellSize * 3.5 + ')rotate(-90)')
      .style('text-anchor', 'middle')
      .text(function(d) {return d;});

    svg.append('g')
        .attr('transform', 'translate(-12,' + cellSize * 0.75 + ')')
        .attr('fill', '#666')
      .selectAll('text').data('Su,M,T,W,Th,F,S'.split(',')).enter().append('text')
        .attr('transform', function(d,i) {return 'translate(0,' + cellSize * i + ')';})
        .style('text-anchor', 'middle')
        .style('font-size', '10pt')
        .text(function(d) {return d;});

    var rect = svg.selectAll('.day')
        .data(function(d) { return d3.time.days(new Date(d, 0, 1), new Date(d + 1, 0, 1)); })
      .enter().append('rect')
        .attr('class', 'day')
        .attr('width', cellSize)
        .attr('height', cellSize)
        .attr('x', function(d) {return week(d) * cellSize; })
        .attr('y', function(d) {return day(d) * cellSize; })
        .attr('fill', 'white')
        .datum(format);

    rect.append('title')
        .text(function(d) {return d;});

    svg.selectAll('.month')
        .data(function(d) {return d3.time.months(new Date(d, 0, 1), new Date(d + 1, 0, 1)); })
      .enter().append('path')
        .attr('class', 'month')
        .attr('d', monthPath);

    var data_lookup = d3.nest()
      .key(function(d) {return format(d.day);})
      .rollup(function(d) {return d[0];})
      .map(data);

    d3.selectAll('.day')
        .filter(function(d) {return d in data_lookup;})
        .attr('fill', function(d) {return color(data_lookup[d].location.n);})
      .select('title')
        .text(function(d) {return d + ' - ' + data_lookup[d].location.n;});

    function monthPath(t0) {
      var t1 = new Date(t0.getFullYear(), t0.getMonth() + 1, 0),
          d0 = +day(t0), w0 = +week(t0),
          d1 = +day(t1), w1 = +week(t1);
      return ("M" + (w0 + 1) * cellSize + "," + d0 * cellSize +
              "H" + w0 * cellSize + "V" + 7 * cellSize +
              "H" + w1 * cellSize + "V" + (d1 + 1) * cellSize +
              "H" + (w1 + 1) * cellSize + "V" + 0 +
              "H" + (w0 + 1) * cellSize + "Z");
    }

    cb();
  };

  return calendar;
}

<commit_msg>Updated color map<commit_after>/* global d3 */
/* exported Calendar */

function Calendar(div) {
  "use strict";
  var calendar = {};

  var colorList = [
    // (C: 0.4-1.3, L: 1-1.5)
    "#8CCCED", "#F89A5F", "#43F8A3", "#FDFD96", "#E1BE32", "#B8D5A0",
    "#8DCE54", "#D2DDF5", "#F7BD94", "#A0F2D5", "#F99AB7",
    // Higher chroma to highlight trips
    "#35DA74", "#EE3FF2", "#F1441A", "#3E8DCE", "#E3E01B", "#EE3A84",
    "#A7720E", "#456CF3", "#B562B6", "#38F42D", "#74A220", "#F5B412",
    "#DE5B4C", "#A4F33D", "#2C8F3C", "#F223AA", "#B776F0", "#E97E21",
    "#ED2D58", "#48B221", "#55E655", "#7B80CD", "#C55A98", "#BE9A0D",
    "#488EEE", "#BD8CE8", "#D140B6", "#EE3F3C", "#767EEB", "#A4B419",
    "#7F8B18", "#E779DE", "#DD4C71", "#C559DE", "#C1E222", "#E1572A",
    "#8ED031", "#956EBF", "#469323", "#DA8D20", "#CE49A3", "#CC6324",
    "#E2578F", "#3FAC43", "#E640D0", "#E971C4", "#E34C5D", "#55C94C",
    "#A45ECC", "#50E026", "#CC54F0", "#FA1B8C", "#F02971", "#DE79ED",
    "#E948AA", "#EB3C93", "#986AEC", "#6C65E9", "#83E529", "#D03F7F"
 ];

  var color = d3.scale.ordinal().range(colorList);

  function primeColorPalette(locationNames) {
    var counts = {};
    locationNames.forEach(function(n) {
      counts[n] = counts[n] + 1 || 1;
    });

    var sortable = [];
    for (var n in counts) {
      sortable.push([n, counts[n]]);
    }

    sortable.sort(function(a, b) {return b[1] - a[1];});

    sortable.forEach(function(s) {
      color(s[0]);
    });
  }

  var day = d3.time.format('%w'),
      week = d3.time.format('%U'),
      year = d3.time.format('%Y'),
      format = d3.time.format('%Y-%m-%d');
  var width = 960,
      height = 136,
      cellSize = 15;

  calendar.render = function(data, params, cb) {

    // ensure div is not hidden
    div.style('display', 'block');

    var year_range = d3.extent(data, function(d) {return +year(d.day);});

    var svg = div.selectAll('svg')
        .data(d3.range(year_range[0],year_range[1] + 1), function(d) {return d;})
      .enter().insert('svg', ':first-child')
        .attr('width', width)
        .attr('height', height)
        .attr('class', 'year')
      .append('g')
        .attr('transform', 'translate(' + ((width - cellSize * 53) / 2) + ',' + (height - cellSize * 7 - 1) + ')');

    svg.append('text')
      .attr('transform', 'translate(-28,' + cellSize * 3.5 + ')rotate(-90)')
      .style('text-anchor', 'middle')
      .text(function(d) {return d;});

    svg.append('g')
        .attr('transform', 'translate(-12,' + cellSize * 0.75 + ')')
        .attr('fill', '#666')
      .selectAll('text').data('Su,M,T,W,Th,F,S'.split(',')).enter().append('text')
        .attr('transform', function(d,i) {return 'translate(0,' + cellSize * i + ')';})
        .style('text-anchor', 'middle')
        .style('font-size', '10pt')
        .text(function(d) {return d;});

    var rect = svg.selectAll('.day')
        .data(function(d) { return d3.time.days(new Date(d, 0, 1), new Date(d + 1, 0, 1)); })
      .enter().append('rect')
        .attr('class', 'day')
        .attr('width', cellSize)
        .attr('height', cellSize)
        .attr('x', function(d) {return week(d) * cellSize; })
        .attr('y', function(d) {return day(d) * cellSize; })
        .attr('fill', '#f3f3f3')
        .datum(format);

    rect.append('title')
        .text(function(d) {return d;});

    svg.selectAll('.month')
        .data(function(d) {return d3.time.months(new Date(d, 0, 1), new Date(d + 1, 0, 1)); })
      .enter().append('path')
        .attr('class', 'month')
        .attr('d', monthPath);

    var data_lookup = d3.nest()
      .key(function(d) {return format(d.day);})
      .rollup(function(d) {return d[0];})
      .map(data);

    primeColorPalette(data.map(function(d) {return d.location.n;}));

    d3.selectAll('.day')
        .filter(function(d) {return d in data_lookup;})
        .attr('fill', function(d) {return color(data_lookup[d].location.n);})
      .select('title')
        .text(function(d) {return d + ' - ' + data_lookup[d].location.n;});

    function monthPath(t0) {
      var t1 = new Date(t0.getFullYear(), t0.getMonth() + 1, 0),
          d0 = +day(t0), w0 = +week(t0),
          d1 = +day(t1), w1 = +week(t1);
      return ("M" + (w0 + 1) * cellSize + "," + d0 * cellSize +
              "H" + w0 * cellSize + "V" + 7 * cellSize +
              "H" + w1 * cellSize + "V" + (d1 + 1) * cellSize +
              "H" + (w1 + 1) * cellSize + "V" + 0 +
              "H" + (w0 + 1) * cellSize + "Z");
    }

    cb();
  };

  return calendar;
}

<|endoftext|>