<commit_before>var subs = (function () {

var exports = {};

var stream_info = {}; // Maps lowercase stream name to stream properties object
// We fetch the stream colors asynchronous while the message feed is
// getting constructed, so we may need to go back and color streams
// that have already been rendered.
var initial_color_fetch = true;

var default_color = "#c2c2c2";
var next_sub_id = 0;

exports.subscribed_streams = function () {
    // TODO: Object.keys() compatibility
    var list = [];
    $.each(Object.keys(stream_info), function (idx, key) {
        var sub = stream_info[key];
        if (sub.subscribed) {
            list.push(sub.name);
        }
    });
    list.sort();
    return list;
};

function should_render_subscribers() {
    return domain !== 'mit.edu';
}

function should_list_all_streams() {
    return domain !== 'mit.edu';
}

function update_table_stream_color(table, stream_name, color) {
    $.each(table.find(".stream_label"), function () {
        if ($(this).text() === stream_name) {
            var parent_label = $(this).parent("td");
            parent_label.css("background-color", color);
            parent_label.prev("td").css("background-color", color);
        }
    });
}

function update_historical_message_color(stream_name, color) {
    update_table_stream_color($(".focused_table"), stream_name, color);
    if ($(".focused_table").attr("id") !== "#zhome") {
        update_table_stream_color($("#zhome"), stream_name, color);
    }
}

function update_stream_color(stream_name, color, opts) {
    opts = $.extend({}, {update_historical: false}, opts);
    var sub = stream_info[stream_name.toLowerCase()];
    sub.color = color;
    var id = parseInt(sub.id, 10);
    $("#subscription_" + id + " .color_swatch").css('background-color', color);
    if (opts.update_historical) {
        update_historical_message_color(stream_name, color);
    }
}

var colorpicker_options = {
    clickoutFiresChange: true,
    showPalette: true,
    palette: [
        ['a47462', 'c2726a', 'e4523d', 'e7664d', 'ee7e4a', 'f4ae55'],
        ['76ce90', '53a063', '94c849', 'bfd56f', 'fae589', 'f5ce6e'],
        ['a6dcbf', 'addfe5', 'a6c7e5', '4f8de4', '95a5fd', 'b0a5fd'],
        ['c2c2c2', 'c8bebf', 'c6a8ad', 'e79ab5', 'bd86e5', '9987e1']
    ],
    change: function (color) {
        // TODO: Kind of a hack.
        var sub_row = $(this).closest('.subscription_row');
        var stream_name = sub_row.find('.subscription_name').text();
        var hex_color = color.toHexString();

        update_stream_color(stream_name, hex_color, {update_historical: true});

        $.ajax({
            type:     'POST',
            url:      '/json/subscriptions/property',
            dataType: 'json',
            data: {
                "property": "stream_colors",
                "stream_name": stream_name,
                "color": hex_color
            },
            timeout:  10*1000
        });
    }
};

function create_sub(stream_name, attrs) {
    var sub = $.extend({}, {name: stream_name, color: default_color, id: next_sub_id++,
                            render_subscribers: should_render_subscribers(),
                            subscribed: true}, attrs);
    stream_info[stream_name.toLowerCase()] = sub;
    return sub;
}

function button_for_sub(sub) {
    var id = parseInt(sub.id, 10);
    return $("#subscription_" + id + " .sub_unsub_button");
}

function mark_subscribed(stream_name) {
    var lstream_name = stream_name.toLowerCase();
    var sub = stream_info[lstream_name];

    if (sub === undefined) {
        sub = create_sub(stream_name, {});
        $('#create_stream_row').after(templates.subscription({subscriptions: [sub]}));
    } else if (! sub.subscribed) {
        sub.subscribed = true;
        var button = button_for_sub(sub);
        if (button.length !== 0) {
            button.text("Unsubscribe").removeClass("btn-primary");
        } else {
            $('#create_stream_row').after(templates.subscription({subscriptions: [sub]}));
        }
    } else {
        // Already subscribed
        return;
    }
    typeahead_helper.update_autocomplete();
}

function mark_unsubscribed(stream_name) {
    var lstream_name = stream_name.toLowerCase();
    var sub = stream_info[lstream_name];

    if (sub === undefined) {
        // We don't know about this stream
        return;
    } else if (sub.subscribed) {
        sub.subscribed = false;
        button_for_sub(sub).text("Subscribe").addClass("btn-primary");
    } else {
        // Already unsubscribed
        return;
    }
    typeahead_helper.update_autocomplete();
}

exports.get_color = function (stream_name) {
    var lstream_name = stream_name.toLowerCase();
    if (stream_info[lstream_name] === undefined) {
        return default_color;
    }
    return stream_info[lstream_name].color;
};

function fetch_colors() {
    $.ajax({
        type:     'GET',
        url:      '/json/subscriptions/property',
        dataType: 'json',
        data: {"property": "stream_colors"},
        timeout:  10*1000,
        success: function (data) {
            if (data) {
                $.each(data.stream_colors, function (index, data) {
                    var stream_name = data[0];
                    var color = data[1];
                    update_stream_color(stream_name, color,
                                        {update_historical: initial_color_fetch});
                });
                initial_color_fetch = false;
            }
        }
    });
}

exports.setup_page = function () {
    util.make_loading_indicator($('#subs_page_loading_indicator'));

    var our_subs;
    var all_streams;

    function maybe_populate_subscriptions() {
        // We only execute if both asynchronous queries have returned
        if (our_subs === undefined || all_streams === undefined) {
            return;
        }

        var sub_rows = [];
        our_subs.forEach(function (elem) {
            var stream_name = elem[0];
            var sub = create_sub(stream_name, {color: elem[1], subscribed: true});
            stream_info[stream_name.toLowerCase()] = sub;
            sub_rows.push(sub);
        });

        all_streams.forEach(function (stream) {
            if (exports.have(stream)) {
                return;
            }
            var sub = create_sub(stream, {subscribed: false});
            stream_info[stream.toLowerCase()] = sub;
            sub_rows.push(sub);
        });

        sub_rows.sort(function (a, b) {
            return a.name.localeCompare(b.name);
        });

        $('#subscriptions_table tr:gt(0)').remove();
        $('#subscriptions_table').append(templates.subscription({subscriptions: sub_rows}));
        util.destroy_loading_indicator($('#subs_page_loading_indicator'));
        $('#create_stream_name').focus().select();
    }

    if (should_list_all_streams()) {
        // This query must go first to prevent a race when we are not
        // listing all streams
        $.ajax({
            type:     'POST',
            url:      '/json/get_public_streams',
            dataType: 'json',
            timeout:  10*1000,
            success: function (data) {
                if (data) {
                    all_streams = data.streams;
                    maybe_populate_subscriptions();
                }
            },
            error: function (xhr) {
                util.destroy_loading_indicator($('#subs_page_loading_indicator'));
                ui.report_error("Error listing subscriptions", xhr, $("#subscriptions-status"));
            }
        });
    } else {
        all_streams = [];
    }

    $.ajax({
        type:     'POST',
        url:      '/json/subscriptions/list',
        dataType: 'json',
        timeout:  10*1000,
        success: function (data) {
            if (data) {
                our_subs = data.subscriptions;
                maybe_populate_subscriptions();
            }
        },
        error: function (xhr) {
            util.destroy_loading_indicator($('#subs_page_loading_indicator'));
            ui.report_error("Error listing subscriptions", xhr, $("#subscriptions-status"));
        }
    });
};

exports.subscribe_for_send = function (stream, prompt_button) {
    $.ajax({
        type:     'POST',
        url:      '/json/subscriptions/add',
        data: {"subscriptions": JSON.stringify([stream]) },
        dataType: 'json',
        timeout:  10*60*1000, // 10 minutes in ms
        success: function (response) {
            mark_subscribed(stream);
            compose.finish();
            if (prompt_button !== undefined)
                prompt_button.stop(true).fadeOut(500);
        },
        error: function (xhr, error_type, exn) {
            ui.report_error("Unable to subscribe", xhr, $("#home-error"));
        }
    });
};

exports.have = function (stream_name) {
    var sub = stream_info[stream_name.toLowerCase()];
    if (sub !== undefined && sub.subscribed) {
        return sub;
    }
    return false;
};

function ajaxSubscribe(stream) {
    $.ajax({
        type: "POST",
        url: "/json/subscriptions/add",
        dataType: 'json', // This seems to be ignored. We still get back an xhr.
        data: {"subscriptions": JSON.stringify([stream]) },
        success: function (resp, statusText, xhr, form) {
            if ($("#create_stream_name").val() === stream) {
                $("#create_stream_name").val("");
            }
            var name, res = $.parseJSON(xhr.responseText);
            if (res.subscribed.length === 0) {
                name = res.already_subscribed[0];
                ui.report_success("Already subscribed to " + name, $("#subscriptions-status"));
            } else {
                name = res.subscribed[0];
            }
            mark_subscribed(name);
        },
        error: function (xhr) {
            ui.report_error("Error adding subscription", xhr, $("#subscriptions-status"));
            $("#create_stream_name").focus();
        }
    });
}

function ajaxUnsubscribe(stream) {
    $.ajax({
        type: "POST",
        url: "/json/subscriptions/remove",
        dataType: 'json', // This seems to be ignored. We still get back an xhr.
        data: {"subscriptions": JSON.stringify([stream]) },
        success: function (resp, statusText, xhr, form) {
            var name, res = $.parseJSON(xhr.responseText);
            if (res.removed.length === 0) {
                name = res.not_subscribed[0];
                ui.report_success("Already not subscribed to " + name,
                               $("#subscriptions-status"));
            } else {
                name = res.removed[0];
            }
            mark_unsubscribed(name);
        },
        error: function (xhr) {
            ui.report_error("Error removing subscription", xhr, $("#subscriptions-status"));
            $("#create_stream_name").focus();
        }
    });
}

$(function () {
    var i;
    // Populate stream_info with data handed over to client-side template.
    for (i = 0; i < stream_list.length; i++) {
        stream_info[stream_list[i].toLowerCase()] = create_sub(stream_list[i]);
    }
    fetch_colors();

    $("#add_new_subscription").on("submit", function (e) {
        e.preventDefault();
        ajaxSubscribe($("#create_stream_name").val());
    });

    $("#subscriptions_table").on("click", ".sub_unsub_button", function (e) {
        e.preventDefault();
        e.stopPropagation();
        var sub_row = $(e.target).closest('.subscription_row');
        var stream_name = sub_row.find('.subscription_name').text();
        var sub = stream_info[stream_name.toLowerCase()];

        if (sub.subscribed) {
            ajaxUnsubscribe(stream_name);
        } else {
            ajaxSubscribe(stream_name);
        }
    });

    $("#subscriptions_table").on("show", ".subscription_settings", function (e) {
        var colorpicker = $(e.target).closest('.subscription_row').find('.colorpicker');
        colorpicker.spectrum(colorpicker_options);
    });

    if (! should_render_subscribers()) {
        return;
    }

    // From here down is only stuff that happens when we're rendering
    // the subscriber settings

    $("#subscriptions_table").on("submit", ".subscriber_list_add form", function (e) {
        e.preventDefault();
        var sub_row = $(e.target).closest('.subscription_row');
        var stream = sub_row.find('.subscription_name').text();
        var text_box = sub_row.find('input[name="principal"]');
        var principal = $.trim(text_box.val());
        // TODO: clean up this error handling
        var error_elem = sub_row.find('.subscriber_list_container .alert-error');
        var warning_elem = sub_row.find('.subscriber_list_container .alert-warning');
        var list = sub_row.find('.subscriber_list_container ul');

        $.ajax({
            type: "POST",
            url: "/json/subscriptions/add",
            dataType: 'json',
            data: {"subscriptions": JSON.stringify([stream]),
                   "principal": principal},
            success: function (data) {
                text_box.val('');
                if (data.subscribed.length) {
                    error_elem.addClass("hide");
                    warning_elem.addClass("hide");
                    var subscriber = people_dict[principal].full_name + ' <' + principal + '>';
                    $('<li>').prependTo(list).text(subscriber);
                } else {
                    error_elem.addClass("hide");
                    warning_elem.removeClass("hide").text("User already subscribed");
                }
            },
            error: function (xhr) {
                warning_elem.addClass("hide");
                error_elem.removeClass("hide").text("Could not add user to this stream");
            }
        });
    });

    $("#subscriptions_table").on("show", ".subscription_settings", function (e) {
        var sub_row = $(e.target).closest('.subscription_row');
        var stream = sub_row.find('.subscription_name').text();
        var error_elem = sub_row.find('.subscriber_list_container .alert-error');
        var list = sub_row.find('.subscriber_list_container ul');
        var indicator_elem = sub_row.find('.subscriber_list_loading_indicator');

        error_elem.addClass('hide');
        list.empty();

        util.make_loading_indicator(indicator_elem);

        $.ajax({
            type: "POST",
            url: "/json/get_subscribers",
            dataType: 'json', // This seems to be ignored. We still get back an xhr.
            data: {stream: stream},
            success: function (data) {
                util.destroy_loading_indicator(indicator_elem);
                var subscribers = $.map(data.subscribers, function (elem) {
                    var person = people_dict[elem];
                    if (person === undefined) {
                        return elem;
                    }
                    return people_dict[elem].full_name + ' <' + elem + '>';
                });
                $.each(subscribers.sort(), function (idx, elem) {
                    $('<li>').appendTo(list).text(elem);
                });
            },
            error: function (xhr) {
                util.destroy_loading_indicator(indicator_elem);
                error_elem.removeClass("hide").text("Could not fetch subscriber list");
            }
        });

        sub_row.find('input[name="principal"]').typeahead({
            source: typeahead_helper.private_message_typeahead_list,
            items: 4,
            highlighter: function (item) {
                var query = this.query;
                return typeahead_helper.highlight_with_escaping(query, item);
            },
            matcher: function (item) {
                var query = $.trim(this.query);
                if (query === '') {
                    return false;
                }
                // Case-insensitive.
                return (item.toLowerCase().indexOf(query.toLowerCase()) !== -1);
            },
            updater: function (item) {
                return typeahead_helper.private_message_mapped[item].email;
            }
        });
    });
});

return exports;

}());
<commit_msg>Expand subscription settings of newly created streams<commit_after>var subs = (function () {

var exports = {};

var stream_info = {}; // Maps lowercase stream name to stream properties object
// We fetch the stream colors asynchronous while the message feed is
// getting constructed, so we may need to go back and color streams
// that have already been rendered.
var initial_color_fetch = true;

var default_color = "#c2c2c2";
var next_sub_id = 0;

exports.subscribed_streams = function () {
    // TODO: Object.keys() compatibility
    var list = [];
    $.each(Object.keys(stream_info), function (idx, key) {
        var sub = stream_info[key];
        if (sub.subscribed) {
            list.push(sub.name);
        }
    });
    list.sort();
    return list;
};

function should_render_subscribers() {
    return domain !== 'mit.edu';
}

function should_list_all_streams() {
    return domain !== 'mit.edu';
}

function update_table_stream_color(table, stream_name, color) {
    $.each(table.find(".stream_label"), function () {
        if ($(this).text() === stream_name) {
            var parent_label = $(this).parent("td");
            parent_label.css("background-color", color);
            parent_label.prev("td").css("background-color", color);
        }
    });
}

function update_historical_message_color(stream_name, color) {
    update_table_stream_color($(".focused_table"), stream_name, color);
    if ($(".focused_table").attr("id") !== "#zhome") {
        update_table_stream_color($("#zhome"), stream_name, color);
    }
}

function update_stream_color(stream_name, color, opts) {
    opts = $.extend({}, {update_historical: false}, opts);
    var sub = stream_info[stream_name.toLowerCase()];
    sub.color = color;
    var id = parseInt(sub.id, 10);
    $("#subscription_" + id + " .color_swatch").css('background-color', color);
    if (opts.update_historical) {
        update_historical_message_color(stream_name, color);
    }
}

var colorpicker_options = {
    clickoutFiresChange: true,
    showPalette: true,
    palette: [
        ['a47462', 'c2726a', 'e4523d', 'e7664d', 'ee7e4a', 'f4ae55'],
        ['76ce90', '53a063', '94c849', 'bfd56f', 'fae589', 'f5ce6e'],
        ['a6dcbf', 'addfe5', 'a6c7e5', '4f8de4', '95a5fd', 'b0a5fd'],
        ['c2c2c2', 'c8bebf', 'c6a8ad', 'e79ab5', 'bd86e5', '9987e1']
    ],
    change: function (color) {
        // TODO: Kind of a hack.
        var sub_row = $(this).closest('.subscription_row');
        var stream_name = sub_row.find('.subscription_name').text();
        var hex_color = color.toHexString();

        update_stream_color(stream_name, hex_color, {update_historical: true});

        $.ajax({
            type:     'POST',
            url:      '/json/subscriptions/property',
            dataType: 'json',
            data: {
                "property": "stream_colors",
                "stream_name": stream_name,
                "color": hex_color
            },
            timeout:  10*1000
        });
    }
};

function create_sub(stream_name, attrs) {
    var sub = $.extend({}, {name: stream_name, color: default_color, id: next_sub_id++,
                            render_subscribers: should_render_subscribers(),
                            subscribed: true}, attrs);
    stream_info[stream_name.toLowerCase()] = sub;
    return sub;
}

function button_for_sub(sub) {
    var id = parseInt(sub.id, 10);
    return $("#subscription_" + id + " .sub_unsub_button");
}

function settings_for_sub(sub) {
    var id = parseInt(sub.id, 10);
    return $("#subscription_settings_" + id);
}

function add_sub_to_table(sub) {
    $('#create_stream_row').after(templates.subscription({subscriptions: [sub]}));
    settings_for_sub(sub).collapse('show');
}

function mark_subscribed(stream_name) {
    var lstream_name = stream_name.toLowerCase();
    var sub = stream_info[lstream_name];

    if (sub === undefined) {
        sub = create_sub(stream_name, {});
        add_sub_to_table(sub);
    } else if (! sub.subscribed) {
        sub.subscribed = true;
        var button = button_for_sub(sub);
        if (button.length !== 0) {
            button.text("Unsubscribe").removeClass("btn-primary");
        } else {
            add_sub_to_table(sub);
        }
    } else {
        // Already subscribed
        return;
    }
    typeahead_helper.update_autocomplete();
}

function mark_unsubscribed(stream_name) {
    var lstream_name = stream_name.toLowerCase();
    var sub = stream_info[lstream_name];

    if (sub === undefined) {
        // We don't know about this stream
        return;
    } else if (sub.subscribed) {
        sub.subscribed = false;
        button_for_sub(sub).text("Subscribe").addClass("btn-primary");
    } else {
        // Already unsubscribed
        return;
    }
    typeahead_helper.update_autocomplete();
}

exports.get_color = function (stream_name) {
    var lstream_name = stream_name.toLowerCase();
    if (stream_info[lstream_name] === undefined) {
        return default_color;
    }
    return stream_info[lstream_name].color;
};

function fetch_colors() {
    $.ajax({
        type:     'GET',
        url:      '/json/subscriptions/property',
        dataType: 'json',
        data: {"property": "stream_colors"},
        timeout:  10*1000,
        success: function (data) {
            if (data) {
                $.each(data.stream_colors, function (index, data) {
                    var stream_name = data[0];
                    var color = data[1];
                    update_stream_color(stream_name, color,
                                        {update_historical: initial_color_fetch});
                });
                initial_color_fetch = false;
            }
        }
    });
}

exports.setup_page = function () {
    util.make_loading_indicator($('#subs_page_loading_indicator'));

    var our_subs;
    var all_streams;

    function maybe_populate_subscriptions() {
        // We only execute if both asynchronous queries have returned
        if (our_subs === undefined || all_streams === undefined) {
            return;
        }

        var sub_rows = [];
        our_subs.forEach(function (elem) {
            var stream_name = elem[0];
            var sub = create_sub(stream_name, {color: elem[1], subscribed: true});
            stream_info[stream_name.toLowerCase()] = sub;
            sub_rows.push(sub);
        });

        all_streams.forEach(function (stream) {
            if (exports.have(stream)) {
                return;
            }
            var sub = create_sub(stream, {subscribed: false});
            stream_info[stream.toLowerCase()] = sub;
            sub_rows.push(sub);
        });

        sub_rows.sort(function (a, b) {
            return a.name.localeCompare(b.name);
        });

        $('#subscriptions_table tr:gt(0)').remove();
        $('#subscriptions_table').append(templates.subscription({subscriptions: sub_rows}));
        util.destroy_loading_indicator($('#subs_page_loading_indicator'));
        $('#create_stream_name').focus().select();
    }

    if (should_list_all_streams()) {
        // This query must go first to prevent a race when we are not
        // listing all streams
        $.ajax({
            type:     'POST',
            url:      '/json/get_public_streams',
            dataType: 'json',
            timeout:  10*1000,
            success: function (data) {
                if (data) {
                    all_streams = data.streams;
                    maybe_populate_subscriptions();
                }
            },
            error: function (xhr) {
                util.destroy_loading_indicator($('#subs_page_loading_indicator'));
                ui.report_error("Error listing subscriptions", xhr, $("#subscriptions-status"));
            }
        });
    } else {
        all_streams = [];
    }

    $.ajax({
        type:     'POST',
        url:      '/json/subscriptions/list',
        dataType: 'json',
        timeout:  10*1000,
        success: function (data) {
            if (data) {
                our_subs = data.subscriptions;
                maybe_populate_subscriptions();
            }
        },
        error: function (xhr) {
            util.destroy_loading_indicator($('#subs_page_loading_indicator'));
            ui.report_error("Error listing subscriptions", xhr, $("#subscriptions-status"));
        }
    });
};

exports.subscribe_for_send = function (stream, prompt_button) {
    $.ajax({
        type:     'POST',
        url:      '/json/subscriptions/add',
        data: {"subscriptions": JSON.stringify([stream]) },
        dataType: 'json',
        timeout:  10*60*1000, // 10 minutes in ms
        success: function (response) {
            mark_subscribed(stream);
            compose.finish();
            if (prompt_button !== undefined)
                prompt_button.stop(true).fadeOut(500);
        },
        error: function (xhr, error_type, exn) {
            ui.report_error("Unable to subscribe", xhr, $("#home-error"));
        }
    });
};

exports.have = function (stream_name) {
    var sub = stream_info[stream_name.toLowerCase()];
    if (sub !== undefined && sub.subscribed) {
        return sub;
    }
    return false;
};

function ajaxSubscribe(stream) {
    $.ajax({
        type: "POST",
        url: "/json/subscriptions/add",
        dataType: 'json', // This seems to be ignored. We still get back an xhr.
        data: {"subscriptions": JSON.stringify([stream]) },
        success: function (resp, statusText, xhr, form) {
            if ($("#create_stream_name").val() === stream) {
                $("#create_stream_name").val("");
            }
            var name, res = $.parseJSON(xhr.responseText);
            if (res.subscribed.length === 0) {
                name = res.already_subscribed[0];
                ui.report_success("Already subscribed to " + name, $("#subscriptions-status"));
            } else {
                name = res.subscribed[0];
            }
            mark_subscribed(name);
        },
        error: function (xhr) {
            ui.report_error("Error adding subscription", xhr, $("#subscriptions-status"));
            $("#create_stream_name").focus();
        }
    });
}

function ajaxUnsubscribe(stream) {
    $.ajax({
        type: "POST",
        url: "/json/subscriptions/remove",
        dataType: 'json', // This seems to be ignored. We still get back an xhr.
        data: {"subscriptions": JSON.stringify([stream]) },
        success: function (resp, statusText, xhr, form) {
            var name, res = $.parseJSON(xhr.responseText);
            if (res.removed.length === 0) {
                name = res.not_subscribed[0];
                ui.report_success("Already not subscribed to " + name,
                               $("#subscriptions-status"));
            } else {
                name = res.removed[0];
            }
            mark_unsubscribed(name);
        },
        error: function (xhr) {
            ui.report_error("Error removing subscription", xhr, $("#subscriptions-status"));
            $("#create_stream_name").focus();
        }
    });
}

$(function () {
    var i;
    // Populate stream_info with data handed over to client-side template.
    for (i = 0; i < stream_list.length; i++) {
        stream_info[stream_list[i].toLowerCase()] = create_sub(stream_list[i]);
    }
    fetch_colors();

    $("#add_new_subscription").on("submit", function (e) {
        e.preventDefault();
        ajaxSubscribe($("#create_stream_name").val());
    });

    $("#subscriptions_table").on("click", ".sub_unsub_button", function (e) {
        e.preventDefault();
        e.stopPropagation();
        var sub_row = $(e.target).closest('.subscription_row');
        var stream_name = sub_row.find('.subscription_name').text();
        var sub = stream_info[stream_name.toLowerCase()];

        if (sub.subscribed) {
            ajaxUnsubscribe(stream_name);
        } else {
            ajaxSubscribe(stream_name);
        }
    });

    $("#subscriptions_table").on("show", ".subscription_settings", function (e) {
        var colorpicker = $(e.target).closest('.subscription_row').find('.colorpicker');
        colorpicker.spectrum(colorpicker_options);
    });

    if (! should_render_subscribers()) {
        return;
    }

    // From here down is only stuff that happens when we're rendering
    // the subscriber settings

    $("#subscriptions_table").on("submit", ".subscriber_list_add form", function (e) {
        e.preventDefault();
        var sub_row = $(e.target).closest('.subscription_row');
        var stream = sub_row.find('.subscription_name').text();
        var text_box = sub_row.find('input[name="principal"]');
        var principal = $.trim(text_box.val());
        // TODO: clean up this error handling
        var error_elem = sub_row.find('.subscriber_list_container .alert-error');
        var warning_elem = sub_row.find('.subscriber_list_container .alert-warning');
        var list = sub_row.find('.subscriber_list_container ul');

        $.ajax({
            type: "POST",
            url: "/json/subscriptions/add",
            dataType: 'json',
            data: {"subscriptions": JSON.stringify([stream]),
                   "principal": principal},
            success: function (data) {
                text_box.val('');
                if (data.subscribed.length) {
                    error_elem.addClass("hide");
                    warning_elem.addClass("hide");
                    var subscriber = people_dict[principal].full_name + ' <' + principal + '>';
                    $('<li>').prependTo(list).text(subscriber);
                } else {
                    error_elem.addClass("hide");
                    warning_elem.removeClass("hide").text("User already subscribed");
                }
            },
            error: function (xhr) {
                warning_elem.addClass("hide");
                error_elem.removeClass("hide").text("Could not add user to this stream");
            }
        });
    });

    $("#subscriptions_table").on("show", ".subscription_settings", function (e) {
        var sub_row = $(e.target).closest('.subscription_row');
        var stream = sub_row.find('.subscription_name').text();
        var error_elem = sub_row.find('.subscriber_list_container .alert-error');
        var list = sub_row.find('.subscriber_list_container ul');
        var indicator_elem = sub_row.find('.subscriber_list_loading_indicator');

        error_elem.addClass('hide');
        list.empty();

        util.make_loading_indicator(indicator_elem);

        $.ajax({
            type: "POST",
            url: "/json/get_subscribers",
            dataType: 'json', // This seems to be ignored. We still get back an xhr.
            data: {stream: stream},
            success: function (data) {
                util.destroy_loading_indicator(indicator_elem);
                var subscribers = $.map(data.subscribers, function (elem) {
                    var person = people_dict[elem];
                    if (person === undefined) {
                        return elem;
                    }
                    return people_dict[elem].full_name + ' <' + elem + '>';
                });
                $.each(subscribers.sort(), function (idx, elem) {
                    $('<li>').appendTo(list).text(elem);
                });
            },
            error: function (xhr) {
                util.destroy_loading_indicator(indicator_elem);
                error_elem.removeClass("hide").text("Could not fetch subscriber list");
            }
        });

        sub_row.find('input[name="principal"]').typeahead({
            source: typeahead_helper.private_message_typeahead_list,
            items: 4,
            highlighter: function (item) {
                var query = this.query;
                return typeahead_helper.highlight_with_escaping(query, item);
            },
            matcher: function (item) {
                var query = $.trim(this.query);
                if (query === '') {
                    return false;
                }
                // Case-insensitive.
                return (item.toLowerCase().indexOf(query.toLowerCase()) !== -1);
            },
            updater: function (item) {
                return typeahead_helper.private_message_mapped[item].email;
            }
        });
    });
});

return exports;

}());
<|endoftext|>


