<commit_before>const render_more_topics = require('../templates/more_topics.hbs');
const render_topic_list_item = require('../templates/topic_list_item.hbs');
const Dict = require('./dict').Dict;
const FoldDict = require('./fold_dict').FoldDict;

/*
    Track all active widgets with a Dict.

    (We have at max one for now, but we may
    eventually allow multiple streams to be
    expanded.)
*/

const active_widgets = new Dict();

// We know whether we're zoomed or not.
let zoomed = false;

exports.remove_expanded_topics = function () {
    stream_popover.hide_topic_popover();

    _.each(active_widgets.values(), function (widget) {
        widget.remove();
    });

    active_widgets.clear();
};

exports.close = function () {
    zoomed = false;
    exports.remove_expanded_topics();
};

exports.zoom_out = function () {
    zoomed = false;

    const stream_ids = active_widgets.keys();

    if (stream_ids.length !== 1) {
        blueslip.error('Unexpected number of topic lists to zoom out.');
        return;
    }

    const stream_id = stream_ids[0];
    const widget = active_widgets.get(stream_id);
    const parent_widget = widget.get_parent();

    exports.rebuild(parent_widget, stream_id);
};

function update_unread_count(unread_count_elem, count) {
    // unread_count_elem is a jquery element...we expect DOM
    // to look like this:
    //

    //
{{unread}}

    //

    const value_span = unread_count_elem.find('.value');

    if (value_span.length === 0) {
        blueslip.error('malformed dom for unread count');
        return;
    }

    if (count === 0) {
        unread_count_elem.addClass("zero_count");
        value_span.text('');
    } else {
        unread_count_elem.removeClass("zero_count");
        unread_count_elem.show();
        value_span.text(count);
    }
}

exports.set_count = function (stream_id, topic, count) {
    const widget = active_widgets.get(stream_id);

    if (widget === undefined) {
        return false;
    }

    return widget.set_count(topic, count);
};

