<commit_before>define(function(require, exports, module) {
    "use strict";

    var app = require("app");
    var async = require("async");

    // view
    var AbstractView = require("modules/view/AbstractView");
    var ArticleListView = require("modules/view/news/ArticleListView");
    var GridListView = require("modules/view/news/GridListView");
    var RecommendArticleView = require("modules/view/news/RecommendArticleView");
    var ArticleListItemView = require("modules/view/news/ArticleListItemView");
    var EventListItemView = require("modules/view/news/EventListItemView");
    var YouTubeListItemView = require("modules/view/news/YouTubeListItemView");

    // models
    var ArticleModel = require("modules/model/article/ArticleModel");

    // collections
    var ArticleCollection = require("modules/collection/article/ArticleCollection");
    var RecommendCollection = require("modules/collection/article/RecommendCollection");
    var FavoriteCollection = require("modules/collection/article/FavoriteCollection");
    var EventsCollection = require("modules/collection/events/EventsCollection");

    // util
    var DateUtil = require("modules/util/DateUtil");
    var BusinessUtil = require("modules/util/BusinessUtil");
    var vexDialog = require("vexDialog");
    var IsNull = require("modules/util/filter/IsNull");
    var Equal = require("modules/util/filter/Equal");
    var And = require("modules/util/filter/And");
    var Or = require("modules/util/filter/Or");
    var Le = require("modules/util/filter/Le");
    var Ge = require("modules/util/filter/Ge");

    /**
     * 記事一覧・詳細のメインとなる画面のViewクラスを作成する。
     * @class 記事一覧・詳細のメインとなる画面のViewクラス
     * @exports NewsView
     * @constructor
     */
    var NewsView = AbstractView.extend({

        template : require("ldsh!templates/{mode}/news/news"),
        model : new ArticleModel(),
        fetchCounter : 0,
        articleCollection : new ArticleCollection(),
        recommendCollection : new RecommendCollection(),
        favoriteCollection : new FavoriteCollection(),
        eventsCollection : new EventsCollection(),
        newsCollection : new Backbone.Collection(),
        events : {
            "click [data-article-id]" : "onClickGridItem"
        },
        beforeRendered : function() {
        },

        afterRendered : function() {
        },

        initialize : function() {
        initialize : function(options) {
            options = options || {};

            this.targetDate = this.targetDate || options.date;

            this.setArticleSearchCondition({
                targetDate : new Date(this.targetDate)
            });
            this.searchArticles();
            this.initEvents();
        },

        /**
         * イベントを初期化する
         * @memberof NewsView#
         */
        initEvents : function() {
            app.router.on("route", this.onRoute.bind(this));
        },

        /**
         * 記事の検索処理を開始する。
         * @memberof NewsView#
         */
        searchArticles : function() {
            this.trackPageView();

            this.showLoading();

            // 現在保持しているデータをクリア
            this.articleCollection.reset();
            this.recommendCollection.reset();
            this.eventsCollection.reset();

            async.parallel([
                    this.loadYouTubeLibrary.bind(this), this.loadArticle.bind(this), this.loadRecommend.bind(this),
                    this.loadEvents.bind(this)
            ], this.onFetchAll.bind(this));
        },

        /**
         * 記事の検索条件を指定する。
         * @param {Object} 検索条件。現在、targetDateプロパティにDateオブジェクトを指定可能。
         * @memberof NewsView#
         */
        setArticleSearchCondition : function(condition) {
            var targetDate = condition.targetDate;
            var dateString = DateUtil.formatDate(targetDate, "yyyy-MM-dd");
            this.articleCollection.condition.filters = [
                    new Or([
                            new Equal("publishedAt", dateString), new And([
                                    new Le("publishedAt", dateString), new Ge("depublishedAt", dateString)
                            ])
                    ]), new IsNull("isDepublish")
            ];

            this.recommendCollection.condition.filters = [
                new Or([
                        new Equal("publishedAt", dateString), new And([
                                new Le("publishedAt", dateString), new Ge("depublishedAt", dateString)
                        ])
                ])
            ];

            this.eventsCollection.condition.filters = new Equal("publishedAt", dateString);
        },
        /**
         * youtubeライブラリを読み込む
         * @param {Function} callback
         * @memberof NewsView#
         */
        loadYouTubeLibrary : function(callback) {
            if (app.gapiLoaded) {
                callback();
                return;
            }
            var self = this;
            var loadScript = function(url) {
                return function(next) {
                    $.getScript(url).done(function() {
                        next(null);
                    }).fail(function(jqXHR, settings, err) {
                        next(err);
                    });
                };
            };
            var onLoadGAPI = function(next) {
                window.onLoadGAPI = function() {
                    delete window.onLoadGAPI;
                    next(null);
                };

                loadScript("https://apis.google.com/js/client.js?onload=onLoadGAPI")(function(err) {
                    if (err) {
                        next(err);
                        return;
                    }
                });
            };
            async.series([
                    onLoadGAPI, loadScript("https://www.youtube.com/iframe_api")
            ], function(err) {
                if (!err) {
                    app.gapiLoaded = true;
                }
                callback(err);
            });
        },

        /**
         * articleを読み込む
         * @param {Function} callback
         * @memberof NewsView#
         */
        loadArticle : function(callback) {
            var self = this;

            this.articleCollection.fetch({
                success : function() {
                    self.loadFavorite(callback);
                },

                error : function onErrorLoadArticle() {
                    callback('err');
                }
            });
        },

        /**
         * favoriteを読み込む
         * @param {Function} callback
         */
        loadFavorite : function(callback) {
            this.favoriteCollection.condition.filters = [
                new Equal("userId", app.user.get("__id"))
            ];

            this.favoriteCollection.fetch({
                success : function() {
                    callback();
                },

                error : function onErrorLoadFavorite() {
                    callback('error');
                }
            });
        },

        /**
         * eventsを読み込む
         * @param {Function} callback
         * @memberof NewsView#
         */
        loadEvents : function(callback) {
            this.eventsCollection.reset();
            this.eventsCollection.fetch({
                success : function() {
                    callback();
                },

                error : function onErrorLoadEvents() {
                    callback('error');
                }
            });
        },
        /**
         * Recommendを読み込む
         * @param {Function} callback
         * @memberof NewsView#
         */
        loadRecommend : function(callback) {
            this.recommendCollection.fetch({
                success : function() {
                    callback();
                },

                error : function() {
                    callback('error');
                }
            });
        },

        /**
         * 全ての情報検索完了後のコールバック関数
         * @param {Error|Undefined} err
         * @memberof NewsView#
         */
        onFetchAll : function(err) {
            if (err) {
                console.error(err);
                return;
            }

            $(".contents__primary").scrollTop(0);

            // this.newsCollection.add(this.youtubeCollection.models);
            this.newsCollection.reset();
            this.newsCollection.add(this.articleCollection.models);
            this.newsCollection.add(this.eventsCollection.models);

            // articleCollectionに切抜き、おすすめ状態を反映する
            this.newsCollection.each($.proxy(function(article) {
                if (article.get("title", "おすすめイベント")) {
                    console.log("aaa");
                }
                this.favoriteCollection.each(function(favorite) {
                    if (article.get("__id") === favorite.get("source")) {
                        article.set("isFavorite", !favorite.get("deletedAt"));
                        article.favorite = favorite;
                    }
                });

                // おすすめ数取得
                var recommends = this.recommendCollection.filter($.proxy(function(recommend) {
                    return article.get("__id") === recommend.get("source");
                }, this));
                article.recommendAmount = _.filter(recommends, function(recommend) {
                    return !recommend.get("deletedAt");
                }).length;

                // 自身のおすすめ情報を記事に付加
                _.each(recommends, $.proxy(function(recommend) {
                    if (recommend.get("isMine")) {
                        article.set("isMyRecommend", !recommend.get("deletedAt"));
                        article.recommend = recommend;
                    }
                }, this));

            }, this));

            // おすすめ記事を検索し、表示する
            var recommendArticle = this.newsCollection.find($.proxy(function(article) {
                return article.get("isRecommend") === "true" && article.get("publishedAt") === this.targetDate;
            }, this));

            if (recommendArticle) {
                // おすすめ記事を表示する
                var articleView = new RecommendArticleView({
                    model : recommendArticle
                });
                this.setView("#recommendArticle", articleView).render();
            } else {
                this.$(".recommendArticleContainer").hide();
            }
            // GridListView初期化
            this.showGridListView();

            // ArticleListView初期化
            this.showArticleListView();

            this.hideLoading();
        },
        /**
         * 記事一覧Viewを表示する要素のセレクタ
         * @memberof NewsView#
         */
        feedListElement: "#contents__top",
        /**
         * 左ペインの記事一覧メニューを表示する。
         * @memberof NewsView#
         */
        showGridListView : function() {
            var gridListView = this.createGridListView();
            gridListView.collection = this.newsCollection;

            this.removeView(this.gridListElement);

            this.setView(this.feedListElement, gridListView);
            gridListView.render();

            if (this.newsCollection.size() === 0) {
                this.showFeetNotFoundMessage();
            }
        },

        /**
         * 記事が見つからなかった場合のメッセージを画面に表示する。
         * @memberof NewsView#
         */
        showFeetNotFoundMessage : function() {
            this.$el.text("記事情報がありません");
        },

        /**
         * 右ペインの記事一覧を表示するViewのインスタンスを作成して返す。
         * @return {GridListView} 生成したGridListViewのインスタンス
         * @memberof NewsView#
         */
        createGridListView : function() {
            return new GridListView();
        },

        /**
         * 右ペインの記事一覧を表示する。
         * @memberof NewsView#
         */
        showArticleListView : function() {
            var articleListView = new ArticleListView();
            articleListView.collection = this.newsCollection;
            this.removeView("#article-list");
            this.setView("#article-list", articleListView);
            articleListView.render();
        },

        /**
         * 指定されたarticleIdの記事までスクロール
         *
         * @param {jQuery.Event} ev
         * @param {Object} param
         * @memberof NewsView#
         */
        onClickGridItem : function(ev, param) {
            var articleId = $(ev.currentTarget).attr("data-article-id");
            app.newsView = this;
            app.router.go("article", articleId);
        },

        /**
         * 指定された記事IDの記事を表示する。
         * @param articleId {String} 記事ID
         * @memberof NewsView#
         */
        showArticle : function(articleId) {
            var model = this.articleCollection.find(function(article) {
                return article.get("__id") === articleId;
            });
            if (!model) {
                vexDialog.defaultOptions.className = 'vex-theme-default';
                vexDialog.alert("指定された記事は存在しません。");
                return;
            }
            var template = require("ldsh!templates/{mode}/news/articleListItem");
            // 記事一覧に追加するViewクラス。
            // 以下の分岐処理で、対象のデータを表示するViewのクラスが設定される。
            var ListItemView;

            switch (model.get("type")) {
            case "1": // RSS
                template = require("ldsh!templates/{mode}/news/articleListItem");
                ListItemView = ArticleListItemView;
                if (model.get("rawHTML")) {
                    template = require("ldsh!templates/{mode}/news/articleListItemForHtml");
                }
                break;
            case "2": // YouTube
                template = require("ldsh!templates/{mode}/news/youTubeListItem");
                ListItemView = YouTubeListItemView;
                break;
            default:
                template = require("ldsh!templates/{mode}/news/eventsDetail");
                ListItemView = EventListItemView;
                break;
            }

            this.insertView(NewsView.SELECTOR_ARTICLE_DESTINATION, new ListItemView({
                model : model,
                template : template
            })).render();
            $("#contents__secondary").hide();
            $("#contents__primary").show();
        },

        /**
         * 記事詳細ページ以外では、記事詳細の要素を隠す
         * @memberof NewsView#
         */
        onRoute : function(route) {
            if (route === "showArticle") {
                $(NewsView.SELECTOR_ARTICLE_DESTINATION).show();
            } else {
                $(NewsView.SELECTOR_ARTICLE_DESTINATION).hide();
            }
        },

        /**
         * Google Analyticsでページビューを記録する
         */
        trackPageView : function() {
            app.ga.trackPageView("/NewsView", "ニュース");
        }
    }, {
        /**
         * 記事詳細を挿入する先のセレクタ
         * @memberof NewsView#
         */
        SELECTOR_ARTICLE_DESTINATION : "[data-news-detail]"
    });

    module.exports = NewsView;
});
<commit_msg>NNAM-348 新聞：発行時刻で発行日切り替え<commit_after>define(function(require, exports, module) {
    "use strict";

    var app = require("app");
    var async = require("async");

    // view
    var AbstractView = require("modules/view/AbstractView");
    var ArticleListView = require("modules/view/news/ArticleListView");
    var GridListView = require("modules/view/news/GridListView");
    var RecommendArticleView = require("modules/view/news/RecommendArticleView");
    var ArticleListItemView = require("modules/view/news/ArticleListItemView");
    var EventListItemView = require("modules/view/news/EventListItemView");
    var YouTubeListItemView = require("modules/view/news/YouTubeListItemView");

    // models
    var ArticleModel = require("modules/model/article/ArticleModel");

    // collections
    var ArticleCollection = require("modules/collection/article/ArticleCollection");
    var RecommendCollection = require("modules/collection/article/RecommendCollection");
    var FavoriteCollection = require("modules/collection/article/FavoriteCollection");
    var EventsCollection = require("modules/collection/events/EventsCollection");

    // util
    var DateUtil = require("modules/util/DateUtil");
    var BusinessUtil = require("modules/util/BusinessUtil");
    var vexDialog = require("vexDialog");
    var IsNull = require("modules/util/filter/IsNull");
    var Equal = require("modules/util/filter/Equal");
    var And = require("modules/util/filter/And");
    var Or = require("modules/util/filter/Or");
    var Le = require("modules/util/filter/Le");
    var Ge = require("modules/util/filter/Ge");

    /**
     * 記事一覧・詳細のメインとなる画面のViewクラスを作成する。
     * @class 記事一覧・詳細のメインとなる画面のViewクラス
     * @exports NewsView
     * @constructor
     */
    var NewsView = AbstractView.extend({

        template : require("ldsh!templates/{mode}/news/news"),
        model : new ArticleModel(),
        fetchCounter : 0,
        articleCollection : new ArticleCollection(),
        recommendCollection : new RecommendCollection(),
        favoriteCollection : new FavoriteCollection(),
        eventsCollection : new EventsCollection(),
        newsCollection : new Backbone.Collection(),
        events : {
            "click [data-article-id]" : "onClickGridItem"
        },
        beforeRendered : function() {
        },

        afterRendered : function() {
        },

        initialize : function(options) {
            options = options || {};

            this.targetDate = this.targetDate || options.date;

            this.setArticleSearchCondition({
                targetDate : new Date(this.targetDate)
            });
            this.searchArticles();
            this.initEvents();
        },

        /**
         * イベントを初期化する
         * @memberof NewsView#
         */
        initEvents : function() {
            app.router.on("route", this.onRoute.bind(this));
        },

        /**
         * 記事の検索処理を開始する。
         * @memberof NewsView#
         */
        searchArticles : function() {
            this.trackPageView();

            this.showLoading();

            // 現在保持しているデータをクリア
            this.articleCollection.reset();
            this.recommendCollection.reset();
            this.eventsCollection.reset();

            async.parallel([
                    this.loadYouTubeLibrary.bind(this), this.loadArticle.bind(this), this.loadRecommend.bind(this),
                    this.loadEvents.bind(this)
            ], this.onFetchAll.bind(this));
        },

        /**
         * 記事の検索条件を指定する。
         * @param {Object} 検索条件。現在、targetDateプロパティにDateオブジェクトを指定可能。
         * @memberof NewsView#
         */
        setArticleSearchCondition : function(condition) {
            var targetDate = condition.targetDate;
            var dateString = DateUtil.formatDate(targetDate, "yyyy-MM-dd");
            this.articleCollection.condition.filters = [
                    new Or([
                            new Equal("publishedAt", dateString), new And([
                                    new Le("publishedAt", dateString), new Ge("depublishedAt", dateString)
                            ])
                    ]), new IsNull("isDepublish")
            ];

            this.recommendCollection.condition.filters = [
                new Or([
                        new Equal("publishedAt", dateString), new And([
                                new Le("publishedAt", dateString), new Ge("depublishedAt", dateString)
                        ])
                ])
            ];

            this.eventsCollection.condition.filters = new Equal("publishedAt", dateString);
        },
        /**
         * youtubeライブラリを読み込む
         * @param {Function} callback
         * @memberof NewsView#
         */
        loadYouTubeLibrary : function(callback) {
            if (app.gapiLoaded) {
                callback();
                return;
            }
            var self = this;
            var loadScript = function(url) {
                return function(next) {
                    $.getScript(url).done(function() {
                        next(null);
                    }).fail(function(jqXHR, settings, err) {
                        next(err);
                    });
                };
            };
            var onLoadGAPI = function(next) {
                window.onLoadGAPI = function() {
                    delete window.onLoadGAPI;
                    next(null);
                };

                loadScript("https://apis.google.com/js/client.js?onload=onLoadGAPI")(function(err) {
                    if (err) {
                        next(err);
                        return;
                    }
                });
            };
            async.series([
                    onLoadGAPI, loadScript("https://www.youtube.com/iframe_api")
            ], function(err) {
                if (!err) {
                    app.gapiLoaded = true;
                }
                callback(err);
            });
        },

        /**
         * articleを読み込む
         * @param {Function} callback
         * @memberof NewsView#
         */
        loadArticle : function(callback) {
            var self = this;

            this.articleCollection.fetch({
                success : function() {
                    self.loadFavorite(callback);
                },

                error : function onErrorLoadArticle() {
                    callback('err');
                }
            });
        },

        /**
         * favoriteを読み込む
         * @param {Function} callback
         */
        loadFavorite : function(callback) {
            this.favoriteCollection.condition.filters = [
                new Equal("userId", app.user.get("__id"))
            ];

            this.favoriteCollection.fetch({
                success : function() {
                    callback();
                },

                error : function onErrorLoadFavorite() {
                    callback('error');
                }
            });
        },

        /**
         * eventsを読み込む
         * @param {Function} callback
         * @memberof NewsView#
         */
        loadEvents : function(callback) {
            this.eventsCollection.reset();
            this.eventsCollection.fetch({
                success : function() {
                    callback();
                },

                error : function onErrorLoadEvents() {
                    callback('error');
                }
            });
        },
        /**
         * Recommendを読み込む
         * @param {Function} callback
         * @memberof NewsView#
         */
        loadRecommend : function(callback) {
            this.recommendCollection.fetch({
                success : function() {
                    callback();
                },

                error : function() {
                    callback('error');
                }
            });
        },

        /**
         * 全ての情報検索完了後のコールバック関数
         * @param {Error|Undefined} err
         * @memberof NewsView#
         */
        onFetchAll : function(err) {
            if (err) {
                console.error(err);
                return;
            }

            $(".contents__primary").scrollTop(0);

            // this.newsCollection.add(this.youtubeCollection.models);
            this.newsCollection.reset();
            this.newsCollection.add(this.articleCollection.models);
            this.newsCollection.add(this.eventsCollection.models);

            // articleCollectionに切抜き、おすすめ状態を反映する
            this.newsCollection.each($.proxy(function(article) {
                if (article.get("title", "おすすめイベント")) {
                    console.log("aaa");
                }
                this.favoriteCollection.each(function(favorite) {
                    if (article.get("__id") === favorite.get("source")) {
                        article.set("isFavorite", !favorite.get("deletedAt"));
                        article.favorite = favorite;
                    }
                });

                // おすすめ数取得
                var recommends = this.recommendCollection.filter($.proxy(function(recommend) {
                    return article.get("__id") === recommend.get("source");
                }, this));
                article.recommendAmount = _.filter(recommends, function(recommend) {
                    return !recommend.get("deletedAt");
                }).length;

                // 自身のおすすめ情報を記事に付加
                _.each(recommends, $.proxy(function(recommend) {
                    if (recommend.get("isMine")) {
                        article.set("isMyRecommend", !recommend.get("deletedAt"));
                        article.recommend = recommend;
                    }
                }, this));

            }, this));

            // おすすめ記事を検索し、表示する
            var recommendArticle = this.newsCollection.find($.proxy(function(article) {
                return article.get("isRecommend") === "true" && article.get("publishedAt") === this.targetDate;
            }, this));

            if (recommendArticle) {
                // おすすめ記事を表示する
                var articleView = new RecommendArticleView({
                    model : recommendArticle
                });
                this.setView("#recommendArticle", articleView).render();
            } else {
                this.$(".recommendArticleContainer").hide();
            }
            // GridListView初期化
            this.showGridListView();

            // ArticleListView初期化
            this.showArticleListView();

            this.hideLoading();
        },
        /**
         * 記事一覧Viewを表示する要素のセレクタ
         * @memberof NewsView#
         */
        feedListElement: "#contents__top",
        /**
         * 左ペインの記事一覧メニューを表示する。
         * @memberof NewsView#
         */
        showGridListView : function() {
            var gridListView = this.createGridListView();
            gridListView.collection = this.newsCollection;

            this.removeView(this.gridListElement);

            this.setView(this.feedListElement, gridListView);
            gridListView.render();

            if (this.newsCollection.size() === 0) {
                this.showFeetNotFoundMessage();
            }
        },

        /**
         * 記事が見つからなかった場合のメッセージを画面に表示する。
         * @memberof NewsView#
         */
        showFeetNotFoundMessage : function() {
            this.$el.text("記事情報がありません");
        },

        /**
         * 右ペインの記事一覧を表示するViewのインスタンスを作成して返す。
         * @return {GridListView} 生成したGridListViewのインスタンス
         * @memberof NewsView#
         */
        createGridListView : function() {
            return new GridListView();
        },

        /**
         * 右ペインの記事一覧を表示する。
         * @memberof NewsView#
         */
        showArticleListView : function() {
            var articleListView = new ArticleListView();
            articleListView.collection = this.newsCollection;
            this.removeView("#article-list");
            this.setView("#article-list", articleListView);
            articleListView.render();
        },

        /**
         * 指定されたarticleIdの記事までスクロール
         *
         * @param {jQuery.Event} ev
         * @param {Object} param
         * @memberof NewsView#
         */
        onClickGridItem : function(ev, param) {
            var articleId = $(ev.currentTarget).attr("data-article-id");
            app.newsView = this;
            app.router.go("article", articleId);
        },

        /**
         * 指定された記事IDの記事を表示する。
         * @param articleId {String} 記事ID
         * @memberof NewsView#
         */
        showArticle : function(articleId) {
            var model = this.articleCollection.find(function(article) {
                return article.get("__id") === articleId;
            });
            if (!model) {
                vexDialog.defaultOptions.className = 'vex-theme-default';
                vexDialog.alert("指定された記事は存在しません。");
                return;
            }
            var template = require("ldsh!templates/{mode}/news/articleListItem");
            // 記事一覧に追加するViewクラス。
            // 以下の分岐処理で、対象のデータを表示するViewのクラスが設定される。
            var ListItemView;

            switch (model.get("type")) {
            case "1": // RSS
                template = require("ldsh!templates/{mode}/news/articleListItem");
                ListItemView = ArticleListItemView;
                if (model.get("rawHTML")) {
                    template = require("ldsh!templates/{mode}/news/articleListItemForHtml");
                }
                break;
            case "2": // YouTube
                template = require("ldsh!templates/{mode}/news/youTubeListItem");
                ListItemView = YouTubeListItemView;
                break;
            default:
                template = require("ldsh!templates/{mode}/news/eventsDetail");
                ListItemView = EventListItemView;
                break;
            }

            this.insertView(NewsView.SELECTOR_ARTICLE_DESTINATION, new ListItemView({
                model : model,
                template : template
            })).render();
            $("#contents__secondary").hide();
            $("#contents__primary").show();
        },

        /**
         * 記事詳細ページ以外では、記事詳細の要素を隠す
         * @memberof NewsView#
         */
        onRoute : function(route) {
            if (route === "showArticle") {
                $(NewsView.SELECTOR_ARTICLE_DESTINATION).show();
            } else {
                $(NewsView.SELECTOR_ARTICLE_DESTINATION).hide();
            }
        },

        /**
         * Google Analyticsでページビューを記録する
         */
        trackPageView : function() {
            app.ga.trackPageView("/NewsView", "ニュース");
        }
    }, {
        /**
         * 記事詳細を挿入する先のセレクタ
         * @memberof NewsView#
         */
        SELECTOR_ARTICLE_DESTINATION : "[data-news-detail]"
    });

    module.exports = NewsView;
});
<|endoftext|>


<commit_before>'use strict';

const Meeting = require('./meeting');
const config = require('../config');
const Channel = require('./channel');

class manager {

    /**
     * @constructor
     * @param  {Object} uprisebot
     */
    constructor(uprisebot) {
        this.meetings = {};
        this.uprisebot = uprisebot;
        this.bindEvents_();
    }

    /**
     * meetingExist - Check if a meeting is ongoing.
     *
     * @param  {String} channelId
     * @return {boolean}
     */
    meetingExist(channelId) {
        return this.meetings[channelId];
    }

    /**
     * create - Creates a meeting.
     *
     * @param  {String} channelId
     * @return {Meeting}
     */
    create(channelId) {
        let meeting = new Meeting(channelId);
        this.meetings[channelId] = meeting;
        return meeting;
    }

    /**
     * destroy - Destroys an existing meeting.
     *
     * @param  {String} channelId
     */
    destroy(channelId) {
        delete this.meetings[channelId];
    }

    /**
     * @private
     *
     * bindEvents_ - Bind interval events.
     */
    bindEvents_() {
        let self = this;

        this.uprisebot
            .hears(['start'], 'ambient', (bot, message) => {
                let channelId = message.channel;

                /**
                 * TODO: After storage implementation get rid of this.
                 */
                let meeting = self.meetingExist(channelId);
                if (meeting && !meeting.isActive)
                    self.destroy(channelId);

                if (meeting)
                    return bot.reply(message,
                        'Sorry, there is an existing meeting in this channel');

                meeting = self.create(channelId);
                let channel = new Channel(self.uprisebot);

                channel
                    .getMembers(channelId)
                    .then((members) => {
                        meeting.setMembers(members);
                        channel
                            .getChannelInfo(channelId)
                            .then((info) => {
                                meeting.setName(info.name);
                                bot.say({
                                    text: 'Good day <#' + info.id +
                                        '>, let\'s start the standup meeting!',
                                    channel: channelId,
                                    link_names: 1,
                                    parse: 'full'
                                });

                                return meeting.start(bot, message);
                            });
                    })
                    .then(() => {
                        self.destroy(channelId);
                    })
                    .catch((err) => {
                        console.error('Error', err);
                    });
            });

        this.uprisebot
            .hears(['status'], 'direct_mention', (bot, message) => {
                bot.reply(message, 'Active meetings are ' +
                    JSON.stringify(self.meetings));
            });

        this.uprisebot
            .hears(['skip', 'dismiss'], 'ambient', (bot, message) => {
                let meeting = self.meetings[message.channel];

                if (!meeting) return;

                meeting.emit(message.text);
            });

        this.uprisebot
            .hears(['quit'], 'ambient', (bot, message) => {
                let meeting = self.meetings[message.channel];

                if (!meeting) return;

                meeting.emit(message.text);
                self.destroy(message.channel);
            });

    }
}

module.exports = manager;
<commit_msg>Fixed "start" issue.<commit_after>'use strict';

const Meeting = require('./meeting');
const config = require('../config');
const Channel = require('./channel');

class manager {

    /**
     * @constructor
     * @param  {Object} uprisebot
     */
    constructor(uprisebot) {
        this.meetings = {};
        this.uprisebot = uprisebot;
        this.bindEvents_();
    }

    /**
     * meetingExist - Check if a meeting is ongoing.
     *
     * @param  {String} channelId
     * @return {boolean}
     */
    meetingExist(channelId) {
        return this.meetings[channelId];
    }

    /**
     * create - Creates a meeting.
     *
     * @param  {String} channelId
     * @return {Meeting}
     */
    create(channelId) {
        let meeting = new Meeting(channelId);
        this.meetings[channelId] = meeting;
        return meeting;
    }

    /**
     * destroy - Destroys an existing meeting.
     *
     * @param  {String} channelId
     */
    destroy(channelId) {
        delete this.meetings[channelId];
    }

    /**
     * @private
     *
     * bindEvents_ - Bind interval events.
     */
    bindEvents_() {
        let self = this;

        this.uprisebot
            .hears(['start'], 'ambient', (bot, message) => {
                let channelId = message.channel;

                /**
                 * TODO: After storage implementation get rid of this.
                 */
                let meeting = self.meetingExist(channelId);

                if (meeting && !meeting.isActive)
                    self.destroy(channelId);

                if (meeting)
                    return bot.reply(message,
                        'Sorry, there is an existing meeting in this channel');

                meeting = self.create(channelId);
                let channel = new Channel(self.uprisebot);

                channel
                    .getMembers(channelId)
                    .then((members) => {
                        meeting.setMembers(members);
                        channel
                            .getChannelInfo(channelId)
                            .then((info) => {
                                meeting.setName(info.name);
                                bot.say({
                                    text: 'Good day <#' + info.id +
                                        '>, let\'s start the standup meeting!',
                                    channel: channelId,
                                    link_names: 1,
                                    parse: 'full'
                                });

                                return meeting.start(bot, message);
                            })
                            .then(() => {
                                self.destroy(channelId);
                            });
                    })
                    .catch((err) => {
                        console.error('Error', err);
                    });
            });

        this.uprisebot
            .hears(['status'], 'direct_mention', (bot, message) => {
                bot.reply(message, 'Active meetings are ' +
                    JSON.stringify(self.meetings));
            });

        this.uprisebot
            .hears(['skip', 'dismiss'], 'ambient', (bot, message) => {
                let meeting = self.meetings[message.channel];

                if (!meeting) return;

                meeting.emit(message.text);
            });

        this.uprisebot
            .hears(['quit'], 'ambient', (bot, message) => {
                let meeting = self.meetings[message.channel];

                if (!meeting) return;

                meeting.emit(message.text);
                self.destroy(message.channel);
            });

    }
}

module.exports = manager;
<|endoftext|>


<commit_before>/**
 * Created by dsd on 19/03/15.
 */

(function()
{

    var sodium = require('sodium').api;
    const _LESALI_NONCE_BYTES = 6;
    const _NONCE_INIT = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];

    function get_nonce(c)
    {
        var end = sodium.crypto_box_NONCEBYTES;
        var start = end - _LESALI_NONCE_BYTES;
        var res = new Buffer(_NONCE_INIT);
        for (var i = start; i < end; i++)
        {
            res[i] = (c % 256);
            c = Math.floor(c / 256);
        }
        return res;
    }

    function nonce_to_Number(nonce)
    {
        var start = sodium.crypto_box_NONCEBYTES - 1;
        var end = start - _LESALI_NONCE_BYTES;
        var res = 0;
        for (var i = start; i > end; i--)
        {
            res *= 256;
            res += nonce[i];
        }
        return res;
    }

    function prepare_envelope(m, p, n_cnt, offset)
    {
        if (typeof(p) === 'number' && 0 < p && p < 256)
        {
            var m_len = m.length;
            var m_p_len = m_len + p;
            var m_p = Buffer(m_p_len);

            // 32 bytes crypto_box overhead + 38 envelope
            var e_len = m_p_len + sodium.crypto_box_BOXZEROBYTES + offset + _LESALI_NONCE_BYTES;
            var e = Buffer(e_len);

            m.copy(m_p);
            for (var i = m_len; i < m_p_len; i++)
            {
                m_p[i] = p;
            }

            var nonce = get_nonce(n_cnt);
            // copy the nonce to the offset
            nonce.copy(e, offset, sodium.crypto_box_NONCEBYTES - _LESALI_NONCE_BYTES, sodium.crypto_box_NONCEBYTES);
            return [e, m_p, nonce];
        }
        else
        {
            //TODO: Meaningful exception
            throw null;
        }
    }

    function encrypt_envelope(m, p, n_cnt, pk, sk, offset)
    {
        var prep = prepare_envelope(m, p, n_cnt, offset);
        sodium.crypto_box(prep[1], prep[2], pk, sk).copy(prep[0], offset + _LESALI_NONCE_BYTES, sodium.crypto_box_BOXZEROBYTES);
        return prep[0];
    }

    function encrypt_envelope_afternm(m, p, n_cnt, k, offset)
    {
        var prep = prepare_envelope(m, p, n_cnt, offset);
        sodium.crypto_box_afternm(prep[1], prep[2], k).copy(prep[0], offset + _LESALI_NONCE_BYTES, sodium.crypto_box_BOXZEROBYTES);
        return prep[0];
    }

    function open_envelope(e, offset)
    {
        var cbox_offset = offset + _LESALI_NONCE_BYTES;
        var nonce = new Buffer(_NONCE_INIT);
        var c = Buffer(e.length - cbox_offset + sodium.crypto_box_BOXZEROBYTES);

        e.copy(nonce, sodium.crypto_box_NONCEBYTES - _LESALI_NONCE_BYTES, offset, cbox_offset);
        e.copy(c, sodium.crypto_box_BOXZEROBYTES, cbox_offset);
        for (var i = 0; i < sodium.crypto_box_BOXZEROBYTES; ++i)
        {
            c[i] = 0;
        }
        return [c, nonce];
    }

    function truncate_padding(m_p)
    {
        var p_len = m_p[m_p.length - 1];
        var m_len = m_p.length - p_len;
        if (p_len > 0 && m_len > 0)
            return m_p.slice(0, m_len);
        return -1;
    }

    function check_authenticate_and_truncate(m_p, nonce)
    {
        if (m_p)
        {
            var m = truncate_padding(m_p);
            if (m !== -1)
                return {n_cnt: nonce_to_Number(nonce), m: m};
            return -2;
        }
        return -1;
    }

    function _public_envelope_extract_pk(e)
    {
        return e.slice(0, sodium.crypto_box_PUBLICKEYBYTES);
    }

    var exports = {
        PUBLIC_ENVELOPE_MIN_LEN: 49,

        public_envelope_extract_pk: _public_envelope_extract_pk,

        /**
         *
         * @param m plaintext message
         * @param p padding length
         * @param cnt message counter
         * @param pk_r public key of the receiver
         * @param keypair_s public/private key pair of the sender
         * @returns {*} buffer containing the envelope
         */
        public_envelope: function (m, p, cnt, pk_r, keypair_s)
        {
            var e = encrypt_envelope(m, p, cnt, pk_r, keypair_s.secretKey, sodium.crypto_box_PUBLICKEYBYTES);
            keypair_s.publicKey.copy(e);
            return e;
        },

        public_envelope_open: function (e, sk)
        {
            var pk = _public_envelope_extract_pk(e);
            var unsliced = open_envelope(e, sodium.crypto_box_PUBLICKEYBYTES);

            var m_p = sodium.crypto_box_open(unsliced[0], unsliced[1], pk, sk);
            return check_authenticate_and_truncate(m_p, unsliced[1]);
        },

        public_envelope_afternm: function (m, p, cnt, k, keypair_s)
        {
            var e = encrypt_envelope_afternm(m, p, cnt, k, sodium.crypto_box_PUBLICKEYBYTES);
            keypair_s.publicKey.copy(e);
            return e;
        },

        public_envelope_open_afternm: function (e, k)
        {
            var unsliced = open_envelope(e, sodium.crypto_box_PUBLICKEYBYTES);
            var m_p = sodium.crypto_box_open_afternm(unsliced[0], unsliced[1], k);
            return check_authenticate_and_truncate(m_p, unsliced[1]);
        },

        /**
         *
         * @param m plaintext message
         * @param p padding length
         * @param cnt message counter
         * @param pk_r public key of the receiver
         * @param keypair_s public/private key pair of the sender
         * @returns {*} buffer containing the envelope
         */
        anonymous_envelope: function (m, p, cnt, pk_r, keypair_s)
        {
            var e = encrypt_envelope(m, p, cnt, pk_r, keypair_s.secretKey, 0);
            return e;
        },

        anonymous_envelope_open: function (e, pk, sk)
        {
            var unsliced = open_envelope(e, 0);

            var m_p = sodium.crypto_box_open(unsliced[0], unsliced[1], pk, sk);
            return check_authenticate_and_truncate(m_p, unsliced[1]);
        },

        anonymous_envelope_afternm: function (m, p, cnt, k)
        {
            return encrypt_envelope_afternm(m, p, cnt, k, 0);
        },

        anonymous_envelope_open_afternm: function (e, k)
        {
            var unsliced = open_envelope(e, 0);
            var m_p = sodium.crypto_box_open_afternm(unsliced[0], unsliced[1], k);
            return check_authenticate_and_truncate(m_p, unsliced[1]);
        }
    };

    if (typeof module !== 'undefined' && typeof module.exports !== 'undefined')
        module.exports = exports;
    else
        window.lesali = exports;
})();<commit_msg>public function to extract nonce from public and anonymous envelope<commit_after>/**
 * Created by dsd on 19/03/15.
 */