<commit_before>import { Class, isEqual } from '../../core/Core.js';
import { appendChildren, create as el } from '../../dom/Element.js';
import { AbstractInputView } from './AbstractInputView.js';

// ---

const HANDLED_KEYS = new Set([
    'ArrowDown',
    'ArrowLeft',
    'ArrowRight',
    'ArrowUp',
]);

/**
    Class: O.RadioGroupView

    Extends: O.AbstractInputView

    A view representing an HTML `<select>` menu. The `value` property is two-way
    bindable, representing the selected option.
*/
const RadioGroupView = Class({
    Name: 'RadioGroupView',

    Extends: AbstractInputView,

    init: function (/* ...mixins */) {
        RadioGroupView.parent.constructor.apply(this, arguments);
        const options = this.get('options');
        const value = this.get('value');
        this.set(
            'selectedIndex',
            options.findIndex((option) => isEqual(value, option.value)),
        );
    },

    icon: null,

    selectedIndex: null,

    tabIndex: 0,

    value: null,

    /**
        Property: O.RadioGroupView#options
        Type: Array

        The array of options to present in the select menu. Each item in the
        array should be an object, with the following properties:

        label       - {String} The text to display for the item
        description - {String} The subtext to display for the item
        value       - {*} The value for the <O.RadioGroupView#value> property to take
                      when this item is selected.
        isDisabled  - {Boolean} (optional) If true, the option will be disabled
                      (unselectable). Defaults to false if not present.
    */
    options: [],

    // --- Render ---

    _domControls: [],

    layerTag: 'fieldset',

    baseClassName: 'v-RadioGroup',

    type: '',

    /**
        Property: O.RadioGroupView#className
        Type: String
        Default: 'v-Select'

        Overrides default in <O.View#className>.
    */
    className: function () {
        const type = this.get('type');
        return (
            this.get('baseClassName') +
            (this.get('isDisabled') ? ' is-disabled' : '') +
            (this.get('isFocused') ? ' is-focused' : '') +
            (type ? ' ' + type : '')
        );
    }.property('type', 'isFocused', 'isDisabled'),

    drawControl(option, index) {
        const id = this.get('id');
        const control = el('input', {
            type: 'radio',
            id: id + '-option-' + index,
            className: this.get('baseClassName') + '-input',
            checked: index === this.get('selectedIndex'),
            disabled: this.get('isDisabled') || option.isDisabled,
            name: this.get('name') || id + '-value',
            tabIndex: '-1',
            onchange: (event) => {
                if (event.target.checked) {
                    this._settingFromInput = true;
                    this.set('selectedIndex', index);
                    this._settingFromInput = false;
                }
            },
        });
        this._domControls.push(control);
        if (control.checked) {
            this._domControl = control;
        }
        return control;
    },

    drawLabel(label) {
        return el('p', [label]);
    },

    /**
        Method: O.ToggleView#draw

        Overridden to draw toggle in layer. See <O.View#draw>.
    */
    drawOption(option, index) {
        const baseClassName = this.get('baseClassName');

        const control = this.drawControl(option, index);

        const label = this.drawLabel(option.label, option);

        let description = option.description;
        if (description) {
            description = this.drawDescription(description, option);
        }

        return el(
            `label.${baseClassName}-option`,
            [control, el(`div.${baseClassName}-text`, [label, description])],
        );
    },

    /**
        Method: O.RadioGroupView#draw

        Overridden to draw select menu in layer. See <O.View#draw>.
    */
    draw(/*layer*/) {
        let label = this.get('label');
        if (label) {
            label = this.drawLabel(label);
        }

        let description = this.get('description');
        if (description) {
            description = this.drawDescription(description);
        }

        this._domControls = [];
        const options = this.get('options').map(this.drawOption, this);

        this.redrawTabIndex();

        return [label, description, options];
    },

    // --- Keep render in sync with state ---

    /**
        Method: O.RadioGroupView#optionsNeedsRedraw

        Calls <O.View#propertyNeedsRedraw> for extra properties requiring
        redraw.
    */
    optionsNeedsRedraw: function (self, property, oldValue) {
        return this.propertyNeedsRedraw(self, property, oldValue);
    }.observes('options'),

    /**
        Method: O.RadioGroupView#redrawOptions

        Updates the DOM representation when the <O.RadioGroupView#options> property
        changes.
    */
    redrawOptions(layer, oldOptions) {
        const options = this.get('options');
        if (!isEqual(options, oldOptions)) {
            const baseElements = this.get('description') ? 2 : 1;
            while (layer.childElementCount > baseElements) {
                layer.removeChild(layer.lastElementChild);
            }

            this._domControls = [];
            appendChildren(layer, options.map(this.drawOption, this));

            if (this.get('isFocused')) {
                this.focus();
            }
        }
    },

    redrawIsDisabled() {
        const isDisabled = this.get('isDisabled');
        const options = this.get('options');
        this._domControls.forEach((control, i) => {
            control.disabled = isDisabled || options[i].isDisabled;
        });
    },

    redrawName() {
        const name = this.get('name');
        this._domControls.forEach((control) => {
            control.name = name;
        });
    },

    selectedIndexDidChange: function (_, __, oldIndex, index) {
        const control = this._domControls[index];
        if (control) {
            control.checked = true;
            control.tabIndex = this.get('tabIndex');
            if (this.get('isFocused')) {
                control.focus();
            }
            this._domControl = control;
        } else {
            this._domControl = this._domControls[0];
        }

        const oldControl = this._domControls[oldIndex];
        if (oldControl) {
            oldControl.checked = false;
            if (index !== -1) {
                oldControl.tabIndex = '-1';
            }
        }

        const newValue = index > -1 ? this.get('options')[index].value : null;
        this.userDidInput(newValue);
    }.observes('selectedIndex'),

    /**
        Method (private): O.AbstractControlView#_updateIsFocused

        Updates the <#isFocused> property.

        Parameters:
            event - {Event} The focus event.
    */
    _updateIsFocused: function (event) {
        if (event.type === 'focusin') {
            this.set('isFocused', true);
        } else if (!this.get('layer').contains(event.relatedTarget)) {
            this.set('isFocused', false);
        }
    }.on('focusin', 'focusout'),

    /**
        Method: O.RadioGroupView#redrawValue

        Checks the corresponding control in the radio group when the
        <O.RadioGroupView#value> property changes.
    */
    valueDidChange: function (_, __, ___, value) {
        if (this._settingFromInput) {
            return;
        }
        const options = this.get('options');
        this.set(
            'selectedIndex',
            options.findIndex((option) => isEqual(value, option.value)),
        );
    }.observes('value'),

    keydown: function (event) {
        const key = event.key;
        if (!HANDLED_KEYS.has(key)) {
            return;
        }
        event.preventDefault();

        let selectedIndex = this.get('selectedIndex');
        if (key === 'ArrowDown' || key === 'ArrowRight') {
            selectedIndex += 1;
        } else {
            selectedIndex -= 1;
        }

        const maxIndex = this.get('options').length - 1;
        if (selectedIndex > maxIndex) {
            selectedIndex = 0;
        } else if (selectedIndex < 0) {
            selectedIndex = maxIndex;
        }
        this.set('selectedIndex', selectedIndex);
    }.on('keydown'),
});