exports.widget = function (parent_elem, my_stream_id) {
    const self = {};

    self.build_list = function () {
        self.topic_items = new FoldDict();
        let topics_selected = 0;
        let more_topics_unreads = 0;

        const max_topics = 5;
        const max_topics_with_unread = 8;
        const topic_names = topic_data.get_recent_names(my_stream_id);

        const ul = $('
');

        _.each(topic_names, function (topic_name, idx) {
            const num_unread = unread.num_unread_for_topic(my_stream_id, topic_name);
            const is_active_topic = self.active_topic === topic_name.toLowerCase();

            if (!zoomed) {
                // We unconditionally skip showing muted topics when
                // not zoomed, even if they have unread messages.
                //
                // We limit the number of topics we show to at most
                // max_topics_with_unread when not zoomed.
                //
                // Ideally, this logic would first check whether the active topic
                // is in the set of those with unreads to avoid ending up with
                // max_topics_with_unread + 1 total topics if the active topic comes
                // after the first several topics with unread messages.
                if (!is_active_topic && (topics_selected >= max_topics_with_unread ||
                                         muting.is_topic_muted(my_stream_id, topic_name))) {
                    if (num_unread > 0 && !muting.is_topic_muted(my_stream_id, topic_name)) {
                        more_topics_unreads += num_unread;
                    }
                    return;
                }

                // Show the most recent topics, as well as any with unread messages
                const show_topic = idx < max_topics || num_unread > 0 || is_active_topic;

                if (!show_topic) {
                    return;
                }
            }

            const topic_info = {
                topic_name: topic_name,
                unread: num_unread,
                is_zero: num_unread === 0,
                is_muted: muting.is_topic_muted(my_stream_id, topic_name),
                url: hash_util.by_stream_topic_uri(my_stream_id, topic_name),
            };
            const li = $(render_topic_list_item(topic_info));
            self.topic_items.set(topic_name, li);
            ul.append(li);
            topics_selected += 1;
        });

        // Now, we decide whether we need to show the "more topics"
        // widget.  We need it if there are at least 5 topics in the
        // frontend's cache, or if we (possibly) don't have all
        // historical topics in the browser's cache.
        const show_more = self.build_more_topics_section(more_topics_unreads);
        const sub = stream_data.get_sub_by_id(my_stream_id);

        if (topic_names.length > max_topics || !stream_data.all_topics_in_cache(sub)) {
            ul.append(show_more);
        }
        return ul;
    };

    self.build_more_topics_section = function (more_topics_unreads) {
        const show_more_html = render_more_topics({
            more_topics_unreads: more_topics_unreads,
        });
        return $(show_more_html);
    };

    self.get_parent = function () {
        return parent_elem;
    };

    self.get_stream_id = function () {
        return my_stream_id;
    };

    self.get_dom = function () {
        return self.dom;
    };

    self.remove = function () {
        self.dom.remove();
    };

    self.num_items = function () {
        return self.topic_items.num_items();
    };

    self.set_count = function (topic, count) {
        let unread_count_elem;
        if (topic === null) {
            // null is used for updating the "more topics" count.
            if (zoomed) {
                return false;
            }
            const unread_count_parent = $(".show-more-topics");
            if (unread_count_parent.length === 0) {
                // If no show-more-topics element is present in the
                // DOM, there are two possibilities.  The most likely
                // is that there are simply no unreads on that topic
                // and there should continue to not be a "more topics"
                // button; we can check this by looking at count.
                if (count === 0) {
                    return false;
                }

                // The alternative is that there is these new messages
                // create the need for a "more topics" widget with a
                // nonzero unread count, and we need to create one and
                // add it to the DOM.
                //
                // With our current implementation, this code path
                // will always have its results overwritten shortly
                // after, because (1) the can only happen when we just
                // added unread counts, (not removing them), and (2)
                // when learning about new (unread) messages,
                // stream_list.update_dom_with_unread_count is always
                // immediately followed by
                // stream_list.update_streams_sidebar, which will
                // rebuilds the topic list from scratch anyway.
                //
                // So this code mostly exists to document this corner
                // case if in the future we adjust the model for
                // managing unread counts.  The code for updating this
                // element would look something like the following:
                //
                // var show_more = self.build_more_topics_section(count);
                // var topic_list_ul = exports.get_stream_li().find(".topic-list").expectOne();
                // topic_list_ul.append(show_more);
                return false;
            }
            unread_count_elem = unread_count_parent.find(".topic-unread-count");
            update_unread_count(unread_count_elem, count);
            return false;
        }

        if (!self.topic_items.has(topic)) {
            // `topic_li` may not exist if the topic is behind "more
            // topics"; We need to update the "more topics" count
            // instead in that case; we do this by returning true to
            // notify the caller to accumulate these.
            if (muting.is_topic_muted(my_stream_id, topic)) {
                // But we don't count unreads in muted topics.
                return false;
            }
            return true;
        }

        const topic_li = self.topic_items.get(topic);
        unread_count_elem = topic_li.find('.topic-unread-count');
        update_unread_count(unread_count_elem, count);
        return false;
    };

    self.activate_topic = function () {
        const li = self.topic_items.get(self.active_topic);
        if (li) {
            li.addClass('active-sub-filter');
        }
    };

    self.show_spinner = function () {
        // The spinner will go away once we get results and redraw
        // the whole list.
        const spinner = self.dom.find('.searching-for-more-topics');
        spinner.show();
    };

    self.show_no_more_topics = function () {
        const elem = self.dom.find('.no-more-topics-found');
        elem.show();
        self.no_more_topics = true;
    };

    self.build = function (active_topic, no_more_topics) {
        self.no_more_topics = false; // for now

        if (active_topic) {
            active_topic = active_topic.toLowerCase();
        }
        self.active_topic = active_topic;

        self.dom = self.build_list();

        parent_elem.append(self.dom);

        // We often rebuild an entire topic list, and the
        // caller will pass us in no_more_topics as true
        // if we were showing "No more topics found" from
        // the initial zooming.
        if (no_more_topics) {
            self.show_no_more_topics();
        }

        if (active_topic) {
            self.activate_topic();
        }
    };

    return self;
};

exports.active_stream_id = function () {
    const stream_ids = active_widgets.keys();

    if (stream_ids.length !== 1) {
        return;
    }

    return stream_ids[0];
};

exports.get_stream_li = function () {
    const widgets = active_widgets.values();

    if (widgets.length !== 1) {
        return;
    }

    const stream_li = widgets[0].get_parent();
    return stream_li;
};

exports.need_to_show_no_more_topics = function (stream_id) {
    // This function is important, and the use case here is kind of
    // subtle.  We do complete redraws of the topic list when new
    // messages come in, and we don't want to overwrite the
    // "no more topics" error message.
    if (!zoomed) {
        return false;
    }

    if (!active_widgets.has(stream_id)) {
        return false;
    }

    const widget = active_widgets.get(stream_id);

    return widget.no_more_topics;
};

exports.rebuild = function (stream_li, stream_id) {
    const active_topic = narrow_state.topic();
    const no_more_topics = exports.need_to_show_no_more_topics(stream_id);

    exports.remove_expanded_topics();
    const widget = exports.widget(stream_li, stream_id);
    widget.build(active_topic, no_more_topics);

    active_widgets.set(stream_id, widget);
};

// For zooming, we only do topic-list stuff here...let stream_list
// handle hiding/showing the non-narrowed streams
exports.zoom_in = function () {
    zoomed = true;

    const stream_id = exports.active_stream_id();
    if (!stream_id) {
        blueslip.error('Cannot find widget for topic history zooming.');
        return;
    }

    const active_widget = active_widgets.get(stream_id);

    const before_count = active_widget.num_items();

    function on_success() {
        if (!active_widgets.has(stream_id)) {
            blueslip.warn('User re-narrowed before topic history was returned.');
            return;
        }

        if (!zoomed) {
            blueslip.warn('User zoomed out before topic history was returned.');
            // Note that we could attempt to re-draw the zoomed out topic list
            // here, given that we have more history, but that might be more
            // confusing than helpful to a user who is likely trying to browse
            // other streams.
            return;
        }

        const widget = active_widgets.get(stream_id);

        exports.rebuild(widget.get_parent(), stream_id);

        const after_count = widget.num_items();

        if (after_count === before_count) {
            widget.show_no_more_topics();
        }
    }

    ui.get_scroll_element($('#stream-filters-container')).scrollTop(0);
    active_widget.show_spinner();
    topic_data.get_server_history(stream_id, on_success);
};

exports.initialize = function () {
    $('#stream_filters').on('click', '.topic-box', function (e) {
        if (e.metaKey || e.ctrlKey) {
            return;
        }
        if ($(e.target).closest('.show-more-topics').length > 0) {
            return;
        }

        // In a more componentized world, we would delegate some
        // of this stuff back up to our parents.

        const stream_row = $(e.target).parents('.narrow-filter');
        const stream_id = parseInt(stream_row.attr('data-stream-id'), 10);
        const sub = stream_data.get_sub_by_id(stream_id);
        const topic = $(e.target).parents('li').attr('data-topic-name');

        narrow.activate([
            {operator: 'stream', operand: sub.name},
            {operator: 'topic', operand: topic}],
                        {trigger: 'sidebar'});

        e.preventDefault();
    });
};

window.topic_list = exports;
<commit_msg>topic_list: Rewrite unzoomed rendering to be readable.<commit_after>const render_more_topics = require('../templates/more_topics.hbs');
const render_topic_list_item = require('../templates/topic_list_item.hbs');
const Dict = require('./dict').Dict;
const FoldDict = require('./fold_dict').FoldDict;

/*
    Track all active widgets with a Dict.

    (We have at max one for now, but we may
    eventually allow multiple streams to be
    expanded.)
*/

const active_widgets = new Dict();

// We know whether we're zoomed or not.
let zoomed = false;

exports.remove_expanded_topics = function () {
    stream_popover.hide_topic_popover();

    _.each(active_widgets.values(), function (widget) {
        widget.remove();
    });

    active_widgets.clear();
};

exports.close = function () {
    zoomed = false;
    exports.remove_expanded_topics();
};

exports.zoom_out = function () {
    zoomed = false;

    const stream_ids = active_widgets.keys();

    if (stream_ids.length !== 1) {
        blueslip.error('Unexpected number of topic lists to zoom out.');
        return;
    }

    const stream_id = stream_ids[0];
    const widget = active_widgets.get(stream_id);
    const parent_widget = widget.get_parent();

    exports.rebuild(parent_widget, stream_id);
};

function update_unread_count(unread_count_elem, count) {
    // unread_count_elem is a jquery element...we expect DOM
    // to look like this:
    //

    //
{{unread}}

    //

    const value_span = unread_count_elem.find('.value');

    if (value_span.length === 0) {
        blueslip.error('malformed dom for unread count');
        return;
    }

    if (count === 0) {
        unread_count_elem.addClass("zero_count");
        value_span.text('');
    } else {
        unread_count_elem.removeClass("zero_count");
        unread_count_elem.show();
        value_span.text(count);
    }
}

exports.set_count = function (stream_id, topic, count) {
    const widget = active_widgets.get(stream_id);

    if (widget === undefined) {
        return false;
    }

    return widget.set_count(topic, count);
};

exports.widget = function (parent_elem, my_stream_id) {
    const self = {};

    self.build_list = function () {
        self.topic_items = new FoldDict();
        let topics_selected = 0;
        let more_topics_unreads = 0;

        const max_topics = 5;
        const max_topics_with_unread = 8;
        const topic_names = topic_data.get_recent_names(my_stream_id);

        const ul = $('
');

        _.each(topic_names, function (topic_name, idx) {
            const num_unread = unread.num_unread_for_topic(my_stream_id, topic_name);
            const is_active_topic = self.active_topic === topic_name.toLowerCase();
            const is_topic_muted = muting.is_topic_muted(my_stream_id, topic_name);

            if (!zoomed) {
                function should_show_topic() {
                    // This function exists just for readability, to
                    // avoid long chained conditionals to determine
                    // which topics to include.

                    // We always show the active topic.  Ideally, this
                    // logic would first check whether the active
                    // topic is in the set of those with unreads to
                    // avoid ending up with max_topics_with_unread + 1
                    // total topics if the active topic comes after
                    // the first several topics with unread messages.
                    if (is_active_topic) {
                        return true;
                    }

                    // We unconditionally skip showing muted topics
                    // when not zoomed, even if they have unread
                    // messages.
                    if (is_topic_muted) {
                        return false;
                    }

                    // We include the most recent max_topics topics,
                    // even if there are no unread messages.
                    if (idx < max_topics) {
                        return true;
                    }

                    // We include older topics with unread messages up
                    // until max_topics_with_unread total topics have
                    // been included.
                    if (num_unread > 0 && topics_selected < max_topics_with_unread) {
                        return true;
                    }

                    // Otherwise, we don't show the topic in the
                    // unzoomed view.  We might display its unread
                    // count in in "more topics" if it is not muted.
                    return false;
                }

                const show_topic = should_show_topic();
                if (!show_topic) {
                    if (!is_topic_muted) {
                        // The "more topics" unread count, like
                        // stream-level counts, only counts messages
                        // on unmuted topics.
                        more_topics_unreads += num_unread;
                    }
                    return;
                }
                topics_selected += 1;
                // We fall through to rendering the topic, using the
                // same code we do when zoomed.
            }

            const topic_info = {
                topic_name: topic_name,
                unread: num_unread,
                is_zero: num_unread === 0,
                is_muted: is_topic_muted,
                url: hash_util.by_stream_topic_uri(my_stream_id, topic_name),
            };
            const li = $(render_topic_list_item(topic_info));
            self.topic_items.set(topic_name, li);
            ul.append(li);
        });

        // Now, we decide whether we need to show the "more topics"
        // widget.  We need it if there are at least 5 topics in the
        // frontend's cache, or if we (possibly) don't have all
        // historical topics in the browser's cache.
        const show_more = self.build_more_topics_section(more_topics_unreads);
        const sub = stream_data.get_sub_by_id(my_stream_id);

        if (topic_names.length > max_topics || !stream_data.all_topics_in_cache(sub)) {
            ul.append(show_more);
        }
        return ul;
    };

    self.build_more_topics_section = function (more_topics_unreads) {
        const show_more_html = render_more_topics({
            more_topics_unreads: more_topics_unreads,
        });
        return $(show_more_html);
    };

    self.get_parent = function () {
        return parent_elem;
    };

    self.get_stream_id = function () {
        return my_stream_id;
    };

    self.get_dom = function () {
        return self.dom;
    };

    self.remove = function () {
        self.dom.remove();
    };

    self.num_items = function () {
        return self.topic_items.num_items();
    };

    self.set_count = function (topic, count) {
        let unread_count_elem;
        if (topic === null) {
            // null is used for updating the "more topics" count.
            if (zoomed) {
                return false;
            }
            const unread_count_parent = $(".show-more-topics");
            if (unread_count_parent.length === 0) {
                // If no show-more-topics element is present in the
                // DOM, there are two possibilities.  The most likely
                // is that there are simply no unreads on that topic
                // and there should continue to not be a "more topics"
                // button; we can check this by looking at count.
                if (count === 0) {
                    return false;
                }

                // The alternative is that there is these new messages
                // create the need for a "more topics" widget with a
                // nonzero unread count, and we need to create one and
                // add it to the DOM.
                //
                // With our current implementation, this code path
                // will always have its results overwritten shortly
                // after, because (1) the can only happen when we just
                // added unread counts, (not removing them), and (2)
                // when learning about new (unread) messages,
                // stream_list.update_dom_with_unread_count is always
                // immediately followed by
                // stream_list.update_streams_sidebar, which will
                // rebuilds the topic list from scratch anyway.
                //
                // So this code mostly exists to document this corner
                // case if in the future we adjust the model for
                // managing unread counts.  The code for updating this
                // element would look something like the following:
                //
                // var show_more = self.build_more_topics_section(count);
                // var topic_list_ul = exports.get_stream_li().find(".topic-list").expectOne();
                // topic_list_ul.append(show_more);
                return false;
            }
            unread_count_elem = unread_count_parent.find(".topic-unread-count");
            update_unread_count(unread_count_elem, count);
            return false;
        }

        if (!self.topic_items.has(topic)) {
            // `topic_li` may not exist if the topic is behind "more
            // topics"; We need to update the "more topics" count
            // instead in that case; we do this by returning true to
            // notify the caller to accumulate these.
            if (muting.is_topic_muted(my_stream_id, topic)) {
                // But we don't count unreads in muted topics.
                return false;
            }
            return true;
        }

        const topic_li = self.topic_items.get(topic);
        unread_count_elem = topic_li.find('.topic-unread-count');
        update_unread_count(unread_count_elem, count);
        return false;
    };

    self.activate_topic = function () {
        const li = self.topic_items.get(self.active_topic);
        if (li) {
            li.addClass('active-sub-filter');
        }
    };

    self.show_spinner = function () {
        // The spinner will go away once we get results and redraw
        // the whole list.
        const spinner = self.dom.find('.searching-for-more-topics');
        spinner.show();
    };

    self.show_no_more_topics = function () {
        const elem = self.dom.find('.no-more-topics-found');
        elem.show();
        self.no_more_topics = true;
    };

    self.build = function (active_topic, no_more_topics) {
        self.no_more_topics = false; // for now

        if (active_topic) {
            active_topic = active_topic.toLowerCase();
        }
        self.active_topic = active_topic;

        self.dom = self.build_list();

        parent_elem.append(self.dom);

        // We often rebuild an entire topic list, and the
        // caller will pass us in no_more_topics as true
        // if we were showing "No more topics found" from
        // the initial zooming.
        if (no_more_topics) {
            self.show_no_more_topics();
        }

        if (active_topic) {
            self.activate_topic();
        }
    };

    return self;
};

exports.active_stream_id = function () {
    const stream_ids = active_widgets.keys();

    if (stream_ids.length !== 1) {
        return;
    }

    return stream_ids[0];
};

exports.get_stream_li = function () {
    const widgets = active_widgets.values();

    if (widgets.length !== 1) {
        return;
    }

    const stream_li = widgets[0].get_parent();
    return stream_li;
};

exports.need_to_show_no_more_topics = function (stream_id) {
    // This function is important, and the use case here is kind of
    // subtle.  We do complete redraws of the topic list when new
    // messages come in, and we don't want to overwrite the
    // "no more topics" error message.
    if (!zoomed) {
        return false;
    }

    if (!active_widgets.has(stream_id)) {
        return false;
    }

    const widget = active_widgets.get(stream_id);

    return widget.no_more_topics;
};

exports.rebuild = function (stream_li, stream_id) {
    const active_topic = narrow_state.topic();
    const no_more_topics = exports.need_to_show_no_more_topics(stream_id);

    exports.remove_expanded_topics();
    const widget = exports.widget(stream_li, stream_id);
    widget.build(active_topic, no_more_topics);

    active_widgets.set(stream_id, widget);
};

// For zooming, we only do topic-list stuff here...let stream_list
// handle hiding/showing the non-narrowed streams
exports.zoom_in = function () {
    zoomed = true;

    const stream_id = exports.active_stream_id();
    if (!stream_id) {
        blueslip.error('Cannot find widget for topic history zooming.');
        return;
    }

    const active_widget = active_widgets.get(stream_id);

    const before_count = active_widget.num_items();

    function on_success() {
        if (!active_widgets.has(stream_id)) {
            blueslip.warn('User re-narrowed before topic history was returned.');
            return;
        }

        if (!zoomed) {
            blueslip.warn('User zoomed out before topic history was returned.');
            // Note that we could attempt to re-draw the zoomed out topic list
            // here, given that we have more history, but that might be more
            // confusing than helpful to a user who is likely trying to browse
            // other streams.
            return;
        }

        const widget = active_widgets.get(stream_id);

        exports.rebuild(widget.get_parent(), stream_id);

        const after_count = widget.num_items();

        if (after_count === before_count) {
            widget.show_no_more_topics();
        }
    }

    ui.get_scroll_element($('#stream-filters-container')).scrollTop(0);
    active_widget.show_spinner();
    topic_data.get_server_history(stream_id, on_success);
};

exports.initialize = function () {
    $('#stream_filters').on('click', '.topic-box', function (e) {
        if (e.metaKey || e.ctrlKey) {
            return;
        }
        if ($(e.target).closest('.show-more-topics').length > 0) {
            return;
        }

        // In a more componentized world, we would delegate some
        // of this stuff back up to our parents.

        const stream_row = $(e.target).parents('.narrow-filter');
        const stream_id = parseInt(stream_row.attr('data-stream-id'), 10);
        const sub = stream_data.get_sub_by_id(stream_id);
        const topic = $(e.target).parents('li').attr('data-topic-name');

        narrow.activate([
            {operator: 'stream', operand: sub.name},
            {operator: 'topic', operand: topic}],
                        {trigger: 'sidebar'});

        e.preventDefault();
    });
};

window.topic_list = exports;
<|endoftext|>


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

var auth = require('connect-auth'),
    databank = require('databank'),
    express = require('express'),
    _ = require('underscore'),
    ev = require('express-validator'),
    api = require('../routes/api'),
    web = require('../routes/web'),
    schema = require('./schema').schema,
    HTTPError = require('./httperror').HTTPError,
    Provider = require('./provider').Provider,
    URLMaker = require('./urlmaker').URLMaker,
    Databank = databank.Databank,
    DatabankObject = databank.DatabankObject;

var makeApp = function(config, callback) {

    var params,
        port = config.port || 31337,
        hostname = config.hostname || 'localhost',
        db;

    // Initiate the DB

    if (_(config).has('params')) {
        params = config.params;
    } else {
        params = {};
    }

    if (_(params).has('schema')) {
        _.extend(params.schema, schema);
    } else {
        params.schema = schema;
    }

    db = Databank.get(config.driver, params);

    // Connect...

    db.connect({}, function(err) {

        var app = express.createServer();

        if (err) {
            callback(err, null);
            return;
        }

        // Configuration

        app.configure(function() {

            // Templates are in public
            app.set('views', __dirname + '/../public/template');
            app.set('view engine', 'utml');
            if (!_(config).has('nologger') || !config.nologger) {
                app.use(express.logger());
            }
            app.use(express.bodyParser());
            app.use(express.cookieParser());
            app.use(express.query());
            app.use(express.methodOverride());
            app.use(express.session({secret: (config.secret || "activitypump")}));
            app.use(express.favicon());

            var provider = new Provider();

            app.use(function(req, res, next) {
                res.local('site', (config.site) ? config.site : "ActivityPump");
                res.local('owner', (config.owner) ? config.owner : "Anonymous");
                res.local('ownerurl', (config.ownerURL) ? config.ownerURL : false);
                // Initialize null
                res.local('remoteUser', null);
                res.local('user', null);
                res.local('client', null);
                res.local('nologin', false);
                next();
            });

            app.use(auth([auth.Oauth({name: "client",
                                      oauth_provider: provider,
                                      oauth_protocol: 'http',
                                      authenticate_provider: null,
                                      authorize_provider: null,
                                      authorization_finished_provider: null
                                     }),
                          auth.Oauth({name: "user",
                                      oauth_provider: provider,
                                      oauth_protocol: 'http',
                                      authenticate_provider: web.authenticate,
                                      authorize_provider: web.authorize,
                                      authorization_finished_provider: web.authorizationFinished
                                     })
                         ]));

            app.use(ev);

            app.use(app.router);

            app.use(express['static'](__dirname + '/../public'));

        });

        app.error(function(err, req, res, next) {
            if (err instanceof HTTPError) {
                if (req.xhr) {
                    res.json({error: err.message}, err.code);
                } else if (req.accepts('html')) {
                    res.render('error', {status: err.code, error: err, title: "Error"});
                } else {
                    res.writeHead(err.code, {'Content-Type': 'text/plain'});
                    res.end(err.message);
                }
            } else {
                next(err);
            }
        });

        // Routes

        api.addRoutes(app);

        // Use "noweb" to disable Web site (API engine only)

        if (!_(config).has('noweb') || !config.noweb) {
            web.addRoutes(app);
        }

        api.setBank(db);
        DatabankObject.bank = db;

        URLMaker.hostname = hostname;
        URLMaker.port = port;

        app.run = function(callback) {
            var self = this,
                removeListeners = function() {
                    self.removeListener('listening', listenSuccessHandler);
                    self.removeListener('err', listenErrorHandler);
                },
                listenErrorHandler = function(err) {
                    removeListeners();
                    callback(err);
                },
                listenSuccessHandler = function() {
                    removeListeners();
                    callback(null);
                };
            this.on('error', listenErrorHandler);
            this.on('listening', listenSuccessHandler);
            this.listen(port);
        };

        callback(null, app);
    });
};

exports.makeApp = makeApp;<commit_msg>Revert "use the express-validator module"<commit_after>// app.js
//
// main function for activity pump application
//
// Copyright 2011-2012, StatusNet Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

var auth = require('connect-auth'),
    databank = require('databank'),
    express = require('express'),
    _ = require('underscore'),
    api = require('../routes/api'),
    web = require('../routes/web'),
    schema = require('./schema').schema,
    HTTPError = require('./httperror').HTTPError,
    Provider = require('./provider').Provider,
    URLMaker = require('./urlmaker').URLMaker,
    Databank = databank.Databank,
    DatabankObject = databank.DatabankObject;

var makeApp = function(config, callback) {

    var params,
        port = config.port || 31337,
        hostname = config.hostname || 'localhost',
        db;

    // Initiate the DB

    if (_(config).has('params')) {
        params = config.params;
    } else {
        params = {};
    }

    if (_(params).has('schema')) {
        _.extend(params.schema, schema);
    } else {
        params.schema = schema;
    }

    db = Databank.get(config.driver, params);

    // Connect...

    db.connect({}, function(err) {

        var app = express.createServer();

        if (err) {
            callback(err, null);
            return;
        }

        // Configuration

        app.configure(function() {

            // Templates are in public
            app.set('views', __dirname + '/../public/template');
            app.set('view engine', 'utml');
            if (!_(config).has('nologger') || !config.nologger) {
                app.use(express.logger());
            }
            app.use(express.bodyParser());
            app.use(express.cookieParser());
            app.use(express.query());
            app.use(express.methodOverride());
            app.use(express.session({secret: (config.secret || "activitypump")}));
            app.use(express.favicon());

            var provider = new Provider();

            app.use(function(req, res, next) {
                res.local('site', (config.site) ? config.site : "ActivityPump");
                res.local('owner', (config.owner) ? config.owner : "Anonymous");
                res.local('ownerurl', (config.ownerURL) ? config.ownerURL : false);
                // Initialize null
                res.local('remoteUser', null);
                res.local('user', null);
                res.local('client', null);
                res.local('nologin', false);
                next();
            });

            app.use(auth([auth.Oauth({name: "client",
                                      oauth_provider: provider,
                                      oauth_protocol: 'http',
                                      authenticate_provider: null,
                                      authorize_provider: null,
                                      authorization_finished_provider: null
                                     }),
                          auth.Oauth({name: "user",
                                      oauth_provider: provider,
                                      oauth_protocol: 'http',
                                      authenticate_provider: web.authenticate,
                                      authorize_provider: web.authorize,
                                      authorization_finished_provider: web.authorizationFinished
                                     })
                         ]));

            app.use(app.router);

            app.use(express['static'](__dirname + '/../public'));

        });

        app.error(function(err, req, res, next) {
            if (err instanceof HTTPError) {
                if (req.xhr) {
                    res.json({error: err.message}, err.code);
                } else if (req.accepts('html')) {
                    res.render('error', {status: err.code, error: err, title: "Error"});
                } else {
                    res.writeHead(err.code, {'Content-Type': 'text/plain'});
                    res.end(err.message);
                }
            } else {
                next(err);
            }
        });

        // Routes

        api.addRoutes(app);

        // Use "noweb" to disable Web site (API engine only)

        if (!_(config).has('noweb') || !config.noweb) {
            web.addRoutes(app);
        }

        api.setBank(db);
        DatabankObject.bank = db;

        URLMaker.hostname = hostname;
        URLMaker.port = port;

        app.run = function(callback) {
            var self = this,
                removeListeners = function() {
                    self.removeListener('listening', listenSuccessHandler);
                    self.removeListener('err', listenErrorHandler);
                },
                listenErrorHandler = function(err) {
                    removeListeners();
                    callback(err);
                },
                listenSuccessHandler = function() {
                    removeListeners();
                    callback(null);
                };
            this.on('error', listenErrorHandler);
            this.on('listening', listenSuccessHandler);
            this.listen(port);
        };

        callback(null, app);
    });
};

exports.makeApp = makeApp;<|endoftext|>


<commit_before>#!/usr/bin/env node

'use strict';

const path = require('path');
const packageInfo = require('../package.json'),
    config = require('./config'),
    utils = require('./utils'),
    commands = require('./commands');

const debug = require('debug')('sockbot');

/**
 * Construct a useragent for sockbot to use
 *
 * @param {object} cfg Instance Configuration to construct User Agent for
 * @returns {string} User-Agent to use for a forum instance
 */
exports.getUserAgent = function getUserAgent(cfg) {
    return `${packageInfo.name}/${packageInfo.version} ` +
        `(${process.platform} ${process.arch}) ` +
        `(nodejs v${process.versions.node}) ` +
        `(v8 v${process.versions.v8}) ` +
        `(user:${cfg.core.username} owner:${cfg.core.owner})`;
};

/**
 * Construct a stringified message to log
 *
 * @param {Array<*>} args Item to stringify and log
 * @returns {string} stringified message
 */
exports._buildMessage = function _buildMessage(args) {
    if (!args || args.length < 1) {
        return '';
    }
    if (!Array.isArray(args)) {
        args = Array.prototype.slice.apply(args);
    }
    args.unshift(`[${new Date().toISOString()}]`);
    return args
        .map((part) => typeof part === 'string' ? part : JSON.stringify(part, null, '\t'))
        .join(' ');
};

/**
 * Log a message to stdout
 *
 * @param {...*} message Message to log to stdout
 */
exports.log = function log() {
    console.log(exports._buildMessage(arguments)); // eslint-disable-line no-console
};

/**
 * Log a message to stderr
 *
 * @param {...*} message Message to log to stderr
 */
exports.error = function error() {
    console.error(exports._buildMessage(arguments)); // eslint-disable-line no-console
};

/**
 * Load a module relative to a local path, or relative to loaded config file
 *
 * @param {string} relativePath Local path to use
 * @param {string} module Module to load
 * @param {function} requireIt Function to use to load module
 * @returns {object | function} Loaded module
 */
exports.relativeRequire = function relativeRequire(relativePath, module, requireIt) {
    try {
        let resolved = `${__dirname}/../${relativePath}/${module}`;
        if (module.startsWith('/') || module.startsWith('./') || module.startsWith('../')) {
            resolved = path.posix.resolve(config.basePath, module);
        }
        debug(`requiring ${relativePath} ${module} as ${resolved}`);
        // Look in plugins first
        return requireIt(resolved);
    } catch (err) {
        // Error! check if it's ENOENT and try raw module
        if (/^Cannot find module/.test(err.message)) {
            debug(`retrying requiring ${relativePath} ${module} as raw`);
            return requireIt(module);
        }
        // Rethrow error if it wasn't ENOENT
        throw err;
    }
};

/**
 * Load plugins for forum instance
 *
 * @param {Provider} forumInstance Provider instance to load plugins into
 * @param {object} botConfig Bot configuration to load plugins with
 * @returns {Promise} Resolves when plugins have been loaded
 */
exports.loadPlugins = function loadPlugins(forumInstance, botConfig) {
    return Promise.all(Object.keys(botConfig.plugins).map((name) => {
        exports.log(`Loading plugin ${name} for ${botConfig.core.username}`);
        const plugin = exports.relativeRequire('plugins', name, require);
        const pluginConfig = botConfig.plugins[name];
        return forumInstance.addPlugin(plugin, pluginConfig).catch((err) => {
            exports.error(`Plugin ${name} failed to load with error: ${err}`);
            throw err;
        });
    }));
};

/**
 * Activate a loaded configuration.
 *
 * @param {object} botConfig Configuration to activate
 * @returns {Promise} Resolves when configuration is fully activated
 */
exports.activateConfig = function activateConfig(botConfig) {
    const Provider = exports.relativeRequire('providers', botConfig.core.provider, require);
    exports.log(`Using provider ${botConfig.core.provider} for ${botConfig.core.username}`);
    const ua = exports.getUserAgent(botConfig);
    const instance = new Provider(botConfig, ua);
    instance.on('log', exports.log);
    instance.on('error', exports.error);
    instance.on('logExtended', utils.logExtended);
    instance.Commands = commands.bindCommands(instance);
    return exports.loadPlugins(instance, botConfig)
        .then(() => {
            exports.log(`${botConfig.core.username} ready for login`);
        })
        .then(() => instance.login())
        .then(() => {
            exports.log(`${botConfig.core.username} login successful`);
            return instance.activate();
        })
        .then(() => exports.log(`${botConfig.core.username} activated`));
};

exports.ponyError = function ponyError(prefix, err) {
    err = err || {};
    if (err.stack) {
        debug(err.stack);
    }
    const pony = ['              __         __----__',
        '                                        /  \\__..--\'\'    _-__\'\'-_',
        '                                       ( /  \\            `-.\'\'\'\'`',
        '                                       | |   `-..__  .,     `.',
        '                         ___           ( \'.  \\ ____`\\ )`-_    `.',
        '                  ___   (   `.         \'\\   __/   __\\\' /-``-.._ \\',
        '                 (   `-. `.   `.       .|\\_  (   / .-| |W)|    ``\'',
        '                  `-.   `-.`.   `.     |\' ( ,\'\\ ( (WW| \\` j',
        '          ..---\'\'\'\'\'-`.    `.\\   _\\   .|   \',  \\_\\_`/   ``-.',
        '        ,\'            _`-,   `  (  |  |\'     `.        \\__/',
        '       /   _         ( ```    __ \\  \\ |     ._:7,______.-\'',
        '      | .-\'/          `-._   (  `.\\  \'\':     \\    /',
        '      \'`  /          .-\'\'>`-. `-. `   |       |  (',
        '         -          /   /    `_: `_:. `.    .  \\  \\',
        '         |          |  |  o()(   (      \\   )\\  ;  |',
        '        .\'          `. |   Oo `---:.__-\'\') /  )/   |',
        '        |            | |  ()o            |/   \'    |',
        '       .\'            |/ \\  o     /             \\__/',
        '       |  ,         .|   |      /-,_______\\       \\',
        '      /  / )        |\' _/      /     |    |\\       \\',
        '    .:.-\' .\'         )/       /     |     | `--,    \\',
        '         /       .  / |      |      |     |   /      )',
        '    .__.\'    ,   :|/_/|      |      |      | (       |',
        '    `-.___.-`;  / \'   |      |      |      |  \\      |',
        '           .:_-\'      |       \\     |       \\  `.___/',
        '                       \\_______)     \\_______)',
        `A-derp! ${prefix}: ${err.message || err}`
    ];
    exports.error(pony.join('\n'));
};

// This is for the automated tests.... ;-)
exports.require = require;

/* istanbul ignore if */
if (require.main === module) {
    process.on('unhandledRejection', (_, reason) => {
        exports.ponyError('Unhandled Promise Rejection', reason);
    });

    const yargs = require('yargs');
    const argv = yargs
        .usage('Usage: $0 <cfgFile>')
        .demand(1, 1, 'A valid configuration file must be provided')
        .argv;
    exports.log(`Starting Sockbot ${packageInfo.version} - ${packageInfo.releaseName}`);
    config.load(argv._[0])
        .then((cfg) => {
            exports.log(`Loaded configuration file: ${argv._[0]}`);
            process.chdir(config.basePath);
            return cfg;
        })
        .then((cfg) => {
            const promises = cfg.map((botConfig) => {
                exports.log(`Activating logon: ${botConfig.core.username}`);
                return exports.activateConfig(botConfig);
            });
            return Promise.all(promises);
        })
        .catch((err) => {
            exports.ponyError('Fatal Startup Error', err);
        });
}
<commit_msg>fix(application): Remove Derpy hooves from critical errors.<commit_after>#!/usr/bin/env node

'use strict';

const path = require('path');
const packageInfo = require('../package.json'),
    config = require('./config'),
    utils = require('./utils'),
    commands = require('./commands');

const debug = require('debug')('sockbot');

/**
 * Construct a useragent for sockbot to use
 *
 * @param {object} cfg Instance Configuration to construct User Agent for
 * @returns {string} User-Agent to use for a forum instance
 */
exports.getUserAgent = function getUserAgent(cfg) {
    return `${packageInfo.name}/${packageInfo.version} ` +
        `(${process.platform} ${process.arch}) ` +
        `(nodejs v${process.versions.node}) ` +
        `(v8 v${process.versions.v8}) ` +
        `(user:${cfg.core.username} owner:${cfg.core.owner})`;
};

/**
 * Construct a stringified message to log
 *
 * @param {Array<*>} args Item to stringify and log
 * @returns {string} stringified message
 */
exports._buildMessage = function _buildMessage(args) {
    if (!args || args.length < 1) {
        return '';
    }
    if (!Array.isArray(args)) {
        args = Array.prototype.slice.apply(args);
    }
    args.unshift(`[${new Date().toISOString()}]`);
    return args
        .map((part) => typeof part === 'string' ? part : JSON.stringify(part, null, '\t'))
        .join(' ');
};

/**
 * Log a message to stdout
 *
 * @param {...*} message Message to log to stdout
 */
exports.log = function log() {
    console.log(exports._buildMessage(arguments)); // eslint-disable-line no-console
};

/**
 * Log a message to stderr
 *
 * @param {...*} message Message to log to stderr
 */
exports.error = function error() {
    console.error(exports._buildMessage(arguments)); // eslint-disable-line no-console
};

/**
 * Load a module relative to a local path, or relative to loaded config file
 *
 * @param {string} relativePath Local path to use
 * @param {string} module Module to load
 * @param {function} requireIt Function to use to load module
 * @returns {object | function} Loaded module
 */
exports.relativeRequire = function relativeRequire(relativePath, module, requireIt) {
    try {
        let resolved = `${__dirname}/../${relativePath}/${module}`;
        if (module.startsWith('/') || module.startsWith('./') || module.startsWith('../')) {
            resolved = path.posix.resolve(config.basePath, module);
        }
        debug(`requiring ${relativePath} ${module} as ${resolved}`);
        // Look in plugins first
        return requireIt(resolved);
    } catch (err) {
        // Error! check if it's ENOENT and try raw module
        if (/^Cannot find module/.test(err.message)) {
            debug(`retrying requiring ${relativePath} ${module} as raw`);
            return requireIt(module);
        }
        // Rethrow error if it wasn't ENOENT
        throw err;
    }
};

/**
 * Load plugins for forum instance
 *
 * @param {Provider} forumInstance Provider instance to load plugins into
 * @param {object} botConfig Bot configuration to load plugins with
 * @returns {Promise} Resolves when plugins have been loaded
 */
exports.loadPlugins = function loadPlugins(forumInstance, botConfig) {
    return Promise.all(Object.keys(botConfig.plugins).map((name) => {
        exports.log(`Loading plugin ${name} for ${botConfig.core.username}`);
        const plugin = exports.relativeRequire('plugins', name, require);
        const pluginConfig = botConfig.plugins[name];
        return forumInstance.addPlugin(plugin, pluginConfig).catch((err) => {
            exports.error(`Plugin ${name} failed to load with error: ${err}`);
            throw err;
        });
    }));
};

/**
 * Activate a loaded configuration.
 *
 * @param {object} botConfig Configuration to activate
 * @returns {Promise} Resolves when configuration is fully activated
 */
exports.activateConfig = function activateConfig(botConfig) {
    const Provider = exports.relativeRequire('providers', botConfig.core.provider, require);
    exports.log(`Using provider ${botConfig.core.provider} for ${botConfig.core.username}`);
    const ua = exports.getUserAgent(botConfig);
    const instance = new Provider(botConfig, ua);
    instance.on('log', exports.log);
    instance.on('error', exports.error);
    instance.on('logExtended', utils.logExtended);
    instance.Commands = commands.bindCommands(instance);
    return exports.loadPlugins(instance, botConfig)
        .then(() => {
            exports.log(`${botConfig.core.username} ready for login`);
        })
        .then(() => instance.login())
        .then(() => {
            exports.log(`${botConfig.core.username} login successful`);
            return instance.activate();
        })
        .then(() => exports.log(`${botConfig.core.username} activated`));
};

exports.ponyError = function ponyError(prefix, err) {
    err = err || {};
    if (err.stack) {
        debug(err.stack);
    }
    const pony = [
        `A-derp! ${prefix}: ${err.message || err}`
    ];
    exports.error(pony.join('\n'));
};

// This is for the automated tests.... ;-)
exports.require = require;

/* istanbul ignore if */
if (require.main === module) {
    process.on('unhandledRejection', (_, reason) => {
        exports.ponyError('Unhandled Promise Rejection', reason);
    });

    const yargs = require('yargs');
    const argv = yargs
        .usage('Usage: $0 <cfgFile>')
        .demand(1, 1, 'A valid configuration file must be provided')
        .argv;
    exports.log(`Starting Sockbot ${packageInfo.version} - ${packageInfo.releaseName}`);
    config.load(argv._[0])
        .then((cfg) => {
            exports.log(`Loaded configuration file: ${argv._[0]}`);
            process.chdir(config.basePath);
            return cfg;
        })
        .then((cfg) => {
            const promises = cfg.map((botConfig) => {
                exports.log(`Activating logon: ${botConfig.core.username}`);
                return exports.activateConfig(botConfig);
            });
            return Promise.all(promises);
        })
        .catch((err) => {
            exports.ponyError('Fatal Startup Error', err);
        });
}
<|endoftext|>


<commit_before>describe( "When using the 'createTrailingSlashRoutes' option", function () {

    beforeEach( function () {
        SubRouteTest.setUp.call(this, "subroute2", {createTrailingSlashRoutes: true});
    } );

    afterEach( function () {
        SubRouteTest.tearDown.call(this);
    } );

    it( 'has a "default" route', function () {
        expect( this.router.routes['subroute2'] ).toEqual( 'handleDefaultRoute' );
    } );

    it( 'has an auto-generated "default" route with a trailing slash', function () {
        console.dir(this.router.routes)
        expect( this.router.routes['subroute2/'] ).toEqual( 'handleDefaultRoute' );
    } );

    it( 'has a "foo" route', function () {
        expect( this.router.routes['subroute2/foo'] ).toEqual( 'handleFooRoute' );
    } );

    it( 'has an auto-generated "foo" route with a trailing slash', function () {
        expect( this.router.routes['subroute2/foo/'] ).toEqual( 'handleFooRoute' );
    } );

    it( 'triggers the "default" route', function () {
        this.router.bind("route:handleDefaultRoute", this.routeSpy);
        this.baseRouter.navigate("subroute2", {trigger:true} );
        expect( this.routeSpy ).toHaveBeenCalledOnce();
        expect( this.routeSpy ).toHaveBeenCalledWith();
    } );

    it( 'triggers the auto-generated "default" route when using a trailing slash', function () {
        this.router.bind("route:handleDefaultRoute", this.routeSpy);
        this.baseRouter.navigate("subroute2/", {trigger:true} );
        expect( this.routeSpy ).toHaveBeenCalledOnce();
        expect( this.routeSpy ).toHaveBeenCalledWith();
    } );

    it( 'triggers the "foo" route', function () {
        this.router.bind("route:handleFooRoute", this.routeSpy);
        this.baseRouter.navigate("subroute2/foo", {trigger:true} );
        expect( this.routeSpy ).toHaveBeenCalledOnce();
        expect( this.routeSpy ).toHaveBeenCalledWith();
    } );

    it( 'does not trigger the "foo" route when using a trailing slash', function () {
        this.router.bind("route:handleFooRoute", this.routeSpy);
        this.baseRouter.navigate("subroute2/foo/", {trigger:true} );
        expect( this.routeSpy ).toHaveBeenCalledOnce();
        expect( this.routeSpy ).toHaveBeenCalledWith();
    } );

} );
    <commit_msg>console.dir...<commit_after>describe( "When using the 'createTrailingSlashRoutes' option", function () {

    beforeEach( function () {
        SubRouteTest.setUp.call(this, "subroute2", {createTrailingSlashRoutes: true});
    } );

    afterEach( function () {
        SubRouteTest.tearDown.call(this);
    } );

    it( 'has a "default" route', function () {
        expect( this.router.routes['subroute2'] ).toEqual( 'handleDefaultRoute' );
    } );

    it( 'has an auto-generated "default" route with a trailing slash', function () {
        expect( this.router.routes['subroute2/'] ).toEqual( 'handleDefaultRoute' );
    } );

    it( 'has a "foo" route', function () {
        expect( this.router.routes['subroute2/foo'] ).toEqual( 'handleFooRoute' );
    } );

    it( 'has an auto-generated "foo" route with a trailing slash', function () {
        expect( this.router.routes['subroute2/foo/'] ).toEqual( 'handleFooRoute' );
    } );

    it( 'triggers the "default" route', function () {
        this.router.bind("route:handleDefaultRoute", this.routeSpy);
        this.baseRouter.navigate("subroute2", {trigger:true} );
        expect( this.routeSpy ).toHaveBeenCalledOnce();
        expect( this.routeSpy ).toHaveBeenCalledWith();
    } );

    it( 'triggers the auto-generated "default" route when using a trailing slash', function () {
        this.router.bind("route:handleDefaultRoute", this.routeSpy);
        this.baseRouter.navigate("subroute2/", {trigger:true} );
        expect( this.routeSpy ).toHaveBeenCalledOnce();
        expect( this.routeSpy ).toHaveBeenCalledWith();
    } );

    it( 'triggers the "foo" route', function () {
        this.router.bind("route:handleFooRoute", this.routeSpy);
        this.baseRouter.navigate("subroute2/foo", {trigger:true} );
        expect( this.routeSpy ).toHaveBeenCalledOnce();
        expect( this.routeSpy ).toHaveBeenCalledWith();
    } );

    it( 'does not trigger the "foo" route when using a trailing slash', function () {
        this.router.bind("route:handleFooRoute", this.routeSpy);
        this.baseRouter.navigate("subroute2/foo/", {trigger:true} );
        expect( this.routeSpy ).toHaveBeenCalledOnce();
        expect( this.routeSpy ).toHaveBeenCalledWith();
    } );

} );
    <|endoftext|>


<commit_before>// # app.js

var _ = require('underscore'),
    path = require("path"),
    fs   = require("fs"),
    util = require("util"),
    feather = require("./feather"),
    featherConfig = require("feather-config"),
    cluster = require('cluster'),
    os = require('os');

/* ------------------------------------------------------------------ */

// Initialize the directory structure and name of the application.
if (fs.existsSync(appDir + '/app.js')) {
  appHooks = require(appDir + "/app.js");
}

var appDir = process.cwd();
var appName = path.basename(appDir);
var appLibDir = path.join(appDir, 'lib');
var libDir = path.join(process.env.FEATHER_HOME, 'lib');

featherConfig.init({

  appDir: appDir,
  defaultConfigPath: libDir + '/config.json',
  defaultOptionsHook: function(defaultOptions) {
    defaultOptions.featherRoot = process.env.FEATHER_HOME + "/lib";
    defaultOptions.appRoot = appDir;
    defaultOptions.daemon.outputPath = appDir + "/" + appName + ".out";
    defaultOptions.daemon.pidPath = '/tmp/' + appName + '.pid';
  },
  commandLineArgsHook: function(arg, remainingArgs, cmdLineOptions) {
    switch(arg) {
      case "env":
        // ignore this, as it is taken care of in feather-config, but chew it up so we don't get unrecognized option warnings.
        remainingArgs.shift();
        break;
      case(appDir):
        break;
      case "daemonize":
        if (!cmdLineOptions.daemon) cmdLineOptions.daemon = {};
        cmdLineOptions.daemon.runAsDaemon = true;
        break;
      case "outputpath":
        if (!cmdLineOptions.daemon) cmdLineOptions.daemon = {};
        cmdLineOptions.daemon.outputPath = remainingArgs.shift();
        break;
      case "pidpath":
        if (!cmdLineOptions.daemon) cmdLineOptions.daemon = {};
        cmdLineOptions.daemon.pidPath = remainingArgs.shift();
        break;
      case "loglevel":
        var level = remainingArgs.shift();
        cmdLineOptions.logging = {
          enabled: (level !== "off"),
          defaultLevel: level
        };
        break;
      case "test/unit":
        console.log("Initiating unit testing.");
        cmdLineOptions.testing = true;
        break;
      case "extras":
        var extras = remainingArgs.shift();
        cmdLineOptions.extras = extras;
        break;
      default:
        console.warn("Unrecognized option: " + arg);
        break;
    } // end switch
  }

}, function(err, config) {

  if (process.argv[1] === "debug") {
    console.info("Final config hierarchy: " + config.dumpBuildInfo());
    console.info("Final config: " + util.inspect(config));
  }

  if (config && config.testing) {
    exports.feather = feather;
  } else {
    if (! err) {

      var appHooks = null;

      if (fs.existsSync(appDir + '/app.js')) {
        appHooks = require(appDir + "/app.js");
      }

      if (appHooks) { // Add hook events from app's app.js.
        config = feather.recursiveExtend(config, appHooks);
      }

      //add the "extra" config options if any were passed in
      if (config.extras) {
        var extras = config.extras.split(',');
        _.each(extras, function(xtra) {
          var parts = xtra.split(':');
          var key = parts[0];
          var value = parts.length == 2 ? parts[1] : true;
          config[key] = value;
        });
      }

      exports.appOptions = config;

      function error(err) {
        if (err) {
          console.error(util.inspect(err));
          process.nextTick(function() {
            process.exit(0);
          });
        }
      }

      function start() {
        if (config.cluster && cluster.isMaster) {

          // tag feather as master and run through startup
          // (certain tasks only need to be performed once)
          // fork workers when those tasks are complete
          config.isMaster = true;
          console.log('master bootstrap process underway...');
          feather.start(config, function(err) {

            if (err) {
              error(err);
            } else {

              console.log('master bootstrap process complete...');

              // spawn the clustered processes (1 per CPU)
              var numWorkers = os.cpus().length;
              for (var i = 0; i < numWorkers; i++) {
                cluster.fork();
              }

              //TODO: do we need to do anything with cluster.workers here?
            }
          });

        } else if (config.cluster) {

          config.isWorker = true;
          console.log('forked worker: ' + cluster.worker.id);
          feather.start(config, error);

        } else {

          // single process: is both master and worker
          config.isMaster = true;
          config.isWorker = true;
          console.log('not clustering - bootstrapping and starting server in one process...');
          feather.start(config, error);
        }
      }

      // if we're running as a daemon, daemonize and then move on
      if (config.daemon.runAsDaemon && !process.env.IS_FEATHER_DAEMON) {
        console.log('daemonizing...');
        var daemon = require("daemon");
        daemon.daemonize(config.daemon.outputPath, config.daemon.pidPath, function(err, pid) {
          if (err) {
            console.error(util.inspect(err));
            process.nextTick(function() {
              process.exit(0);
            });
          } else {
            process.env.IS_FEATHER_DAEMON = true;
            start();
          }
        });
      } else {
        start();
      }

    } else {
      console.error(err);
      process.nextTick(function() {
        process.exit(0);
      });
    }
  }

});
<commit_msg>enabling debug now displays config breadcrumb and final rendered config in console.<commit_after>// # app.js

var _ = require('underscore'),
    path = require("path"),
    fs   = require("fs"),
    util = require("util"),
    feather = require("./feather"),
    featherConfig = require("feather-config"),
    cluster = require('cluster'),
    os = require('os');

/* ------------------------------------------------------------------ */

// Initialize the directory structure and name of the application.
if (fs.existsSync(appDir + '/app.js')) {
  appHooks = require(appDir + "/app.js");
}

var appDir = process.cwd();
var appName = path.basename(appDir);
var appLibDir = path.join(appDir, 'lib');
var libDir = path.join(process.env.FEATHER_HOME, 'lib');

featherConfig.init({

  appDir: appDir,
  defaultConfigPath: libDir + '/config.json',
  defaultOptionsHook: function(defaultOptions) {
    defaultOptions.featherRoot = process.env.FEATHER_HOME + "/lib";
    defaultOptions.appRoot = appDir;
    defaultOptions.daemon.outputPath = appDir + "/" + appName + ".out";
    defaultOptions.daemon.pidPath = '/tmp/' + appName + '.pid';
  },
  commandLineArgsHook: function(arg, remainingArgs, cmdLineOptions) {
    switch(arg) {
      case "env":
        // ignore this, as it is taken care of in feather-config, but chew it up so we don't get unrecognized option warnings.
        remainingArgs.shift();
        break;
      case(appDir):
        break;
      case "daemonize":
        if (!cmdLineOptions.daemon) cmdLineOptions.daemon = {};
        cmdLineOptions.daemon.runAsDaemon = true;
        break;
      case "outputpath":
        if (!cmdLineOptions.daemon) cmdLineOptions.daemon = {};
        cmdLineOptions.daemon.outputPath = remainingArgs.shift();
        break;
      case "pidpath":
        if (!cmdLineOptions.daemon) cmdLineOptions.daemon = {};
        cmdLineOptions.daemon.pidPath = remainingArgs.shift();
        break;
      case "loglevel":
        var level = remainingArgs.shift();
        cmdLineOptions.logging = {
          enabled: (level !== "off"),
          defaultLevel: level
        };
        break;
      case "test/unit":
        console.log("Initiating unit testing.");
        cmdLineOptions.testing = true;
        break;
      case "extras":
        var extras = remainingArgs.shift();
        cmdLineOptions.extras = extras;
        break;
      default:
        console.warn("Unrecognized option: " + arg);
        break;
    } // end switch
  }

}, function(err, config) {
  if (!err && _.contains(process.execArgv, '--debug-brk')) {
    console.info("Final config hierarchy: " + config.dumpBuildInfo());
    console.info("Final config: " + JSON.stringify(config));
  }

  if (config && config.testing) {
    exports.feather = feather;
  } else {
    if (! err) {

      var appHooks = null;

      if (fs.existsSync(appDir + '/app.js')) {
        appHooks = require(appDir + "/app.js");
      }

      if (appHooks) { // Add hook events from app's app.js.
        config = feather.recursiveExtend(config, appHooks);
      }

      //add the "extra" config options if any were passed in
      if (config.extras) {
        var extras = config.extras.split(',');
        _.each(extras, function(xtra) {
          var parts = xtra.split(':');
          var key = parts[0];
          var value = parts.length == 2 ? parts[1] : true;
          config[key] = value;
        });
      }

      exports.appOptions = config;

      function error(err) {
        if (err) {
          console.error(util.inspect(err));
          process.nextTick(function() {
            process.exit(0);
          });
        }
      }

      function start() {
        if (config.cluster && cluster.isMaster) {

          // tag feather as master and run through startup
          // (certain tasks only need to be performed once)
          // fork workers when those tasks are complete
          config.isMaster = true;
          console.log('master bootstrap process underway...');
          feather.start(config, function(err) {

            if (err) {
              error(err);
            } else {

              console.log('master bootstrap process complete...');

              // spawn the clustered processes (1 per CPU)
              var numWorkers = os.cpus().length;
              for (var i = 0; i < numWorkers; i++) {
                cluster.fork();
              }

              //TODO: do we need to do anything with cluster.workers here?
            }
          });

        } else if (config.cluster) {

          config.isWorker = true;
          console.log('forked worker: ' + cluster.worker.id);
          feather.start(config, error);

        } else {

          // single process: is both master and worker
          config.isMaster = true;
          config.isWorker = true;
          console.log('not clustering - bootstrapping and starting server in one process...');
          feather.start(config, error);
        }
      }

      // if we're running as a daemon, daemonize and then move on
      if (config.daemon.runAsDaemon && !process.env.IS_FEATHER_DAEMON) {
        console.log('daemonizing...');
        var daemon = require("daemon");
        daemon.daemonize(config.daemon.outputPath, config.daemon.pidPath, function(err, pid) {
          if (err) {
            console.error(util.inspect(err));
            process.nextTick(function() {
              process.exit(0);
            });
          } else {
            process.env.IS_FEATHER_DAEMON = true;
            start();
          }
        });
      } else {
        start();
      }

    } else {
      console.error(err);
      process.nextTick(function() {
        process.exit(0);
      });
    }
  }

});
<|endoftext|>


<commit_before>var nodemailer = require('nodemailer'),
    express    = require('express'),
    flatten    = require('flatten.js').flatten,
    path       = require('path'),
    app        = express(),
    hbs        = require('./hbs'),
    options    = require('./config'),
    store      = require('./store')(options.store),
    models     = require('./models').createModels(store),
    routes     = require('./routes'),
    handlers   = require('./handlers'),
    middleware = require('./middleware'),
    metrics    = require('./metrics'),
    url        = require('url'),
    github     = require('./github')(options), // if used, contains github.id
    crypto     = require('crypto'),
    flattened,
    generateSessionSecret = function () {
      'use strict';
      console.log('Warning: Generating a session key - please see http://learn.jsbin.com/help/session-secret');
      return crypto.createHash('md5').update(Math.random() + '').digest('hex');
    };

/**
 * JS Bin configuration
 */

app.store  = store;
// Create model singletons
// models.createModels(store);

app.mailer = (function (mail) {
  var mailTransport = null,
      method   = mail && mail.adapter,
      settings = mail && mail[method];

  if (method && options) {
    mailTransport = nodemailer.createTransport(method, settings);
  }

  return new handlers.MailHandler(mailTransport, app.render.bind(app));
})(options.mail);

app.PRODUCTION  = 'production';
app.DEVELOPMENT = 'development';

// Set the NODE_ENV variable as this is used by Express, we want the
// environment to take precedence but allow it to be set using the config file
// too.
if (process.env.NODE_ENV) {
  options.env = process.env.NODE_ENV;
}
process.env.NODE_ENV = options.env;

// Need to set the node environment to run in the same timezone as the database
// This will ideally be UTC in both cases but if not this can be set either
// using the TZ environment variable or the "timezone" option.
// This is because the mysql library coerces MySQL dates (without timezones)
// into JavaScript date objects.
if (!process.env.TZ && options.timezone) {
  process.env.TZ = options.timezone;
}

// Sort out the port.
(function () {
  var port = process.env.PORT;

  // if we're running from the bin/jsbin file, then we modify
  // both the listen port AND the options.url
  if (process.env.JSBIN_PORT) {
    if (options.url.host.indexOf(':') === -1) {
      options.url.host += ':' + process.env.JSBIN_PORT;
    } else {
      options.url.host = options.url.host.replace(/\:\d+$/, function () {
        return ':' + process.env.JSBIN_PORT;
      });
    }
  }

  if (!port) {
    options.url.host.replace(/\:(\d+)$/, function (m, p) {
      if (p.length) {
        port = p;
      }
    });
  }

  if (!port) {
    port = 80;
  }

  // NOTE: this is *only* used for running the server.listen()
  // it's not used in the urls to access assets, etc.
  options.port = port;
})();

// Strip trailing slash from the prefix
options.url.prefix = options.url.prefix.replace(/\/$/, '');

// Apply the keys from the config file. All nested properties are
// space delimited to match the express style.
//
// For example, app.set('url prefix'); //=> '/'
flattened = flatten(options, ' ');

Object.getOwnPropertyNames(flattened).forEach(function (key) {
  app.set(key, flattened[key]);
});

// in live, we run behind a proxy - so this will give us our IPs again:
// http://expressjs.com/guide.html#proxies
if (process.env.NODE_ENV === app.PRODUCTION || process.env.JSBIN_PROXY) {
  app.enable('trust proxy');
}

app.set('root', path.resolve(path.join(__dirname, '..')));

// ensure jsbin is running from the expected root
process.chdir(app.set('root'));

app.set('version', require('../package').version);

app.set('view engine', 'html');
app.set('views', path.join(app.set('root'), 'views'));
app.set('url prefix', options.url.prefix);
app.set('url ssl', options.url.ssl);
app.set('url full', 'http://' + app.set('url host') + app.set('url prefix'));
app.set('basepath', app.set('url prefix'));
app.set('is_production', app.get('env') === app.PRODUCTION);

if (options.url.static) {
  app.set('static url', 'http://' + app.set('url static'));
} else {
  app.set('static url', app.set('url full'));
}

if (options.url.runner) {
  // strip trailing slash, just in case
  options.url.runner = options.url.runner.replace(/\/$/, '');
  app.set('url runner', options.url.runner);
} else {
  app.set('url runner', app.set('url full'));
}

app.set('views', 'views');
app.set('view engine', 'html');
app.engine('html', hbs.__express);
app.engine('txt', hbs.__express); // used in email

// Define some generic template variables.
app.locals({
  version: app.set('version'),
  client: options.client
});

app.connect = function (callback) {
  app.emit('before', app);

  // Register all the middleware.
  app.configure(function () {
    var mount = app.set('url prefix') || '/',
        logger;

    logger = process.env.JSBIN_LOGGER || app.set('server logger') || 'tiny';

    if (logger !== 'none') {
      app.use(express.logger(logger));
    }

    // Redirect gist.jsbin urls to /gist/:id
    app.use(function (req, res, next) {
      if (req.headers.host.match(/gist.jsbin/)) {
        var parsedUrl = url.parse(req.originalUrl),
            segments = parsedUrl.pathname.split('/'),
            gistId = segments.slice(-1).join('');
        return res.redirect((app.get('url ssl') ? 'https://' : 'http://') + options.url.host + '/gist/' + gistId);
      }
      next();
    });
    app.use(mount, (function () {
      var staticRouter = express.static(path.join(app.set('root'), 'public'));

      return function (req, res, next) {
        var url = req.protocol + '://' + req.get('host') + req.url;
        if (options.url.static) {
          if (url.indexOf(options.url.static) === -1) {
            return next();
          }
        }

        return staticRouter(req, res, next);
      };
    })());
    app.use(middleware.limitContentLength({limit: app.set('max-request-size')}));
    var sessionSecret = app.get('session secret') || generateSessionSecret();
    app.use(express.cookieParser(sessionSecret));
    app.use(express.cookieSession({
      key: 'jsbin',
      cookie: {
        maxAge: 365 * 24 * 60 * 60 * 1000
        // domain: '.' + app.set('url host')
      }
    }));
    // Passport
    if (options.github && options.github.id) {
      github.initialize(app);
    }
    app.use(express.urlencoded());
    app.use(express.json());
    app.use(middleware.csrf({ ignore: ['/', /^\/api\//] }));
    app.use(middleware.api({ app: app }));
    app.use(middleware.subdomain(app));
    app.use(middleware.noslashes());
    app.use(middleware.ajax());
    app.use(middleware.cors());
    app.use(middleware.jsonp());
    app.use(middleware.flash());
    app.use(middleware.protocolCheck);
    app.use(mount, app.router);

    // Register all routes
    routes(app);
  });

  app.emit('after', app);

  store.connect(function (err) {
    if (err) {
      metrics.increment('error.store.connect');
      throw err;
    }

    var port = app.set('port');
    module.exports.listen(port);

    if (typeof callback === 'function') {
      callback();
    }
    app.emit('connected');

    process.stdout.write('JS Bin v' + app.set('version') + ' is up and running on port ' + app.set('port') + '. Now point your browser at ' + app.set('url full') + '\n');
  });
};

// Export the application to allow it to be included.
module.exports = app;

// Run a local development server if this file is called directly.
if (require.main === module) {
  app.connect();
}
<commit_msg>Add domain field to cookie with prefixed '.' This fixes the redirect, but locally login isn't working, this seems to be an issue with which cookie the session reads from<commit_after>var nodemailer = require('nodemailer'),
    express    = require('express'),
    flatten    = require('flatten.js').flatten,
    path       = require('path'),
    app        = express(),
    hbs        = require('./hbs'),
    options    = require('./config'),
    store      = require('./store')(options.store),
    models     = require('./models').createModels(store),
    routes     = require('./routes'),
    handlers   = require('./handlers'),
    middleware = require('./middleware'),
    metrics    = require('./metrics'),
    url        = require('url'),
    github     = require('./github')(options), // if used, contains github.id
    crypto     = require('crypto'),
    flattened,
    generateSessionSecret = function () {
      'use strict';
      console.log('Warning: Generating a session key - please see http://learn.jsbin.com/help/session-secret');
      return crypto.createHash('md5').update(Math.random() + '').digest('hex');
    };

/**
 * JS Bin configuration
 */

app.store  = store;
// Create model singletons
// models.createModels(store);

app.mailer = (function (mail) {
  var mailTransport = null,
      method   = mail && mail.adapter,
      settings = mail && mail[method];

  if (method && options) {
    mailTransport = nodemailer.createTransport(method, settings);
  }

  return new handlers.MailHandler(mailTransport, app.render.bind(app));
})(options.mail);

app.PRODUCTION  = 'production';
app.DEVELOPMENT = 'development';

// Set the NODE_ENV variable as this is used by Express, we want the
// environment to take precedence but allow it to be set using the config file
// too.
if (process.env.NODE_ENV) {
  options.env = process.env.NODE_ENV;
}
process.env.NODE_ENV = options.env;

// Need to set the node environment to run in the same timezone as the database
// This will ideally be UTC in both cases but if not this can be set either
// using the TZ environment variable or the "timezone" option.
// This is because the mysql library coerces MySQL dates (without timezones)
// into JavaScript date objects.
if (!process.env.TZ && options.timezone) {
  process.env.TZ = options.timezone;
}

// Sort out the port.
(function () {
  var port = process.env.PORT;

  // if we're running from the bin/jsbin file, then we modify
  // both the listen port AND the options.url
  if (process.env.JSBIN_PORT) {
    if (options.url.host.indexOf(':') === -1) {
      options.url.host += ':' + process.env.JSBIN_PORT;
    } else {
      options.url.host = options.url.host.replace(/\:\d+$/, function () {
        return ':' + process.env.JSBIN_PORT;
      });
    }
  }

  if (!port) {
    options.url.host.replace(/\:(\d+)$/, function (m, p) {
      if (p.length) {
        port = p;
      }
    });
  }

  if (!port) {
    port = 80;
  }

  // NOTE: this is *only* used for running the server.listen()
  // it's not used in the urls to access assets, etc.
  options.port = port;
})();

// Strip trailing slash from the prefix
options.url.prefix = options.url.prefix.replace(/\/$/, '');

// Apply the keys from the config file. All nested properties are
// space delimited to match the express style.
//
// For example, app.set('url prefix'); //=> '/'
flattened = flatten(options, ' ');

Object.getOwnPropertyNames(flattened).forEach(function (key) {
  app.set(key, flattened[key]);
});

// in live, we run behind a proxy - so this will give us our IPs again:
// http://expressjs.com/guide.html#proxies
if (process.env.NODE_ENV === app.PRODUCTION || process.env.JSBIN_PROXY) {
  app.enable('trust proxy');
}

app.set('root', path.resolve(path.join(__dirname, '..')));

// ensure jsbin is running from the expected root
process.chdir(app.set('root'));

app.set('version', require('../package').version);

app.set('view engine', 'html');
app.set('views', path.join(app.set('root'), 'views'));
app.set('url prefix', options.url.prefix);
app.set('url ssl', options.url.ssl);
app.set('url full', 'http://' + app.set('url host') + app.set('url prefix'));
app.set('basepath', app.set('url prefix'));
app.set('is_production', app.get('env') === app.PRODUCTION);

if (options.url.static) {
  app.set('static url', 'http://' + app.set('url static'));
} else {
  app.set('static url', app.set('url full'));
}

if (options.url.runner) {
  // strip trailing slash, just in case
  options.url.runner = options.url.runner.replace(/\/$/, '');
  app.set('url runner', options.url.runner);
} else {
  app.set('url runner', app.set('url full'));
}

app.set('views', 'views');
app.set('view engine', 'html');
app.engine('html', hbs.__express);
app.engine('txt', hbs.__express); // used in email

// Define some generic template variables.
app.locals({
  version: app.set('version'),
  client: options.client
});

app.connect = function (callback) {
  app.emit('before', app);

  // Register all the middleware.
  app.configure(function () {
    var mount = app.set('url prefix') || '/',
        logger;

    logger = process.env.JSBIN_LOGGER || app.set('server logger') || 'tiny';

    if (logger !== 'none') {
      app.use(express.logger(logger));
    }

    // Redirect gist.jsbin urls to /gist/:id
    app.use(function (req, res, next) {
      if (req.headers.host.match(/gist.jsbin/)) {
        var parsedUrl = url.parse(req.originalUrl),
            segments = parsedUrl.pathname.split('/'),
            gistId = segments.slice(-1).join('');
        return res.redirect((app.get('url ssl') ? 'https://' : 'http://') + options.url.host + '/gist/' + gistId);
      }
      next();
    });
    app.use(mount, (function () {
      var staticRouter = express.static(path.join(app.set('root'), 'public'));

      return function (req, res, next) {
        var url = req.protocol + '://' + req.get('host') + req.url;
        if (options.url.static) {
          if (url.indexOf(options.url.static) === -1) {
            return next();
          }
        }

        return staticRouter(req, res, next);
      };
    })());
    app.use(middleware.limitContentLength({limit: app.set('max-request-size')}));
    var sessionSecret = app.get('session secret') || generateSessionSecret();
    app.use(express.cookieParser(sessionSecret));
    app.use(express.cookieSession({
      key: 'jsbin',
      cookie: {
        maxAge: 365 * 24 * 60 * 60 * 1000,
        domain: '.' + app.set('url host')
      }
    }));
    // Passport
    if (options.github && options.github.id) {
      github.initialize(app);
    }
    app.use(express.urlencoded());
    app.use(express.json());
    app.use(middleware.csrf({ ignore: ['/', /^\/api\//] }));
    app.use(middleware.api({ app: app }));
    app.use(middleware.subdomain(app));
    app.use(middleware.noslashes());
    app.use(middleware.ajax());
    app.use(middleware.cors());
    app.use(middleware.jsonp());
    app.use(middleware.flash());
    app.use(middleware.protocolCheck);
    app.use(mount, app.router);

    // Register all routes
    routes(app);
  });

  app.emit('after', app);

  store.connect(function (err) {
    if (err) {
      metrics.increment('error.store.connect');
      throw err;
    }

    var port = app.set('port');
    module.exports.listen(port);

    if (typeof callback === 'function') {
      callback();
    }
    app.emit('connected');

    process.stdout.write('JS Bin v' + app.set('version') + ' is up and running on port ' + app.set('port') + '. Now point your browser at ' + app.set('url full') + '\n');
  });
};

// Export the application to allow it to be included.
module.exports = app;

// Run a local development server if this file is called directly.
if (require.main === module) {
  app.connect();
}
<|endoftext|>


<commit_before>require.config({
    baseUrl: "app",
    shim: {
        backbone: {
            deps: [
                'underscore',
                'jquery'
            ],
            exports: 'Backbone'
        }
    },
    paths: {
        backbone: "../libs/backbone/backbone",
        bootstrap: "../libs/bootstrap/dist/js/bootstrap",
        codebird: "../libs/codebird/codebird",
        handlebars: "../libs/require-handlebars-plugin/Handlebars",
        sha1: "../libs/codebird/sha1",
        jquery: "../libs/jquery/jquery",
        underscore: "../libs/lodash/dist/lodash.underscore",
        hbs: "../libs/require-handlebars-plugin/hbs",
        json2: "../libs/require-handlebars-plugin/hbs/json2",
        i18nprecompile: "../libs/require-handlebars-plugin/hbs/i18nprecompile"
    },
    hbs: {
        disableI18n: true
    }
});

require([
    'app'
], function(App) {
    App.initialize();
});<commit_msg>Экспортируем Codebird как модуль<commit_after>require.config({
    baseUrl: "app",
    shim: {
        backbone: {
            deps: [
                'underscore',
                'jquery'
            ],
            exports: 'Backbone'
        },
        codebird: {
            deps: [
                'sha1'
            ],
            exports: 'Codebird'
        }
    },
    paths: {
        backbone: "../libs/backbone/backbone",
        bootstrap: "../libs/bootstrap/dist/js/bootstrap",
        codebird: "../libs/codebird/codebird",
        handlebars: "../libs/require-handlebars-plugin/Handlebars",
        sha1: "../libs/codebird/sha1",
        jquery: "../libs/jquery/jquery",
        underscore: "../libs/lodash/dist/lodash.underscore",
        hbs: "../libs/require-handlebars-plugin/hbs",
        json2: "../libs/require-handlebars-plugin/hbs/json2",
        i18nprecompile: "../libs/require-handlebars-plugin/hbs/i18nprecompile"
    },
    hbs: {
        disableI18n: true
    }
});

require([
    'app'
], function(App) {
    App.initialize();
});<|endoftext|>


<commit_before>/**
 * @author oldj
 * @blog http://oldj.net
 */

'use strict';

const config = require('./config');

const app = require('app');  // Module to control application life.
const BrowserWindow = require('browser-window');  // Module to create native browser window.
const http = require('http');

//app.__is_debug = true;
app.__is_debug = false;

// Report crashes to our server.
require('crash-reporter').start();

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the javascript object is GCed.
let mainWindow = null;

// Quit when all windows are closed.
app.on('window-all-closed', function () {
    if (process.platform != 'darwin') {
        app.quit();
    }
});

// This method will be called when atom-shell has done everything
// initialization and ready for creating browser windows.
app.on('ready', function () {
    // Create the browser window.
    mainWindow = new BrowserWindow({width: 800, height: 600});

    // and load the index.html of the app.
    mainWindow.loadURL('file://' + __dirname + '/index.html');

    if (app.__is_debug) {
        mainWindow.toggleDevTools();
    }

    mainWindow.on('close', function (e) {
        if (!app.__force_quit) {
            e.preventDefault();
            mainWindow.hide();
        }
    });

    // You can use 'before-quit' instead of (or with) the close event
    app.on('before-quit', function (e) {
        // Handle menu-item or keyboard shortcut quit here
        if (!app.__force_quit) {
            e.preventDefault();
            mainWindow.hide();
        }
    });

    // Remove mainWindow.on('closed'), as it is redundant
    /*
     // Emitted when the window is closed.
     mainWindow.on('closed', function () {
     // Dereference the window object, usually you would store windows
     // in an array if your app supports multi windows, this is the time
     // when you should delete the corresponding element.
     mainWindow = null;
     app.quit();
     });*/

    app.on('activate', function () {
        mainWindow.show();
    });

    require('./js/menu').makeMenu(app, mainWindow);
    require('./js/tray').makeTray(app);
});
<commit_msg>修复 mac 下在 dock 右键菜单中无法退出的问题。<commit_after>/**
 * @author oldj
 * @blog http://oldj.net
 */

'use strict';

const config = require('./config');

const app = require('app');  // Module to control application life.
const BrowserWindow = require('browser-window');  // Module to create native browser window.
const http = require('http');

//app.__is_debug = true;
app.__is_debug = false;

// Report crashes to our server.
require('crash-reporter').start();

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the javascript object is GCed.
let mainWindow = null;

// Quit when all windows are closed.
app.on('window-all-closed', function () {
    if (process.platform != 'darwin') {
        app.quit();
    }
});

// This method will be called when atom-shell has done everything
// initialization and ready for creating browser windows.
app.on('ready', function () {
    // Create the browser window.
    mainWindow = new BrowserWindow({width: 800, height: 600});

    // and load the index.html of the app.
    mainWindow.loadURL('file://' + __dirname + '/index.html');
    //app.dock.hide();

    if (app.__is_debug) {
        mainWindow.toggleDevTools();
    }

    mainWindow.on('close', function (e) {
        console.log('close');
        if (!app.__force_quit) {
            e.preventDefault();
            mainWindow.hide();
        }
    });

    // You can use 'before-quit' instead of (or with) the close event
    app.on('before-quit', function (e) {
        console.log('before-quit');
        // Handle menu-item or keyboard shortcut quit here
        app.__force_quit = true;
    });

    // Remove mainWindow.on('closed'), as it is redundant
    /*
     // Emitted when the window is closed.
     mainWindow.on('closed', function () {
     // Dereference the window object, usually you would store windows
     // in an array if your app supports multi windows, this is the time
     // when you should delete the corresponding element.
     mainWindow = null;
     app.quit();
     });*/

    app.on('activate', function () {
        mainWindow.show();
    });

    require('./js/menu').makeMenu(app, mainWindow);
    require('./js/tray').makeTray(app);
});
<|endoftext|>


<commit_before>"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var platform_browser_dynamic_1 = require("@angular/platform-browser-dynamic");
var app_module_1 = require("./app.module");
platform_browser_dynamic_1.platformBrowserDynamic().bootstrapModule(app_module_1.AppModule);
//# sourceMappingURL=main.js.map<commit_msg>Delete main.js<commit_after><|endoftext|>


<commit_before>require([
	"namespace",

	// Libs
	"jquery",
	"use!backbone",//depends on underscore so _ is loaded too

	// Modules - Only need Leaguevine and modules with Routers
	"modules/leaguevine",
	"modules/settings",
	"modules/team",
	"modules/tournament",
	"modules/trackedgame",
],
/*
 * The following callback is called after the dependices are loaded.
 * The dependencies are passed to the callback in the order they are required.
 * The Require callback normally returns an object that defines the module,
 * but in this case we are defining the jQuery ready function which will execute
 * once everything has finished loading.
 */
function(namespace, $, Backbone, Leaguevine) {

	var app = namespace.app; //Shorthanded app namespace.

	/*
	 * Loading modules also instantiate their routers.
	 * There is also a top-level router here that matches ""
	 * TODO: "" matches index, index should execute settings or a scoreboard instead of teams.
	 */

	 // Defining the application router, you can attach sub routers here.
	var Router = Backbone.Router.extend({

		//Ask tbranyen why this is app.router.useLayout(name) instead of app.useLayout(name)
		//the context (this) is only used for storing currentLayout so theoretically this function
		//could go anywhere.
		useLayout: function(name) {// Super-simple layout swapping and reusing
			var currentLayout = this.currentLayout;
	        // If there is an existing layout and its the current one, return it.
	        if (currentLayout && currentLayout.options.template == name) { return currentLayout;}
	        // Create the new layout and set it as current.
	        this.currentLayout = new Backbone.LayoutManager({template: name});
	        return this.currentLayout;
	    },

		routes: {
			"": "index",
		},

		index: function () {
			Backbone.history.navigate('teams', true); // Only works if I have a route to match teams
		}
	});

	// Treat the jQuery ready function as the entry point to the application.
	// Inside this function, kick-off all initialization, everything up to this
	// point should be definitions.
	$(function() {
		// Define your master router on the application namespace and trigger all
		// navigation from this instance.
		app.router = new Router();
		app.api = Leaguevine.API;

		// Trigger the initial route and enable HTML5 History API support
		Backbone.history.start({ pushState: false });
	});

  // All navigation that is relative should be passed through the navigate
  // method, to be processed by the router.  If the link has a data-bypass
  // attribute, bypass the delegation completely.
	$(document).on("click", "a:not([data-bypass])", function(evt) {
		// Get the anchor href and protcol
		var href = $(this).attr("href");
		var protocol = this.protocol + "//";

		// Ensure the protocol is not part of URL, meaning its relative.
		if (href && href.slice(0, protocol.length) !== protocol &&
			href.indexOf("javascript:") !== 0) {
			// Stop the default event to ensure the link will not cause a page
			// refresh.
			evt.preventDefault();

			// This uses the default router defined above, and not any routers
			// that may be placed in modules.  To have this work globally (at the
			// cost of losing all route events) you can change the following line
			// to: Backbone.history.navigate(href, true);
			app.router.navigate(href, true);
		}
	});

});
<commit_msg>I brought this to tbranyen's attention a while ago but he never answered me. I assumed I was wrong, I guess not. https://github.com/tbranyen/backbone-boilerplate/commit/f10c889a480bef98c7fdb1b33ae375081b56e2c3<commit_after>require([
	"namespace",

	// Libs
	"jquery",
	"use!backbone",//depends on underscore so _ is loaded too

	// Modules - Only need Leaguevine and modules with Routers
	"modules/leaguevine",
	"modules/settings",
	"modules/team",
	"modules/tournament",
	"modules/trackedgame",
],
/*
 * The following callback is called after the dependices are loaded.
 * The dependencies are passed to the callback in the order they are required.
 * The Require callback normally returns an object that defines the module,
 * but in this case we are defining the jQuery ready function which will execute
 * once everything has finished loading.
 */
function(namespace, $, Backbone, Leaguevine) {

	var app = namespace.app; //Shorthanded app namespace.

	/*
	 * Loading modules also instantiate their routers.
	 * There is also a top-level router here that matches ""
	 * TODO: "" matches index, index should execute settings or a scoreboard instead of teams.
	 */

	 // Defining the application router, you can attach sub routers here.
	var Router = Backbone.Router.extend({

		//Ask tbranyen why this is app.router.useLayout(name) instead of app.useLayout(name)
		//the context (this) is only used for storing currentLayout so theoretically this function
		//could go anywhere.
		useLayout: function(name) {// Super-simple layout swapping and reusing
			var currentLayout = this.currentLayout;
	        // If there is an existing layout and its the current one, return it.
	        if (currentLayout && currentLayout.options.template == name) { return currentLayout;}
	        // Create the new layout and set it as current.
	        this.currentLayout = new Backbone.LayoutManager({template: name});
	        return this.currentLayout;
	    },

		routes: {
			"": "index",
		},

		index: function () {
			Backbone.history.navigate('teams', true); // Only works if I have a route to match teams
		}
	});

	// Treat the jQuery ready function as the entry point to the application.
	// Inside this function, kick-off all initialization, everything up to this
	// point should be definitions.
	$(function() {
		// Define your master router on the application namespace and trigger all
		// navigation from this instance.
		app.router = new Router();
		app.api = Leaguevine.API;

		// Trigger the initial route and enable HTML5 History API support
		Backbone.history.start({ pushState: false });
	});

  // All navigation that is relative should be passed through the navigate
  // method, to be processed by the router.  If the link has a data-bypass
  // attribute, bypass the delegation completely.
	$(document).on("click", "a:not([data-bypass])", function(evt) {
		// Get the anchor href and protcol
		var href = $(this).attr("href");
		var protocol = this.protocol + "//";

		// Ensure the protocol is not part of URL, meaning its relative.
		if (href && href.slice(0, protocol.length) !== protocol &&
			href.indexOf("javascript:") !== 0) {
			// Stop the default event to ensure the link will not cause a page
			// refresh.
			evt.preventDefault();

			// `Backbone.history.navigate` is sufficient for all Routers and will
			// trigger the correct events.  The Router's internal `navigate` method
			// calls this anyways.
			Backbone.history.navigate(href, true);
		}
	});

});
<|endoftext|>