(function()
{
    var sodium = require('sodium').api;
    const _LESALI_NONCE_BYTES = 6;
    const _NONCE_INIT = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];

    function get_nonce(c)
    {
        var end = sodium.crypto_box_NONCEBYTES;
        var start = end - _LESALI_NONCE_BYTES;
        var res = new Buffer(_NONCE_INIT);
        for (var i = start; i < end; i++)
        {
            res[i] = (c % 256);
            c = Math.floor(c / 256);
        }
        return res;
    }

    /**
     * Reads a little-endian integer of length _LESALI_NONCE_BYTES at offset _offset and returns a Number.
     *
     * @param nonce
     * @param _offset
     * @returns {number}
     */
    function nonce_to_Number(nonce, _offset)
    {
        var offset = _offset || sodium.crypto_box_NONCEBYTES;
        var res = 0;
        for (var i = offset+_LESALI_NONCE_BYTES-1; i >= offset; i--)
        {
            res *= 256;
            res += nonce[i];
        }
        return res;
    }

    function prepare_envelope(m, p, n_cnt, offset)
    {
        if (typeof(p) === 'number' && 0 < p && p < 256)
        {
            var m_len = m.length;
            var m_p_len = m_len + p;
            var m_p = Buffer(m_p_len);

            // 32 bytes crypto_box overhead + 38 envelope
            var e_len = m_p_len + sodium.crypto_box_BOXZEROBYTES + offset + _LESALI_NONCE_BYTES;
            var e = Buffer(e_len);

            m.copy(m_p);
            for (var i = m_len; i < m_p_len; i++)
            {
                m_p[i] = p;
            }

            var nonce = get_nonce(n_cnt);
            // copy the nonce to the offset
            nonce.copy(e, offset, sodium.crypto_box_NONCEBYTES - _LESALI_NONCE_BYTES, sodium.crypto_box_NONCEBYTES);
            return [e, m_p, nonce];
        }
        else
        {
            //TODO: Meaningful exception
            throw null;
        }
    }

    function encrypt_envelope(m, p, n_cnt, pk, sk, offset)
    {
        var prep = prepare_envelope(m, p, n_cnt, offset);
        sodium.crypto_box(prep[1], prep[2], pk, sk).copy(prep[0], offset + _LESALI_NONCE_BYTES, sodium.crypto_box_BOXZEROBYTES);
        return prep[0];
    }

    function encrypt_envelope_afternm(m, p, n_cnt, k, offset)
    {
        var prep = prepare_envelope(m, p, n_cnt, offset);
        sodium.crypto_box_afternm(prep[1], prep[2], k).copy(prep[0], offset + _LESALI_NONCE_BYTES, sodium.crypto_box_BOXZEROBYTES);
        return prep[0];
    }

    function open_envelope(e, offset)
    {
        var cbox_offset = offset + _LESALI_NONCE_BYTES;
        var nonce = new Buffer(_NONCE_INIT);
        var c = Buffer(e.length - cbox_offset + sodium.crypto_box_BOXZEROBYTES);

        e.copy(nonce, sodium.crypto_box_NONCEBYTES - _LESALI_NONCE_BYTES, offset, cbox_offset);
        e.copy(c, sodium.crypto_box_BOXZEROBYTES, cbox_offset);
        for (var i = 0; i < sodium.crypto_box_BOXZEROBYTES; ++i)
        {
            c[i] = 0;
        }
        return [c, nonce];
    }

    function truncate_padding(m_p)
    {
        var p_len = m_p[m_p.length - 1];
        var m_len = m_p.length - p_len;
        if (p_len > 0 && m_len > 0)
            return m_p.slice(0, m_len);
        return -1;
    }

    function check_authenticate_and_truncate(m_p, nonce)
    {
        if (m_p)
        {
            var m = truncate_padding(m_p);
            if (m !== -1)
                return {n_cnt: nonce_to_Number(nonce), m: m};
            return -2;
        }
        return -1;
    }

    function _public_envelope_extract_pk(e)
    {
        return e.slice(0, sodium.crypto_box_PUBLICKEYBYTES);
    }

    var exports = {
        PUBLIC_ENVELOPE_MIN_LEN: 49,

        public_envelope_extract_pk: _public_envelope_extract_pk,

        public_envelope_extract_nonce: function(e)
        {
            return nonce_to_Number(e, sodium.crypto_box_PUBLICKEYBYTES);
        },

        anonymous_envelope_extract_nonce: function(e)
        {
            return nonce_to_Number(e, 0);
        },

        /**
         *
         * @param m plaintext message
         * @param p padding length
         * @param cnt message counter
         * @param pk_r public key of the receiver
         * @param keypair_s public/private key pair of the sender
         * @returns {*} buffer containing the envelope
         */
        public_envelope: function (m, p, cnt, pk_r, keypair_s)
        {
            var e = encrypt_envelope(m, p, cnt, pk_r, keypair_s.secretKey, sodium.crypto_box_PUBLICKEYBYTES);
            keypair_s.publicKey.copy(e);
            return e;
        },

        public_envelope_open: function (e, sk)
        {
            var pk = _public_envelope_extract_pk(e);
            var unsliced = open_envelope(e, sodium.crypto_box_PUBLICKEYBYTES);

            var m_p = sodium.crypto_box_open(unsliced[0], unsliced[1], pk, sk);
            return check_authenticate_and_truncate(m_p, unsliced[1]);
        },

        public_envelope_afternm: function (m, p, cnt, k, keypair_s)
        {
            var e = encrypt_envelope_afternm(m, p, cnt, k, sodium.crypto_box_PUBLICKEYBYTES);
            keypair_s.publicKey.copy(e);
            return e;
        },

        public_envelope_open_afternm: function (e, k)
        {
            var unsliced = open_envelope(e, sodium.crypto_box_PUBLICKEYBYTES);
            var m_p = sodium.crypto_box_open_afternm(unsliced[0], unsliced[1], k);
            return check_authenticate_and_truncate(m_p, unsliced[1]);
        },

        /**
         *
         * @param m plaintext message
         * @param p padding length
         * @param cnt message counter
         * @param pk_r public key of the receiver
         * @param keypair_s public/private key pair of the sender
         * @returns {*} buffer containing the envelope
         */
        anonymous_envelope: function (m, p, cnt, pk_r, keypair_s)
        {
            var e = encrypt_envelope(m, p, cnt, pk_r, keypair_s.secretKey, 0);
            return e;
        },

        anonymous_envelope_open: function (e, pk, sk)
        {
            var unsliced = open_envelope(e, 0);

            var m_p = sodium.crypto_box_open(unsliced[0], unsliced[1], pk, sk);
            return check_authenticate_and_truncate(m_p, unsliced[1]);
        },

        anonymous_envelope_afternm: function (m, p, cnt, k)
        {
            return encrypt_envelope_afternm(m, p, cnt, k, 0);
        },

        anonymous_envelope_open_afternm: function (e, k)
        {
            var unsliced = open_envelope(e, 0);
            var m_p = sodium.crypto_box_open_afternm(unsliced[0], unsliced[1], k);
            return check_authenticate_and_truncate(m_p, unsliced[1]);
        }
    };

    if (typeof module !== 'undefined' && typeof module.exports !== 'undefined')
        module.exports = exports;
    else
        window.lesali = exports;
})();<|endoftext|>