export { RadioGroupView };
<commit_msg>RadioGroupView: only call userDidInput when value changes due to user interaction<commit_after>import { Class, isEqual } from '../../core/Core.js';
import { appendChildren, create as el } from '../../dom/Element.js';
import { AbstractInputView } from './AbstractInputView.js';

// ---

const HANDLED_KEYS = new Set([
    'ArrowDown',
    'ArrowLeft',
    'ArrowRight',
    'ArrowUp',
]);

/**
    Class: O.RadioGroupView

    Extends: O.AbstractInputView

    A view representing an HTML `<select>` menu. The `value` property is two-way
    bindable, representing the selected option.
*/
const RadioGroupView = Class({
    Name: 'RadioGroupView',

    Extends: AbstractInputView,

    init: function (/* ...mixins */) {
        RadioGroupView.parent.constructor.apply(this, arguments);
        const options = this.get('options');
        const value = this.get('value');
        this.set(
            'selectedIndex',
            options.findIndex((option) => isEqual(value, option.value)),
        );
    },

    icon: null,

    selectedIndex: null,

    tabIndex: 0,

    value: null,

    /**
        Property: O.RadioGroupView#options
        Type: Array

        The array of options to present in the select menu. Each item in the
        array should be an object, with the following properties:

        label       - {String} The text to display for the item
        description - {String} The subtext to display for the item
        value       - {*} The value for the <O.RadioGroupView#value> property to take
                      when this item is selected.
        isDisabled  - {Boolean} (optional) If true, the option will be disabled
                      (unselectable). Defaults to false if not present.
    */
    options: [],

    // --- Render ---

    _domControls: [],

    layerTag: 'fieldset',

    baseClassName: 'v-RadioGroup',

    type: '',

    /**
        Property: O.RadioGroupView#className
        Type: String
        Default: 'v-Select'

        Overrides default in <O.View#className>.
    */
    className: function () {
        const type = this.get('type');
        return (
            this.get('baseClassName') +
            (this.get('isDisabled') ? ' is-disabled' : '') +
            (this.get('isFocused') ? ' is-focused' : '') +
            (type ? ' ' + type : '')
        );
    }.property('type', 'isFocused', 'isDisabled'),

    drawControl(option, index) {
        const id = this.get('id');
        const control = el('input', {
            type: 'radio',
            id: id + '-option-' + index,
            className: this.get('baseClassName') + '-input',
            checked: index === this.get('selectedIndex'),
            disabled: this.get('isDisabled') || option.isDisabled,
            name: this.get('name') || id + '-value',
            tabIndex: '-1',
            onchange: (event) => {
                if (event.target.checked) {
                    this._settingFromInput = true;
                    this.set('selectedIndex', index);
                    this._settingFromInput = false;
                }
            },
        });
        this._domControls.push(control);
        if (control.checked) {
            this._domControl = control;
        }
        return control;
    },

    drawLabel(label) {
        return el('p', [label]);
    },

    /**
        Method: O.ToggleView#draw

        Overridden to draw toggle in layer. See <O.View#draw>.
    */
    drawOption(option, index) {
        const baseClassName = this.get('baseClassName');

        const control = this.drawControl(option, index);

        const label = this.drawLabel(option.label, option);

        let description = option.description;
        if (description) {
            description = this.drawDescription(description, option);
        }

        return el(
            `label.${baseClassName}-option`,
            [control, el(`div.${baseClassName}-text`, [label, description])],
        );
    },

    /**
        Method: O.RadioGroupView#draw

        Overridden to draw select menu in layer. See <O.View#draw>.
    */
    draw(/*layer*/) {
        let label = this.get('label');
        if (label) {
            label = this.drawLabel(label);
        }

        let description = this.get('description');
        if (description) {
            description = this.drawDescription(description);
        }

        this._domControls = [];
        const options = this.get('options').map(this.drawOption, this);

        this.redrawTabIndex();

        return [label, description, options];
    },

    // --- Keep render in sync with state ---

    /**
        Method: O.RadioGroupView#optionsNeedsRedraw

        Calls <O.View#propertyNeedsRedraw> for extra properties requiring
        redraw.
    */
    optionsNeedsRedraw: function (self, property, oldValue) {
        return this.propertyNeedsRedraw(self, property, oldValue);
    }.observes('options'),

    /**
        Method: O.RadioGroupView#redrawOptions

        Updates the DOM representation when the <O.RadioGroupView#options> property
        changes.
    */
    redrawOptions(layer, oldOptions) {
        const options = this.get('options');
        if (!isEqual(options, oldOptions)) {
            const baseElements = this.get('description') ? 2 : 1;
            while (layer.childElementCount > baseElements) {
                layer.removeChild(layer.lastElementChild);
            }

            this._domControls = [];
            appendChildren(layer, options.map(this.drawOption, this));

            if (this.get('isFocused')) {
                this.focus();
            }
        }
    },

    redrawIsDisabled() {
        const isDisabled = this.get('isDisabled');
        const options = this.get('options');
        this._domControls.forEach((control, i) => {
            control.disabled = isDisabled || options[i].isDisabled;
        });
    },

    redrawName() {
        const name = this.get('name');
        this._domControls.forEach((control) => {
            control.name = name;
        });
    },

    selectedIndexDidChange: function (_, __, oldIndex, index) {
        const control = this._domControls[index];
        if (control) {
            control.checked = true;
            control.tabIndex = this.get('tabIndex');
            if (this.get('isFocused')) {
                control.focus();
            }
            this._domControl = control;
        } else {
            this._domControl = this._domControls[0];
        }

        const oldControl = this._domControls[oldIndex];
        if (oldControl) {
            oldControl.checked = false;
            if (index !== -1) {
                oldControl.tabIndex = '-1';
            }
        }

        const newValue = index > -1 ? this.get('options')[index].value : null;
        if (this._settingFromInput) {
            this.userDidInput(newValue);
        }
    }.observes('selectedIndex'),

    /**
        Method (private): O.AbstractControlView#_updateIsFocused

        Updates the <#isFocused> property.

        Parameters:
            event - {Event} The focus event.
    */
    _updateIsFocused: function (event) {
        if (event.type === 'focusin') {
            this.set('isFocused', true);
        } else if (!this.get('layer').contains(event.relatedTarget)) {
            this.set('isFocused', false);
        }
    }.on('focusin', 'focusout'),

    /**
        Method: O.RadioGroupView#redrawValue

        Checks the corresponding control in the radio group when the
        <O.RadioGroupView#value> property changes.
    */
    valueDidChange: function (_, __, ___, value) {
        if (this._settingFromInput) {
            return;
        }
        const options = this.get('options');
        this.set(
            'selectedIndex',
            options.findIndex((option) => isEqual(value, option.value)),
        );
    }.observes('value'),

    keydown: function (event) {
        const key = event.key;
        if (!HANDLED_KEYS.has(key)) {
            return;
        }
        event.preventDefault();

        let selectedIndex = this.get('selectedIndex');
        if (key === 'ArrowDown' || key === 'ArrowRight') {
            selectedIndex += 1;
        } else {
            selectedIndex -= 1;
        }

        const maxIndex = this.get('options').length - 1;
        if (selectedIndex > maxIndex) {
            selectedIndex = 0;
        } else if (selectedIndex < 0) {
            selectedIndex = maxIndex;
        }
        this._settingFromInput = true;
        this.set('selectedIndex', selectedIndex);
        this._settingFromInput = false;
    }.on('keydown'),
});

