<commit_before>var colors = {
    0: "lightgray",
    1: "#edc240",
    2: "#afd8f8",
    3: "#cb4b4b",
    4: "#4da74d",
    5: "#9440ed",
    6: "rgb(189, 155, 51)",
    7: "rgb(140,172,198)"
};

var target = $('#target');
$(document)
    .ajaxStart(function () {
        $('html, body').css('cursor', 'wait');
        target.overlay();
    })
    .ajaxStop(function () {
        $('html, body').css('cursor', 'auto');
        target.overlayout();
    });

$(function () {
    initLegend();

    // Load the last 14 days
    getFortnight(function (rawdata) {
        render(rawdata)
    });
});

function initLegend() {
    $('#detail-legend').append('

');
};

function render(rawdata) {

    if (!rawdata) {
        // TODO Show error message
        console.log('Sorry, no data found.');
        return;
    }

    var data = convertDataForFlot(rawdata);

    // Legend -------------------------------------------------------------
    var legendContainer = $('#detail-legend');
    var legendHasNoData = legendContainer.find('.table-row-data').length === 0;
    if (legendHasNoData) {
        $.each(data, function (key, val) {
            var tablerow = '
' +
                '
<input type="checkbox" name="' + key + '" checked="checked" id="id' + key + '"></input>
' +
                '

' +
                '
' + val.label + '
' +
                '

' +
                '
';
            legendContainer.find('.div-table').append(tablerow);
        });
    }
    legendContainer.find("input").click(updatePlot);

    var currentSelection = {
        min: undefined,
        max: undefined
    };

    // Main plotting function ---------------------------------------------
    function updatePlot() {

        var plotdata = [];

        legendContainer.find('input:checked').each(function () {
            var key = $(this).attr('name');
            if (key && data[key]) {
                plotdata.push(data[key])
            }
        });

        var rangeselectionCallback = function (o) {
            console.log('rangeselectionCallback invoked...');
            var xaxis = detailPlot.getAxes().xaxis;

            if (o.start <= xaxis.datamin) {
                loadAnotherFortnight(xaxis.datamin, xaxis.datamax);
            } else {
                xaxis.options.min = o.start;
                xaxis.options.max = o.end;
                detailPlot.setupGrid();
                detailPlot.draw();

                currentSelection.min = o.start;
                currentSelection.max = o.end;
            }
        };

        var detailPlaceholder = $('#detail-placeholder');
        var detailPlot = $.plot(detailPlaceholder, plotdata, {
            xaxis: {
                mode: 'time'
            },
            yaxis: {
                panRange: false
            },
            series: {
                lines: {
                    show: true
                },
                points: {
                    show: false
                }
            },
            legend: {
                show: false // Note: The legend is configured manually (see legendContainer)
            },
            grid: {
                hoverable: true,
                clickable: true,
                autoHighlight: false,
                borderWidth: 0,
                markings: nightAreas
            },
            crosshair: {
                mode: 'x'
            },
            pan: {
                interactive: true
            }
        });

        var overviewPlaceholder = $('#overview-placeholder');
        var overviewData = $.extend(true, [], plotdata);
        for (var i = 0; i < overviewData.length; i++) {
            overviewData[i].color = '#ccc';
            overviewData[i].label = undefined;
        }

        function initializeOverview(plot) {
            var rangeselectionEnd = plot.getData()[0].data[plot.getData()[0].data.length - 1][0];
            var endDate = new Date(rangeselectionEnd);
            endDate.setDate(endDate.getDate() - 1); // endDate = endDate - 1day
            var rangeselectionInitialStart = endDate.getTime();
            plot.getAxes().xaxis.options.min = rangeselectionInitialStart;
            plot.setupGrid();
            plot.draw();

            var initialOverview = {
                start: rangeselectionInitialStart,
                end: rangeselectionEnd
            };
            return initialOverview;
        }
        var overview = initializeOverview(detailPlot);

        var overviewPlot = $.plot(overviewPlaceholder, overviewData, {
            series: {
                lines: {
                    show: true,
                    lineWidth: 1
                },
                shadowSize: 0,
                points: {
                    show: false
                }
            },
            xaxis: {
                mode: "time"
            },
            yaxis: {
                labelWidth: 16,
                reserveSpace: true,
                show: false
            },
            legend: {
                show: false
            },
            grid: {
                hoverable: false,
                borderWidth: {
                    'top': 1,
                    'right': 1,
                    'left': 1,
                    'bottom': 1
                },
                borderColor: 'darkgray',
                markings: weekendAreas
            },
            rangeselection: {
                color: '#999',
                enabled: true,
                start: overview.start,
                end: overview.end,
                callback: rangeselectionCallback
            }
        });

        // Since the canvas is redrawn after every interaction, we have to manually set the selection range (if it exists)
        if (currentSelection.min !== undefined && currentSelection.max !== undefined) {
            var xaxis = detailPlot.getAxes().xaxis;
            xaxis.options.min = currentSelection.min;
            xaxis.options.max = currentSelection.max;
            detailPlot.setupGrid();
            detailPlot.draw();

            overviewPlot.getOptions().rangeselection.start = currentSelection.min;
            overviewPlot.getOptions().rangeselection.end = currentSelection.max;
            overviewPlot.setupGrid();
            overviewPlot.draw();
        }

        // PLOTHOVER: UPDATE VALUE IN LEGEND
        detailPlaceholder.bind('plothover', function (event, pos, item) {
            var updateDetailSelectionTimeout = null;
            var latestPosition = null;

            function updateDetailSelection() {
                updateDetailSelectionTimeout = null;
                var pos = latestPosition;
                var axes = detailPlot.getAxes();
                if (pos.x < axes.xaxis.min || pos.x > axes.xaxis.max ||
                    pos.y < axes.yaxis.min || pos.y > axes.yaxis.max) {
                    //tooltipContainer.hide('slow');
                    return;
                }

                var seriesIndex, dataPointInSeries, dataset = plotdata;

                // Iterate of each time series ('seriesIndex')
                var yMax;
                for (seriesIndex = 0; seriesIndex < dataset.length; ++seriesIndex) {
                    var series = dataset[seriesIndex];

                    // Find the nearest points, x-wise
                    for (dataPointInSeries = 0; series.data.length; ++dataPointInSeries) {
                        if (series.data[dataPointInSeries][0] > pos.x) {
                            break;
                        }
                    }

                    // Now interpolate
                    var y,
                        p1 = series.data[dataPointInSeries - 1],
                        p2 = series.data[dataPointInSeries];

                    if (p1 == null) {
                        y = p2[1];
                    } else if (p2 == null) {
                        y = p1[1];
                    } else {
                        y = p1[1] + (p2[1] - p1[1]) * (pos.x - p1[0]) / (p2[0] - p1[0]);
                    }

                    // Set the correct value for the current sensor
                    if ($.isNumeric(y)) {
                        var temperatureValueContainer = legendContainer.find('#temperature-value' + series.id);
                        if (temperatureValueContainer) {
                            temperatureValueContainer.text(y.toFixed(1));
                        }
                    }

                    if (y > yMax) {
                        yMax = y;
                    }
                }

                // Current time
                var m = moment(pos.x);
                m.add(-2, 'hours'); // fucking time stuff
                $('#detail-selection-header').text(m.format('YYYY-MM-DD HH:mm (ddd)'));
            };

            latestPosition = pos;
            if (!updateDetailSelectionTimeout) {
                updateDetailSelectionTimeout = setTimeout(updateDetailSelection, 400);
            }
        });

        // PLOTPAN: UPDATE OVERVIEW PLOT
        detailPlaceholder.bind('plotpan', function (event, plot) {
            var currentMin = plot.getXAxes()[0].min;
            var dataMin = plot.getXAxes()[0].datamin;
            //            console.log('-------')
            //            console.log(new Date(currentMin));
            //            console.log(new Date(dataMin));
            if (currentMin <= dataMin) {
                loadAnotherFortnight(dataMin, overviewPlot.getAxes().xaxis.datamax);
                return;
            } else {
                function clamp(min, value, max) {
                    return value < min ? min : (value > max ? max : value);
                };

                function clampOverViewPlot(value) {
                    overviewMin = overviewPlot.getAxes().xaxis.min;
                    overviewMax = overviewPlot.getAxes().xaxis.max;
                    return clamp(overviewMin, value, overviewMax);
                };

                currentSelection.min = clampOverViewPlot(detailPlot.getAxes().xaxis.min);
                currentSelection.max = clampOverViewPlot(detailPlot.getAxes().xaxis.max);
                overviewPlot.setSelection(currentSelection.min, currentSelection.max);
            }
        });
    };

    updatePlot();

};

/* From flot demo: http://www.flotcharts.org/flot/examples/visitors/index.html */
function weekendAreas(axes) {
    var markings = [],
        d = new Date(axes.xaxis.min);

    // go to the first Saturday
    d.setUTCDate(d.getUTCDate() - ((d.getUTCDay() + 1) % 7))
    d.setUTCSeconds(0);
    d.setUTCMinutes(0);
    d.setUTCHours(0);

    var i = d.getTime();

    do {
        markings.push({
            xaxis: {
                from: i,
                to: i + 2 * 24 * 60 * 60 * 1000
            },
            color: '#FBF8EF'
        });
        i += 7 * 24 * 60 * 60 * 1000;
    } while (i < axes.xaxis.max);

    return markings;
};

function nightAreas(axes) {
    var lat = 49.477246;
    var lng = 10.989707;
    var markings = [];

    var day = 24 * 60 * 60 * 1000; // ms
    for (var i = 0; true; i++) {
        var entry = axes.xaxis.min + (i * day);
        var t = SunCalc.getTimes(entry, lat, lng);
        markings.push({
            xaxis: {
                from: (t.sunrise.getTime() + (1000 * 60 * 60 * 2)) <= axes.xaxis.min ? axes.xaxis.min : (t.sunrise.getTime() + (1000 * 60 * 60 * 2)),
                to: t.sunset.getTime() >= axes.xaxis.max ? axes.xaxis.max : (t.sunset.getTime() + (1000 * 60 * 60 * 2))
            },
            color: '#ffff99'
        });
        if (entry + day > axes.xaxis.max + (1000 * 60 * 60 * 2)) {
            break;
        }
    }

    return markings;
};

function loadAnotherFortnight(oldMin, max) {
    console.log('Reloading with oldMin value: ' + new Date(oldMin));
    console.log('Reloading with max value: ' + new Date(max));

    getByFilter(oldMin, max, function (rawdata) {
        console.log('call getByFilter is done.');
        render(rawdata);
    });
};

// Prepare data -------------------------------------------------------------------------------------------
var formatDate = function (date) {
    // requires moment.js
    return moment.utc(new Date(date)).format('ddd, YYYY MMM DD HH:mm');
};

var convertDataForFlot = function (data) {
    var flotdata = [];
    flotdata.push(mapTimeSeries(data, 'WA', 'outside', colors[0]));
    flotdata.push(mapTimeSeries(data, 'Z2', '2nd floor', colors[1]));
    flotdata.push(mapTimeSeries(data, 'Z3', '1st floor (work)', colors[2]));
    flotdata.push(mapTimeSeries(data, 'Z4', '1st floor (bathroom)', colors[3]));
    flotdata.push(mapTimeSeries(data, 'Z5', '1st floor (bedroom)', colors[4]));
    flotdata.push(mapTimeSeries(data, 'Z6', 'ground floor', colors[5]));
    return flotdata;
};

var mapTimeSeries = function (data, sensorName, label, col) {
    var series = [];
    for (var i = 0; i < data.length; i++) {
        if (data[i].sensorName === sensorName) {
            var mDate = moment(data[i].timestamp + ' +02:00', 'YYYY-MM-DD HH:mm:ss ZZ');
            mDate.add(2, 'hours'); // still don't understand why this is really necessary.
            var millis = new Date(mDate.unix()) * 1000;
            series.push([millis, data[i].degreeCelsius]);
        }
    }

    // Note: Add an 'id' property (because the label property is a descriptive string)
    return {
        data: series,
        label: label,
        color: col,
        id: sensorName
    };
};
<commit_msg>Changed detailplot highlighting: Now highligths night instead of day.<commit_after>var colors = {
    0: "lightgray",
    1: "#edc240",
    2: "#afd8f8",
    3: "#cb4b4b",
    4: "#4da74d",
    5: "#9440ed",
    6: "rgb(189, 155, 51)",
    7: "rgb(140,172,198)"
};

var target = $('#target');
$(document)
    .ajaxStart(function () {
        $('html, body').css('cursor', 'wait');
        target.overlay();
    })
    .ajaxStop(function () {
        $('html, body').css('cursor', 'auto');
        target.overlayout();
    });

$(function () {
    initLegend();

    // Load the last 14 days
    getFortnight(function (rawdata) {
        render(rawdata)
    });
});

function initLegend() {
    $('#detail-legend').append('

');
};

function render(rawdata) {

    if (!rawdata) {
        // TODO Show error message
        console.log('Sorry, no data found.');
        return;
    }

    var data = convertDataForFlot(rawdata);

    // Legend -------------------------------------------------------------
    var legendContainer = $('#detail-legend');
    var legendHasNoData = legendContainer.find('.table-row-data').length === 0;
    if (legendHasNoData) {
        $.each(data, function (key, val) {
            var tablerow = '
' +
                '
<input type="checkbox" name="' + key + '" checked="checked" id="id' + key + '"></input>
' +
                '

' +
                '
' + val.label + '
' +
                '

' +
                '
';
            legendContainer.find('.div-table').append(tablerow);
        });
    }
    legendContainer.find("input").click(updatePlot);

    var currentSelection = {
        min: undefined,
        max: undefined
    };

    // Main plotting function ---------------------------------------------
    function updatePlot() {

        var plotdata = [];

        legendContainer.find('input:checked').each(function () {
            var key = $(this).attr('name');
            if (key && data[key]) {
                plotdata.push(data[key])
            }
        });

        var rangeselectionCallback = function (o) {
            console.log('rangeselectionCallback invoked...');
            var xaxis = detailPlot.getAxes().xaxis;

            if (o.start <= xaxis.datamin) {
                loadAnotherFortnight(xaxis.datamin, xaxis.datamax);
            } else {
                xaxis.options.min = o.start;
                xaxis.options.max = o.end;
                detailPlot.setupGrid();
                detailPlot.draw();

                currentSelection.min = o.start;
                currentSelection.max = o.end;
            }
        };

        var detailPlaceholder = $('#detail-placeholder');
        var detailPlot = $.plot(detailPlaceholder, plotdata, {
            xaxis: {
                mode: 'time'
            },
            yaxis: {
                panRange: false
            },
            series: {
                lines: {
                    show: true
                },
                points: {
                    show: false
                }
            },
            legend: {
                show: false // Note: The legend is configured manually (see legendContainer)
            },
            grid: {
                hoverable: true,
                clickable: true,
                autoHighlight: false,
                borderWidth: 0,
                markings: nightAreas
            },
            crosshair: {
                mode: 'x'
            },
            pan: {
                interactive: true
            }
        });

        var overviewPlaceholder = $('#overview-placeholder');
        var overviewData = $.extend(true, [], plotdata);
        for (var i = 0; i < overviewData.length; i++) {
            overviewData[i].color = '#ccc';
            overviewData[i].label = undefined;
        }

        function initializeOverview(plot) {
            var rangeselectionEnd = plot.getData()[0].data[plot.getData()[0].data.length - 1][0];
            var endDate = new Date(rangeselectionEnd);
            endDate.setDate(endDate.getDate() - 1); // endDate = endDate - 1day
            var rangeselectionInitialStart = endDate.getTime();
            plot.getAxes().xaxis.options.min = rangeselectionInitialStart;
            plot.setupGrid();
            plot.draw();

            var initialOverview = {
                start: rangeselectionInitialStart,
                end: rangeselectionEnd
            };
            return initialOverview;
        }
        var overview = initializeOverview(detailPlot);

        var overviewPlot = $.plot(overviewPlaceholder, overviewData, {
            series: {
                lines: {
                    show: true,
                    lineWidth: 1
                },
                shadowSize: 0,
                points: {
                    show: false
                }
            },
            xaxis: {
                mode: "time"
            },
            yaxis: {
                labelWidth: 16,
                reserveSpace: true,
                show: false
            },
            legend: {
                show: false
            },
            grid: {
                hoverable: false,
                borderWidth: {
                    'top': 1,
                    'right': 1,
                    'left': 1,
                    'bottom': 1
                },
                borderColor: 'darkgray',
                markings: weekendAreas
            },
            rangeselection: {
                color: '#999',
                enabled: true,
                start: overview.start,
                end: overview.end,
                callback: rangeselectionCallback
            }
        });

        // Since the canvas is redrawn after every interaction, we have to manually set the selection range (if it exists)
        if (currentSelection.min !== undefined && currentSelection.max !== undefined) {
            var xaxis = detailPlot.getAxes().xaxis;
            xaxis.options.min = currentSelection.min;
            xaxis.options.max = currentSelection.max;
            detailPlot.setupGrid();
            detailPlot.draw();

            overviewPlot.getOptions().rangeselection.start = currentSelection.min;
            overviewPlot.getOptions().rangeselection.end = currentSelection.max;
            overviewPlot.setupGrid();
            overviewPlot.draw();
        }

        // PLOTHOVER: UPDATE VALUE IN LEGEND
        detailPlaceholder.bind('plothover', function (event, pos, item) {
            var updateDetailSelectionTimeout = null;
            var latestPosition = null;

            function updateDetailSelection() {
                updateDetailSelectionTimeout = null;
                var pos = latestPosition;
                var axes = detailPlot.getAxes();
                if (pos.x < axes.xaxis.min || pos.x > axes.xaxis.max ||
                    pos.y < axes.yaxis.min || pos.y > axes.yaxis.max) {
                    //tooltipContainer.hide('slow');
                    return;
                }

                var seriesIndex, dataPointInSeries, dataset = plotdata;

                // Iterate of each time series ('seriesIndex')
                var yMax;
                for (seriesIndex = 0; seriesIndex < dataset.length; ++seriesIndex) {
                    var series = dataset[seriesIndex];

                    // Find the nearest points, x-wise
                    for (dataPointInSeries = 0; series.data.length; ++dataPointInSeries) {
                        if (series.data[dataPointInSeries][0] > pos.x) {
                            break;
                        }
                    }

                    // Now interpolate
                    var y,
                        p1 = series.data[dataPointInSeries - 1],
                        p2 = series.data[dataPointInSeries];

                    if (p1 == null) {
                        y = p2[1];
                    } else if (p2 == null) {
                        y = p1[1];
                    } else {
                        y = p1[1] + (p2[1] - p1[1]) * (pos.x - p1[0]) / (p2[0] - p1[0]);
                    }

                    // Set the correct value for the current sensor
                    if ($.isNumeric(y)) {
                        var temperatureValueContainer = legendContainer.find('#temperature-value' + series.id);
                        if (temperatureValueContainer) {
                            temperatureValueContainer.text(y.toFixed(1));
                        }
                    }

                    if (y > yMax) {
                        yMax = y;
                    }
                }

                // Current time
                var m = moment(pos.x);
                m.add(-2, 'hours'); // fucking time stuff
                $('#detail-selection-header').text(m.format('YYYY-MM-DD HH:mm (ddd)'));
            };

            latestPosition = pos;
            if (!updateDetailSelectionTimeout) {
                updateDetailSelectionTimeout = setTimeout(updateDetailSelection, 400);
            }
        });

        // PLOTPAN: UPDATE OVERVIEW PLOT
        detailPlaceholder.bind('plotpan', function (event, plot) {
            var currentMin = plot.getXAxes()[0].min;
            var dataMin = plot.getXAxes()[0].datamin;
            //            console.log('-------')
            //            console.log(new Date(currentMin));
            //            console.log(new Date(dataMin));
            if (currentMin <= dataMin) {
                loadAnotherFortnight(dataMin, overviewPlot.getAxes().xaxis.datamax);
                return;
            } else {
                function clamp(min, value, max) {
                    return value < min ? min : (value > max ? max : value);
                };

                function clampOverViewPlot(value) {
                    overviewMin = overviewPlot.getAxes().xaxis.min;
                    overviewMax = overviewPlot.getAxes().xaxis.max;
                    return clamp(overviewMin, value, overviewMax);
                };

                currentSelection.min = clampOverViewPlot(detailPlot.getAxes().xaxis.min);
                currentSelection.max = clampOverViewPlot(detailPlot.getAxes().xaxis.max);
                overviewPlot.setSelection(currentSelection.min, currentSelection.max);
            }
        });
    };

    updatePlot();

};

/* From flot demo: http://www.flotcharts.org/flot/examples/visitors/index.html */
function weekendAreas(axes) {
    var markings = [],
        d = new Date(axes.xaxis.min);

    // go to the first Saturday
    d.setUTCDate(d.getUTCDate() - ((d.getUTCDay() + 1) % 7))
    d.setUTCSeconds(0);
    d.setUTCMinutes(0);
    d.setUTCHours(0);

    var i = d.getTime();

    do {
        markings.push({
            xaxis: {
                from: i,
                to: i + 2 * 24 * 60 * 60 * 1000
            },
            color: '#FBF8EF'
        });
        i += 7 * 24 * 60 * 60 * 1000;
    } while (i < axes.xaxis.max);

    return markings;
};

function nightAreas(axes) {
    var lat = 49.477246;
    var lng = 10.989707;
    var markings = [];

    var day = 24 * 60 * 60 * 1000; // ms
    var offset = (1000 * 60 * 60 * 2); // ms

    var previousSunset = axes.xaxis.min;
    for (var i = 0; true; i++) {
        var entry = axes.xaxis.min + (i * day);
        var t = SunCalc.getTimes(entry, lat, lng);

        var currentSunset = t.sunset.getTime() + offset <= axes.xaxis.min ? axes.xaxis.min : (t.sunset.getTime() + offset);
        var sunrise = t.sunrise.getTime() + offset >= axes.xaxis.max ? axes.xaxis.max : (t.sunrise.getTime() + offset);

        if (previousSunset <= sunrise) {
            markings.push({
                xaxis: {
                    from: previousSunset,
                    to: sunrise
                },
                color: '#B0C4DE'
            });
        }

        previousSunset = currentSunset;

        if (entry > axes.xaxis.max + offset) {
            break;
        }
    }

    return markings;
};

function loadAnotherFortnight(oldMin, max) {
    console.log('Reloading with oldMin value: ' + new Date(oldMin));
    console.log('Reloading with max value: ' + new Date(max));

    getByFilter(oldMin, max, function (rawdata) {
        console.log('call getByFilter is done.');
        render(rawdata);
    });
};

// Prepare data -------------------------------------------------------------------------------------------
var formatDate = function (date) {
    // requires moment.js
    return moment.utc(new Date(date)).format('ddd, YYYY MMM DD HH:mm');
};

var convertDataForFlot = function (data) {
    var flotdata = [];
    flotdata.push(mapTimeSeries(data, 'WA', 'outside', colors[0]));
    flotdata.push(mapTimeSeries(data, 'Z2', '2nd floor', colors[1]));
    flotdata.push(mapTimeSeries(data, 'Z3', '1st floor (work)', colors[2]));
    flotdata.push(mapTimeSeries(data, 'Z4', '1st floor (bathroom)', colors[3]));
    flotdata.push(mapTimeSeries(data, 'Z5', '1st floor (bedroom)', colors[4]));
    flotdata.push(mapTimeSeries(data, 'Z6', 'ground floor', colors[5]));
    return flotdata;
};

var mapTimeSeries = function (data, sensorName, label, col) {
    var series = [];
    for (var i = 0; i < data.length; i++) {
        if (data[i].sensorName === sensorName) {
            var mDate = moment(data[i].timestamp + ' +02:00', 'YYYY-MM-DD HH:mm:ss ZZ');
            mDate.add(2, 'hours'); // still don't understand why this is really necessary.
            var millis = new Date(mDate.unix()) * 1000;
            series.push([millis, data[i].degreeCelsius]);
        }
    }

    // Note: Add an 'id' property (because the label property is a descriptive string)
    return {
        data: series,
        label: label,
        color: col,
        id: sensorName
    };
};
<|endoftext|>


<commit_before>$(document).ready(function () {
    var status = $("#dynamic-status");

    status.load("/status", function (res, stat) {
        if (stat == "error") {
            status.html(down_html());
        }
    });

    setInterval(function () {
        status.load("/status", function (res, stat) {
            if (stat == "error") {
                status.html(down_html());
            }
        });
    }, 3000);

    var upgdg = new mdc.dialog.MDCDialog($("#upgrade-server-dialog")[0]);

    upgdg.listen("MDCDialog:accept", function () {
        window.location.href = "/upgrade";
    });

    $(".restart-server").click(function () {
        window.location.href = "/restart";
    });

    $(".upgrade-server-button").click(function () {
        $("#upgrade-server-dialog").find("#server-version").html(btn.attr("version"));

        upgdg.show()
    });
});

function down_html() {
    var html = "";

    html += "
Server Status
";
    html += "
";
    html += "
Service
";
    html += "
";
    html += "
";
    html += "            ";
    html += "                ";
    html += "              ";
    html += "            ";
    html += "                HB";
    html += "                Not running";
    html += "            ";
    html += "
";
    html += "
";
    html += "            ";
    html += "                ";
    html += "              ";
    html += "            ";
    html += "                Console";
    html += "                Not running";
    html += "            ";
    html += "
";
    html += "
";
    html += "
";

    return html;
}<commit_msg>upgrade server dialog<commit_after>$(document).ready(function () {
    var status = $("#dynamic-status");

    status.load("/status", function (res, stat) {
        if (stat == "error") {
            status.html(down_html());
        }
    });

    setInterval(function () {
        status.load("/status", function (res, stat) {
            if (stat == "error") {
                status.html(down_html());
            }
        });
    }, 3000);

    var upgdg = new mdc.dialog.MDCDialog($("#upgrade-server-dialog")[0]);

    upgdg.listen("MDCDialog:accept", function () {
        window.location.href = "/upgrade";
    });

    $(".restart-server").click(function () {
        window.location.href = "/restart";
    });

    $(".upgrade-server-button").click(function () {
        $("#upgrade-server-dialog").find("#server-version").html($(this).attr("version"));
        upgdg.show()
    });
});

function down_html() {
    var html = "";

    html += "
Server Status
";
    html += "
";
    html += "
Service
";
    html += "
";
    html += "
";
    html += "            ";
    html += "                ";
    html += "              ";
    html += "            ";
    html += "                HB";
    html += "                Not running";
    html += "            ";
    html += "
";
    html += "
";
    html += "            ";
    html += "                ";
    html += "              ";
    html += "            ";
    html += "                Console";
    html += "                Not running";
    html += "            ";
    html += "
";
    html += "
";
    html += "
";

    return html;
}<|endoftext|>


<commit_before>'use strict';
$('#widget-form').hide();
// var host = document.location.href.split("//")[1].split("/")[0];

// Twitch.init({clientId: 'pxic46d4dsydwhxvlh341kb7dgdnc6'}, function(error, status) {
//   if (status.authenticated) {
// $('.twitch-connect').hide();
// $('#widget-form').show();
//
// }
// });
//
$('.twitch-connect').click(function () {
  // Twitch.login({
  // 	redirect_uri: 'https://bittoolscod301.herokuapp.com',
  //   scope: ['user_read', 'channel_read']
  // });
  window.location = 'https://api.twitch.tv/kraken/oauth2/authorize?response_type=code&client_id=pxic46d4dsydwhxvlh341kb7dgdnc6&redirect_uri=https://bittoolscod301.herokuapp.com&scope=user_read+channel_read'
  console.log(document.location.hash);
    // if (document.location.hash != "")
  			// {
  				var newValue = document.location.hash.split("=")[1].split("&")[0];
          console.log(newValue);
  // }
  $.ajax({
       url: 'https://api.twitch.tv/kraken',
       beforeSend: function(xhr) {
            xhr.setRequestHeader("Accept", "application/vnd.twitchtv.v5+json"),
            xhr.setRequestHeader("Authorization", `OAuth ${newValue}`)
       }, success: function(data){
           console.log(data);
           //process the JSON data etc
       }
})
})
<commit_msg>debugging<commit_after>'use strict';
// $('#widget-form').hide();
// var host = document.location.href.split("//")[1].split("/")[0];

// Twitch.init({clientId: 'pxic46d4dsydwhxvlh341kb7dgdnc6'}, function(error, status) {
//   if (status.authenticated) {
// $('.twitch-connect').hide();
// $('#widget-form').show();
//
// }
// });
//
$('.twitch-connect').click(function () {
  // Twitch.login({
  // 	redirect_uri: 'https://bittoolscod301.herokuapp.com',
  //   scope: ['user_read', 'channel_read']
  // });
  window.location = 'https://api.twitch.tv/kraken/oauth2/authorize?response_type=code&client_id=pxic46d4dsydwhxvlh341kb7dgdnc6&redirect_uri=https://bittoolscod301.herokuapp.com&scope=user_read+channel_read'
  console.log(document.location.hash);
    // if (document.location.hash != "")
  			// {
  				var newValue = document.location.hash.split("=")[1].split("&")[0];
          console.log(newValue);
  // }
  $.ajax({
       url: 'https://api.twitch.tv/kraken',
       beforeSend: function(xhr) {
            xhr.setRequestHeader("Accept", "application/vnd.twitchtv.v5+json"),
            xhr.setRequestHeader("Authorization", `OAuth ${newValue}`)
       }, success: function(data){
           console.log(data);
           //process the JSON data etc
       }
})
})
<|endoftext|>


<commit_before>import Ractive from 'ractive';
import Router from 'ractive-route';
import { EventAggregator } from 'pubsub';

import './lib/ractive/transitions.es6';
import './lib/ractive/components.es6';
import './lib/ractive/helpers.es6';
import './main.less';

import About from './About.es6';
import Home from './Home.es6';
import MarkupExamples from './MarkupExamples.es6';
import Sidebar from './Sidebar.es6';

let sidebar = new Sidebar({
  el: '#sidebar'
});

let pubsub = new EventAggregator();
pubsub.subscribe('add', msg => sidebar.add('clicks'));

let router = new Router({
  el: '#main',
  basePath: '/',
  data() {
    return {
      pubsub: pubsub
    }
  }
});

router.addRoute('/', Home);
router.addRoute('/about', About);
router.addRoute('/markup', MarkupExamples);

router.dispatch(window.location.pathname, { noHistory: true })
      .watchLinks()
      .watchState();
<commit_msg>Add import for date formatting<commit_after>import Ractive from 'ractive';
import Router from 'ractive-route';
import { EventAggregator } from 'pubsub';

import './lib/ractive/transitions.es6';
import './lib/ractive/components.es6';
import './lib/ractive/helpers.es6';
import 'ractive-datetime';
import './main.less';

import About from './About.es6';
import Home from './Home.es6';
import MarkupExamples from './MarkupExamples.es6';
import Sidebar from './Sidebar.es6';

let sidebar = new Sidebar({
  el: '#sidebar'
});

let pubsub = new EventAggregator();
pubsub.subscribe('add', msg => sidebar.add('clicks'));

let router = new Router({
  el: '#main',
  basePath: '/',
  data() {
    return {
      pubsub: pubsub
    }
  }
});

router.addRoute('/', Home);
router.addRoute('/about', About);
router.addRoute('/markup', MarkupExamples);

router.dispatch(window.location.pathname, { noHistory: true })
      .watchLinks()
      .watchState();
<|endoftext|>


<commit_before>var addon = self;

var panelApp = angular.module('panelApp', ["ngRoute", "ngSanitize"]).config(['$provide', function($provide) {
  $provide.decorator('$sniffer', ['$delegate', function($delegate) {
    $delegate.history = false;
    return $delegate;
  }]);
}]);

panelApp.controller("PanelCtrl", function($scope, $document) {
  $scope.teamSpinner = true;
  $scope.domainSpinner = true;
  $scope.trailSpinner = true;
  $scope.extracted_tools = [];
  // $scope.datawake = addon.options.datawakeInfo;
  // $scope.current_url = addon.options.current_url;
  // $scope.domainFeaturesEnabled = addon.options.useDomainFeatures;
  // $scope.rankingEnabled = addon.options.useRanking;
  // $scope.versionNumber = addon.options.versionNumber;
  // $scope.user = addon.options.userInfo;
  $scope.invalid = {};
  // $scope.pageVisits = addon.options.pageVisits;
  $scope.headerPartial = "partials/header-partial.html";
  $scope.createTrailPartial = "partials/trail-modal-partial.html";
  $scope.createDomainPartial = "partials/domain-modal-partial.html";
  $scope.createTeamPartial = "partials/team-modal-partial.html"
    // $scope.domains = addon.options.domains;
  $scope.teamMembers = null;
  if (!$scope.domains) $scope.domains = [];
  $scope.trails = []

  addon.port.on("ready", function(prefs) {
    console.log("Got Ready")
    console.log("Datawake Prefs")
    console.log(prefs.datawakeInfo)
    $scope.datawake = prefs.datawakeInfo;
    $scope.current_url = prefs.current_url;
    $scope.lookaheadEnabled = prefs.useLookahead;
    $scope.domainFeaturesEnabled = prefs.useDomainFeatures;
    $scope.rankingEnabled = prefs.useRanking;
    $scope.versionNumber = prefs.versionNumber;
    $scope.pageVisits = prefs.pageVisits;
    $scope.starUrl = prefs.starUrl
  });

  //  console.log("opening panel for tab: "+addon.options.tabId)
  console.log($scope.datawake)

  // TEAMS
  $scope.teams = [];
  if ($scope.datawake && $scope.datawake.team) $scope.teams.push($scope.datawake.team)
  $scope.selectedTeam = ($scope.datawake && $scope.datawake.team) ? $scope.datawake.team : null;

  // when teams are returned  from the server set the selected team as the matching element from the teams array
  addon.port.on("teams", function(teams) {
    $scope.teams = teams;
    $scope.selectedTeam = null;
    if ($scope.datawake.team) {
      for (i in $scope.teams) {
        if ($scope.teams[i].id == $scope.datawake.team.id) {
          $scope.selectedTeam = $scope.teams[i]
        }
      }
    }
    $scope.teamSpinner = false;
    $scope.$apply();
    console.log("GOT TEAMS")
    console.log(teams)
  });

  $scope.teamChanged = function(team) {
    $scope.datawake.team = team;
    $scope.datawake.domain = null;
    $scope.datawake.trail = null;
    $scope.isDatawakeOn = false;
    $scope.domains = []
    $scope.trails = []
    $scope.domainSpinner = true;
    addon.port.emit("changeTeam", {
      tabId: addon.options.tabId,
      team: team
    });
    console.log("teamChanged")
    console.log($scope.datawake)
  };

  $scope.teamChangedOnTeamModal = function(team) {
    $scope.teamMembers = null;
    addon.port.emit("getTeamMembers", team)
  }

  addon.port.on("gotTeamMembers", function(teamMembers) {
    $scope.teamMembers = teamMembers
    console.log("got team members")
    console.log(teamMembers)
    $scope.$apply();
  })

  $scope.createTeam = function(name, description) {
    console.log("create team: (" + name + "," + description + ")")
    var data = {
      name: name,
      description: description
    }
    $scope.newTeamDisabled = true;
    addon.port.emit("createTeam", data)
  }

  addon.port.on("teamCreated", function(team) {
    console.log("teamCreated")
    console.log(team)
    $scope.newTeamDisabled = false;
    $scope.datawake.team = team;
    $scope.teams.push(team)
    $scope.$apply()
  })

  $scope.addTeamMemeber = function(team, email) {
    console.log("adding team member")
    console.log(team)
    console.log(email)
    var data = {
      team_id: team.id,
      email: email
    }
    addon.port.emit("addTeamMember", data)

  }

  $scope.removeTeamMember = function(team, email) {
    console.log("removing team member")
    console.log(team)
    console.log(email)
    var data = {
      team_id: team.id,
      email: email
    }
    addon.port.emit("removeTeamMember", data)
  }

  // DOMAINS
  // if (! $scope.datawake.team) $scope.domainSpinner = false;
  // $scope.domains =[];
  // if ($scope.datawake && $scope.datawake.domain ) $scope.domains.push($scope.datawake.domain)
  // $scope.selectedDomain = ($scope.datawake && $scope.datawake.domain ) ? $scope.datawake.domian : null;

  // when domains come in from the server set the selected domain to the matching element
  addon.port.on("domains", function(domains) {
    $scope.selectedDomain = null;
    $scope.domains = domains;
    if ($scope.datawake.domain && $scope.domains) {
      for (i in $scope.domains) {
        if ($scope.domains[i].id == $scope.datawake.domain.id) {
          $scope.selectedDomain = $scope.domains[i]
        }
      }
    }
    $scope.domainSpinner = false;
    $scope.$apply()
    console.log("GOT DOMAINS")
    console.log(domains)
  });

  $scope.domainChanged = function(domain) {
    $scope.datawake.domain = domain;
    $scope.datawake.trail = null;
    $scope.isDatawakeOn = false;
    $scope.trails = [];
    $scope.trailSpinner = true;
    addon.port.emit("changeDomain", {
      tabId: addon.options.tabId,
      domain: $scope.datawake.domain
    });
    console.log("domainChanged")
    console.log($scope.datawake)

  };

  // TRAILS
  // if ( !$scope.datawake.team || ! $scope.datawake.domain ) $scope.trailSpinner = false;
  // $scope.trails = [];
  // if ($scope.datawake && $scope.datawake.trail) $scope.trails.push($scope.datawake.trail);
  // $scope.selectedTrail = ($scope.datawake && $scope.datawake.trail) ? $scope.datawake.trail : null;

  addon.port.on("trails", function(trails) {
    $scope.trails = trails;
    $scope.selectedTrail = null;
    if ($scope.datawake.trail && $scope.trails) {
      for (i in $scope.trails) {
        if ($scope.trails[i].id == $scope.datawake.trail.id) {
          $scope.selectedTrail = $scope.trails[i]
        }
      }
    }
    $scope.trailSpinner = false;
    $scope.$apply();
    console.log("GOT TRAILS")
    console.log(trails)
  });

  $scope.trailChanged = function(trail) {
    $scope.datawake.trail = trail;
    $scope.datawake.isDatawakeOn = false;
    addon.port.emit("infochanged", {
      tabId: addon.options.tabId,
      info: $scope.datawake
    });
    console.log("trailChanged")
    console.log($scope.datawake)
  };

  $scope.newTrail = function(team, domain, newTrailName, newTrailDesc) {
    var data = {}
    data.team_id = team.id;
    data.domain_id = domain.id;
    data.name = newTrailName
    data.description = (newTrailDesc) ? newTrailDesc : "";
    $scope.trailChanged(null);
    $scope.newTrailName = null;
    addon.port.emit("createTrail", data);
  }

  addon.port.on("trailCreated", function(trail) {
    $scope.trails.push(trail)
    $scope.selectedTrail = trail
    $scope.datawake.trail = trail;
    addon.port.emit("infochanged", {
      tabId: addon.options.tabId,
      info: $scope.datawake
    });
    $scope.apply()
  });

  // Recording
  $scope.recordingChange = function(recording) {
    $scope.datawake.isDatawakeOn = recording;
    addon.port.emit("infochanged", {
      tabId: addon.options.tabId,
      info: $scope.datawake
    });
    console.log("recordingChange")
    console.log($scope.datawake)
  }

  addon.port.on("manualFeatures", function(features) {
    console.log("Got manual features")
    console.log(features)
    $scope.$apply(function() {
      $scope.manualFeatures = features;
    });
  });

  addon.port.on("trailEntities", function(entities_obj) {
    console.log("Got trail entities")
    console.log(entities_obj)
    $scope.irrelevantEntities = entities_obj.irrelevantEntities;
    $scope.trailEntities = entities_obj.entities;
    $scope.trailEntitiesIter = createIterableEntityListForSorting(entities_obj.entities);
    $scope.irrelevantEntitiesIter = createIterableEntityListForSorting(entities_obj.irrelevantEntities);
    $scope.$apply();
  });

  addon.port.on("trailLinks", function(links_obj) {
    console.log("got trail links")
    console.log(links_obj)
    $scope.visitedLinks = links_obj.visited;
    $scope.notVisitedLinks = links_obj.notVisited;
    $scope.$apply();
  });

  addon.port.on("ranking", function(rankingInfo) {
    $scope.$apply(function() {
      $scope.ranking = rankingInfo.ranking;
      var starRating = $("#star_rating");
      starRating.attr("data-average", rankingInfo.ranking);
      createStarRating(addon.options.starUrl);
    });
  });

  addon.port.on("features", function(features) {
    $scope.extracted_entities_dict = features;
    $scope.$apply();
  });

  addon.port.on("domain_features", function(features) {
    $scope.domain_extracted_entities_dict = features;
    $scope.$apply();
  });

  addon.port.on("externalLinks", function(links) {
    console.debug("Loading External Entities..");
    $scope.$apply(function() {
      $scope.extracted_tools = links;
    });
  });

  $scope.signOut = function() {
    addon.port.emit("signOut");
  }

  $scope.openExternalLink = function(externalUrl) {
    addon.port.emit("openExternalLink", {
      externalUrl: externalUrl
    });
  };

  $scope.markInvalid = function(type, entity) {
    var postObj = {};
    postObj.team_id = $scope.datawake.team.id;
    postObj.domain_id = $scope.datawake.domain.id;
    postObj.trail_id = $scope.datawake.trail.id;
    postObj.feature_type = type;
    postObj.feature_value = entity;
    addon.port.emit("markInvalid", postObj);
    $scope.invalid[entity] = true;
  };

  addon.port.on("markedFeatures", function(features) {
    for (i in features) {
      var feature = features[i];
      $scope.invalid[feature.value] = true;
    }
    $scope.$apply()

  });

  $scope.isExtracted = function(type, name) {
    if ($scope.entities_in_domain && $scope.entities_in_domain.hasOwnProperty(type)) {
      return $scope.entities_in_domain[type].indexOf(name) >= 0;
    }
  };

  $scope.editFeatures = function() {
    if (!$scope.allowEditFeatures) {
      $scope.allowEditFeatures = true;
    } else {
      $scope.allowEditFeatures = false;
    }
  }

  $scope.getHostName = function(url) {
    return new URL(url).hostname
  };

  $scope.showEntities = function(link) {
    if (!link.show) {
      var data = {};
      data.domain = $scope.datawake.domain.name;
      data.trail = $scope.datawake.trail.name;
      data.url = link.url;
      addon.port.emit("getUrlEntities", data);

      function updateLink(entities) {
        link.entities = entities;
        link.show = !link.show;
        $scope.$apply();
      }

      addon.port.once("urlEntities", updateLink);
    } else {
      link.show = !link.show;
    }
  };

  addon.port.on("infosaved", function(datawakeinfo) {
    $scope.datawake = datawakeinfo;
    $scope.$apply()
    console.log("ON INFO SAVED")
    console.log($scope.datawake)
  })

  function createIterableEntityListForSorting(entities) {
    var arr = [];
    $.map(entities, function(item, key) {
      arr.push({
        entity: key,
        rank: item
      });
    });
    return arr;
  }

  function createStarRating(starUrl) {
    var starRating = $("#star_rating");
    starRating.jRating({
      type: 'big', // type of the rate.. can be set to 'small' or 'big'
      length: 10, // nb of stars
      rateMax: 10,
      bigStarsPath: starUrl + 'stars.png',
      smallStarsPath: starUrl + 'small.png',
      sendRequest: false,
      canRateAgain: true,
      nbRates: 9999999,
      onClick: function(element, rate) {
        setUrlRank(rate);
        $scope.$apply(function() {
          $scope.ranking = rate;
        });
      }
    });

  }

  function setUrlRank(rank) {
    var rank_data = {
      team_id: $scope.datawake.team.id,
      domain_id: $scope.datawake.domain.id,
      trail_id: $scope.datawake.trail.id,
      url: $scope.current_url,
      rank: rank
    };
    addon.port.emit("setUrlRank", rank_data);
  }

  addon.port.emit("init");

});

panelApp.config(['$routeProvider',
  function($routeProvider) {
    $routeProvider.
    when('/features/all', {
      templateUrl: 'partials/extracted-entities-partial.html'
    }).
    when('/features/domain', {
      templateUrl: 'partials/domain-extracted-partial.html'
    }).
    when('/features/manual', {
      templateUrl: 'partials/manual-features-partial.html'
    }).
    when('/trail/explored', {
      templateUrl: 'partials/trail-based-explored-partial.html'
    }).
    when('/trail/unexplored', {
      templateUrl: 'partials/trail-based-unexplored-partial.html'
    }).
    when('/trail/entities/relevant', {
      templateUrl: 'partials/trail-based-relevant-entities-partial.html'
    }).
    when('/trail/entities/irrelevant', {
      templateUrl: 'partials/trail-based-irrelevant-entities-partial.html'
    }).
    otherwise({
      redirectTo: 'partials/extracted-entities-partial.html'
    });
  }
]);
<commit_msg>refreshing entities/trails and removing spaces from urls<commit_after>var addon = self;

var panelApp = angular.module('panelApp', ["ngRoute", "ngSanitize"]).config(['$provide', function($provide) {
  $provide.decorator('$sniffer', ['$delegate', function($delegate) {
    $delegate.history = false;
    return $delegate;
  }]);
}]);

panelApp.controller("PanelCtrl", function($scope, $document) {
  $scope.teamSpinner = true;
  $scope.domainSpinner = true;
  $scope.trailSpinner = true;
  $scope.extracted_tools = [];
  $scope.invalid = {};
  $scope.headerPartial = "partials/header-partial.html";
  $scope.createTrailPartial = "partials/trail-modal-partial.html";
  $scope.createDomainPartial = "partials/domain-modal-partial.html";
  $scope.createTeamPartial = "partials/team-modal-partial.html"
  $scope.teamMembers = null;
  if (!$scope.domains) $scope.domains = [];
  $scope.trails = []

  addon.port.on("ready", function(prefs) {
    console.log("Got Ready")
    console.log("Datawake Prefs")
    console.log(prefs.datawakeInfo)
    $scope.datawake = prefs.datawakeInfo;

    $scope.datawake.domain = 'memex'
    $scope.datawake.trail = 'trail'

    $scope.current_url = prefs.current_url;
    $scope.lookaheadEnabled = prefs.useLookahead;
    $scope.domainFeaturesEnabled = prefs.useDomainFeatures;
    $scope.rankingEnabled = prefs.useRanking;
    $scope.versionNumber = prefs.versionNumber;
    $scope.pageVisits = prefs.pageVisits;
    $scope.starUrl = prefs.starUrl
  });

  console.log($scope.datawake);

  addon.port.on("trailEntities", function(entities_obj) {
    console.log("Got trail entities")
    console.log(entities_obj)
    $scope.irrelevantEntities = entities_obj.irrelevantEntities;
    $scope.trailEntities = entities_obj.entities;
    $scope.trailEntitiesIter = createIterableEntityListForSorting(entities_obj.entities);
    $scope.irrelevantEntitiesIter = createIterableEntityListForSorting(entities_obj.irrelevantEntities);
    $scope.$apply();
  });

  addon.port.on("trailLinks", function(links_obj) {
    console.log("got trail links")
    console.log(links_obj)
    $scope.visitedLinks = links_obj.visited;
    $scope.notVisitedLinks = links_obj.notVisited;
    $scope.$apply();
  });

  addon.port.on("ranking", function(rankingInfo) {
    $scope.$apply(function() {
      $scope.ranking = rankingInfo.ranking;
      var starRating = $("#star_rating");
      starRating.attr("data-average", rankingInfo.ranking);
      createStarRating(addon.options.starUrl);
    });
  });

  addon.port.on("features", function(features) {
    $scope.extracted_entities_dict = features;
    $scope.$apply();
  });

  addon.port.on("domain_features", function(features) {
    $scope.domain_extracted_entities_dict = features;
    $scope.$apply();
  });

  addon.port.on("externalLinks", function(links) {
    console.debug("Loading External Entities..");
    $scope.$apply(function() {
      $scope.extracted_tools = links;
    });
  });

  $scope.signOut = function() {
    addon.port.emit("signOut");
  }

  $scope.openExternalLink = function(externalUrl) {
    addon.port.emit("openExternalLink", {
      externalUrl: externalUrl
    });
  };

  $scope.markInvalid = function(type, entity) {
    var postObj = {};
    postObj.team_id = $scope.datawake.team.id;
    postObj.domain_id = $scope.datawake.domain.id;
    postObj.trail_id = $scope.datawake.trail.id;
    postObj.feature_type = type;
    postObj.feature_value = entity;
    addon.port.emit("markInvalid", postObj);
    $scope.invalid[entity] = true;
  };

  addon.port.on("markedFeatures", function(features) {
    for (i in features) {
      var feature = features[i];
      $scope.invalid[feature.value] = true;
    }
    $scope.$apply()

  });

  $scope.isExtracted = function(type, name) {
    if ($scope.entities_in_domain && $scope.entities_in_domain.hasOwnProperty(type)) {
      return $scope.entities_in_domain[type].indexOf(name) >= 0;
    }
  };

  $scope.editFeatures = function() {
    if (!$scope.allowEditFeatures) {
      $scope.allowEditFeatures = true;
    } else {
      $scope.allowEditFeatures = false;
    }
  }

  $scope.getHostName = function(url) {
    //For some reason, sometimes errant spaces were apearing in the urls.
    url = url.replace(/\s+/g, '');
    return new URL(url).hostname
  };

  $scope.showEntities = function(link) {
    if (!link.show) {
      var data = {};
      // data.domain = $scope.datawake.domain.name;
      // data.trail = $scope.datawake.trail.name;
      data.domain = 'memex';
      data.trail = 'trail';

      data.url = link.url;
      addon.port.emit("getUrlEntities", data);

      function updateLink(entities) {
        link.entities = entities;
        link.show = !link.show;
        $scope.$apply();
      }

      addon.port.once("urlEntities", updateLink);
    } else {
      link.show = !link.show;
    }
  };

  addon.port.on("infosaved", function(datawakeinfo) {
    $scope.datawake = datawakeinfo;
    $scope.$apply()
    console.log("ON INFO SAVED")
    console.log($scope.datawake)
  })

  $scope.refreshWebPages = function () {
      addon.port.emit("refreshWebPages", {domain: "memex", trail: "trail"});
  };

  $scope.refreshEntities = function () {
      addon.port.emit("refreshEntities", {domain: "memex", trail: "trail"});
  };

  function createIterableEntityListForSorting(entities) {
    var arr = [];
    $.map(entities, function(item, key) {
      arr.push({
        entity: key,
        rank: item
      });
    });
    return arr;
  }

  // function createStarRating(starUrl) {
  //   var starRating = $("#star_rating");
  //   starRating.jRating({
  //     type: 'big', // type of the rate.. can be set to 'small' or 'big'
  //     length: 10, // nb of stars
  //     rateMax: 10,
  //     bigStarsPath: starUrl + 'stars.png',
  //     smallStarsPath: starUrl + 'small.png',
  //     sendRequest: false,
  //     canRateAgain: true,
  //     nbRates: 9999999,
  //     onClick: function(element, rate) {
  //       setUrlRank(rate);
  //       $scope.$apply(function() {
  //         $scope.ranking = rate;
  //       });
  //     }
  //   });
  //
  // }

  function setUrlRank(rank) {
    var rank_data = {
      team_id: $scope.datawake.team.id,
      domain_id: $scope.datawake.domain.id,
      trail_id: $scope.datawake.trail.id,
      url: $scope.current_url,
      rank: rank
    };
    addon.port.emit("setUrlRank", rank_data);
  }

  addon.port.emit("init");

});

panelApp.config(['$routeProvider',
  function($routeProvider) {
    $routeProvider.
    // when('/features/all', {
    //   templateUrl: 'partials/extracted-entities-partial.html'
    // }).
    // when('/features/domain', {
    //   templateUrl: 'partials/domain-extracted-partial.html'
    // }).
    // when('/features/manual', {
    //   templateUrl: 'partials/manual-features-partial.html'
    // }).
    when('/trail/explored', {
      templateUrl: 'partials/trail-based-explored-partial.html'
    }).
    when('/trail/unexplored', {
      templateUrl: 'partials/trail-based-unexplored-partial.html'
    }).
    when('/trail/entities/relevant', {
      templateUrl: 'partials/trail-based-relevant-entities-partial.html'
    }).
    when('/trail/entities/irrelevant', {
      templateUrl: 'partials/trail-based-irrelevant-entities-partial.html'
    }).
    otherwise({
      redirectTo: 'partials/extracted-entities-partial.html'
    });
  }
]);
<|endoftext|>


<commit_before>function GameBoard() {

    this.rows = 7;
    this.columns = 7;

    this.tile_size = 1;

    this.black = new Color(0, 0, 0);
    this.red = new Color(0xA0, 25, 25);
    this.red_tile = new Color(0x90, 25, 25);

    this.pieces = [];
    this.tiles = [];

    var tile_color = this.red_tile;

    // Initialize gameboard with tiles (rows * columns)
    for (var x = 0; x <= this.rows; x++) {

        if (x % 2 === 0)
            tile_color = this.black;
        else
            tile_color = this.red_tile;

        for (var y = 0; y <= this.columns; y++) {

            var tile = new Tile(x, y, tile_color);
            this.tiles.push(tile);

            if (tile_color === this.red_tile)
                tile_color = this.black;
            else if (tile_color === this.black)
                tile_color = this.red_tile;
        }
    }
}

GameBoard.prototype.draw = function () {

    for (var i = 0; i < this.tiles.length; ++i) {
        this.tiles[i].draw();
    }
}

// Highlight a tile
GameBoard.prototype.highlight = function (x, y) {

    iso.add(new Path([
        Point(x, y, -0.5),
        Point(x, (y + 1), -0.5),
        Point((x + 1), (y + 1), -0.5),
        Point((x + 1), y, -0.5)
    ]), new Color(100, 250, 60));
}

GameBoard.prototype.possibleMoves = function (piece) {

    // Determines direction (does the piece move up or down)
    var dy = piece.color === Colour.BLACK ? 1 : -1;

    var moves = [];

    var left = {};
    var right = {};

    // Move objects for promoted pieces,
    // e.g. var bLeft / bRight = {} (backwards left / backwards right)

    left.x = piece.x - 1;
    right.x = piece.x + 1;

    left.y = piece.y + dy;
    right.y = piece.y + dy;

    // Check boundaries and overlapping
    if (this.validMovement(piece, left, -1, dy)) {
        moves.push(left);
    }

    if (this.validMovement(piece, right, 1, dy)) {
        moves.push(right);
    }

    if (piece.isPromoted()) {

        dy *= -1;

        var bLeft = {};
        var bRight = {};

        bLeft.x = piece.x - 1;
        bRight.x = piece.x + 1;

        bLeft.y = piece.y + dy;
        bRight.y = piece.y + dy;

        if (this.validMovement(piece, bLeft, -1, dy)) {
            moves.push(bLeft);
        }

        if (this.validMovement(piece, bRight, 1, dy)) {
            moves.push(bRight);
        }
    }

    return moves;
}

GameBoard.prototype.validMovement = function (piece, coordinates, dx, dy) {

    if (this.isInsideBoard(coordinates)) {

        var neighbour = this.pieceAt(coordinates.x, coordinates.y);

        if (neighbour === null) {
            return true;
        } else if (piece.color !== neighbour.color) {

            coordinates["remove"] = neighbour;

            coordinates.x += dx;
            coordinates.y += dy;

            this.validMovement(piece, coordinates, dx, dy);

            if (this.isInsideBoard(coordinates) && this.pieceAt(coordinates.x, coordinates.y) === null) {
                return true;
            }
        }
    }

    return false;
}

GameBoard.prototype.pieceAt = function (x, y) {

    for (var i = 0; i < this.pieces.length; ++i) {

        var piece = this.pieces[i];

        if (piece.x === x && piece.y === y) {
            return piece;
        }
    }

    return null;
}

GameBoard.prototype.isInsideBoard = function (coordinates) {

    var x = coordinates.x;
    var y = coordinates.y;

    if (x < 0 || x > this.rows) {
        return false;
    }

    if (y < 0 || y > this.columns) {
        return false;
    }

    return true;
}

GameBoard.prototype.removePieceAt = function (x, y) {

    for (var i = 0; i < this.pieces.length; ++i) {

        var piece = this.pieces[i];

        if (piece.x === x && piece.y === y) {
            this.pieces.splice(i, 1);
        }
    }
}

GameBoard.prototype.recalculate = function () {

    for (var i = 0; i < this.pieces.length; ++i) {
        this.pieces[i].recalculate();
    }

    for (var j = 0; j < this.tiles.length; ++j) {
        this.tiles[j].recalculate();
    }
}
<commit_msg>Fix jumping over multiple pieces - #1<commit_after>function GameBoard() {

    this.rows = 7;
    this.columns = 7;

    this.tile_size = 1;

    this.black = new Color(0, 0, 0);
    this.red = new Color(0xA0, 25, 25);
    this.red_tile = new Color(0x90, 25, 25);

    this.pieces = [];
    this.tiles = [];

    var tile_color = this.red_tile;

    // Initialize gameboard with tiles (rows * columns)
    for (var x = 0; x <= this.rows; x++) {

        if (x % 2 === 0)
            tile_color = this.black;
        else
            tile_color = this.red_tile;

        for (var y = 0; y <= this.columns; y++) {

            var tile = new Tile(x, y, tile_color);
            this.tiles.push(tile);

            if (tile_color === this.red_tile)
                tile_color = this.black;
            else if (tile_color === this.black)
                tile_color = this.red_tile;
        }
    }
}

GameBoard.prototype.draw = function () {

    for (var i = 0; i < this.tiles.length; ++i) {
        this.tiles[i].draw();
    }
}

// Highlight a tile
GameBoard.prototype.highlight = function (x, y) {

    iso.add(new Path([
        Point(x, y, -0.5),
        Point(x, (y + 1), -0.5),
        Point((x + 1), (y + 1), -0.5),
        Point((x + 1), y, -0.5)
    ]), new Color(100, 250, 60));
}

GameBoard.prototype.possibleMoves = function (piece) {

    // Determines direction (does the piece move up or down)
    var dy = piece.color === Colour.BLACK ? 1 : -1;

    var moves = [];

    var left = {};
    var right = {};

    // Move objects for promoted pieces,
    // e.g. var bLeft / bRight = {} (backwards left / backwards right)

    left.x = piece.x - 1;
    right.x = piece.x + 1;

    left.y = piece.y + dy;
    right.y = piece.y + dy;

    // Check boundaries and overlapping
    if (this.validMovement(piece, left, -1, dy)) {
        moves.push(left);
    }

    if (this.validMovement(piece, right, 1, dy)) {
        moves.push(right);
    }

    if (piece.isPromoted()) {

        dy *= -1;

        var bLeft = {};
        var bRight = {};

        bLeft.x = piece.x - 1;
        bRight.x = piece.x + 1;

        bLeft.y = piece.y + dy;
        bRight.y = piece.y + dy;

        if (this.validMovement(piece, bLeft, -1, dy)) {
            moves.push(bLeft);
        }

        if (this.validMovement(piece, bRight, 1, dy)) {
            moves.push(bRight);
        }
    }

    return moves;
}

GameBoard.prototype.validMovement = function (piece, coordinates, dx, dy) {

    if (this.isInsideBoard(coordinates)) {

        var neighbour = this.pieceAt(coordinates.x, coordinates.y);

        if (neighbour === null) {
            return true;
        } else if (piece.color !== neighbour.color) {

            // Trying to jump over two pieces, not possible
            if (coordinates['remove'] !== undefined) {
                return false;
            }

            coordinates['remove'] = neighbour;

            coordinates.x += dx;
            coordinates.y += dy;

            this.validMovement(piece, coordinates, dx, dy);

            if (this.isInsideBoard(coordinates) && this.pieceAt(coordinates.x, coordinates.y) === null) {
                return true;
            }
        }
    }

    return false;
}

GameBoard.prototype.pieceAt = function (x, y) {

    for (var i = 0; i < this.pieces.length; ++i) {

        var piece = this.pieces[i];

        if (piece.x === x && piece.y === y) {
            return piece;
        }
    }

    return null;
}

GameBoard.prototype.isInsideBoard = function (coordinates) {

    var x = coordinates.x;
    var y = coordinates.y;

    if (x < 0 || x > this.rows) {
        return false;
    }

    if (y < 0 || y > this.columns) {
        return false;
    }

    return true;
}

GameBoard.prototype.removePieceAt = function (x, y) {

    for (var i = 0; i < this.pieces.length; ++i) {

        var piece = this.pieces[i];

        if (piece.x === x && piece.y === y) {
            this.pieces.splice(i, 1);
        }
    }
}

GameBoard.prototype.recalculate = function () {

    for (var i = 0; i < this.pieces.length; ++i) {
        this.pieces[i].recalculate();
    }

    for (var j = 0; j < this.tiles.length; ++j) {
        this.tiles[j].recalculate();
    }
}
<|endoftext|>


<commit_before>// https://gist.github.com/aaronlidman/4712709
var osm2geo = function(osm) {
    var xml = parse(osm),
        geo = {
            "type" : "FeatureCollection",
            "features" : []
        },
        nodesCache = cacheNodes();

    function parse(xml) {
        var string = new XMLSerializer().serializeToString(xml),
            parser = new DOMParser();
        return parser.parseFromString(string, 'text/xml');
    }

    // set the bounding box [minX,minY,maxX,maxY]; x -> long, y -> lat
    function getBounds(bounds) {
        var bbox = [];

        if (bounds.length) {
            bbox = [
                +bounds[0].getAttribute('minlon'),
                +bounds[0].getAttribute('minlat'),
                +bounds[0].getAttribute('maxlon'),
                +bounds[0].getAttribute('maxlat')
            ];
        }

        return bbox;
    }

    geo.bbox = getBounds(xml.getElementsByTagName('bounds'));

    // set properties for a feature
    function setProps(element){
        var properties = {},
            tags = element.getElementsByTagName('tag'),
            t = tags.length;

        while (t--) {
            properties[tags[t].getAttribute('k')] = tags[t].getAttribute('v');
        }

        return properties;
    }

    // create a feature of given type
    function getFeature(element, type){
        return {
            "geometry" : {
                "type" : type,
                "coordinates" : []
            },
            "type" : "Feature",
            "properties" : setProps(element)
        };
    }

    function cacheNodes() {
        var nodes = xml.getElementsByTagName('node'),
            n = nodes.length,
            coords = {};
            withTags = [];

        while (n--) {
            var tags = nodes[n].getElementsByTagName('tag');

            coords[nodes[n].getAttribute('id')] =
                [nodes[n].getAttribute('lon'), nodes[n].getAttribute('lat')];

            if (tags.length) withTags.push(nodes[n]);
        }

        return {
            coords: coords,
            withTags: withTags
        };
    }

    function buildRelations() {
        // features, multipolygons halfway done
        // done, a list of feature refs that will finish the features and the multipolygons they belong to
        var relations = xml.getElementsByTagName('relation'),
            r = relations.length,
            features = [],
            done = {},
            count = 0;

        while (r--) {
            var tags = [],
                type = false;

            // don't know it's a multipolygon for sure, check the type
            // or enforce so it's just type=multipolygon we're parsing
                // probably simpler
            feature = getFeature(relations[r], "MultiPolygon");

            if (feature.properties.type == 'multipolygon') {
                // other types require more work that I don't have time for right now

                feature.geometry.coordinates.push([]);

                // // probably going away
                // // if we can get actual multipolygons working this can go away
                // // if not, then we're just going to copy tags to all child ways/nodes
                // var relTags = relations[r].getElementsByTagName('tag'),
                //     x = relTags.length;
                // while (x--) {
                //     if (relTags[x].getAttribute('k') == 'type') {
                //         type = relTags[x].getAttribute('k');
                //     } else {
                //         tags.push([relTags[x].getAttribute('k'), relTags[x].getAttribute('v')]);
                //     }
                // }

                var members = relations[r].getElementsByTagName('member'),
                    m = members.length;
                while (m--) {
                    done[members[m].getAttribute('ref')] = count;
                    // feature.geometry.coordinates[0].push([]);

                    // .getAttribute('role') stuff would go somewhere around here
                }

                console.log(feature);

                delete feature.properties.type;
                features[count] = feature;
                count++;
            }
        }

        return {
            features: features,
            done: done
        };
    }

    var relational = buildRelations();
    console.log(relational);

    var ways = xml.getElementsByTagName('way');

    var w = ways.length;
    while (w--) {
        var feature = {},
            nds = ways[w].getElementsByTagName('nd');

        // If first and last nd are the same then its a polygon
        if (nds[0].getAttribute('ref') === nds[nds.length-1].getAttribute('ref')) {
            feature = getFeature(ways[w], "Polygon");
            feature.geometry.coordinates.push([]);
        } else {
            feature = getFeature(ways[w], "LineString");
        }

        var n = nds.length;
        while (n--) {
            var cords = nodesCache.coords[nds[n].getAttribute('ref')];

            if (feature.geometry.type === "Polygon") {
                feature.geometry.coordinates[0].push(cords);
            } else {
                feature.geometry.coordinates.push(cords);
            }
        }

        if (relational.done[ways[w].getAttribute('id')]) {
            var relCount = relational.done[ways[w].getAttribute('id')];
            console.log('relational.features[relCount].geometry');
            console.log(relational.features[relCount].geometry);
            relational.features[relCount].geometry.coordinates[0].push(feature.geometry.coordinates);
        } else {
            geo.features.push(feature);
        }
    }

    var r = relational.features.length;
    while (r--) {
        console.log(relational.features[r]);
        geo.features.push(relational.features[r]);
    }

    var points = nodesCache.withTags,
        p = points.length;

    while (p--) {
        var feature = getFeature(points[p], "Point");

        feature.geometry.coordinates = [
            points[p].getAttribute('lon'),
            points[p].getAttribute('lat')
        ];

        geo.features.push(feature);
    }

    return geo;
};<commit_msg>Multipolygon tag inheritance, Points rearrangement, cleanup<commit_after>// https://gist.github.com/aaronlidman/4712709
var osm2geo = function(osm) {
    var xml = parse(osm),
        geo = {
            "type" : "FeatureCollection",
            "features" : []
        },
        nodesCache = cacheNodes();

    function parse(xml) {
        var string = new XMLSerializer().serializeToString(xml),
            parser = new DOMParser();
        return parser.parseFromString(string, 'text/xml');
    }

    // set the bounding box [minX,minY,maxX,maxY]; x -> long, y -> lat
    function getBounds(bounds) {
        var bbox = [];

        if (bounds.length) {
            bbox = [
                +bounds[0].getAttribute('minlon'),
                +bounds[0].getAttribute('minlat'),
                +bounds[0].getAttribute('maxlon'),
                +bounds[0].getAttribute('maxlat')
            ];
        }

        return bbox;
    }

    geo.bbox = getBounds(xml.getElementsByTagName('bounds'));

    // set properties for a feature
    function setProps(element){
        var properties = {},
            tags = element.getElementsByTagName('tag'),
            t = tags.length;

        while (t--) {
            properties[tags[t].getAttribute('k')] = tags[t].getAttribute('v');
        }

        return properties;
    }

    // create a feature of given type
    function getFeature(element, type){
        return {
            "geometry" : {
                "type" : type,
                "coordinates" : []
            },
            "type" : "Feature",
            "properties" : setProps(element)
        };
    }

    function cacheNodes() {
        var nodes = xml.getElementsByTagName('node'),
            n = nodes.length,
            coords = {};
            withTags = [];

        while (n--) {
            var tags = nodes[n].getElementsByTagName('tag');

            coords[nodes[n].getAttribute('id')] =
                [nodes[n].getAttribute('lon'), nodes[n].getAttribute('lat')];

            if (tags.length) withTags.push(nodes[n]);
        }

        return {
            coords: coords,
            withTags: withTags
        };
    }

    function buildRelations() {
        var relations = xml.getElementsByTagName('relation'),
            r = relations.length,
            features = [],
            done = {},
            count = 0;

        while (r--) {
            var tags = [],
                type = false;

            feature = getFeature(relations[r], "MultiPolygon");

            if (feature.properties.type == 'multipolygon') {
                // other types require more work that I don't have time for right now

                feature.geometry.coordinates.push([]);

                var members = relations[r].getElementsByTagName('member'),
                    m = members.length;
                while (m--) {
                    done[members[m].getAttribute('ref')] = count;
                    // feature.geometry.coordinates[0].push([]);

                    // .getAttribute('role') stuff would go somewhere around here
                }

                delete feature.properties.type;
                features[count] = feature;
                count++;
            }
        }

        return {
            features: features,
            done: done
        };
    }

    // Points
    var points = nodesCache.withTags,
        p = points.length;

    while (p--) {
        var feature = getFeature(points[p], "Point");

        feature.geometry.coordinates = [
            +points[p].getAttribute('lon'),
            +points[p].getAttribute('lat')
        ];

        geo.features.push(feature);
    }

    // MultiPolygons, mostly in buildRelations()
    var relational = buildRelations(),
        ways = xml.getElementsByTagName('way');

    // Polygons/LineStrings
    var w = ways.length;
    while (w--) {
        var feature = {},
            nds = ways[w].getElementsByTagName('nd');

        // If first and last nd are the same then its a polygon
        if (nds[0].getAttribute('ref') === nds[nds.length-1].getAttribute('ref')) {
            feature = getFeature(ways[w], "Polygon");
            feature.geometry.coordinates.push([]);
        } else {
            feature = getFeature(ways[w], "LineString");
        }

        var n = nds.length;
        while (n--) {
            var cords = nodesCache.coords[nds[n].getAttribute('ref')];

            if (feature.geometry.type === "Polygon") {
                feature.geometry.coordinates[0].push(cords);
            } else {
                feature.geometry.coordinates.push(cords);
            }
        }

        if (relational.done[ways[w].getAttribute('id')]) {
            var relWay = relational.done[ways[w].getAttribute('id')];
            relational.features[relWay].geometry.coordinates[0].push(feature.geometry.coordinates);

            // transfer the way (polygon) properties over to the relation (multipolygon)
            // no overwriting, relation tags take precedence
            for (var wayProp in feature.properties) {
                if (!relational.features[relWay].properties[wayProp]) {
                    relational.features[relWay].properties[wayProp] = feature.properties[wayProp];
                }
            }
        } else {
            geo.features.push(feature);
        }
    }

    var r = relational.features.length;
    while (r--) {
        geo.features.push(relational.features[r]);
    }

    return geo;
};<|endoftext|>


<commit_before>/*
 * Requires:
 *     jquery
 *     backbone
 *     underscore
 */

/****************
 * Internals    *
 ***************/

// Sets up global notifications pub/sub
// Notifications get submitted here (via trigger) and subscribed to (via on)
Backbone.Notifications = {};
_.extend(Backbone.Notifications, Backbone.Events);

var optoutmessage = "By leaving this page, you opt out of the experiment.";
var startTask = function () {
	psiTurk.saveData();

	$.ajax("inexp", {
			type: "POST",
			async: true,
			data: {uniqueId: uniqueId}
	});

	// Provide opt-out
	$(window).on("beforeunload", function(){
		psiTurk.saveData();

		$.ajax("quitter", {
				type: "POST",
				async: false,
				data: {uniqueId: uniqueId}
		});
		alert(msg);
		return "Are you sure you want to leave the experiment?";
	});
};
Backbone.Notifications.on('_psiturk_finishedinstructions', startTask);
Backbone.Notifications.on('_psiturk_finishedtask', function(msg) { $(window).off("beforeunload"); });

$(window).blur( function() {
	Backbone.Notifications.trigger('_psiturk_lostfocus');
});

$(window).focus( function() {
	Backbone.Notifications.trigger('_psiturk_gainedfocus');
});

// track changes in window size
function triggerResize() {
	Backbone.Notifications.trigger('_psiturk_windowresize', [window.innerWidth, window.innerHeight]);
};

var to = false;
$(window).resize(function(){
 if(to !== false)
    clearTimeout(to);
 to = setTimeout(triggerResize, 200);
});

/****************
 * HTML PAGES   *
 ***************/

var Page = Backbone.Model.extend({
	defaults: {
		name: "",
		html: ""
	}
});

var PageCollection = Backbone.Collection.extend({
	model: Page,
	url: '/pages',

	// need this because Flask won't return JSON with
	// array at the top level
	parse: function(response) {
		return response.collection;
	},

	getHTML: function(pagename, callback) {
		matches = this.where({name: pagename});
		if (matches.length > 0) callback(matches[0].get('html'));
		else console.log("Page not found: ", pagename);
	}
});

/****************
 * IMAGES       *
 ***************/

var ExpImage = Backbone.Model.extend({
	defaults: {
		name: "",
		loc: "",
		el: null
	},
	initialize: function() {
		var img = new Image();
		img.src = this.get('loc');
		this.set('el', img);
	}
});

var ImageCollection = Backbone.Collection.extend({

	model: ExpImage,
	url: '/images',

	parse: function(response) {
		return response.collection;
	},

	getImage: function(imagename, callback) {
		matches = this.where({name: imagename});
		if (matches.length > 0) {
			callback(matches[0].get('el'));
		} else {
			console.log('Image not found: ', imagename);
		}
	}
});

/****************
 * TASK DATA    *
 ***************/

var TaskData = Backbone.Model.extend({
	urlRoot: "/sync", // Save will PUT to /data, with mimetype 'application/JSON'
	id: uniqueId,

	defaults: {
		condition: 0,
		counterbalance: 0,
		assignmentId: 0,
		workerId: 0,
		hitId: 0,
		currenttrial: 0,
		bonus: 0,
		data: "",
		questiondata: {},
		eventdata: [],
		useragent: ""
	},

	initialize: function() {
		this.useragent = navigator.userAgent;
		this.addEvent('initialized', null);
		this.addEvent('window_resize', [window.innerWidth, window.innerHeight]);

		this.listenTo(Backbone.Notifications, '_psiturk_lostfocus', function() { this.addEvent('focus', 'off'); });
		this.listenTo(Backbone.Notifications, '_psiturk_gainedfocus', function() { this.addEvent('focus', 'on'); });
		this.listenTo(Backbone.Notifications, '_psiturk_windowresize', function(newsize) { this.addEvent('window_resize', newsize); });
	},

	addTrialData: function(trialdata) {
		this.set({"data": this.get("data").concat(trialdata, "\n")});
		this.set({"currenttrial": this.get("currenttrial")+1});
	},

	addUnstructuredData: function(field, response) {
		qd = this.get("questiondata");
		qd[field] = response;
		this.set("questiondata", qd);
	},

	addEvent: function(eventtype, value) {

		var ed = this.get('eventdata');
		var timestamp = new Date().getTime();

		if (eventtype == 'initialized') {
			var interval = 0;
		} else {
			var interval = timestamp - ed[ed.length-1]['timestamp'];
		}

		ed.push({'eventtype': eventtype, 'value': value, 'timestamp': timestamp, 'interval': interval});
		this.set('eventdata', ed);
	},

});

/*******
 * API *
 ******/
var PsiTurk = function() {
	var taskdata = new TaskData();
	taskdata.fetch({async: false});

	// Load pages and images:
	var pages = new PageCollection();
	pages.fetch({async: false});

	var images = new ImageCollection();
	images.fetch({async: false});

	return {
		/*  DATA: */
		pages: pages,
		images: images,
		taskdata: taskdata,

		/*  METHODS: */
		// Get HTML file from collection and pass on to a callback
		getPage: function(pagename, callback) {
			pages.getHTML(pagename, callback);
		},

		// Get a single image element and pass to callback
		getImage: function(imagename, callback) {
			images.getImage(imagename, callback);
		},

		// Add a line of data with any number of columns
		recordTrialData: function(trialdata) {
			taskdata.addTrialData(trialdata);
		},

		// Add data value for a named column. If a value already
		// exists for that column, it will be overwritten
		recordUnstructuredData: function(field, value) {
			taskdata.addUnstructuredData(field, value);
		},

		// Save data to server
		saveData: function(callbacks) {
			taskdata.save(undefined, callbacks);
		},

		// Notify app that participant has begun main experiment
		finishInstructions: function(optmessage) {
			Backbone.Notifications.trigger('_psiturk_finishedinstructions', optmessage);
		},

		teardownTask: function(optmessage) {
			Backbone.Notifications.trigger('_psiturk_finishedtask', optmessage);
		}
	};
};

psiTurk = new PsiTurk();

// vi: noexpandtab nosmartindent shiftwidth=4 tabstop=4
<commit_msg>JSLint formatting fixes.<commit_after>/*
 * Requires:
 *     jquery
 *     backbone
 *     underscore
 */

/****************
 * Internals    *
 ***************/

// Sets up global notifications pub/sub
// Notifications get submitted here (via trigger) and subscribed to (via on)
Backbone.Notifications = {};
_.extend(Backbone.Notifications, Backbone.Events);

var optoutmessage = "By leaving this page, you opt out of the experiment.";
var startTask = function () {
	psiTurk.saveData();

	$.ajax("inexp", {
			type: "POST",
			async: true,
			data: {uniqueId: uniqueId}
	});

	// Provide opt-out
	$(window).on("beforeunload", function(){
		psiTurk.saveData();

		$.ajax("quitter", {
				type: "POST",
				async: false,
				data: {uniqueId: uniqueId}
		});
		alert(msg);
		return "Are you sure you want to leave the experiment?";
	});
};
Backbone.Notifications.on('_psiturk_finishedinstructions', startTask);
Backbone.Notifications.on('_psiturk_finishedtask', function(msg) { $(window).off("beforeunload"); });

$(window).blur( function() {
	Backbone.Notifications.trigger('_psiturk_lostfocus');
});

$(window).focus( function() {
	Backbone.Notifications.trigger('_psiturk_gainedfocus');
});

// track changes in window size
triggerResize = function() {
	Backbone.Notifications.trigger('_psiturk_windowresize', [window.innerWidth, window.innerHeight]);
};

var to = false;
$(window).resize(function(){
 if(to !== false)
    clearTimeout(to);
 to = setTimeout(triggerResize, 200);
});

/****************
 * HTML PAGES   *
 ***************/

var Page = Backbone.Model.extend({
	defaults: {
		name: "",
		html: ""
	}
});

var PageCollection = Backbone.Collection.extend({
	model: Page,
	url: '/pages',

	// need this because Flask won't return JSON with
	// array at the top level
	parse: function(response) {
		return response.collection;
	},

	getHTML: function(pagename, callback) {
		matches = this.where({name: pagename});
		if (matches.length > 0) callback(matches[0].get('html'));
		else console.log("Page not found: ", pagename);
	}
});

/****************
 * IMAGES       *
 ***************/

var ExpImage = Backbone.Model.extend({
	defaults: {
		name: "",
		loc: "",
		el: null
	},
	initialize: function() {
		var img = new Image();
		img.src = this.get('loc');
		this.set('el', img);
	}
});

var ImageCollection = Backbone.Collection.extend({

	model: ExpImage,
	url: '/images',

	parse: function(response) {
		return response.collection;
	},

	getImage: function(imagename, callback) {
		matches = this.where({name: imagename});
		if (matches.length > 0) {
			callback(matches[0].get('el'));
		} else {
			console.log('Image not found: ', imagename);
		}
	}
});

/****************
 * TASK DATA    *
 ***************/

var TaskData = Backbone.Model.extend({
	urlRoot: "/sync", // Save will PUT to /data, with mimetype 'application/JSON'
	id: uniqueId,

	defaults: {
		condition: 0,
		counterbalance: 0,
		assignmentId: 0,
		workerId: 0,
		hitId: 0,
		currenttrial: 0,
		bonus: 0,
		data: "",
		questiondata: {},
		eventdata: [],
		useragent: ""
	},

	initialize: function() {
		this.useragent = navigator.userAgent;
		this.addEvent('initialized', null);
		this.addEvent('window_resize', [window.innerWidth, window.innerHeight]);

		this.listenTo(Backbone.Notifications, '_psiturk_lostfocus', function() { this.addEvent('focus', 'off'); });
		this.listenTo(Backbone.Notifications, '_psiturk_gainedfocus', function() { this.addEvent('focus', 'on'); });
		this.listenTo(Backbone.Notifications, '_psiturk_windowresize', function(newsize) { this.addEvent('window_resize', newsize); });
	},

	addTrialData: function(trialdata) {
		this.set({"data": this.get("data").concat(trialdata, "\n")});
		this.set({"currenttrial": this.get("currenttrial")+1});
	},

	addUnstructuredData: function(field, response) {
		qd = this.get("questiondata");
		qd[field] = response;
		this.set("questiondata", qd);
	},

	addEvent: function(eventtype, value) {
		var interval,
		    ed = this.get('eventdata'),
		    timestamp = new Date().getTime();

		if (eventtype == 'initialized') {
			interval = 0;
		} else {
			interval = timestamp - ed[ed.length-1]['timestamp'];
		}

		ed.push({'eventtype': eventtype, 'value': value, 'timestamp': timestamp, 'interval': interval});
		this.set('eventdata', ed);
	}
});

/*******
 * API *
 ******/
var PsiTurk = function() {
	var taskdata = new TaskData();
	taskdata.fetch({async: false});

	// Load pages and images:
	var pages = new PageCollection();
	pages.fetch({async: false});

	var images = new ImageCollection();
	images.fetch({async: false});

	return {
		/*  DATA: */
		pages: pages,
		images: images,
		taskdata: taskdata,

		/*  METHODS: */
		// Get HTML file from collection and pass on to a callback
		getPage: function(pagename, callback) {
			pages.getHTML(pagename, callback);
		},

		// Get a single image element and pass to callback
		getImage: function(imagename, callback) {
			images.getImage(imagename, callback);
		},

		// Add a line of data with any number of columns
		recordTrialData: function(trialdata) {
			taskdata.addTrialData(trialdata);
		},

		// Add data value for a named column. If a value already
		// exists for that column, it will be overwritten
		recordUnstructuredData: function(field, value) {
			taskdata.addUnstructuredData(field, value);
		},

		// Save data to server
		saveData: function(callbacks) {
			taskdata.save(undefined, callbacks);
		},

		// Notify app that participant has begun main experiment
		finishInstructions: function(optmessage) {
			Backbone.Notifications.trigger('_psiturk_finishedinstructions', optmessage);
		},

		teardownTask: function(optmessage) {
			Backbone.Notifications.trigger('_psiturk_finishedtask', optmessage);
		}
	};
};

psiTurk = new PsiTurk();

// vi: noexpandtab nosmartindent shiftwidth=4 tabstop=4
<|endoftext|>