<commit_before>export default (sequelize, DataTypes) => {
  const rsbuddy = sequelize.define("rsbuddy", {
    id: {
      defaultValue: DataTypes.UUIDV1,
      primaryKey: true,
      type: DataTypes.UUID
    },
    ts: {
      allowNull: false,
      type: DataTypes.BIGINT
    },
    buyingPrice: {
      field: "buying_price",
      type: DataTypes.BIGINT
    },
    buyingCompleted: {
      field: "buying_completed",
      type: DataTypes.BIGINT
    },
    sellingPrice: {
      field: "selling_pice",
      type: DataTypes.BIGINT
    },
    sellingCompleted: {
      field: "selling_completed",
      type: DataTypes.BIGINT
    },
    overallPrice: {
      field: "overall_price",
      type: DataTypes.BIGINT
    },
    overallCompleted: {
      field: "overall_completed",
      type: DataTypes.BIGINT
    }
  })

  return rsbuddy
}
<commit_msg>Add freezeTableName property to rsbuddy schema<commit_after>export default (sequelize, DataTypes) => {
  const rsbuddy = sequelize.define(
    "rsbuddy",
    {
      id: {
        defaultValue: DataTypes.UUIDV1,
        primaryKey: true,
        type: DataTypes.UUID
      },
      ts: {
        allowNull: false,
        type: DataTypes.BIGINT
      },
      buyingPrice: {
        field: "buying_price",
        type: DataTypes.BIGINT
      },
      buyingCompleted: {
        field: "buying_completed",
        type: DataTypes.BIGINT
      },
      sellingPrice: {
        field: "selling_pice",
        type: DataTypes.BIGINT
      },
      sellingCompleted: {
        field: "selling_completed",
        type: DataTypes.BIGINT
      },
      overallPrice: {
        field: "overall_price",
        type: DataTypes.BIGINT
      },
      overallCompleted: {
        field: "overall_completed",
        type: DataTypes.BIGINT
      }
    },
    { freezeTableName: true }
  )

  return rsbuddy
}
<|endoftext|>