export { RadioGroupView };
<|endoftext|>


<commit_before>/*
* loglevel - https://github.com/pimterry/loglevel
*
* Copyright (c) 2013 Tim Perry
* Licensed under the MIT license.
*/
(function (root, definition) {
    "use strict";
    if (typeof define === 'function' && define.amd) {
        define(definition);
    } else if (typeof module === 'object' && module.exports) {
        module.exports = definition();
    } else {
        root.log = definition();
    }
}(this, function () {
    "use strict";

    // Slightly dubious tricks to cut down minimized file size
    var noop = function() {};
    var undefinedType = "undefined";

    var logMethods = [
        "trace",
        "debug",
        "info",
        "warn",
        "error"
    ];

    // Cross-browser bind equivalent that works at least back to IE6
    function bindMethod(obj, methodName) {
        var method = obj[methodName];
        if (typeof method.bind === 'function') {
            return method.bind(obj);
        } else {
            try {
                return Function.prototype.bind.call(method, obj);
            } catch (e) {
                // Missing bind shim or IE8 + Modernizr, fallback to wrapping
                return function() {
                    return Function.prototype.apply.apply(method, [obj, arguments]);
                };
            }
        }
    }

    // Build the best logging method possible for this env
    // Wherever possible we want to bind, not wrap, to preserve stack traces
    function realMethod(methodName) {
        if (typeof console === undefinedType) {
            return false; // No method possible, for now - fixed later by enableLoggingWhenConsoleArrives
        } else if (console[methodName] !== undefined) {
            return bindMethod(console, methodName);
        } else if (console.log !== undefined) {
            return bindMethod(console, 'log');
        } else {
            return noop;
        }
    }

    // These private functions always need `this` to be set properly

    function replaceLoggingMethods(level, loggerName) {
        /*jshint validthis:true */
        for (var i = 0; i < logMethods.length; i++) {
            var methodName = logMethods[i];
            this[methodName] = (i < level) ?
                noop :
                this.methodFactory(methodName, level, loggerName);
        }
    }

    // In old IE versions, the console isn't present until you first open it.
    // We build realMethod() replacements here that regenerate logging methods
    function enableLoggingWhenConsoleArrives(methodName, level, loggerName) {
        return function () {
            if (typeof console !== undefinedType) {
                replaceLoggingMethods.call(this, level, loggerName);
                this[methodName].apply(this, arguments);
            }
        };
    }

    // By default, we use closely bound real methods wherever possible, and
    // otherwise we wait for a console to appear, and then try again.
    function defaultMethodFactory(methodName, level, loggerName) {
        /*jshint validthis:true */
        return realMethod(methodName) ||
               enableLoggingWhenConsoleArrives.apply(this, arguments);
    }

    function Logger(name, defaultLevel, factory) {
      var self = this;
      var currentLevel;
      var storageKey = "loglevel";
      if (name) {
        storageKey += ":" + name;
      }

      function persistLevelIfPossible(levelNum) {
          var levelName = (logMethods[levelNum] || 'silent').toUpperCase();

          if (typeof window !== undefinedType) {
            // Use localStorage if available
            try {
                window.localStorage[storageKey] = levelName;
                return;
            } catch (ignore) {}

            // Use session cookie as fallback
            try {
                window.document.cookie =
                  encodeURIComponent(storageKey) + "=" + levelName + ";";
            } catch (ignore) {}
          }
      }

      function getPersistedLevel() {
          var storedLevel;

          if (typeof window !== undefinedType) {
            try {
                storedLevel = window.localStorage[storageKey];
            } catch (ignore) {}

            // Fallback to cookies if local storage gives us nothing
            if (typeof storedLevel === undefinedType) {
                try {
                    var cookie = window.document.cookie;
                    var location = cookie.indexOf(
                        encodeURIComponent(storageKey) + "=");
                    if (location) {
                        storedLevel = /^([^;]+)/.exec(cookie.slice(location))[1];
                    }
                } catch (ignore) {}
            }
          }

          // If the stored level is not valid, treat it as if nothing was stored.
          if (self.levels[storedLevel] === undefined) {
              storedLevel = undefined;
          }

          return storedLevel;
      }

      /*
       *
       * Public logger API - see https://github.com/pimterry/loglevel for details
       *
       */

      self.levels = { "TRACE": 0, "DEBUG": 1, "INFO": 2, "WARN": 3,
          "ERROR": 4, "SILENT": 5};

      self.methodFactory = factory || defaultMethodFactory;

      self.getLevel = function () {
          return currentLevel;
      };

      self.setLevel = function (level, persist) {
          if (typeof level === "string" && self.levels[level.toUpperCase()] !== undefined) {
              level = self.levels[level.toUpperCase()];
          }
          if (typeof level === "number" && level >= 0 && level <= self.levels.SILENT) {
              currentLevel = level;
              if (persist !== false) {  // defaults to true
                  persistLevelIfPossible(level);
              }
              replaceLoggingMethods.call(self, level, name);
              if (typeof console === undefinedType && level < self.levels.SILENT) {
                  return "No console available for logging";
              }
          } else {
              throw "log.setLevel() called with invalid level: " + level;
          }
      };

      self.setDefaultLevel = function (level) {
          if (!getPersistedLevel()) {
              self.setLevel(level, false);
          }
      };

      self.enableAll = function(persist) {
          self.setLevel(self.levels.TRACE, persist);
      };

      self.disableAll = function(persist) {
          self.setLevel(self.levels.SILENT, persist);
      };

      // Initialize with the right level
      var initialLevel = getPersistedLevel();
      if (initialLevel == null) {
          initialLevel = defaultLevel == null ? "WARN" : defaultLevel;
      }
      self.setLevel(initialLevel, false);
    }

    /*
     *
     * Top-level API
     *
     */

    var defaultLogger = new Logger();

    var _loggersByName = {};
    defaultLogger.getLogger = function getLogger(name) {
        if (typeof name !== "string" || name === "") {
          throw new TypeError("You must supply a name when creating a logger.");
        }

        var logger = _loggersByName[name];
        if (!logger) {
          logger = _loggersByName[name] = new Logger(
            name, defaultLogger.getLevel(), defaultLogger.methodFactory);
        }
        return logger;
    };

    // Grab the current global log variable in case of overwrite
    var _log = (typeof window !== undefinedType) ? window.log : undefined;
    defaultLogger.noConflict = function() {
        if (typeof window !== undefinedType &&
               window.log === defaultLogger) {
            window.log = _log;
        }

        return defaultLogger;
    };

    return defaultLogger;
}));
<commit_msg>Cleanup<commit_after>/*
* loglevel - https://github.com/pimterry/loglevel
*
* Copyright (c) 2013 Tim Perry
* Licensed under the MIT license.
*/
(function (root, definition) {
    "use strict";
    if (typeof define === 'function' && define.amd) {
        define(definition);
    } else if (typeof module === 'object' && module.exports) {
        module.exports = definition();
    } else {
        root.log = definition();
    }
}(this, function () {
    "use strict";

    // Slightly dubious tricks to cut down minimized file size
    var noop = function() {};
    var undefinedType = "undefined";

    var logMethods = [
        "trace",
        "debug",
        "info",
        "warn",
        "error"
    ];

    // Cross-browser bind equivalent that works at least back to IE6
    function bindMethod(obj, methodName) {
        var method = obj[methodName];
        if (typeof method.bind === 'function') {
            return method.bind(obj);
        } else {
            try {
                return Function.prototype.bind.call(method, obj);
            } catch (e) {
                // Missing bind shim or IE8 + Modernizr, fallback to wrapping
                return function() {
                    return Function.prototype.apply.apply(method, [obj, arguments]);
                };
            }
        }
    }

    // Build the best logging method possible for this env
    // Wherever possible we want to bind, not wrap, to preserve stack traces
    function realMethod(methodName) {
        if (typeof console === undefinedType) {
            return false; // No method possible, for now - fixed later by enableLoggingWhenConsoleArrives
        } else if (console[methodName] !== undefined) {
            return bindMethod(console, methodName);
        } else if (console.log !== undefined) {
            return bindMethod(console, 'log');
        } else {
            return noop;
        }
    }

    // These private functions always need `this` to be set properly

    function replaceLoggingMethods(level, loggerName) {
        /*jshint validthis:true */
        for (var i = 0; i < logMethods.length; i++) {
            var methodName = logMethods[i];
            this[methodName] = (i < level) ?
                noop :
                this.methodFactory(methodName, level, loggerName);
        }
    }

    // In old IE versions, the console isn't present until you first open it.
    // We build realMethod() replacements here that regenerate logging methods
    function enableLoggingWhenConsoleArrives(methodName, level, loggerName) {
        return function () {
            if (typeof console !== undefinedType) {
                replaceLoggingMethods.call(this, level, loggerName);
                this[methodName].apply(this, arguments);
            }
        };
    }

    // By default, we use closely bound real methods wherever possible, and
    // otherwise we wait for a console to appear, and then try again.
    function defaultMethodFactory(methodName, level, loggerName) {
        /*jshint validthis:true */
        return realMethod(methodName) ||
               enableLoggingWhenConsoleArrives.apply(this, arguments);
    }

    function Logger(name, defaultLevel, factory) {
      var self = this;
      var currentLevel;
      var storageKey = "loglevel";
      if (name) {
        storageKey += ":" + name;
      }

      function persistLevelIfPossible(levelNum) {
          var levelName = (logMethods[levelNum] || 'silent').toUpperCase();

          if (typeof window !== undefinedType) return;

          // Use localStorage if available
          try {
              window.localStorage[storageKey] = levelName;
              return;
          } catch (ignore) {}

          // Use session cookie as fallback
          try {
              window.document.cookie =
                encodeURIComponent(storageKey) + "=" + levelName + ";";
          } catch (ignore) {}
      }

      function getPersistedLevel() {
          var storedLevel;

          if (typeof window !== undefinedType) return storedLevel;

          try {
              storedLevel = window.localStorage[storageKey];
          } catch (ignore) {}

          // Fallback to cookies if local storage gives us nothing
          if (typeof storedLevel === undefinedType) {
              try {
                  var cookie = window.document.cookie;
                  var location = cookie.indexOf(
                      encodeURIComponent(storageKey) + "=");
                  if (location) {
                      storedLevel = /^([^;]+)/.exec(cookie.slice(location))[1];
                  }
              } catch (ignore) {}
          }

          // If the stored level is not valid, treat it as if nothing was stored.
          if (self.levels[storedLevel] === undefined) {
              storedLevel = undefined;
          }

          return storedLevel;
      }

      /*
       *
       * Public logger API - see https://github.com/pimterry/loglevel for details
       *
       */

      self.levels = { "TRACE": 0, "DEBUG": 1, "INFO": 2, "WARN": 3,
          "ERROR": 4, "SILENT": 5};

      self.methodFactory = factory || defaultMethodFactory;

      self.getLevel = function () {
          return currentLevel;
      };

      self.setLevel = function (level, persist) {
          if (typeof level === "string" && self.levels[level.toUpperCase()] !== undefined) {
              level = self.levels[level.toUpperCase()];
          }
          if (typeof level === "number" && level >= 0 && level <= self.levels.SILENT) {
              currentLevel = level;
              if (persist !== false) {  // defaults to true
                  persistLevelIfPossible(level);
              }
              replaceLoggingMethods.call(self, level, name);
              if (typeof console === undefinedType && level < self.levels.SILENT) {
                  return "No console available for logging";
              }
          } else {
              throw "log.setLevel() called with invalid level: " + level;
          }
      };

      self.setDefaultLevel = function (level) {
          if (!getPersistedLevel()) {
              self.setLevel(level, false);
          }
      };

      self.enableAll = function(persist) {
          self.setLevel(self.levels.TRACE, persist);
      };

      self.disableAll = function(persist) {
          self.setLevel(self.levels.SILENT, persist);
      };

      // Initialize with the right level
      var initialLevel = getPersistedLevel();
      if (initialLevel == null) {
          initialLevel = defaultLevel == null ? "WARN" : defaultLevel;
      }
      self.setLevel(initialLevel, false);
    }

    /*
     *
     * Top-level API
     *
     */

    var defaultLogger = new Logger();

    var _loggersByName = {};
    defaultLogger.getLogger = function getLogger(name) {
        if (typeof name !== "string" || name === "") {
          throw new TypeError("You must supply a name when creating a logger.");
        }

        var logger = _loggersByName[name];
        if (!logger) {
          logger = _loggersByName[name] = new Logger(
            name, defaultLogger.getLevel(), defaultLogger.methodFactory);
        }
        return logger;
    };

    // Grab the current global log variable in case of overwrite
    var _log = (typeof window !== undefinedType) ? window.log : undefined;
    defaultLogger.noConflict = function() {
        if (typeof window !== undefinedType &&
               window.log === defaultLogger) {
            window.log = _log;
        }

        return defaultLogger;
    };

    return defaultLogger;
}));
<|endoftext|>


<commit_before>/**
 * Routes for CasparCG
 *
 * Includes routes test
 *
 * Function parameters:
 *  - router. Express router object
 *  - connection. MySQL connection object for queries
 *
 * Required:
 *  - casparcg      CasparCG Node.js library (https://github.com/respectTheCode/node-caspar-cg) for communication
 *
 */

var CasparCG = require('caspar-cg');
var ccg = new CasparCG("192.168.1.100", 5250);
var _cg_connected = false;

function _connect(callback) {
    ccg.connect(function () {
        ccg.on('connected', function () {
            _cg_connected = true;
            callback();
            return true;
        });

        ccg.on('error', function(error) {
            callback(error);
            return false;
        });

        ccg.on('end', function() {
            ccg.connect();
            return false;
        });
    });
}

function _disconnect() {
    ccg.disconnect();
    _cg_connected = false;
    return true;
}

module.exports = function(router, connection) {
    router.post('/casparcg/connect', function(req, res) {
        _connect(function(error) {
            if(_cg_connected) {
                res.status(200).json({success: true, message: 'Connection successful'});
            } else {
                res.status(401).json({success: false, message: error});
            }
        });
    });

    router.post('/casparcg/disconnect', function(req, res) {
        _disconnect();
        return res.status(200).json({success: true, message: 'Disconnected from server'});
    });

    router.post('/casparcg/play', function(req, res) {
        var fields = req.body;

        if(!_cg_connected) {
            return res.status(300).json({success: false, message: 'Not connected to server'});
        }

        ccg.loadTemplate(fields.channel, 'JS/' + fields.template, 1, fields.inputs);
    });

    router.post('/casparcg/stop', function() {
        if(!_cg_connected) {
            return false;
        }

        ccg.stopTemplate('1-1');
    });

    router.put('/casparcg/update', function(req, res) {
        var fields = req.body;

        if(!_cg_connected) {
            return res.status(300).json({success: false, message: 'Not connected to server'});
        }

        ccg.updateTemplateData('1-1', fields.inputs);
    });
};<commit_msg>Commented routes and code. Changed error codes.<commit_after>/**
 * Routes for CasparCG
 *
 * Includes routes connect, disconnect, play, stop
 *
 * Function parameters:
 *  - router. Express router object
 *  - connection. MySQL connection object for queries
 *
 * Required:
 *  - caspar-cg      CasparCG Node.js library (https://github.com/respectTheCode/node-caspar-cg) for communication
 *
 */