<commit_before>
const bot = require('./../bot');
/**
 * This method should return the response directly to the channel
 * @param {*string array} params
 * @param {*message} message
 */
async function command(params, message) {
    let res = "";
    for (let i = 0; i < params.length; i++) {
        let emoji = bot.emojis.find('name', params[i]);
        if (emoji)
            res = res + " " + emoji.toString();
        else
            res = params[i];
    }
    message.channel.sendMessage(res);
}
/**
 * description of the command
 */
const description = "Converts text to emoji";
/**
 * Define Exports
 */
module.exports = {
    execute: command,
    description: description
};
<commit_msg>fix emojify concat<commit_after>
const bot = require('./../bot');
/**
 * This method should return the response directly to the channel
 * @param {*string array} params
 * @param {*message} message
 */
async function command(params, message) {
    let res = "";
    for (let i = 0; i < params.length; i++) {
        let emoji = bot.emojis.find('name', params[i]);
        if (emoji)
            res = res + " " + emoji.toString();
        else
            res = res + " " + params[i];
    }
    message.channel.sendMessage(res);
}
/**
 * description of the command
 */
const description = "Converts text to emoji";
/**
 * Define Exports
 */
module.exports = {
    execute: command,
    description: description
};
<|endoftext|>


<commit_before>/**
 * Output as CSV to /output/csv/
 * Removes any existing commas before converting
 * output file to csv.
 */
module.exports = function(options, opfile, columns, log, timer, moduleCallback) {
  var table = options.source + '.' + options.table,
    fs = require('fs'),
    mkdirp = require('mkdirp'),
    async = require('async');

  log.group('CSV Output').log('Copying file from opfile tmp to output/csv/' + table + '.csv');

  async.waterfall([
    function(cb) {
      mkdirp('./output/csv/', function(err) {
        if (err) return cb(err);
        cb(null);
      })
    },
    function(cb) {
      fs.readFile(opfile.filename, 'utf-8', function(err, data) {
        if (err) return cb(err);
        cb(null, data);
      })
    },
    function(data, cb) {
      /*fs.writeFile('./output/csv/' + table + '.csv', data.replace(/,/g, '').replace(/\t/g, ','), 'utf-8', function(err) {
        if (err) return cb(err);
        log.log('Copied file, removed all , and then replaced tabs with commas.');
        cb(null);
      });*/
      fs.writeFileSync('./output/csv/' + table + '.csv', data.replace(/,/g, '').replace(/\t/g, ','));
      //log.log('Copied file, removed all commas and replaces tabs with commas.');
      cb(null);
    }
  ], function(err) {
    if (err) return moduleCallback(err);
    moduleCallback(null, opfile);
  })
}
<commit_msg>npm deploy<commit_after>/**
 * Output as CSV to /output/csv/
 * Removes any existing commas before converting
 * output file to csv.
 */
module.exports = function(options, opfile, columns, log, timer, moduleCallback) {
  var table = options.source + '.' + options.table,
    fs = require('graceful-fs'),
    mkdirp = require('mkdirp'),
    async = require('async');

  log.group('CSV Output').log('Copying file from opfile tmp to output/csv/' + table + '.csv');

  async.waterfall([
    function(cb) {
      mkdirp('./output/csv/', function(err) {
        if (err) return cb(err);
        cb(null);
      })
    },
    function(cb) {
      fs.readFile(opfile.filename, 'utf-8', function(err, data) {
        if (err) return cb(err);
        cb(null, data);
      })
    },
    function(data, cb) {
      /*fs.writeFile('./output/csv/' + table + '.csv', data.replace(/,/g, '').replace(/\t/g, ','), 'utf-8', function(err) {
        if (err) return cb(err);
        log.log('Copied file, removed all , and then replaced tabs with commas.');
        cb(null);
      });*/
      fs.writeFileSync('./output/csv/' + table + '.csv', data.replace(/,/g, '').replace(/\t/g, ','));
      //log.log('Copied file, removed all commas and replaces tabs with commas.');
      cb(null);
    }
  ], function(err) {
    if (err) return moduleCallback(err);
    moduleCallback(null, opfile);
  })
}
<|endoftext|>