var CasparCG = require('caspar-cg');
var ccg = new CasparCG("192.168.1.100", 5250);
var _cg_connected = false;

/**
 * Helper function for connection because it's used multiple times
 */
function _connect(callback) {
    ccg.connect(function () {
        ccg.on('connected', function () {
            _cg_connected = true;
            callback();
            return true;
        });

        ccg.on('error', function(error) {
            callback(error);
            return false;
        });

        ccg.on('end', function() {
            ccg.connect();
            return false;
        });
    });
}

/**
 * Helper function for disconnection
 */
function _disconnect() {
    ccg.disconnect();
    _cg_connected = false;
    return true;
}

module.exports = function(router, connection) {

    /**
     * Route: connect
     * Path: /api/casparcg/connect/
     *
     * Connects to CasparCG server
     *
     * Returns either a success code (200), or an error code 503 and message.
     */
    router.post('/casparcg/connect', function(req, res) {
        _connect(function(error) {
            if(_cg_connected) {
                res.status(200).json({success: true, message: 'Connection successful'});
            } else {
                res.status(503).json({success: false, message: error});
            }
        });
    });

    /**
     * Route: disconnect
     * Path: /api/casparcg/disconnect/
     *
     * Disconnects from CasparCG server
     *
     * Returns a success code (200) and message
     */
    router.post('/casparcg/disconnect', function(req, res) {
        _disconnect();
        return res.status(200).json({success: true, message: 'Disconnected from server'});
    });

    /**
     * Route: play
     * Path: /api/casparcg/play/
     *
     * Plays a given template in given channel passing on data from inputs as object
     * Request body example: { channel: '1-1', template: 'information', inputs: { f0: 'First', f1: 'Second', f2: 'Third } }
     *
     */
    router.post('/casparcg/play', function(req, res) {
        var fields = req.body;

        // Has to be changed to handle not connected -> reconnect. Experienced problems on first test
        if(!_cg_connected) {
            return res.status(503).json({success: false, message: 'Not connected to server'});
        }

        ccg.loadTemplate(fields.channel, 'JS/' + fields.template, 1, fields.inputs);
    });

    /**
     * Route: stop
     * Path: /api/casparcg/stop/
     *
     * Stops a template in given channel
     * Request body example: { channel: '1-1' }
     *
     */
    router.post('/casparcg/stop', function() {
        // Has to be changed to handle not connected -> reconnect. Experienced problems on first test
        if(!_cg_connected) {
            return res.status(503).json({success: false, message: 'Not connected to server'});
        }

        // Change this to get channel from request
        ccg.stopTemplate('1-1');
    });

    /**
     * Route: update
     * Path: /api/casparcg/update/
     *
     * Updates a template in given channel with given inputs
     * Request body example: { channel: '1-1', inputs: { f0: 'First', f1: 'Second', f2: 'Third } }
     *
     */
    router.put('/casparcg/update', function(req, res) {
        var fields = req.body;

        // Has to be changed to handle not connected -> reconnect. Experienced problems on first test
        if(!_cg_connected) {
            return res.status(503).json({success: false, message: 'Not connected to server'});
        }

        ccg.updateTemplateData('1-1', fields.inputs);
    });
};<|endoftext|>


<commit_before>// Karma configuration
// Generated on Thu Jul 04 2013 11:39:34 GMT+0200 (CEST)