<commit_before>function renderMessage (message, direction, time, type) {
  var renderers = {
    mediaShare: renderMessageAsPost,
    text: renderMessageAsText,
    like: renderMessageAsLike,
    media: renderMessageAsImage,
    raven_media: renderMessageAsRavenImage,
    reel_share: renderMessageAsUserStory, // replying to a user's story
    link: renderMessageAsLink,
    animated_media: renderMessageAsAnimatedMedia,
    actionLog: renderMessageAsActionLog,
    voice_media: renderMessageAsVoiceMedia,
    placeholder: renderPlaceholderAsText,
  }

  var div = dom(`<div class="message clearfix ${direction}"></div>`);
  var divContent = dom('

');

  if (direction === 'inward') {
    var senderUsername = window.chat.accounts.find((account) => {
      return account.id == message._params.accountId
    })._params.username;
    divContent.appendChild(dom(`<p class="message-sender">${senderUsername}</p>`));
  }

  if (!type && typeof message === 'string') type = 'text';

  if (renderers[type]) renderers[type](divContent, message);
  else renderMessageAsText(divContent, '<unsupported message format>', true);

  divContent.appendChild(dom(
    `<p class="message-time">${time ? formatTime(time) : 'Sending...'}</p>`)
  );

  if (message._params) renderMessageReactions(divContent, message._params.reactions);
  div.appendChild(divContent);

  return div
}

function renderMessageAsActionLog(container, message) {
  renderMessageAsText(container, message._params.actionLog.description);
}

function renderMessageReactions(container, reactions) {
  if (!reactions) return;

  var div = dom('

')
  reactions.likes.forEach((like) => {
    div.appendChild(dom(`<img data-user-id="${like.sender_id}">`));
    getDisplayPictureUrl(like.sender_id);
  });
  container.appendChild(div);
}

function renderDisplayPicture(displayPicture) {
  document.querySelectorAll(`img[data-user-id="${displayPicture.userId}"]`).forEach((img) => {
    img.src = displayPicture.url;
  });
}

function renderMessageAsPost (container, message) {
  var post = message.mediaShare._params;

  if (post.images) {
    // carousels have nested arrays before getting to image url
    var img = dom(`<img src="${post.images[0].url || post.images[0][0].url}">`);
    img.onload = conditionedScrollToBottom();
    container.appendChild(img);
  }

  if (post.caption) {
    container.appendChild(dom(`<p class="post-caption">${truncate(post.caption, 30)}</p>`));
  }
  container.classList.add('ig-media');
  container.onclick = () => renderPost(post)
}

function renderPost (post) {
  const postDom = dom('

');
  if (post.videos) {
    postDom.appendChild(dom(`<video width="${post.videos[0].width}" controls>
                                <source src="${post.videos[0].url}" type="video/mp4">
                              </video>`));
  } else if (post.carouselMedia && post.carouselMedia.length) {
    window.carouselInit(postDom, post.images.map((el) => el[0].url))
  } else {
    postDom.appendChild(dom(`<img src="${post.images[0].url}"/>`));
  }
  if (post.caption) {
    postDom.appendChild(dom(`<p class="post-caption">${post.caption}</p>`));
  }
  const browserLink = dom('<button class="view-on-ig">View on Instagram</button>')
  browserLink.onclick = () => openInBrowser(post.webLink)
  postDom.appendChild(browserLink);
  showInViewer(postDom);
}

function renderMessageAsUserStory (container, message) {
  container.classList.add('ig-media');
  if (message._params.reelShare.media.image_versions2) {
    var url = message._params.reelShare.media.image_versions2.candidates[0].url
    var img = dom(`<img src="${url}">`);
    img.onload = conditionedScrollToBottom();
    container.appendChild(img);

    container.addEventListener('click', () => {
      if (message._params.reelShare.media.video_versions) {
        const videoUrl = message._params.reelShare.media.video_versions[0].url;
        showInViewer(dom(`<video controls src="${videoUrl}">`));
      } else {
        showInViewer(dom(`<img src="${url}">`));
      }
    })
  }

  if (message._params.reelShare.text) {
    container.appendChild(dom(`<p class="post-caption">${message._params.reelShare.text}</p>`));
  }
}

function renderMessageAsImage (container, message) {
  var url = typeof message === 'string' ? message : message._params.media[0].url
  var img = dom(`<img src="${url}">`);
  img.onload = conditionedScrollToBottom();
  container.appendChild(img);
  container.classList.add('ig-media');

  container.addEventListener('click', () => {
    showInViewer(dom(`<img src="${url}">`));
  })
  container.oncontextmenu = () => renderImageContextMenu(url);
}

function renderMessageAsRavenImage (container, message) {
  if (message._params.visualMedia && message._params.visualMedia.media.image_versions2) {
    container.classList.add('ig-media');
    var url = message._params.visualMedia.media.image_versions2.candidates[0].url
    var img = dom(`<img src="${url}">`);
    img.onload = conditionedScrollToBottom();
    container.appendChild(img);

    container.addEventListener('click', () => {
      showInViewer(dom(`<img src="${url}">`));
    })
    container.oncontextmenu = () => renderImageContextMenu(url);
  } else {
    renderMessageAsText(container, '<unsupported message format>', true);
  }
}

function renderMessageAsLike (container) {
  renderMessageAsImage(container, 'img/love.png');
}

function renderMessageAsText (container, message, noContext) {
  var text = typeof message === 'string' ? message : message._params.text;
  container.appendChild(document.createTextNode(text));
  if (!noContext) container.oncontextmenu = () => renderContextMenu(text);
}

function linkUsernames (text) {
  return text.replace (/@([\w.]+)/g, '@$1');
}

function renderPlaceholderAsText (container, message) {
  var text = "";
  if (!message.placeholder._params.is_linked) {
    text = message.placeholder._params.message;
  } else {
    text = linkUsernames (message.placeholder._params.message);
  }
  var placeholderDom = dom("
" + text + "
");
  placeholderDom.classList.add('placeholder');

  container.appendChild(placeholderDom);
  container.classList.add('ig-media');
}

function renderMessageAsLink (container, message) {
  const { link } = message.link._params;
  const text = message.link._params.text;
  if (link.image && link.image.url) {
    var img = dom(`<img src="${link.image.url}">`);
    img.onload = conditionedScrollToBottom();
    container.appendChild(img);
  }
  // replace all contained links with anchor tags
  container.innerHTML += text.replace(URL_REGEX, (url) => {
    return `<a class="link-in-message">${url}</a>`;
  });
  container.classList.add('ig-media');
  container.onclick = () => {
    // for links that don't have protocol included
    const url = /^(http|https):\/\//.test(link.url) ? link.url : `http://${link.url}`;
    openInBrowser(url);
  }
}

function renderMessageAsAnimatedMedia (container, message) {
  var { url } = message._params.animatedMedia.images.fixed_height;
  var img = dom(`<img src="${url}">`);
  img.onload = conditionedScrollToBottom();
  container.appendChild(img);
  container.classList.add('ig-media');

  container.addEventListener('click', () => {
    showInViewer(dom(`<img src="${url}">`));
  })
}

function renderContextMenu (text) {
  const menu = new Menu();
  const menuItem = new MenuItem({
    label: 'Quote Message',
    click: () => quoteText(text)
  });
  menu.append(menuItem);
  menu.popup({});
}

function renderImageContextMenu (url) {
  const menu = new Menu();
  const menuItem = new MenuItem({
    label: 'Copy URL to clipboard',
    click: () => copyToCliboard(url)
  });
  menu.append(menuItem);
  menu.popup({});
}

function renderChatListItem(chatTitle, msgPreview, thumbnail, id) {
  var li = document.createElement('li');
  li.classList.add('col-12', 'p-3');
  li.appendChild(dom(`<div><img class="thumb" src="${thumbnail}"></div>`));
  li.appendChild(dom(`<div class="username ml-3 d-none d-sm-inline-block"><b>${chatTitle}</b><br>${msgPreview}</div>`));
  if (id) li.setAttribute("id", `chatlist-${id}`);

  return li;
}

function renderSearchResult (users) {
  var ul = document.querySelector('.chat-list ul');
  ul.innerHTML = "";
  users.forEach((user) => {
    var li = renderChatListItem(user._params.username, 'Send a message', user._params.picture);
    li.onclick = () => {
      setActive(li);
      if (window.chatUsers[user.id]) {
        getChat(window.chatUsers[user.id]);
      } else {
        window.currentChatId = DUMMY_CHAT_ID;
        renderChat({items: [], accounts: [user]});
      }
    }
    ul.appendChild(li);
  })
}

function renderChatList (chatList) {
  var ul = document.querySelector('.chat-list ul');
  ul.innerHTML = "";
  chatList.forEach((chat_) => {
    var msgPreview = getMsgPreview(chat_);
    var chatTitle = getChatTitle(chat_);
    let thumbnail = '';
    if (chat_.accounts[0]) {
      thumbnail = chat_.accounts[0]._params.picture;
    }
    var li = renderChatListItem(chatTitle, msgPreview, thumbnail, chat_.id);

    registerChatUser(chat_);
    if (isActive(chat_)) setActive(li);
    // don't move this down!
    addNotification(li, chat_);
    window.chatListHash[chat_.id] = chat_;

    li.onclick = () => {
      markAsRead(chat_.id, li);
      setActive(li);
      // render the cached chat before fetching latest
      // to avoid visible latency
      if (window.chatCache[chat_.id]) {
        renderChat(window.chatCache[chat_.id]);
      }
      getChat(chat_.id);
    }
    ul.appendChild(li);
  })
}

function renderChatHeader (chat_) {
  let chatTitle = (chat_.id ? getChatTitle(chat_) : getUsernames(chat_)); // if chat_.id is not defined, it is a new contact
  let b = dom(`<b>${chatTitle}</b>`);

  if (chat_.accounts.length === 1) {
    // open user profile in browser
    b.onclick = () =>
      openInBrowser(
        `https://instagram.com/${getUsernames(chat_)}`
      );
  }
  let chatTitleContainer = document.querySelector('.chat-title');
  chatTitleContainer.innerHTML = '';
  chatTitleContainer.appendChild(b);
}

function renderChat (chat_) {
  window.chat = chat_;
  window.chatCache[chat_.id] = chat_;

  var msgContainer = document.querySelector(CHAT_WINDOW_SELECTOR);
  msgContainer.innerHTML = '';
  renderChatHeader(chat_);
  var messages = chat_.items.slice().reverse();
  if (canRenderOlderMessages()) {
    // load older messages if they exist too
    messages = window.olderMessages.slice().reverse().concat(messages);
  }
  messages.forEach((message) => {
    var div = renderMessage(message, getMsgDirection(message),
      message._params.created, message._params.type
    );
    msgContainer.appendChild(div);
  })
  renderMessageSeenText(msgContainer, chat_);
  scrollToChatBottom();
  loadOlderMsgsOnScrollTop();

  addSubmitHandler(chat_);
  addAttachmentSender(chat_);
  document.querySelector(MSG_INPUT_SELECTOR).focus();
}

function renderOlderMessages (messages) {
  const msgContainer = document.querySelector(CHAT_WINDOW_SELECTOR);
  const domPostion = msgContainer.firstChild;
  messages.forEach((message) => {
    var div = renderMessage(message, getMsgDirection(message),
      message._params.created, message._params.type
    );
    msgContainer.prepend(div);
  });
  // scroll back to dom position before the older messages were rendered
  domPostion.scrollIntoView();
}

function renderMessageAsVoiceMedia(container, message) {
  var src = message._params.voiceMedia.media.audio.audio_src;
  var audio = dom(`<audio controls src="${src}"/>`);
  container.appendChild(audio);
}

function renderMessageSeenText (container, chat_) {
  container.appendChild(dom(`<div class="seen italic outward"><p>${getIsSeenText(chat_)}</p></div>`));
}

function renderUnfollowers (users) {
  var ul = dom(`<ul class="unfollowers"></ul>`);
  users.forEach((user) => {
    var li = dom(
      `

${user._params.username}

`
    );
    var unfollowButton = dom(`<button class="unfollow-button">Unfollow</button>`);
    unfollowButton.onclick = () => {
      unfollow(user.id);
      li.remove();
    }
    li.appendChild(unfollowButton);
    ul.appendChild(li);
  })

  showInViewer(ul);
}
<commit_msg>Code styling fix<commit_after>function renderMessage (message, direction, time, type) {
  var renderers = {
    mediaShare: renderMessageAsPost,
    text: renderMessageAsText,
    like: renderMessageAsLike,
    media: renderMessageAsImage,
    raven_media: renderMessageAsRavenImage,
    reel_share: renderMessageAsUserStory, // replying to a user's story
    link: renderMessageAsLink,
    animated_media: renderMessageAsAnimatedMedia,
    actionLog: renderMessageAsActionLog,
    voice_media: renderMessageAsVoiceMedia,
    placeholder: renderPlaceholderAsText,
  }

  var div = dom(`<div class="message clearfix ${direction}"></div>`);
  var divContent = dom('

');

  if (direction === 'inward') {
    var senderUsername = window.chat.accounts.find((account) => {
      return account.id == message._params.accountId
    })._params.username;
    divContent.appendChild(dom(`<p class="message-sender">${senderUsername}</p>`));
  }

  if (!type && typeof message === 'string') type = 'text';

  if (renderers[type]) renderers[type](divContent, message);
  else renderMessageAsText(divContent, '<unsupported message format>', true);

  divContent.appendChild(dom(
    `<p class="message-time">${time ? formatTime(time) : 'Sending...'}</p>`)
  );

  if (message._params) renderMessageReactions(divContent, message._params.reactions);
  div.appendChild(divContent);

  return div
}

function renderMessageAsActionLog(container, message) {
  renderMessageAsText(container, message._params.actionLog.description);
}

function renderMessageReactions(container, reactions) {
  if (!reactions) return;

  var div = dom('

')
  reactions.likes.forEach((like) => {
    div.appendChild(dom(`<img data-user-id="${like.sender_id}">`));
    getDisplayPictureUrl(like.sender_id);
  });
  container.appendChild(div);
}

function renderDisplayPicture(displayPicture) {
  document.querySelectorAll(`img[data-user-id="${displayPicture.userId}"]`).forEach((img) => {
    img.src = displayPicture.url;
  });
}

function renderMessageAsPost (container, message) {
  var post = message.mediaShare._params;

  if (post.images) {
    // carousels have nested arrays before getting to image url
    var img = dom(`<img src="${post.images[0].url || post.images[0][0].url}">`);
    img.onload = conditionedScrollToBottom();
    container.appendChild(img);
  }

  if (post.caption) {
    container.appendChild(dom(`<p class="post-caption">${truncate(post.caption, 30)}</p>`));
  }
  container.classList.add('ig-media');
  container.onclick = () => renderPost(post)
}

function renderPost (post) {
  const postDom = dom('

');
  if (post.videos) {
    postDom.appendChild(dom(`<video width="${post.videos[0].width}" controls>
                                <source src="${post.videos[0].url}" type="video/mp4">
                              </video>`));
  } else if (post.carouselMedia && post.carouselMedia.length) {
    window.carouselInit(postDom, post.images.map((el) => el[0].url))
  } else {
    postDom.appendChild(dom(`<img src="${post.images[0].url}"/>`));
  }
  if (post.caption) {
    postDom.appendChild(dom(`<p class="post-caption">${post.caption}</p>`));
  }
  const browserLink = dom('<button class="view-on-ig">View on Instagram</button>')
  browserLink.onclick = () => openInBrowser(post.webLink)
  postDom.appendChild(browserLink);
  showInViewer(postDom);
}

function renderMessageAsUserStory (container, message) {
  container.classList.add('ig-media');
  if (message._params.reelShare.media.image_versions2) {
    var url = message._params.reelShare.media.image_versions2.candidates[0].url
    var img = dom(`<img src="${url}">`);
    img.onload = conditionedScrollToBottom();
    container.appendChild(img);

    container.addEventListener('click', () => {
      if (message._params.reelShare.media.video_versions) {
        const videoUrl = message._params.reelShare.media.video_versions[0].url;
        showInViewer(dom(`<video controls src="${videoUrl}">`));
      } else {
        showInViewer(dom(`<img src="${url}">`));
      }
    })
  }

  if (message._params.reelShare.text) {
    container.appendChild(dom(`<p class="post-caption">${message._params.reelShare.text}</p>`));
  }
}

function renderMessageAsImage (container, message) {
  var url = typeof message === 'string' ? message : message._params.media[0].url
  var img = dom(`<img src="${url}">`);
  img.onload = conditionedScrollToBottom();
  container.appendChild(img);
  container.classList.add('ig-media');

  container.addEventListener('click', () => {
    showInViewer(dom(`<img src="${url}">`));
  })
  container.oncontextmenu = () => renderImageContextMenu(url);
}

function renderMessageAsRavenImage (container, message) {
  if (message._params.visualMedia && message._params.visualMedia.media.image_versions2) {
    container.classList.add('ig-media');
    var url = message._params.visualMedia.media.image_versions2.candidates[0].url
    var img = dom(`<img src="${url}">`);
    img.onload = conditionedScrollToBottom();
    container.appendChild(img);

    container.addEventListener('click', () => {
      showInViewer(dom(`<img src="${url}">`));
    })
    container.oncontextmenu = () => renderImageContextMenu(url);
  } else {
    renderMessageAsText(container, '<unsupported message format>', true);
  }
}

function renderMessageAsLike (container) {
  renderMessageAsImage(container, 'img/love.png');
}

function renderMessageAsText (container, message, noContext) {
  var text = typeof message === 'string' ? message : message._params.text;
  container.appendChild(document.createTextNode(text));
  if (!noContext) container.oncontextmenu = () => renderContextMenu(text);
}

function linkUsernames (text) {
  return text.replace(/@([\w.]+)/g, '@$1');
}

function renderPlaceholderAsText (container, message) {
  var html = "";
  if (!message.placeholder._params.is_linked) {
    html = message.placeholder._params.message;
  } else {
    html = linkUsernames(message.placeholder._params.message);
  }
  var placeholderDom = dom("
" + html + "
");
  placeholderDom.classList.add('placeholder');

  container.appendChild(placeholderDom);
  container.classList.add('ig-media');
}

function renderMessageAsLink (container, message) {
  const { link } = message.link._params;
  const text = message.link._params.text;
  if (link.image && link.image.url) {
    var img = dom(`<img src="${link.image.url}">`);
    img.onload = conditionedScrollToBottom();
    container.appendChild(img);
  }
  // replace all contained links with anchor tags
  container.innerHTML += text.replace(URL_REGEX, (url) => {
    return `<a class="link-in-message">${url}</a>`;
  });
  container.classList.add('ig-media');
  container.onclick = () => {
    // for links that don't have protocol included
    const url = /^(http|https):\/\//.test(link.url) ? link.url : `http://${link.url}`;
    openInBrowser(url);
  }
}

function renderMessageAsAnimatedMedia (container, message) {
  var { url } = message._params.animatedMedia.images.fixed_height;
  var img = dom(`<img src="${url}">`);
  img.onload = conditionedScrollToBottom();
  container.appendChild(img);
  container.classList.add('ig-media');

  container.addEventListener('click', () => {
    showInViewer(dom(`<img src="${url}">`));
  })
}

function renderContextMenu (text) {
  const menu = new Menu();
  const menuItem = new MenuItem({
    label: 'Quote Message',
    click: () => quoteText(text)
  });
  menu.append(menuItem);
  menu.popup({});
}

function renderImageContextMenu (url) {
  const menu = new Menu();
  const menuItem = new MenuItem({
    label: 'Copy URL to clipboard',
    click: () => copyToCliboard(url)
  });
  menu.append(menuItem);
  menu.popup({});
}

function renderChatListItem(chatTitle, msgPreview, thumbnail, id) {
  var li = document.createElement('li');
  li.classList.add('col-12', 'p-3');
  li.appendChild(dom(`<div><img class="thumb" src="${thumbnail}"></div>`));
  li.appendChild(dom(`<div class="username ml-3 d-none d-sm-inline-block"><b>${chatTitle}</b><br>${msgPreview}</div>`));
  if (id) li.setAttribute("id", `chatlist-${id}`);

  return li;
}

function renderSearchResult (users) {
  var ul = document.querySelector('.chat-list ul');
  ul.innerHTML = "";
  users.forEach((user) => {
    var li = renderChatListItem(user._params.username, 'Send a message', user._params.picture);
    li.onclick = () => {
      setActive(li);
      if (window.chatUsers[user.id]) {
        getChat(window.chatUsers[user.id]);
      } else {
        window.currentChatId = DUMMY_CHAT_ID;
        renderChat({items: [], accounts: [user]});
      }
    }
    ul.appendChild(li);
  })
}

function renderChatList (chatList) {
  var ul = document.querySelector('.chat-list ul');
  ul.innerHTML = "";
  chatList.forEach((chat_) => {
    var msgPreview = getMsgPreview(chat_);
    var chatTitle = getChatTitle(chat_);
    let thumbnail = '';
    if (chat_.accounts[0]) {
      thumbnail = chat_.accounts[0]._params.picture;
    }
    var li = renderChatListItem(chatTitle, msgPreview, thumbnail, chat_.id);

    registerChatUser(chat_);
    if (isActive(chat_)) setActive(li);
    // don't move this down!
    addNotification(li, chat_);
    window.chatListHash[chat_.id] = chat_;

    li.onclick = () => {
      markAsRead(chat_.id, li);
      setActive(li);
      // render the cached chat before fetching latest
      // to avoid visible latency
      if (window.chatCache[chat_.id]) {
        renderChat(window.chatCache[chat_.id]);
      }
      getChat(chat_.id);
    }
    ul.appendChild(li);
  })
}

function renderChatHeader (chat_) {
  let chatTitle = (chat_.id ? getChatTitle(chat_) : getUsernames(chat_)); // if chat_.id is not defined, it is a new contact
  let b = dom(`<b>${chatTitle}</b>`);

  if (chat_.accounts.length === 1) {
    // open user profile in browser
    b.onclick = () =>
      openInBrowser(
        `https://instagram.com/${getUsernames(chat_)}`
      );
  }
  let chatTitleContainer = document.querySelector('.chat-title');
  chatTitleContainer.innerHTML = '';
  chatTitleContainer.appendChild(b);
}

function renderChat (chat_) {
  window.chat = chat_;
  window.chatCache[chat_.id] = chat_;

  var msgContainer = document.querySelector(CHAT_WINDOW_SELECTOR);
  msgContainer.innerHTML = '';
  renderChatHeader(chat_);
  var messages = chat_.items.slice().reverse();
  if (canRenderOlderMessages()) {
    // load older messages if they exist too
    messages = window.olderMessages.slice().reverse().concat(messages);
  }
  messages.forEach((message) => {
    var div = renderMessage(message, getMsgDirection(message),
      message._params.created, message._params.type
    );
    msgContainer.appendChild(div);
  })
  renderMessageSeenText(msgContainer, chat_);
  scrollToChatBottom();
  loadOlderMsgsOnScrollTop();

  addSubmitHandler(chat_);
  addAttachmentSender(chat_);
  document.querySelector(MSG_INPUT_SELECTOR).focus();
}

function renderOlderMessages (messages) {
  const msgContainer = document.querySelector(CHAT_WINDOW_SELECTOR);
  const domPostion = msgContainer.firstChild;
  messages.forEach((message) => {
    var div = renderMessage(message, getMsgDirection(message),
      message._params.created, message._params.type
    );
    msgContainer.prepend(div);
  });
  // scroll back to dom position before the older messages were rendered
  domPostion.scrollIntoView();
}

function renderMessageAsVoiceMedia(container, message) {
  var src = message._params.voiceMedia.media.audio.audio_src;
  var audio = dom(`<audio controls src="${src}"/>`);
  container.appendChild(audio);
}

function renderMessageSeenText (container, chat_) {
  container.appendChild(dom(`<div class="seen italic outward"><p>${getIsSeenText(chat_)}</p></div>`));
}

function renderUnfollowers (users) {
  var ul = dom(`<ul class="unfollowers"></ul>`);
  users.forEach((user) => {
    var li = dom(
      `

${user._params.username}

`
    );
    var unfollowButton = dom(`<button class="unfollow-button">Unfollow</button>`);
    unfollowButton.onclick = () => {
      unfollow(user.id);
      li.remove();
    }
    li.appendChild(unfollowButton);
    ul.appendChild(li);
  })

  showInViewer(ul);
}
<|endoftext|>


<commit_before>var APILightbox = window.APILightbox ? window.APILightbox : {};

// function called by eval-ing response from Flickr
function jsonFlickrApi(response) {
  if(response.stat == 'ok') {
    APILightbox.populateResults(response);
  }
}

APILightbox = {
  flickrApiKey:      '7b491816b7ce18f1f848e52962352ee7',
  flickrRestUrlBase: 'https://www.flickr.com/services/rest/',
  searchText:        'delphinium',
  currentPhotoIndex: 0,
  maxPhotoIndex:     24,
  photos:            {},

  utils: {
    ajax: function(method, url, data, callback) {
      var xhr = new XMLHttpRequest();
      xhr.onreadystatechange = readystatechange;
      xhr.open(method, url);
      xhr.send(data && JSON.stringify(data));

      function readystatechange() {
        if(xhr.readyState == xhr.DONE) {
          if(xhr.status == 200) {
            callback(xhr.status, xhr.response);
          } else {
            callback(xhr.status, null);
          }
        }
      }
    },

    buildUrlParams: function(options) {
      var params = "?";
      for(var key in options) {
        value = options[key];

        if(typeof key == "string" && typeof value == "string") {
          params += key + "=" + value + "&";
        }
      }
      // Remove that extraneous last '&'
      params = params.replace(/&$/, '');

      return params;
    },

    buildFlickrOptions: function(options) {
      options = options ? options : {};

      options.format = 'json';
      options.api_key = APILightbox.flickrApiKey;

      return options;
    },

    buildFlickrPhotoUrl: function(photo) {
      /*
      ** Flickr photo urls take the following format:
      **
      ** https://farm{farm-id}.staticflickr.com/{server-id}/{id}_{secret}_{size}.jpg
      */

      return "https://farm" + photo.farm +
             ".staticflickr.com/" + photo.server + "/" +
             photo.id + "_" + photo.secret + "_" + "c.jpg";
    }
  },

  getImages: function(searchText, maxCount) {
    var flickrMaxCountPerPage = 500;

    if(maxCount && typeof (maxCount * 1) == 'number' &&
       maxCount <= flickrMaxCountPerPage &&
       maxCount != APILightbox.maxPhotoIndex + 1) {
      APILightbox.maxPhotoIndex = maxCount;
    }

    if(searchText && typeof searchText == 'string') {
      APILightbox.searchText = searchText;
    }

    var flickrOptions = {
      method:   'flickr.photos.search',
      text:     searchText ? searchText : APILightbox.searchText,
      page:     1,
      per_page: maxCount ? maxCount : APILightbox.maxPhotoIndex + 1,
      media:    'photos'
    };

    flickrOptions = APILightbox.utils.buildFlickrOptions(flickrOptions);

    var url = APILightbox.flickrRestUrlBase;

    url += APILightbox.utils.buildUrlParams(flickrOptions);

    APILightbox.utils.ajax('get', url, null, function(status, response) {
      if(status == 200) {
        eval(response);
      }
    });
  },

  populateResults: function(response) {
    APILightbox.photos = response.photos;
    APILightbox.showImage(0, response.photos);

    var searchString = document.querySelectorAll('h1 .search-string')[0];
    var previous = document.querySelectorAll('.lightbox .paging-controls .previous')[0];
    var next = document.querySelectorAll('.lightbox .paging-controls .next')[0];

    searchString.innerText = APILightbox.searchText;
    previous.addEventListener('click', APILightbox.showPreviousImage, false);
    next.addEventListener('click', APILightbox.showNextImage, false);
  },

  showImage: function(index) {
    var title = document.querySelectorAll('.lightbox .title')[0];
    var image = document.querySelectorAll('.response .photo')[0];

    APILightbox.currentPhotoIndex = index;
    photo                         = APILightbox.photos.photo[index];
    title.innerText               = photo.title;
    image.src                     = APILightbox.utils.buildFlickrPhotoUrl(photo);
  },

  showPreviousImage: function() {
    var previousIndex = APILightbox.currentPhotoIndex - 1;
    if(previousIndex < 0) previousIndex = APILightbox.maxPhotoIndex;

    APILightbox.showImage(previousIndex);
  },

  showNextImage: function() {
    var nextIndex = APILightbox.currentPhotoIndex + 1;
    if(nextIndex > APILightbox.maxPhotoIndex) nextIndex = 0;

    APILightbox.showImage(nextIndex);
  }
};
<commit_msg>Add type to ajax function.<commit_after>var APILightbox = window.APILightbox ? window.APILightbox : {};

// function called by eval-ing response from Flickr
function jsonFlickrApi(response) {
  if(response.stat == 'ok') {
    APILightbox.populateResults(response);
  }
}

APILightbox = {
  flickrApiKey:      '7b491816b7ce18f1f848e52962352ee7',
  flickrRestUrlBase: 'https://www.flickr.com/services/rest/',
  searchText:        'delphinium',
  currentPhotoIndex: 0,
  maxPhotoIndex:     24,
  photos:            {},

  utils: {
    ajax: function(method, url, data, callback, type) {
      var xhr = new XMLHttpRequest();
      xhr.onreadystatechange = readystatechange;
      xhr.responseType = type ? type : "";
      xhr.open(method, url);
      xhr.send(data && JSON.stringify(data));

      function readystatechange() {
        if(xhr.readyState == xhr.DONE) {
          if(xhr.status == 200) {
            callback(xhr.status, xhr.response);
          } else {
            callback(xhr.status, null);
          }
        }
      }
    },

    buildUrlParams: function(options) {
      var params = "?";
      for(var key in options) {
        value = options[key];

        if(typeof key == "string" && typeof value == "string") {
          params += key + "=" + value + "&";
        }
      }
      // Remove that extraneous last '&'
      params = params.replace(/&$/, '');

      return params;
    },

    buildFlickrOptions: function(options) {
      options = options ? options : {};

      options.format = 'json';
      options.api_key = APILightbox.flickrApiKey;

      return options;
    },

    buildFlickrPhotoUrl: function(photo) {
      /*
      ** Flickr photo urls take the following format:
      **
      ** https://farm{farm-id}.staticflickr.com/{server-id}/{id}_{secret}_{size}.jpg
      */

      return "https://farm" + photo.farm +
             ".staticflickr.com/" + photo.server + "/" +
             photo.id + "_" + photo.secret + "_" + "c.jpg";
    }
  },

  getImages: function(searchText, maxCount) {
    var flickrMaxCountPerPage = 500;

    if(maxCount && typeof (maxCount * 1) == 'number' &&
       maxCount <= flickrMaxCountPerPage &&
       maxCount != APILightbox.maxPhotoIndex + 1) {
      APILightbox.maxPhotoIndex = maxCount;
    }

    if(searchText && typeof searchText == 'string') {
      APILightbox.searchText = searchText;
    }

    var flickrOptions = {
      method:   'flickr.photos.search',
      text:     searchText ? searchText : APILightbox.searchText,
      page:     1,
      per_page: maxCount ? maxCount : APILightbox.maxPhotoIndex + 1,
      media:    'photos'
    };

    flickrOptions = APILightbox.utils.buildFlickrOptions(flickrOptions);

    var url = APILightbox.flickrRestUrlBase;

    url += APILightbox.utils.buildUrlParams(flickrOptions);

    APILightbox.utils.ajax('get', url, null, function(status, response) {
      if(status == 200) {
        eval(response);
      }
    });
  },

  populateResults: function(response) {
    APILightbox.photos = response.photos;
    APILightbox.showImage(0, response.photos);

    var searchString = document.querySelectorAll('h1 .search-string')[0];
    var previous = document.querySelectorAll('.lightbox .paging-controls .previous')[0];
    var next = document.querySelectorAll('.lightbox .paging-controls .next')[0];

    searchString.innerText = APILightbox.searchText;
    previous.addEventListener('click', APILightbox.showPreviousImage, false);
    next.addEventListener('click', APILightbox.showNextImage, false);
  },

  showImage: function(index) {
    var title = document.querySelectorAll('.lightbox .title')[0];
    var image = document.querySelectorAll('.response .photo')[0];

    APILightbox.currentPhotoIndex = index;
    photo                         = APILightbox.photos.photo[index];
    title.innerText               = photo.title;
    image.src                     = APILightbox.utils.buildFlickrPhotoUrl(photo);
  },

  showPreviousImage: function() {
    var previousIndex = APILightbox.currentPhotoIndex - 1;
    if(previousIndex < 0) previousIndex = APILightbox.maxPhotoIndex;

    APILightbox.showImage(previousIndex);
  },

  showNextImage: function() {
    var nextIndex = APILightbox.currentPhotoIndex + 1;
    if(nextIndex > APILightbox.maxPhotoIndex) nextIndex = 0;

    APILightbox.showImage(nextIndex);
  }
};
<|endoftext|>


<commit_before>const through = require('through2');
const path = require('path');
const util = require('util');

function injectReact() {
  return `
    ;const scope = window.__hmr = (window.__hmr || {});
    (function() {
      if (typeof window === 'undefined') return;

      if (!scope.initialized) {
        require('browserify-react-live/browserify/injectReactDeps')(scope);
        require('browserify-react-live/browserify/injectWebSocket')(scope);
        scope.initialized = true;
      }
    })();
  `;
}

function overrideRequire() {
  return `
    require = require('browserify-react-live/browserify/overrideRequire')(scope, require);
  `;
}

function overrideExports() {
  return `
    ;(function() {
      if (module.exports.name || module.exports.displayName) {
        module.exports = scope.makeHot(module.exports);
      }
    })();
  `;
}

module.exports = function applyReactHotAPI(file, options) {
  var content = [];

  return through(
    function transform(part, enc, next) {
      content.push(part);
      next();
    },

    function finish(done) {
      content = content.join('');
      const bundle = util.format('%s%s%s%s',
        injectReact(),
        overrideRequire(),
        content,
        overrideExports()
      );

      this.push(bundle);
      done();
    }
  );
};
<commit_msg>Remove multiline for node users<commit_after>const through = require('through2');
const path = require('path');
const util = require('util');

function injectReact() {
  return '' +
    ';const scope = window.__hmr = (window.__hmr || {});' +
    '(function() {' +
      'if (typeof window === \'undefined\') return;' +
      'if (!scope.initialized) {' +
        'require(\'browserify-react-live/browserify/injectReactDeps\')(scope);' +
        'require(\'browserify-react-live/browserify/injectWebSocket\')(scope);' +
        'scope.initialized = true;' +
      '}' +
    '})();';
}

function overrideRequire() {
  return '' +
    'require = require(\'browserify-react-live/browserify/overrideRequire\')' +
    '(scope, require);';
}

function overrideExports() {
  return '' +
    ';(function() {' +
      'if (module.exports.name || module.exports.displayName) {' +
        'module.exports = scope.makeHot(module.exports);' +
      '}' +
    '})();';
}

module.exports = function applyReactHotAPI(file, options) {
  var content = [];

  return through(
    function transform(part, enc, next) {
      content.push(part);
      next();
    },

    function finish(done) {
      content = content.join('');
      const bundle = util.format('%s%s%s%s',
        injectReact(),
        overrideRequire(),
        content,
        overrideExports()
      );

      this.push(bundle);
      done();
    }
  );
};
<|endoftext|>


<commit_before>//
// GitHub
//
(function(hello){

function formatError(o,headers){
	var code = headers ? headers.statusCode : ( o && "meta" in o && "status" in o.meta && o.meta.status );
	if( (code===401||code===403) ){
		o.error = {
			code : "access_denied",
			message : o.message || (o.data?o.data.message:"Could not get response")
		};
		delete o.message;
	}
}

function formatUser(o){
	if(o.id){
		o.thumbnail = o.picture = o.avatar_url;
		o.name = o.login;
	}
}

function paging(res,headers,req){
	if(res.data&&res.data.length&&headers&&headers.Link){
		var next = headers.Link.match(/<(.*?)>;\s*rel=\"next\"/);
		if(next){
			res.paging = {
				next : next[1]
			};
		}
	}
}

hello.init({
	github : {
		name : 'GitHub',
		oauth : {
			version : 2,
			auth : 'https://github.com/login/oauth/authorize',
			grant : 'https://github.com/login/oauth/access_token',
			response_type : 'code'
		},

		scope : {
			basic           : '',
			email           : 'user:email'
		},
		base : 'https://api.github.com/',
		get : {
			'me' : 'user',
			'me/friends' : 'user/following?per_page=@{limit|100}',
			'me/following' : 'user/following?per_page=@{limit|100}',
			'me/followers' : 'user/followers?per_page=@{limit|100}'
		},
		wrap : {
			me : function(o,headers){

				formatError(o,headers);
				formatUser(o);

				return o;
			},
			"default" : function(o,headers,req){

				formatError(o,headers);

				if(Object.prototype.toString.call(o) === '[object Array]'){
					o = {data:o};
					paging(o,headers,req);
					for(var i=0;i<o.data.length;i++){
						formatUser(o.data[i]);
					}
				}
				return o;
			}
		}
	}
});

})(hello);
<commit_msg>Github serialize payload to JSON, fix #116<commit_after>//
// GitHub
//
(function(hello){

function formatError(o,headers){
	var code = headers ? headers.statusCode : ( o && "meta" in o && "status" in o.meta && o.meta.status );
	if( (code===401||code===403) ){
		o.error = {
			code : "access_denied",
			message : o.message || (o.data?o.data.message:"Could not get response")
		};
		delete o.message;
	}
}

function formatUser(o){
	if(o.id){
		o.thumbnail = o.picture = o.avatar_url;
		o.name = o.login;
	}
}

function paging(res,headers,req){
	if(res.data&&res.data.length&&headers&&headers.Link){
		var next = headers.Link.match(/<(.*?)>;\s*rel=\"next\"/);
		if(next){
			res.paging = {
				next : next[1]
			};
		}
	}
}

hello.init({
	github : {
		name : 'GitHub',
		oauth : {
			version : 2,
			auth : 'https://github.com/login/oauth/authorize',
			grant : 'https://github.com/login/oauth/access_token',
			response_type : 'code'
		},

		scope : {
			basic           : '',
			email           : 'user:email'
		},
		base : 'https://api.github.com/',
		get : {
			'me' : 'user',
			'me/friends' : 'user/following?per_page=@{limit|100}',
			'me/following' : 'user/following?per_page=@{limit|100}',
			'me/followers' : 'user/followers?per_page=@{limit|100}'
		},
		wrap : {
			me : function(o,headers){

				formatError(o,headers);
				formatUser(o);

				return o;
			},
			"default" : function(o,headers,req){

				formatError(o,headers);

				if(Object.prototype.toString.call(o) === '[object Array]'){
					o = {data:o};
					paging(o,headers,req);
					for(var i=0;i<o.data.length;i++){
						formatUser(o.data[i]);
					}
				}
				return o;
			}
		},
		xhr : function(p){

			if( p.method !== 'get' && p.data ){
				// Serialize payload as JSON
				p.headers = p.headers || {};
				p.headers['Content-Type'] = 'application/json';
				if (typeof(p.data) === 'object'){
					p.data = JSON.stringify(p.data);
				}
			}

			return true;
		}
	}
});

})(hello);
<|endoftext|>