module.exports = function(karma) {

  karma.set({

    // base path, that will be used to resolve files and exclude
    basePath: '../../',

    // frameworks to use
    //frameworks: ['jasmine', 'junit-reporter'],
    frameworks: ['jasmine'],

    // list of files / patterns to load in the browser
    files: [
      'test/lib/jasmine-1.3.1/jasmine-html.js',
      'test/lib/jslint.js',

      //Templates
      {pattern: 'frontend/js/templates/*.ejs', served: true, included: false, watched: true},

      'frontend/js/lib/jquery-2.1.0.min.js',
      'frontend/js/lib/jquery-ui-1.9.2.custom.js',
      'frontend/js/lib/jquery.dataTables.js',
      'frontend/js/lib/jquery.autogrow.js',
      'frontend/js/lib/jquery.jeditable.js',
      'frontend/js/lib/jquery.jeditable.autogrow.js',
      'frontend/js/lib/jquery.snippet.js',
      'frontend/js/lib/jquery.slideto.min.js',
      'frontend/js/lib/jquery.wiggle.min.js',
      'frontend/js/lib/jquery.contextmenu.js',
      'frontend/js/lib/handlebars-1.0.rc.1.js',
      'frontend/js/lib/underscore.js',
      'frontend/js/lib/backbone.js',
      'frontend/js/lib/d3.v3.min.js',
      'frontend/js/lib/nv.d3.js',
      'frontend/js/lib/d3.fisheye.js',
      'frontend/js/lib/ColVis.js',
      'frontend/js/lib/bootstrap.js',
      'frontend/js/lib/bootstrap-pagination.js',
      'frontend/src/ace.js',
      'frontend/js/lib/jqconsole.min.js',
      'frontend/js/lib/splitter.js',
      'frontend/js/lib/swagger.js',
      'frontend/js/lib/swagger-ui.js',
      'frontend/js/lib/highlight.7.3.pack.js',

      // Template Engine Mock
      'test/mocks/disableEJS.js',

      // arangodb
      'frontend/js/arango/arango.js',
      'frontend/js/shell/browser.js',
      'frontend/js/modules/org/arangodb/arango-collection-common.js',
      'frontend/js/modules/org/arangodb/arango-collection.js',
      'frontend/js/modules/org/arangodb/arango-database.js',
      'frontend/js/modules/org/arangodb/arango-query-cursor.js',
      'frontend/js/modules/org/arangodb/arango-statement-common.js',
      'frontend/js/modules/org/arangodb/arango-statement.js',
      'frontend/js/modules/org/arangodb/arangosh.js',
      'frontend/js/modules/org/arangodb/graph-common.js',
      'frontend/js/modules/org/arangodb/graph.js',
      'frontend/js/modules/org/arangodb/mimetypes.js',
      'frontend/js/modules/org/arangodb/simple-query-common.js',
      'frontend/js/modules/org/arangodb/simple-query.js',
      'frontend/js/modules/org/arangodb/aql/functions.js',
      'frontend/js/modules/org/arangodb/graph/traversal.js',
      'frontend/js/modules/org/arangodb-common.js',
      'frontend/js/modules/org/arangodb.js',
      'frontend/js/bootstrap/errors.js',
      'frontend/js/bootstrap/monkeypatches.js',
      'frontend/js/bootstrap/module-internal.js',
      'frontend/js/client/bootstrap/module-internal.js',
      'frontend/js/client/client.js',

      // Mocks
      'test/specs/graphViewer/helper/eventHelper.js',
      'test/specs/graphViewer/helper/objectsHelper.js',
      'test/specs/graphViewer/helper/mocks.js',
      'test/specs/graphViewer/helper/commMock.js',
      'test/specs/graphViewer/helper/uiMatchers.js',

      // GraphViewer
      // Core Modules
      'frontend/js/graphViewer/graphViewer.js',
      'frontend/js/graphViewer/graph/domObserverFactory.js',
      'frontend/js/graphViewer/graph/colourMapper.js',
      'frontend/js/graphViewer/graph/communityNode.js',
      'frontend/js/graphViewer/graph/webWorkerWrapper.js',
      'frontend/js/graphViewer/graph/nodeShaper.js',
      'frontend/js/graphViewer/graph/abstractAdapter.js',
      'frontend/js/graphViewer/graph/JSONAdapter.js',
      'frontend/js/graphViewer/graph/arangoAdapter.js',
      'frontend/js/graphViewer/graph/foxxAdapter.js',
      'frontend/js/graphViewer/graph/previewAdapter.js',
      'frontend/js/graphViewer/graph/edgeShaper.js',
      'frontend/js/graphViewer/graph/forceLayouter.js',
      'frontend/js/graphViewer/graph/eventDispatcher.js',
      'frontend/js/graphViewer/graph/eventLibrary.js',
      'frontend/js/graphViewer/graph/zoomManager.js',
      'frontend/js/graphViewer/graph/nodeReducer.js',
      'frontend/js/graphViewer/graph/modularityJoiner.js',

      // UI Modules
      'frontend/js/graphViewer/ui/modalDialogHelper.js',
      'frontend/js/graphViewer/ui/contextMenuHelper.js',
      'frontend/js/graphViewer/ui/nodeShaperControls.js',
      'frontend/js/graphViewer/ui/edgeShaperControls.js',
      'frontend/js/graphViewer/ui/arangoAdapterControls.js',
      'frontend/js/graphViewer/ui/layouterControls.js',
      'frontend/js/graphViewer/ui/uiComponentsHelper.js',
      'frontend/js/graphViewer/ui/eventDispatcherControls.js',
      'frontend/js/graphViewer/ui/graphViewerUI.js',
      'frontend/js/graphViewer/ui/graphViewerWidget.js',
      'frontend/js/graphViewer/ui/graphViewerPreview.js',

      // Models
      'frontend/js/models/currentDatabase.js',
      'frontend/js/models/arangoCollection.js',
      'frontend/js/models/arangoDatabase.js',
      'frontend/js/models/arangoDocument.js',
      'frontend/js/models/arangoLog.js',
      'frontend/js/models/arangoStatistics.js',
      'frontend/js/models/arangoStatisticsDescription.js',
      'frontend/js/models/foxx.js',
      'frontend/js/models/notification.js',
      'frontend/js/models/graph.js',
      'clusterFrontend/js/models/clusterServer.js',
      'clusterFrontend/js/models/clusterCoordinator.js',
      'clusterFrontend/js/models/clusterDatabase.js',
      'clusterFrontend/js/models/clusterCollection.js',
      'clusterFrontend/js/models/clusterShard.js',

      // Collections
      'frontend/js/collections/arangoCollections.js',
      'frontend/js/collections/arangoDocuments.js',
      'frontend/js/collections/arangoDocument.js',
      'frontend/js/collections/arangoDatabase.js',
      'frontend/js/collections/arangoLogs.js',
      'frontend/js/collections/arangoUsers.js',
      'frontend/js/collections/arangoStatisticsCollection.js',
      'frontend/js/collections/arangoStatisticsDescriptionCollection.js',
      'frontend/js/collections/foxxCollection.js',
      'frontend/js/collections/graphCollection.js',
      'clusterFrontend/js/collections/clusterServers.js',
      'clusterFrontend/js/collections/clusterCoordinators.js',
      'clusterFrontend/js/collections/clusterDatabases.js',
      'clusterFrontend/js/collections/clusterCollections.js',
      'clusterFrontend/js/collections/clusterShards.js',
      'frontend/js/collections/notificationCollection.js',

      // Views
      'frontend/js/views/navigationView.js',
      'frontend/js/views/apiView.js',
      'frontend/js/views/footerView.js',
      'frontend/js/views/queryView.js',
      'frontend/js/views/shellView.js',
      'frontend/js/views/dashboardView.js',
      'frontend/js/views/collectionsView.js',
      'frontend/js/views/collectionView.js',
      'frontend/js/views/collectionInfoView.js',
      'frontend/js/views/newCollectionView.js',
      'frontend/js/views/collectionsItemView.js',
      'frontend/js/views/documentsView.js',
      'frontend/js/views/documentView.js',
      'frontend/js/views/logsView.js',
      'frontend/js/views/applicationsView.js',
      'frontend/js/views/foxxActiveView.js',
      'frontend/js/views/foxxInstalledView.js',
      'frontend/js/views/foxxEditView.js',
      'frontend/js/views/foxxMountView.js',
      'frontend/js/views/appDocumentationView.js',
      'frontend/js/views/graphView.js',
      'frontend/js/views/graphManagementView.js',
      'frontend/js/views/addNewGraphView.js',
      'frontend/js/views/deleteGraphView.js',
      'frontend/js/views/dbSelectionView.js',
      'frontend/js/views/editListEntryView.js',
      'frontend/js/views/loginView.js',
      'frontend/js/views/clusterDashboardView.js',
      'frontend/js/views/clusterOverviewView.js',
      'frontend/js/views/clusterServerView.js',
      'frontend/js/views/clusterCoordinatorView.js',
      'frontend/js/views/clusterDatabaseView.js',
      'frontend/js/views/clusterCollectionView.js',
      'frontend/js/views/clusterShardsView.js',

      'frontend/js/views/statisticBarView.js',
      'frontend/js/views/userBarView.js',

      //Views Planner
      'clusterFrontend/js/views/planScenarioSelectorView.js',
      'clusterFrontend/js/views/planSymmetricView.js',
      'clusterFrontend/js/views/planTestView.js',

      //Views cluster
      'clusterFrontend/js/views/showClusterView.js',

      // Router
      'frontend/js/routers/router.js',
      'clusterFrontend/js/routers/clusterRouter.js',

      // Specs
      // GraphViewer
      'test/specs/graphViewer/specColourMapper/colourMapperSpec.js',
      'test/specs/graphViewer/specWindowObjects/domObserverFactorySpec.js',
      'test/specs/graphViewer/specCommunityNode/communityNodeSpec.js',
      'test/specs/graphViewer/specAdapter/interfaceSpec.js',
      'test/specs/graphViewer/specAdapter/abstractAdapterSpec.js',
      'test/specs/graphViewer/specAdapter/jsonAdapterSpec.js',
      'test/specs/graphViewer/specAdapter/arangoAdapterSpec.js',
      'test/specs/graphViewer/specAdapter/foxxAdapterSpec.js',
      'test/specs/graphViewer/specAdapter/previewAdapterSpec.js',
      'test/specs/graphViewer/specAdapter/arangoAdapterUISpec.js',
      'test/specs/graphViewer/specNodeShaper/nodeShaperSpec.js',
      'test/specs/graphViewer/specNodeShaper/nodeShaperUISpec.js',
      'test/specs/graphViewer/specEdgeShaper/edgeShaperSpec.js',
      'test/specs/graphViewer/specEdgeShaper/edgeShaperUISpec.js',
      'test/specs/graphViewer/specForceLayouter/forceLayouterSpec.js',
      'test/specs/graphViewer/specForceLayouter/forceLayouterUISpec.js',
      'test/specs/graphViewer/specEvents/eventLibrarySpec.js',
      'test/specs/graphViewer/specEvents/eventDispatcherSpec.js',
      'test/specs/graphViewer/specEvents/eventDispatcherUISpec.js',
      'test/specs/graphViewer/specZoomManager/zoomManagerSpec.js',
      'test/specs/graphViewer/specGraphViewer/graphViewerSpec.js',
      'test/specs/graphViewer/specGraphViewer/graphViewerUISpec.js',
      'test/specs/graphViewer/specGraphViewer/graphViewerWidgetSpec.js',
      'test/specs/graphViewer/specGraphViewer/graphViewerPreviewSpec.js',
      'test/specs/graphViewer/specNodeReducer/nodeReducerSpec.js',
//      'test/specs/graphViewer/specNodeReducer/modularityJoinerSpec.js',
//      'test/specs/graphViewer/specWindowObjects/workerWrapperSpec.js',
      'test/specs/graphViewer/specContextMenu/contextMenuSpec.js',
      // Arango
      'test/specs/arango/arangoSpec.js',

      // Models
      'test/specs/models/currentDatabaseSpec.js',
      'test/specs/models/graphSpec.js',

      // Collections
      'test/specs/collections/clusterServersSpec.js',
      'test/specs/collections/clusterDatabasesSpec.js',
      'test/specs/collections/clusterCollectionsSpec.js',
      'test/specs/collections/clusterShardsSpec.js',

      // Views
      'test/specs/views/editListEntryViewSpec.js',
      'test/specs/views/collectionViewSpec.js',
      'test/specs/views/collectionsViewSpec.js',
      'test/specs/views/foxxEditViewSpec.js',
      'test/specs/views/dbSelectionViewSpec.js',
      'test/specs/views/navigationViewSpec.js',
      'test/specs/views/graphViewSpec.js',
      'test/specs/views/graphManagementViewSpec.js',
      'test/specs/views/addNewGraphViewSpec.js',
      'test/specs/views/clusterDashboardViewSpec.js',
      'test/specs/views/clusterOverviewViewSpec.js',
      'test/specs/views/clusterServerViewSpec.js',
      'test/specs/views/clusterCoordinatorViewSpec.js',
      'test/specs/views/clusterDatabaseViewSpec.js',
      'test/specs/views/clusterCollectionViewSpec.js',
      'test/specs/views/clusterShardsViewSpec.js',
      // Router
      'test/specs/router/routerSpec.js',

      //Planner
      //Router
      'test/specs/planner/router/routerSpec.js',
      //View
      'test/specs/planner/views/planSymmetricViewSpec.js',
      'test/specs/planner/views/planTestViewSpec.js',
      'test/specs/planner/views/planScenarioSelectorViewSpec.js',
      'test/specJSLint/jsLintSpec.js'
    ],

    // list of files to exclude
    exclude: [

    ],

    // test results reporter to use
    // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
    reporters: ['dots'],

    // web server port
    port: 9876,

    // cli runner port
    runnerPort: 9100,

    // enable / disable colors in the output (reporters and logs)
    colors: false,

    // level of logging
    // possible values: karma.LOG_DISABLE || karma.LOG_ERROR || karma.LOG_WARN || karma.LOG_INFO || karma.LOG_DEBUG
    logLevel: karma.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: false,

    // Start these browsers, currently available:
    // - Chrome
    // - ChromeCanary
    // - Firefox
    // - Opera
    // - Safari (only Mac)
    // - PhantomJS
    // - IE (only Windows)
    browsers: ["PhantomJS"],

    // If browser does not capture in given timeout [ms], kill it
    captureTimeout: 60000,

    // Continuous Integration mode
    // if true, it capture browsers, run tests and exit
    singleRun: true
  });
};
<commit_msg>Removed unused views from tests<commit_after>// Karma configuration
// Generated on Thu Jul 04 2013 11:39:34 GMT+0200 (CEST)

module.exports = function(karma) {

  karma.set({

    // base path, that will be used to resolve files and exclude
    basePath: '../../',

    // frameworks to use
    //frameworks: ['jasmine', 'junit-reporter'],
    frameworks: ['jasmine'],

    // list of files / patterns to load in the browser
    files: [
      'test/lib/jasmine-1.3.1/jasmine-html.js',
      'test/lib/jslint.js',

      //Templates
      {pattern: 'frontend/js/templates/*.ejs', served: true, included: false, watched: true},

      'frontend/js/lib/jquery-2.1.0.min.js',
      'frontend/js/lib/jquery-ui-1.9.2.custom.js',
      'frontend/js/lib/jquery.dataTables.js',
      'frontend/js/lib/jquery.autogrow.js',
      'frontend/js/lib/jquery.jeditable.js',
      'frontend/js/lib/jquery.jeditable.autogrow.js',
      'frontend/js/lib/jquery.snippet.js',
      'frontend/js/lib/jquery.slideto.min.js',
      'frontend/js/lib/jquery.wiggle.min.js',
      'frontend/js/lib/jquery.contextmenu.js',
      'frontend/js/lib/handlebars-1.0.rc.1.js',
      'frontend/js/lib/underscore.js',
      'frontend/js/lib/backbone.js',
      'frontend/js/lib/d3.v3.min.js',
      'frontend/js/lib/nv.d3.js',
      'frontend/js/lib/d3.fisheye.js',
      'frontend/js/lib/ColVis.js',
      'frontend/js/lib/bootstrap.js',
      'frontend/js/lib/bootstrap-pagination.js',
      'frontend/src/ace.js',
      'frontend/js/lib/jqconsole.min.js',
      'frontend/js/lib/splitter.js',
      'frontend/js/lib/swagger.js',
      'frontend/js/lib/swagger-ui.js',
      'frontend/js/lib/highlight.7.3.pack.js',

      // Template Engine Mock
      'test/mocks/disableEJS.js',

      // arangodb
      'frontend/js/arango/arango.js',
      'frontend/js/shell/browser.js',
      'frontend/js/modules/org/arangodb/arango-collection-common.js',
      'frontend/js/modules/org/arangodb/arango-collection.js',
      'frontend/js/modules/org/arangodb/arango-database.js',
      'frontend/js/modules/org/arangodb/arango-query-cursor.js',
      'frontend/js/modules/org/arangodb/arango-statement-common.js',
      'frontend/js/modules/org/arangodb/arango-statement.js',
      'frontend/js/modules/org/arangodb/arangosh.js',
      'frontend/js/modules/org/arangodb/graph-common.js',
      'frontend/js/modules/org/arangodb/graph.js',
      'frontend/js/modules/org/arangodb/mimetypes.js',
      'frontend/js/modules/org/arangodb/simple-query-common.js',
      'frontend/js/modules/org/arangodb/simple-query.js',
      'frontend/js/modules/org/arangodb/aql/functions.js',
      'frontend/js/modules/org/arangodb/graph/traversal.js',
      'frontend/js/modules/org/arangodb-common.js',
      'frontend/js/modules/org/arangodb.js',
      'frontend/js/bootstrap/errors.js',
      'frontend/js/bootstrap/monkeypatches.js',
      'frontend/js/bootstrap/module-internal.js',
      'frontend/js/client/bootstrap/module-internal.js',
      'frontend/js/client/client.js',

      // Mocks
      'test/specs/graphViewer/helper/eventHelper.js',
      'test/specs/graphViewer/helper/objectsHelper.js',
      'test/specs/graphViewer/helper/mocks.js',
      'test/specs/graphViewer/helper/commMock.js',
      'test/specs/graphViewer/helper/uiMatchers.js',

      // GraphViewer
      // Core Modules
      'frontend/js/graphViewer/graphViewer.js',
      'frontend/js/graphViewer/graph/domObserverFactory.js',
      'frontend/js/graphViewer/graph/colourMapper.js',
      'frontend/js/graphViewer/graph/communityNode.js',
      'frontend/js/graphViewer/graph/webWorkerWrapper.js',
      'frontend/js/graphViewer/graph/nodeShaper.js',
      'frontend/js/graphViewer/graph/abstractAdapter.js',
      'frontend/js/graphViewer/graph/JSONAdapter.js',
      'frontend/js/graphViewer/graph/arangoAdapter.js',
      'frontend/js/graphViewer/graph/foxxAdapter.js',
      'frontend/js/graphViewer/graph/previewAdapter.js',
      'frontend/js/graphViewer/graph/edgeShaper.js',
      'frontend/js/graphViewer/graph/forceLayouter.js',
      'frontend/js/graphViewer/graph/eventDispatcher.js',
      'frontend/js/graphViewer/graph/eventLibrary.js',
      'frontend/js/graphViewer/graph/zoomManager.js',
      'frontend/js/graphViewer/graph/nodeReducer.js',
      'frontend/js/graphViewer/graph/modularityJoiner.js',

      // UI Modules
      'frontend/js/graphViewer/ui/modalDialogHelper.js',
      'frontend/js/graphViewer/ui/contextMenuHelper.js',
      'frontend/js/graphViewer/ui/nodeShaperControls.js',
      'frontend/js/graphViewer/ui/edgeShaperControls.js',
      'frontend/js/graphViewer/ui/arangoAdapterControls.js',
      'frontend/js/graphViewer/ui/layouterControls.js',
      'frontend/js/graphViewer/ui/uiComponentsHelper.js',
      'frontend/js/graphViewer/ui/eventDispatcherControls.js',
      'frontend/js/graphViewer/ui/graphViewerUI.js',
      'frontend/js/graphViewer/ui/graphViewerWidget.js',
      'frontend/js/graphViewer/ui/graphViewerPreview.js',

      // Models
      'frontend/js/models/currentDatabase.js',
      'frontend/js/models/arangoCollection.js',
      'frontend/js/models/arangoDatabase.js',
      'frontend/js/models/arangoDocument.js',
      'frontend/js/models/arangoLog.js',
      'frontend/js/models/arangoStatistics.js',
      'frontend/js/models/arangoStatisticsDescription.js',
      'frontend/js/models/foxx.js',
      'frontend/js/models/notification.js',
      'frontend/js/models/graph.js',
      'clusterFrontend/js/models/clusterServer.js',
      'clusterFrontend/js/models/clusterCoordinator.js',
      'clusterFrontend/js/models/clusterDatabase.js',
      'clusterFrontend/js/models/clusterCollection.js',
      'clusterFrontend/js/models/clusterShard.js',

      // Collections
      'frontend/js/collections/arangoCollections.js',
      'frontend/js/collections/arangoDocuments.js',
      'frontend/js/collections/arangoDocument.js',
      'frontend/js/collections/arangoDatabase.js',
      'frontend/js/collections/arangoLogs.js',
      'frontend/js/collections/arangoUsers.js',
      'frontend/js/collections/arangoStatisticsCollection.js',
      'frontend/js/collections/arangoStatisticsDescriptionCollection.js',
      'frontend/js/collections/foxxCollection.js',
      'frontend/js/collections/graphCollection.js',
      'clusterFrontend/js/collections/clusterServers.js',
      'clusterFrontend/js/collections/clusterCoordinators.js',
      'clusterFrontend/js/collections/clusterDatabases.js',
      'clusterFrontend/js/collections/clusterCollections.js',
      'clusterFrontend/js/collections/clusterShards.js',
      'frontend/js/collections/notificationCollection.js',

      // Views
      'frontend/js/views/navigationView.js',
      'frontend/js/views/apiView.js',
      'frontend/js/views/footerView.js',
      'frontend/js/views/queryView.js',
      'frontend/js/views/shellView.js',
      'frontend/js/views/dashboardView.js',
      'frontend/js/views/collectionsView.js',
      'frontend/js/views/collectionView.js',
      'frontend/js/views/collectionInfoView.js',
      'frontend/js/views/newCollectionView.js',
      'frontend/js/views/collectionsItemView.js',
      'frontend/js/views/documentsView.js',
      'frontend/js/views/documentView.js',
      'frontend/js/views/logsView.js',
      'frontend/js/views/applicationsView.js',
      'frontend/js/views/foxxActiveView.js',
      'frontend/js/views/foxxInstalledView.js',
      'frontend/js/views/foxxEditView.js',
      'frontend/js/views/foxxMountView.js',
      'frontend/js/views/appDocumentationView.js',
      'frontend/js/views/graphView.js',
      'frontend/js/views/graphManagementView.js',
      'frontend/js/views/addNewGraphView.js',
      'frontend/js/views/deleteGraphView.js',
      'frontend/js/views/dbSelectionView.js',
      'frontend/js/views/editListEntryView.js',
      'frontend/js/views/loginView.js',
      'frontend/js/views/clusterDashboardView.js',
      'frontend/js/views/clusterOverviewView.js',
      'frontend/js/views/clusterServerView.js',
      'frontend/js/views/clusterCoordinatorView.js',
      'frontend/js/views/clusterDatabaseView.js',
      'frontend/js/views/clusterCollectionView.js',
      'frontend/js/views/clusterShardsView.js',

      'frontend/js/views/statisticBarView.js',
      'frontend/js/views/userBarView.js',

      //Views Planner
      'clusterFrontend/js/views/planScenarioSelectorView.js',
      'clusterFrontend/js/views/planSymmetricView.js',
      'clusterFrontend/js/views/planTestView.js',

      //Views cluster
      'clusterFrontend/js/views/showClusterView.js',

      // Router
      'frontend/js/routers/router.js',
      'clusterFrontend/js/routers/clusterRouter.js',

      // Specs
      // GraphViewer
      'test/specs/graphViewer/specColourMapper/colourMapperSpec.js',
      'test/specs/graphViewer/specWindowObjects/domObserverFactorySpec.js',
      'test/specs/graphViewer/specCommunityNode/communityNodeSpec.js',
      'test/specs/graphViewer/specAdapter/interfaceSpec.js',
      'test/specs/graphViewer/specAdapter/abstractAdapterSpec.js',
      'test/specs/graphViewer/specAdapter/jsonAdapterSpec.js',
      'test/specs/graphViewer/specAdapter/arangoAdapterSpec.js',
      'test/specs/graphViewer/specAdapter/foxxAdapterSpec.js',
      'test/specs/graphViewer/specAdapter/previewAdapterSpec.js',
      'test/specs/graphViewer/specAdapter/arangoAdapterUISpec.js',
      'test/specs/graphViewer/specNodeShaper/nodeShaperSpec.js',
      'test/specs/graphViewer/specNodeShaper/nodeShaperUISpec.js',
      'test/specs/graphViewer/specEdgeShaper/edgeShaperSpec.js',
      'test/specs/graphViewer/specEdgeShaper/edgeShaperUISpec.js',
      'test/specs/graphViewer/specForceLayouter/forceLayouterSpec.js',
      'test/specs/graphViewer/specForceLayouter/forceLayouterUISpec.js',
      'test/specs/graphViewer/specEvents/eventLibrarySpec.js',
      'test/specs/graphViewer/specEvents/eventDispatcherSpec.js',
      'test/specs/graphViewer/specEvents/eventDispatcherUISpec.js',
      'test/specs/graphViewer/specZoomManager/zoomManagerSpec.js',
      'test/specs/graphViewer/specGraphViewer/graphViewerSpec.js',
      'test/specs/graphViewer/specGraphViewer/graphViewerUISpec.js',
      'test/specs/graphViewer/specGraphViewer/graphViewerWidgetSpec.js',
      'test/specs/graphViewer/specGraphViewer/graphViewerPreviewSpec.js',
      'test/specs/graphViewer/specNodeReducer/nodeReducerSpec.js',
//      'test/specs/graphViewer/specNodeReducer/modularityJoinerSpec.js',
//      'test/specs/graphViewer/specWindowObjects/workerWrapperSpec.js',
      'test/specs/graphViewer/specContextMenu/contextMenuSpec.js',
      // Arango
      'test/specs/arango/arangoSpec.js',

      // Models
      'test/specs/models/currentDatabaseSpec.js',
      'test/specs/models/graphSpec.js',

      // Collections
      'test/specs/collections/clusterServersSpec.js',
      'test/specs/collections/clusterDatabasesSpec.js',
      'test/specs/collections/clusterCollectionsSpec.js',
      'test/specs/collections/clusterShardsSpec.js',

      // Views
      'test/specs/views/editListEntryViewSpec.js',
      'test/specs/views/collectionViewSpec.js',
      'test/specs/views/collectionsViewSpec.js',
      'test/specs/views/foxxEditViewSpec.js',
      'test/specs/views/dbSelectionViewSpec.js',
      'test/specs/views/navigationViewSpec.js',
      'test/specs/views/graphViewSpec.js',
      'test/specs/views/graphManagementViewSpec.js',
      'test/specs/views/addNewGraphViewSpec.js',
      /*
      'test/specs/views/clusterDashboardViewSpec.js',
      'test/specs/views/clusterOverviewViewSpec.js',
      'test/specs/views/clusterServerViewSpec.js',
      'test/specs/views/clusterCoordinatorViewSpec.js',
      'test/specs/views/clusterDatabaseViewSpec.js',
      'test/specs/views/clusterCollectionViewSpec.js',
      'test/specs/views/clusterShardsViewSpec.js',
      */
      // Router
      'test/specs/router/routerSpec.js',

      //Planner
      //Router
      'test/specs/planner/router/routerSpec.js',
      //View
      'test/specs/planner/views/planSymmetricViewSpec.js',
      'test/specs/planner/views/planTestViewSpec.js',
      'test/specs/planner/views/planScenarioSelectorViewSpec.js',
      'test/specJSLint/jsLintSpec.js'
    ],

    // list of files to exclude
    exclude: [

    ],

    // test results reporter to use
    // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
    reporters: ['dots'],

    // web server port
    port: 9876,

    // cli runner port
    runnerPort: 9100,

    // enable / disable colors in the output (reporters and logs)
    colors: false,

    // level of logging
    // possible values: karma.LOG_DISABLE || karma.LOG_ERROR || karma.LOG_WARN || karma.LOG_INFO || karma.LOG_DEBUG
    logLevel: karma.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: false,

    // Start these browsers, currently available:
    // - Chrome
    // - ChromeCanary
    // - Firefox
    // - Opera
    // - Safari (only Mac)
    // - PhantomJS
    // - IE (only Windows)
    browsers: ["PhantomJS"],

    // If browser does not capture in given timeout [ms], kill it
    captureTimeout: 60000,

    // Continuous Integration mode
    // if true, it capture browsers, run tests and exit
    singleRun: true
  });
};
<|endoftext|>