<commit_before>var melted_node = require('melted-node'),
    melted_xml  = require('node-mlt'),
    fs          = require('fs'),
    yaml        = require('yaml'),
    sys         = require('sys'),
    exec        = require('child_process').exec;

function mosto(configFile) {
    var self = this;

    mosto.prototype.startPlaylistsWatch = function() {
        console.log("mbc-mosto: [INFO] Start watching playlists dir " + self.config.playlists.base) ;
        fs.watch(self.config.playlists.base, function(event, filename) {
            self.readPlaylists();
        });
    }

    mosto.prototype.readConfig = function(callback) {
        console.log("mbc-mosto: [INFO] Reading configuration from " + self.configFile);
        var fileContents = fs.readFileSync(self.configFile);
        if (!fileContents)
            throw new Error("mbc-mosto: [ERROR] Couldnt read config file " + self.configFile);
        self.config = yaml.eval(fileContents.toString());
        callback();
    };

    mosto.prototype.readPlaylists = function() {
        console.log("mbc-mosto: [INFO] Start reading playlists from " + self.config.playlists.base)
        aux = fs.readdirSync(self.config.playlists.base);
        aux.forEach(function(element, index, array){
            var fileContents = fs.readFileSync(self.config.playlists.base + "/" + element);
            if (!fileContents) {
                //TODO: do it better
                console.error("mbc-mosto: [ERROR] Couldnt read playlist file " + element);
            } else {
                console.log("mbc-mosto: [INFO] Reading playlist: " + element);
                var playlist = yaml.eval(fileContents.toString());
                eval("playlist.name='" + element + "'");
                console.log("mbc-mosto: [INFO] Adding playlist:");
                console.log(playlist);
                self.playlists.push(playlist);
            }
            self.orderPlaylist();
        });
    }

    mosto.prototype.orderPlaylist = function() {
        console.log("mbc-mosto: [INFO] Start ordering playlists:");
        console.log(self.playlists);
        self.playlists.sort(function (item1, item2) {
            if (item1.startDate < item2.startDate)
                return -1;
            else if (item1.startDate > item2.startDate)
                return 1;
            else
                return 0;
        });
        console.log("mbc-mosto: [INFO] Ordered playlists:");
        console.log(self.playlists);
        self.playlists.forEach(function(element, index, array){
           self.playPlaylist(element);
        });
    }

    mosto.prototype.playPlaylist = function(playlist) {
        console.log("mbc-mosto: [INFO] Preparing xml for playlist " + playlist.name);
        playlist.files.forEach(function(element, index, array){
            var xml = new melted_xml();

            var type;
            var file;
            if (typeof element === 'string') {
                type = self.config.types.defecto;
                file = element;
            } else {
                type = element.type;
                file = element.file;
            }
            var filters = self.config.types[type].filters;

            console.log("mbc-mosto: [INFO] Adding file " + file);
            var video = new melted_xml.Producer.Video({source: file});
            xml.push(video);

            var pl = new melted_xml.Playlist;
            pl.entry({producer: video});
            xml.push(pl);

            var track = new melted_xml.Multitrack.Track(pl);

            var multitrack = new melted_xml.Multitrack;
            multitrack.addTrack(track);

            var tractor = new melted_xml.Tractor;
            tractor.push(multitrack);

            filters.forEach(function(element, index, array){
                var filter = self.config.filters[element];
                var filterObj = new melted_xml.Filter[filter.filter.charAt(0).toUpperCase() + filter.filter.slice(1)](filter.properties);
                console.log("mbc-mosto: [INFO] Adding filter " + filter.filter);
                console.log(filter.properties);
                tractor.push(filterObj);
            });

            xml.push(tractor);

            var fileName = file.substring(file.lastIndexOf("/") + 1);
            var xmlFile = self.config.playlists.xml + "/" + fileName + ".xml";

            console.log("mbc-mosto: [INFO] Writig file " + xmlFile);
            fs.writeFile(xmlFile, xml.toString({pretty:true}), function(err){
                if (err) {
                    console.error("mbc-mosto: [ERROR] " + err.toString());
                    return;
                } else {
                    console.log("mbc-mosto: [INFO] File ready: " + xmlFile + " at " + playlist.startDate)
                    self.sendXml(xmlFile)
                    //self.scheduleFile(xmlFile, playlist.startDate);
                }
            });
        });
    }

    mosto.prototype.scheduleFile = function(xmlFile, date) {
        var hours   = date.getHours();
        var minutes = date.getMinutes();
        var seconds = date.getSeconds();
        var day     = date.getDay();
        var month   = date.getMonth();
        var year    = date.getFullYear();

        month++;

        /*if (hours   < 10) {hours   = "0"+hours;}
        if (minutes < 10) {minutes = "0"+minutes;}
        if (seconds < 10) {seconds = "0"+seconds;}*/

        exec("at " + hours + ":" + minutes + " " + month + "-" + day + "-" + year + " " + " echo '" + xmlFile + "' >> " + self.config.playlists.go, function(error, stdout, stderr){
            sys.print('stdout: ' + stdout);
            sys.print('stderr: ' + stderr);
            if (error !== null)
                console.log("mbc-mosto: [ERROR] Error scheduling file " + xmlFile + " for date " + date.toString() + ": " + error);
        });
    }

    mosto.prototype.sendXml = function(xmlFile) {
        console.log('mbc-mosto: [INFO] Sending file ' + xmlFile);
        self.mlt.sendCommand("apnd u0 " + xmlFile, "200 OK");
        self.mlt.sendCommand("play u0", "200 OK");
    }

    mosto.prototype.startWatching = function() {
        console.log("mbc-mosto: [INFO] Start watching config file " + self.configFile) ;
        fs.watch(self.configFile, function(event, filename) {
            if (event == 'rename')
                throw new Error("mbc-mosto: [ERROR] Config file renaming is not supported");
            self.readConfig();
        });

        self.startPlaylistsWatch();
        self.readPlaylists();
    }

    mosto.prototype.startMeltedServer = function() {
        var result = self.mlt.connect();
        result.then(function() {
            self.startWatching();
        });
    }

    this.configFile = configFile;
    this.config     = false;
    this.playlists  = [];
    this.mlt = new melted_node();

    if (!this.configFile)
        this.configFile = "mosto-config.yml";

    self.readConfig(self.startMeltedServer);

}

exports = module.exports = function(configFile) {
    var mosto_server = new mosto(configFile);
    return mosto_server;
}
var mosto_server = new mosto();<commit_msg>Fixed bug of playlists not respecting the order of the files<commit_after>var melted_node = require('melted-node'),
    melted_xml  = require('node-mlt'),
    fs          = require('fs'),
    yaml        = require('yaml'),
    sys         = require('util'),
    exec        = require('child_process').exec,
    Q           = require('q');

function mosto(configFile) {
    var self = this;

    mosto.prototype.startPlaylistsWatch = function() {
        console.log("mbc-mosto: [INFO] Start watching playlists dir " + self.config.playlists.base) ;
        fs.watch(self.config.playlists.base, function(event, filename) {
            self.readPlaylists();
        });
    };

    mosto.prototype.readConfig = function(callback) {
        console.log("mbc-mosto: [INFO] Reading configuration from " + self.configFile);
        var fileContents = fs.readFileSync(self.configFile);
        if (!fileContents)
            throw new Error("mbc-mosto: [ERROR] Couldnt read config file " + self.configFile);
        self.config = yaml.eval(fileContents.toString());
        callback();
    };

    mosto.prototype.readPlaylists = function() {
        console.log("mbc-mosto: [INFO] Start reading playlists from " + self.config.playlists.base);
        aux = fs.readdirSync(self.config.playlists.base);
        aux.forEach(function(element, index, array){
            var fileContents = fs.readFileSync(self.config.playlists.base + "/" + element);
            if (!fileContents) {
                //TODO: do it better
                console.error("mbc-mosto: [ERROR] Couldnt read playlist file " + element);
            } else {
                console.log("mbc-mosto: [INFO] Reading playlist: " + element);
                var playlist = yaml.eval(fileContents.toString());
                eval("playlist.name='" + element + "'");
                console.log("mbc-mosto: [INFO] Adding playlist:");
                console.log(playlist);
                self.playlists.push(playlist);
            }
            self.orderPlaylists();
        });
    };

    mosto.prototype.orderPlaylists = function() {
        console.log("mbc-mosto: [INFO] Start ordering playlists:");
        console.log(self.playlists);
        self.playlists.sort(function (item1, item2) {
            if (item1.startDate < item2.startDate)
                return -1;
            else if (item1.startDate > item2.startDate)
                return 1;
            else
                return 0;
        });
        console.log("mbc-mosto: [INFO] Ordered playlists:");
        console.log(self.playlists);
        self.playlists.forEach(function(element, index, array){
           self.playPlaylist(element);
        });
    };

    mosto.prototype.playPlaylist = function(playlist) {
        console.log("mbc-mosto: [INFO] Preparing xml for playlist " + playlist.name);
        var xmlFiles = [];
        playlist.files.forEach(function(element, index, array){
            var xml = new melted_xml();

            var type;
            var file;
            if (typeof element === 'string') {
                type = self.config.types.defecto;
                file = element;
            } else {
                type = element.type;
                file = element.file;
            }
            var filters = self.config.types[type].filters;

            console.log("mbc-mosto: [INFO] Adding file " + file);
            var video = new melted_xml.Producer.Video({source: file});
            xml.push(video);

            console.log("mbc-mosto: [INFO] Creating playlist xml object for file " + file);
            var pl = new melted_xml.Playlist;
            pl.entry({producer: video});
            xml.push(pl);

            console.log("mbc-mosto: [INFO] Creating track xml object for file " + file);
            var track = new melted_xml.Multitrack.Track(pl);

            console.log("mbc-mosto: [INFO] Creating multitrack xml object for file " + file);
            var multitrack = new melted_xml.Multitrack;
            multitrack.addTrack(track);

            console.log("mbc-mosto: [INFO] Creating tractor xml object for file " + file);
            var tractor = new melted_xml.Tractor;
            tractor.push(multitrack);

//            console.log("mbc-mosto: [INFO] Creating filter xml objects for file " + file);
//            console.log("mbc-mosto: [INFO] Filters: " + filters);
//            filters.forEach(function(element, index, array){
//                var filter = self.config.filters[element];
//                var filterObj = new melted_xml.Filter[filter.filter.charAt(0).toUpperCase() + filter.filter.slice(1)](filter.properties);
//                console.log("mbc-mosto: [INFO] Adding filter " + filter.filter);
//                console.log(filter.properties);
//                tractor.push(filterObj);
//            });

            console.log("mbc-mosto: [INFO] Pushing xml for file " + file);
            xml.push(tractor);

            var fileName = file.substring(file.lastIndexOf("/") + 1);
            var xmlFile = self.config.playlists.xml + "/" + fileName + ".xml";
            xmlFiles.push(xmlFile);

            console.log("mbc-mosto: [INFO] Writing file " + xmlFile);
            fs.writeFile(xmlFile, xml.toString({pretty:true}), function(err){
                if (err) {
                    console.error("mbc-mosto: [ERROR] " + err.toString());
                    return;
                } else {
                    console.log("mbc-mosto: [INFO] File ready: " + xmlFile + " at " + playlist.startDate);
                    //self.sendXml(xmlFile);
                    //self.scheduleFile(xmlFile, playlist.startDate);
                    if (index === (array.length - 1))
                        self.sendFiles(xmlFiles);
                }
            });
        });
    };

    mosto.prototype.sendFiles = function(xmlFiles) {
        console.log("mbc-mosto: [INFO] Received files: " + xmlFiles);
        var xmlFile = xmlFiles.shift();
        if (xmlFile !== undefined) {
            console.log('mbc-mosto: [INFO] Sending file ' + xmlFile);
            self.mlt.sendCommand("apnd u0 " + xmlFile, "200 OK", function() {
                self.mlt.sendCommand("play u0", "200 OK", function() {
                    self.sendFiles(xmlFiles);
                }, function(error) {
                    var err = new Error("mbc-mosto: [ERROR] Error playing u0 for file: " + xmlFile + " [" + error + "]");
                    console.error(err);
                    throw err;
                });
            }, function(error) {
                var err = new Error("mbc-mosto: [ERROR] Error appending u0 for file: " + xmlFile + " [" + error + "]");
                console.error(err);
                throw err;
            });
        }
    };

    mosto.prototype.scheduleFile = function(xmlFile, date) {
        var hours   = date.getHours();
        var minutes = date.getMinutes();
        var seconds = date.getSeconds();
        var day     = date.getDay();
        var month   = date.getMonth();
        var year    = date.getFullYear();

        month++;

        /*if (hours   < 10) {hours   = "0"+hours;}
        if (minutes < 10) {minutes = "0"+minutes;}
        if (seconds < 10) {seconds = "0"+seconds;}*/

        exec("at " + hours + ":" + minutes + " " + month + "-" + day + "-" + year + " " + " echo '" + xmlFile + "' >> " + self.config.playlists.go, function(error, stdout, stderr){
            sys.print('stdout: ' + stdout);
            sys.print('stderr: ' + stderr);
            if (error !== null)
                console.log("mbc-mosto: [ERROR] Error scheduling file " + xmlFile + " for date " + date.toString() + ": " + error);
        });
    };

    mosto.prototype.sendXml = function(xmlFile) {
        console.log('mbc-mosto: [INFO] Sending file ' + xmlFile);
        self.mlt.sendCommand("apnd u0 " + xmlFile, "200 OK");
        self.mlt.sendCommand("play u0", "200 OK");
    };

    mosto.prototype.startWatching = function() {
        console.log("mbc-mosto: [INFO] Start watching config file " + self.configFile);
        fs.watch(self.configFile, function(event, filename) {
            if (event === 'rename')
                throw new Error("mbc-mosto: [ERROR] Config file renaming is not supported");
            self.readConfig();
        });

        self.startPlaylistsWatch();
        self.readPlaylists();
    };

    mosto.prototype.startMeltedServer = function() {
        var result = self.mlt.connect();
        result.then(function() {
            self.startWatching();
        });
    };

    console.log("mbc-mosto: [INFO] Starting mbc-mosto... " + self.configFile) ;

    this.configFile = configFile;
    this.config     = false;
    this.playlists  = [];
    this.mlt = new melted_node();

    if (!this.configFile)
        this.configFile = "mosto-config.yml";

    self.readConfig(self.startMeltedServer);

}

exports = module.exports = function(configFile) {
    var mosto_server = new mosto(configFile);
    return mosto_server;
};
var mosto_server = new mosto();<|endoftext|>


<commit_before>var feature_flags = (function () {

var exports = {};

// Helpers
var internal_24_hour_people= _.contains([],
    page_params.email);

var zulip_mit_emails = [];
var is_zulip_mit_user = _.contains(zulip_mit_emails, page_params.email);

var iceland = page_params.domain === 'customer8.invalid';

var customer4_realms = [
  'customer4.invalid',
  'users.customer4.invalid'
];
var is_customer4 = _.contains(customer4_realms, page_params.domain);

// Enterprise-related flags
exports.do_not_share_the_love = page_params.enterprise;

// Manually-flipped debugging flags
exports.log_send_times = false;
exports.collect_send_times = false;

// Permanent realm-specific stuff:
exports.disable_message_editing = _.contains(['mit.edu'], page_params.domain);

exports.twenty_four_hour_time = internal_24_hour_people || iceland;

exports.dropbox_integration = page_params.staging || _.contains(['dropbox.com'], page_params.domain);

exports.mandatory_topics = _.contains([
    'customer7.invalid'
    ],
    page_params.domain
);

exports.left_side_userlist = _.contains(['customer7.invalid'], page_params.domain);

// Still very beta:
exports.fade_users_when_composing = page_params.staging || is_customer4;
exports.use_socket = page_params.staging;

exports.clicking_notification_causes_narrow = page_params.staging || is_customer4 ||
    _.contains(['customer25.invalid', 'mit.edu', 'customer21.invalid'], page_params.domain);

exports.experimental_background = page_params.staging || _.contains(['mit.edu'], page_params.domain);
exports.full_width = page_params.staging;

exports.show_digest_email_setting = page_params.staging;

// Still burning in...
exports.topic_zooming = true;
exports.mark_read_at_bottom = true;
exports.propagate_topic_edits = true;
exports.summarize_read_while_narrowed = false;

// Ready for deprecation.
exports.collapsible = false;

return exports;

}());
<commit_msg>Turn on left_side_userlist on staging.<commit_after>var feature_flags = (function () {

var exports = {};

// Helpers
var internal_24_hour_people= _.contains([],
    page_params.email);

var zulip_mit_emails = [];
var is_zulip_mit_user = _.contains(zulip_mit_emails, page_params.email);

var iceland = page_params.domain === 'customer8.invalid';

var customer4_realms = [
  'customer4.invalid',
  'users.customer4.invalid'
];
var is_customer4 = _.contains(customer4_realms, page_params.domain);

// Enterprise-related flags
exports.do_not_share_the_love = page_params.enterprise;

// Manually-flipped debugging flags
exports.log_send_times = false;
exports.collect_send_times = false;

// Permanent realm-specific stuff:
exports.disable_message_editing = _.contains(['mit.edu'], page_params.domain);

exports.twenty_four_hour_time = internal_24_hour_people || iceland;

exports.dropbox_integration = page_params.staging || _.contains(['dropbox.com'], page_params.domain);

exports.mandatory_topics = _.contains([
    'customer7.invalid'
    ],
    page_params.domain
);

exports.left_side_userlist = page_params.staging || _.contains(['customer7.invalid'], page_params.domain);

// Still very beta:
exports.fade_users_when_composing = page_params.staging || is_customer4;
exports.use_socket = page_params.staging;

exports.clicking_notification_causes_narrow = page_params.staging || is_customer4 ||
    _.contains(['customer25.invalid', 'mit.edu', 'customer21.invalid'], page_params.domain);

exports.experimental_background = page_params.staging || _.contains(['mit.edu'], page_params.domain);
exports.full_width = page_params.staging;

exports.show_digest_email_setting = page_params.staging;

// Still burning in...
exports.topic_zooming = true;
exports.mark_read_at_bottom = true;
exports.propagate_topic_edits = true;
exports.summarize_read_while_narrowed = false;

// Ready for deprecation.
exports.collapsible = false;

return exports;

}());
<|endoftext|>


<commit_before>define(['jquery', 'backbone', 'underscore', 'util/spin','backbone.localStorage'],
function($, Backbone, _, Spinner) {
    function addSpinner(el) {
        var opts = {
            length: 5,
            width: 2,
            radius: 5,
            corners: 1,
            rotate: 69,
            color: '#666',
            speed: 1.3,
            trail: 42
        };
        var spinner = new Spinner(opts).spin(el[0]);
    }
    function removeSpinner(el) {
        el.find(".spinner").remove();
    }

    function DropDownMenu(options) {
        var self = this;
        options = (options || {});
        options.container = options.container ?
            $(options.container) : $("body");
        options.listTemplate = options.listTemplate ?
            $(options.listTemplate) : $("#ddListTemplate");
        options.itemTemplate = options.itemTemplate ?
            $(options.itemTemplate) : $("#ddItemTemplate")
        options.parseItem = (options.parseItem || function () {});
        options.itemLink  = (options.itemLink  || function (item) {
            return "#" + item.id;
        });
        var DDItem = Backbone.Model.extend({
            defaults: {
                title: "blank",
            },
            parse: function (data) {
                options.parseItem(data, this);
                this.link = options.itemLink(this);
                return data;
            }
        });
        var DDItemView = Backbone.View.extend({
            tagName: "li",
            template: _.template(options.itemTemplate.html()),
            events: {
                "click": "selectItem"
            },
            selectItem: function () {
                this.$el.parent().children().removeClass('active');
                this.$el.addClass('active');
                this.model.select(this.model, this.$el);
            },
            render: function() {
                this.$el.append(this.template(this.model));
                return this;
            }
        });

        var DDListView = Backbone.View.extend({
            el: options.container,
            template: _.template(options.listTemplate.html()),
            initialize: function() {
                this.collection.bind("sync", this.renderItems, this);
                this.render();
            },
            render: function() {
                this.$el.append(this.template({
                    label:  this.options.label,
                    listId: this.options.listId,
                    title:  this.options.title
                }));
            },
            renderItems: function () {
                var $topics = this.$el.find("#" + this.options.listId);
                $topics.empty();
                if (this.collection.length) {
                    _.each(this.collection.models, function (item) {
                        var itemView = new DDItemView({ model: item });
                        $topics.append(itemView.render().el);
                    }, this);
                } else {
                    $topics.append($("<li>")
                        .css("padding", "5px")
                        .addClass("text-warning")
                        .text('No ' + this.options.name));
                }
            }
        });

        self.create = function (args) {
            args = (args || {});
            args.label = (args.label || args.name + "-label");
            args.title = (args.title || args.name);
            args.itemSelect = (args.itemSelect || function () {});

            var TypedDDItem = DDItem.extend({
                type:   args.itemType,
                select: args.itemSelect
            })
            TypedDDItem.type = args.itemType;
            var ddList = new (Backbone.Collection.extend({
                model: TypedDDItem
            }));
            ddList.url = args.url;
            var ddListView = new DDListView({
                collection: ddList,
                label:      args.label,
                title:      args.title,
                name:       args.name,
                listId:     args.name + '-select'
            });
            ddListView.fetch = function (options) {
                options = options ? _.clone(options) : {};
                var fetched = false;
                var spinTarget =
                    ddListView.$el.find("#" + this.options.listId).parent();
                setInterval(function () {
                    if (!fetched) {
                        addSpinner(spinTarget);
                        spinTarget.fadeTo(1, 0.2);
                    }
                }, 500);
                for (var opt in options) {
                    ddList[opt] = options[opt];
                }
                ddList.fetch({ success: function () {
                    removeSpinner(spinTarget);
                    spinTarget.fadeTo(1, 1);
                    fetched = true;
                }});
            };
            return ddListView;
        }
    }
    return DropDownMenu;
});
<commit_msg>Fixed path to backbone.localstorage.<commit_after>define(['jquery', 'backbone', 'underscore', 'util/spin','backbone.localstorage'],
function($, Backbone, _, Spinner) {
    function addSpinner(el) {
        var opts = {
            length: 5,
            width: 2,
            radius: 5,
            corners: 1,
            rotate: 69,
            color: '#666',
            speed: 1.3,
            trail: 42
        };
        var spinner = new Spinner(opts).spin(el[0]);
    }
    function removeSpinner(el) {
        el.find(".spinner").remove();
    }

    function DropDownMenu(options) {
        var self = this;
        options = (options || {});
        options.container = options.container ?
            $(options.container) : $("body");
        options.listTemplate = options.listTemplate ?
            $(options.listTemplate) : $("#ddListTemplate");
        options.itemTemplate = options.itemTemplate ?
            $(options.itemTemplate) : $("#ddItemTemplate")
        options.parseItem = (options.parseItem || function () {});
        options.itemLink  = (options.itemLink  || function (item) {
            return "#" + item.id;
        });
        var DDItem = Backbone.Model.extend({
            defaults: {
                title: "blank",
            },
            parse: function (data) {
                options.parseItem(data, this);
                this.link = options.itemLink(this);
                return data;
            }
        });
        var DDItemView = Backbone.View.extend({
            tagName: "li",
            template: _.template(options.itemTemplate.html()),
            events: {
                "click": "selectItem"
            },
            selectItem: function () {
                this.$el.parent().children().removeClass('active');
                this.$el.addClass('active');
                this.model.select(this.model, this.$el);
            },
            render: function() {
                this.$el.append(this.template(this.model));
                return this;
            }
        });

        var DDListView = Backbone.View.extend({
            el: options.container,
            template: _.template(options.listTemplate.html()),
            initialize: function() {
                this.collection.bind("sync", this.renderItems, this);
                this.render();
            },
            render: function() {
                this.$el.append(this.template({
                    label:  this.options.label,
                    listId: this.options.listId,
                    title:  this.options.title
                }));
            },
            renderItems: function () {
                var $topics = this.$el.find("#" + this.options.listId);
                $topics.empty();
                if (this.collection.length) {
                    _.each(this.collection.models, function (item) {
                        var itemView = new DDItemView({ model: item });
                        $topics.append(itemView.render().el);
                    }, this);
                } else {
                    $topics.append($("<li>")
                        .css("padding", "5px")
                        .addClass("text-warning")
                        .text('No ' + this.options.name));
                }
            }
        });

        self.create = function (args) {
            args = (args || {});
            args.label = (args.label || args.name + "-label");
            args.title = (args.title || args.name);
            args.itemSelect = (args.itemSelect || function () {});

            var TypedDDItem = DDItem.extend({
                type:   args.itemType,
                select: args.itemSelect
            })
            TypedDDItem.type = args.itemType;
            var ddList = new (Backbone.Collection.extend({
                model: TypedDDItem
            }));
            ddList.url = args.url;
            var ddListView = new DDListView({
                collection: ddList,
                label:      args.label,
                title:      args.title,
                name:       args.name,
                listId:     args.name + '-select'
            });
            ddListView.fetch = function (options) {
                options = options ? _.clone(options) : {};
                var fetched = false;
                var spinTarget =
                    ddListView.$el.find("#" + this.options.listId).parent();
                setInterval(function () {
                    if (!fetched) {
                        addSpinner(spinTarget);
                        spinTarget.fadeTo(1, 0.2);
                    }
                }, 500);
                for (var opt in options) {
                    ddList[opt] = options[opt];
                }
                ddList.fetch({ success: function () {
                    removeSpinner(spinTarget);
                    spinTarget.fadeTo(1, 1);
                    fetched = true;
                }});
            };
            return ddListView;
        }
    }
    return DropDownMenu;
});
<|endoftext|>


<commit_before>module.exports = function(grunt) {

   grunt.initConfig({
      pkg: grunt.file.readJSON('package.json'),
      notify: {
         watch: {
            options: {
               title: 'E.B.U.R.P.',
               message: 'Game build complete.'
            }
         }
      },

      /**
       * Concatenate data files
       */
      concat: {
         options: {
            separator: '\n'
         },
         game: {
            src: [
               "data/game.js",
               "data/*.js"
            ],
            dest: 'web/<%= pkg.name %>.data.js'
         },
         maps: {
            src: [
               "data/maps/*.js"
            ],
            dest: 'web/<%= pkg.name %>.maps.js'
         }
      },

      /**
       * Compile CoffeeScript
       */
      coffee: {
         options:{
            join:true
         },
         game: {
            src: [
               "src/device.coffee",
               "src/core/util.coffee",
               "src/core/view.coffee",
               "src/core/*.coffee",
               "src/model/*.coffee",
               "src/adventure/*.coffee",
               "src/combat/*.coffee",
               "src/gurk.coffee"
            ],
            dest: 'web/<%= pkg.name %>.js',
            ext: '.js'
         }
      },

      /**
       * Build sprite sheets.
       */
      sprites: {
         game: {
            options: {
               metaFile: 'web/<%= pkg.name %>.sprites.js'
            },
            files: [
               {src: 'data/textures/characters/*.png', dest: 'web/images/characters'},
               {src: 'data/textures/animation/*.png', dest: 'web/images/animation'},
               {src: 'data/textures/creatures/*.png', dest: 'web/images/creatures'},
               {src: 'data/textures/environment/*.png', dest: 'web/images/environment'},
               {src: 'data/textures/equipment/*.png', dest: 'web/images/equipment'},
               {src: 'data/textures/items/*.png', dest: 'web/images/items'},
               {src: 'data/textures/ui/*.png', dest: 'web/images/ui'}
            ]
         }
      },

      /**
       * Uglify the output javascript files in production builds.  This task is only
       * ever invoked with `heroku:production`, and simply obfuscates/minifies the existing
       * files.
       */
      uglify: {
         options: {
            banner: '\n/*!\n  <%= pkg.name %> - v<%= pkg.version %>\n  built: <%= grunt.template.today("yyyy-mm-dd") %>\n */\n'
         },
         game: {
            files: {
               'web/<%= pkg.name %>.data.js'    : ['web/<%= pkg.name %>.data.js'],
               'web/<%= pkg.name %>.maps.js'    : ['web/<%= pkg.name %>.maps.js'],
               'web/<%= pkg.name %>.sprites.js' : ['web/<%= pkg.name %>.sprites.js'],
               'web/<%= pkg.name %>.js'         : ['web/<%= pkg.name %>.js']
            }
         }
      },

      /**
       * Game server. Useful for deploys (e.g. to heroku), and for getting
       * around security restrictions of running the game from index.html on your
       * hard drive.
       */
      express: {
         options: {
            script: 'tools/gameServer.js',
            port: 5215
         },
         production: {
            options: {
               node_env: 'production'
            }
         }
      },
      /**
       * Trigger a new build when files change
       */
      watch: {
         options:{
            atBegin:true
         },
         code: {
            files: [
               '<%= coffee.game.src %>'
            ],
            tasks: ['default']
         },
         maps: {
            files: [
               '<%= concat.maps.src %>'
            ],
            tasks: ['concat']
         },
         sprites: {
            files: [
               'data/textures/**/*.png'
            ],
            tasks: ['sprites']
         },
         express: {
            files:  [ 'index.html', 'tools/gameServer.js' ],
            tasks:  [ 'express' ],
            options: {
               nospawn: true //Without this option specified express won't be reloaded
            }
         }
      }
   });

   grunt.registerMultiTask('sprites', 'Pack sprites into output sheets', function()
   {
      var Q = require('q');
      var done = this.async();
      var spritePacker = require('./tools/spritePacker');
      var queue = [];
      var options = this.options({
         metaFile: null
      });
      this.files.forEach(function(f) {
         queue.push(spritePacker(f.src, {
            outName: f.dest,
            scale: 1
         }));
      });
      var jsChunks = [];
      Q.all(queue).then(function(results){
         results.forEach(function(r){
            grunt.log.writeln('File "' + r.file + '" created.');
            jsChunks.push("eburp.registerSprites('" + r.name + "'," + JSON.stringify(r.meta,null,3)+");");
         });
         // Write out metadata if specified.
         if(options.metaFile){
            grunt.file.write(options.metaFile,jsChunks.join('\n'));
            grunt.log.writeln('File "' + options.metaFile + '" created.');
         }
         done();
      },function(error){
         grunt.log.error('Failed to create spritesheet: ' + error);
         done(error);
      });
   });

   grunt.loadNpmTasks('grunt-contrib-coffee');
   grunt.loadNpmTasks('grunt-contrib-concat');
   grunt.loadNpmTasks('grunt-contrib-uglify');
   // Support system notifications in non-production environments
   if(process.env.NODE_ENV !== 'production'){
      grunt.loadNpmTasks('grunt-express-server');
      grunt.loadNpmTasks('grunt-contrib-watch');
      grunt.loadNpmTasks('grunt-notify');
      grunt.registerTask('default', ['sprites', 'concat', 'coffee', 'notify']);
   }
   grunt.registerTask('default', ['sprites', 'concat', 'coffee']);
   grunt.registerTask('heroku:production', ['sprites','concat','coffee']);
};<commit_msg>Uglify is okay, it was some weird issue with preloader calling back before the last image was loaded.<commit_after>module.exports = function(grunt) {

   grunt.initConfig({
      pkg: grunt.file.readJSON('package.json'),
      notify: {
         watch: {
            options: {
               title: 'E.B.U.R.P.',
               message: 'Game build complete.'
            }
         }
      },

      /**
       * Concatenate data files
       */
      concat: {
         options: {
            separator: '\n'
         },
         game: {
            src: [
               "data/game.js",
               "data/*.js"
            ],
            dest: 'web/<%= pkg.name %>.data.js'
         },
         maps: {
            src: [
               "data/maps/*.js"
            ],
            dest: 'web/<%= pkg.name %>.maps.js'
         }
      },

      /**
       * Compile CoffeeScript
       */
      coffee: {
         options:{
            join:true
         },
         game: {
            src: [
               "src/device.coffee",
               "src/core/util.coffee",
               "src/core/view.coffee",
               "src/core/*.coffee",
               "src/model/*.coffee",
               "src/adventure/*.coffee",
               "src/combat/*.coffee",
               "src/gurk.coffee"
            ],
            dest: 'web/<%= pkg.name %>.js',
            ext: '.js'
         }
      },

      /**
       * Build sprite sheets.
       */
      sprites: {
         game: {
            options: {
               metaFile: 'web/<%= pkg.name %>.sprites.js'
            },
            files: [
               {src: 'data/textures/characters/*.png', dest: 'web/images/characters'},
               {src: 'data/textures/animation/*.png', dest: 'web/images/animation'},
               {src: 'data/textures/creatures/*.png', dest: 'web/images/creatures'},
               {src: 'data/textures/environment/*.png', dest: 'web/images/environment'},
               {src: 'data/textures/equipment/*.png', dest: 'web/images/equipment'},
               {src: 'data/textures/items/*.png', dest: 'web/images/items'},
               {src: 'data/textures/ui/*.png', dest: 'web/images/ui'}
            ]
         }
      },

      /**
       * Uglify the output javascript files in production builds.  This task is only
       * ever invoked with `heroku:production`, and simply obfuscates/minifies the existing
       * files.
       */
      uglify: {
         options: {
            banner: '\n/*!\n  <%= pkg.name %> - v<%= pkg.version %>\n  built: <%= grunt.template.today("yyyy-mm-dd") %>\n */\n'
         },
         game: {
            files: {
               'web/<%= pkg.name %>.data.js'    : ['web/<%= pkg.name %>.data.js'],
               'web/<%= pkg.name %>.maps.js'    : ['web/<%= pkg.name %>.maps.js'],
               'web/<%= pkg.name %>.sprites.js' : ['web/<%= pkg.name %>.sprites.js'],
               'web/<%= pkg.name %>.js'         : ['web/<%= pkg.name %>.js']
            }
         }
      },

      /**
       * Game server. Useful for deploys (e.g. to heroku), and for getting
       * around security restrictions of running the game from index.html on your
       * hard drive.
       */
      express: {
         options: {
            script: 'tools/gameServer.js',
            port: 5215
         },
         production: {
            options: {
               node_env: 'production'
            }
         }
      },
      /**
       * Trigger a new build when files change
       */
      watch: {
         options:{
            atBegin:true
         },
         code: {
            files: [
               '<%= coffee.game.src %>'
            ],
            tasks: ['default']
         },
         maps: {
            files: [
               '<%= concat.maps.src %>'
            ],
            tasks: ['concat']
         },
         sprites: {
            files: [
               'data/textures/**/*.png'
            ],
            tasks: ['sprites']
         },
         express: {
            files:  [ 'index.html', 'tools/gameServer.js' ],
            tasks:  [ 'express' ],
            options: {
               nospawn: true //Without this option specified express won't be reloaded
            }
         }
      }
   });

   grunt.registerMultiTask('sprites', 'Pack sprites into output sheets', function()
   {
      var Q = require('q');
      var done = this.async();
      var spritePacker = require('./tools/spritePacker');
      var queue = [];
      var options = this.options({
         metaFile: null
      });
      this.files.forEach(function(f) {
         queue.push(spritePacker(f.src, {
            outName: f.dest,
            scale: 1
         }));
      });
      var jsChunks = [];
      Q.all(queue).then(function(results){
         results.forEach(function(r){
            grunt.log.writeln('File "' + r.file + '" created.');
            jsChunks.push("eburp.registerSprites('" + r.name + "'," + JSON.stringify(r.meta,null,3)+");");
         });
         // Write out metadata if specified.
         if(options.metaFile){
            grunt.file.write(options.metaFile,jsChunks.join('\n'));
            grunt.log.writeln('File "' + options.metaFile + '" created.');
         }
         done();
      },function(error){
         grunt.log.error('Failed to create spritesheet: ' + error);
         done(error);
      });
   });

   grunt.loadNpmTasks('grunt-contrib-coffee');
   grunt.loadNpmTasks('grunt-contrib-concat');
   grunt.loadNpmTasks('grunt-contrib-uglify');
   // Support system notifications in non-production environments
   if(process.env.NODE_ENV !== 'production'){
      grunt.loadNpmTasks('grunt-express-server');
      grunt.loadNpmTasks('grunt-contrib-watch');
      grunt.loadNpmTasks('grunt-notify');
      grunt.registerTask('default', ['sprites', 'concat', 'coffee', 'notify']);
   }
   grunt.registerTask('default', ['sprites', 'concat', 'coffee']);
   grunt.registerTask('heroku:production', ['sprites','concat','coffee','uglify']);
};<|endoftext|>


<commit_before>var builder    = require('node-xmpp'),
    PubSub     = require('xmpp-ftw-pubsub'),
    presence   = require('../presence'),
    dataForm   = require('../utils/xep-0004'),
    disco      = require('xmpp-ftw-disco'),
    async      = require('async')

var Buddycloud = function() {
    this.itemParser
    this.channelServer
    this.disco = new disco()
    this.presence = new presence()
}

Buddycloud.prototype = new PubSub()

var init = Buddycloud.prototype.init
Buddycloud.prototype.init = function(manager) {
    init.call(this, manager)
    this.disco.init(manager)
    this.presence.init(manager)
}

Buddycloud.prototype.NS_BUDDYCLOUD = 'http://buddycloud.org/v1'

Buddycloud.prototype.registerEvents = function() {
    var self = this
    this.socket.on('xmpp.buddycloud.discover', function(data, callback) {
        self.discover(data, callback)
    })
    this.socket.on('xmpp.buddycloud.presence', function() {
        if (!self._checkCall({})) return
        self.presence.sendPresence({
            to: self.channelServer,
            priority: -1,
            status: 'buddycloud',
            show: 'online'
        })
    })
    this.socket.on('xmpp.buddycloud.retrieve', function(data, callback) {
        if (!self._checkCall(data, callback)) return
        delete(data.id)
        self.getItems(data, callback)
    })
    this.socket.on('xmpp.buddycloud.subscribe', function(data, callback) {
        if (!self._checkCall(data, callback)) return
        delete(data.jid)
        self.subscribe(data, callback)
    })
    this.socket.on('xmpp.buddycloud.unsubscribe', function(data, callback) {
        if (!self._checkCall(data, callback)) return
        delete(data.jid)
        self.unsubscribe(data, callback)
    })
    this.socket.on('xmpp.buddycloud.item.delete', function(data, callback) {
        if (!self._checkCall(data, callback)) return
        self.deleteItem(data, callback)
    })
    this.socket.on('xmpp.buddycloud.config.set', function(data, callback) {
        if (!self._checkCall(data, callback)) return
        self.setNodeConfiguration(data, callback)
    })
    this.socket.on('xmpp.buddycloud.config.get', function(data, callback) {
        if (!self._checkCall(data, callback)) return
        self.getNodeConfiguration(data, callback)
    })
}

Buddycloud.prototype.handles = function(stanza) {
    return (this.channelServer && (stanza.attrs.from == this.channelServer))
}

Buddycloud.prototype.handle = function(stanza) {
    var self = this
    if (null != stanza.getChild('event', this.NS_EVENT))
        return this._eventNotification(stanza)
    return false
}

Buddycloud.prototype._checkCall = function(data, callback) {
    if (!this.channelServer) {
        this._clientError(
            'You must perform discovery first!', data, callback
        )
        return false
    }
    data.to = this.channelServer
    return true
}

Buddycloud.prototype.discover = function(data, callback) {
    var self = this
    this.disco.getItems({of: this.manager.jid.split('@')[1]}, function(error, items) {
        if (error) return callback(error)
        jobs = {}
        items.forEach(function(item) {
            jobs[item.jid] = function(infoCallback) {
                self.disco.getFeatures({of:item.jid}, function(error, features) {
                    if (error) features = []
                    infoCallback(null, features)
                })
            }
        })
        async.parallel(jobs, function(error, results) {
            var discovered = false
            if (error) return callback(error)
            for (var i in results) {
                results[i].forEach(function(feature) {
                    if (('identity' == feature.kind)
                        && (feature.category && 'pubsub' == feature.category)
                        && (feature.type && 'channels' == feature.type)) {
                        self.channelServer = i
                        console.log("Found buddycloud channel server @ " + i)
                        discovered = true
                        return callback(null, i)
                    }
                })
            }
            if (false == discovered)
                return callback('No buddycloud server found')
        })
    })
}

Buddycloud.prototype._itemDeleteNotification = function(stanza, items) {
    var data = {}
    this._getItemData(items, data)
    this.socket.emit('xmpp.buddycloud.push.retract', data)
    return true
}

Buddycloud.prototype._itemNotification = function(stanza, items) {
    var data = {}
    this._getItemData(items, data)
    this.socket.emit('xmpp.buddycloud.push.item', data)
    return true
}

Buddycloud.prototype._configurationUpdate = function(stanza, configuration) {
    var data = {}
    this._getConfigurationChanges(configuration, data)
    this.socket.emit('xmpp.buddycloud.push.configuration', data)
    return true
}

Buddycloud.prototype._deleteNodeNotification = function(stanza, del) {
    var data = { }
    this._getDeleteNodeNotification(del, data)
    this.socket.emit('xmpp.buddycloud.push.delete', data)
    return true
}

module.exports = Buddycloud
<commit_msg>Moved buddycloud to its own module<commit_after><|endoftext|>


<commit_before>"use strict";

function SlackGhost(opts) {
    this._bridge = opts.bridge;

    this._user_id = opts.user_id;
    this._display_name = opts.display_name;

    this._intent = opts.intent;
}

SlackGhost.fromEntry = function(bridge, entry, intent) {
    return new SlackGhost({
        bridge: bridge,

        user_id: entry.id,
        display_name: entry.display_name,

        intent: intent,
    });
};

SlackGhost.prototype.toEntry = function() {
    var entry = {
        id: this._user_id,
        display_name: this._display_name,
    };

    return entry;
};

SlackGhost.prototype.getIntent = function() {
    return this._intent;
};

SlackGhost.prototype.update = function(message) {
    return this.updateDisplayname(message.user_name);
};

SlackGhost.prototype.updateDisplayname = function(display_name) {
    if (this._display_name === display_name) return Promise.resolve();

    return this.getIntent().setDisplayName(display_name).then(() => {
        this._display_name = display_name;
        return this._bridge.putUserToStore(this);
    });
};

SlackGhost.prototype.sendText = function(room_id, text) {
    this.getIntent().sendText(room_id, text);
};

SlackGhost.prototype.sendMessage = function(room_id, msg) {
    this.getIntent().sendMessage(room_id, msg);
};

SlackGhost.prototype.uploadContent = function(file) {
    return this.getIntent().getClient().uploadContent({
            stream: new Buffer(file._content, "binary"),
            name: file.title,
            type: file.mimetype,
    }).then((response) => {
        var content_uri = JSON.parse(response).content_uri;

        console.log("Media uploaded to " + content_uri);
        return content_uri;
    });
};

module.exports = SlackGhost;
<commit_msg>Initial hack at grabbing avatar images from Slack (fixes #25)<commit_after>"use strict";

var rp = require('request-promise');

function SlackGhost(opts) {
    this._bridge = opts.bridge;

    this._user_id = opts.user_id;
    this._display_name = opts.display_name;

    this._intent = opts.intent;
}

SlackGhost.fromEntry = function(bridge, entry, intent) {
    return new SlackGhost({
        bridge: bridge,

        user_id: entry.id,
        display_name: entry.display_name,

        intent: intent,
    });
};

SlackGhost.prototype.toEntry = function() {
    var entry = {
        id: this._user_id,
        display_name: this._display_name,
    };

    return entry;
};

SlackGhost.prototype.getIntent = function() {
    return this._intent;
};

SlackGhost.prototype.update = function(message) {
    return Promise.all([
        this.updateDisplayname(message.user_name),
        this.updateAvatar(message),
    ]).catch((e) => {
        console.log("Ghost update failed:", e);
    });
};

SlackGhost.prototype.updateDisplayname = function(display_name) {
    if (this._display_name === display_name) return Promise.resolve();

    return this.getIntent().setDisplayName(display_name).then(() => {
        this._display_name = display_name;
        return this._bridge.putUserToStore(this);
    });
};

SlackGhost.prototype.updateAvatar = function(message) {
    var team_token = this._bridge.getTeamToken(message.team_domain);
    if (!team_token) return Promise.resolve();

    // TODO(paul): cache these lookups from slack for (a configurable) 10 minutes
    return rp({
        uri: 'https://slack.com/api/users.info',
        qs: {
            token: team_token,
            user: message.user_id,
        },
        json: true,
    }).then((response) => {
        if (!response.user || !response.user.profile) return;

        var avatar_url = response.user.profile.image_original;
        if (this._avatar_url === avatar_url) return;

        var shortname = avatar_url.match(/\/([^\/]+)$/)[1];

        return rp({
            uri: avatar_url,
            resolveWithFullResponse: true,
            encoding: null,
        }).then((response) => {
            return this.uploadContent({
                _content: response.body,
                title: shortname,
                mimetype: response.headers["content-type"],
            });
        }).then((content_uri) => {
            this.getIntent().setAvatarUrl(content_uri);
        }).then(() => {
            this._avatar_url = avatar_url;
            this._bridge.putUserToStore(this);
        });
    });
};

SlackGhost.prototype.sendText = function(room_id, text) {
    this.getIntent().sendText(room_id, text);
};

SlackGhost.prototype.sendMessage = function(room_id, msg) {
    this.getIntent().sendMessage(room_id, msg);
};

SlackGhost.prototype.uploadContent = function(file) {
    return this.getIntent().getClient().uploadContent({
            stream: new Buffer(file._content, "binary"),
            name: file.title,
            type: file.mimetype,
    }).then((response) => {
        var content_uri = JSON.parse(response).content_uri;

        console.log("Media uploaded to " + content_uri);
        return content_uri;
    });
};

module.exports = SlackGhost;
<|endoftext|>


<commit_before>NGApp.factory('TicketViewService', function($rootScope, $resource, $routeParams, $timeout, NotificationService, AccountService, SocketService, MainNavigationService, TicketService) {
	var service = {
		isTyping: false,
		id_support: false,
		messages: [],
		socket: SocketService.socket
	};

	service.sideInfo = { id_support: null };

	service.sideInfo.setTicket = function( id_support ){
		service.sideInfo.id_support = id_support;
		service.sideInfo.reset();
	}

	service.sideInfo.reset = function(){
		service.sideInfo.data = { id_support: service.sideInfo.id_support, messages: [], page: 0, loaded: 0, total: null, has_more: false };
		service._private = { first_load: true, current_scroll: 0, could_load: true };
		service.sideInfo.update_controller();
		setTimeout( function(){
			service.sideInfo.update_controller();
		} )
	};

	service.sideInfo.force_first_page = function(){
		service.sideInfo.reset();
		service.sideInfo.load();
	}

	service.sideInfo.scroll = function( action ){
		var container = $('.support-chat-contents-scroll');
		switch( action ){
			case 'begin':
				$timeout( function(){
					container.stop( true, false ).animate( { scrollTop: container[0].scrollHeight }, 0 );
					service.sideInfo.release();
				}, 300 );
				break;
			case 'current':
				service._private.current_scroll = container[0].scrollHeight;
				break;
			default:
				var position = action;
				setTimeout(function() {
					var finalScroll = container[0].scrollHeight;
					var goToScroll = finalScroll - service._private.current_scroll;
					container[0].scrollTop = goToScroll;
					service.sideInfo.release();
				}, 100 );
				break;
		}
	}

	service.sideInfo.release = function(){
		setTimeout(function() { service._private.could_load = true }, 500 );
	}

	service.sideInfo.add_message = function( message ){
		service.sideInfo.data.messages.push( message );
		service.sideInfo.data.total++;
		service.sideInfo.data.loaded++;
		service.sideInfo.update_controller();
		service.sideInfo.scroll( 'begin' );
	}

	service.sideInfo.load_ticket_page = function(){
		if( service.sideInfo.id_support ){
			MainNavigationService.link( '/ticket/' + service.sideInfo.id_support );
		}
	}

	service.sideInfo.update_controller = function(){
		$rootScope.$broadcast( 'triggerTicketInfoUpdated', service.sideInfo.data );
	}

	service.sideInfo.load = function(){

		if( service.sideInfo.id_support ){
			if( !service._private.could_load ){
				return false;
			}
			if( service.sideInfo.data.total !== null && service.sideInfo.data.loaded >= service.sideInfo.data.total ){
				return;
			}
			service._private.could_load = false;

			service.sideInfo.data.page++;

			var params = { id_support: service.sideInfo.id_support, page: service.sideInfo.data.page };

			TicketService.side_info( params, function( data ){
				service.sideInfo.scroll( 'current' );
				service.sideInfo.data.pexcard = data.pexcard;
				service.sideInfo.data.restaurant = data.restaurant;
				service.sideInfo.data.order = data.order;
				service.sideInfo.data.total = data.messages.total;
				var messages = [];
				for( x in data.messages.list ){
					if( data.messages.list[ x ].id_support_message ){
						messages.push( data.messages.list[ x ] );
					}
				}
				for( x in service.sideInfo.data.messages ){
					if( service.sideInfo.data.messages[ x ].id_support_message ){
						messages.push( service.sideInfo.data.messages[ x ] );
					}
				}
				service.sideInfo.data.messages = messages;
				service.sideInfo.data.loaded = service.sideInfo.data.messages.length;
				service.sideInfo.data.has_more = ( service.sideInfo.data.loaded >= service.sideInfo.data.total ) ? false : true;
				service.sideInfo.update_controller();
				if( service._private.first_load ){
					service.sideInfo.scroll( 'begin' );
					service._private.first_load = false;
					service.sideInfo.load_ticket_page();
					$rootScope.$broadcast( 'triggerSideViewTicket', {} );
				} else {
					service.sideInfo.scroll();
				}
			} );
			return true;
		}
		return false;
	};

	service.setViewTicket = function(id) {
		service.scope.viewTicket = id;
		service.sideInfo.setTicket( id );
	};

	$rootScope.$on('triggerViewTicket', function(e, ticket) {

		if( service.scope.viewTicket && service.scope.ticket &&
				service.scope.viewTicket.id_support == ticket.id_support &&
				service.scope.ticket.id_support == ticket.id_support ){
			service.sideInfo.force_first_page();
			return;
		}

		NotificationService.check();

		service.scope.viewTicket = ticket;
		service.scope.ticket = ticket;

		if (service.scope.viewTicket) {
			if( typeof service.scope.viewTicket == 'object' ){
				var id_support = service.scope.viewTicket.id_support
			} else {
				var id_support = service.scope.viewTicket;
			}
			service.socket.emit('event.subscribe', 'ticket.' + id_support);
		}
	});

	var notified  = new Array();

	$rootScope.$on('userAuthUpdated', function(e, data) {

		if (AccountService.user && AccountService.user.id_admin) {

			service.socket.emit('event.subscribe', 'user.preference.' + AccountService.user.id_admin );

			service.socket.on('user.preference', function(payload) {
				AccountService.user.prefs[payload.key] = payload.value;
			});

			SocketService.listen( 'user.preference.' + AccountService.user.id_admin, $rootScope).on( 'user.preference', function( payload ){
				AccountService.user.prefs[payload.key] = payload.value;
			} )

			if (AccountService.isSupport) {

				service.socket.emit('event.subscribe', 'ticket.update');

				SocketService.listen('ticket.update', $rootScope).on( 'change_ticket_status', function(){
					$rootScope.$broadcast( 'updateSideTickets' );
					$rootScope.$broadcast( 'updateHeartbeat' );
				} )

				SocketService.listen('tickets', $rootScope)
					.on('message', function(d) {
						console.debug('Recieved chat message: ', d);

						if (notified.indexOf(d.id_support_message) > -1) {
							console.log('already notified ', d.id_support_message);
							return;
						}

						notified.push(d.id_support_message);

						// update the chat room
						console.log('service.sideInfo.id_support',service.sideInfo.id_support);
						console.log('d.id_support',d.id_support);
						if( service.sideInfo.id_support && d.id_support && parseInt( service.sideInfo.id_support ) != parseInt( d.id_support ) ){
							console.log('force_first_page??');
							// service.sideInfo.force_first_page();
						}

						// https://github.com/crunchbutton/crunchbutton/issues/7579#issuecomment-172934677
						if (d.from == 'rep' ) {
							return;
						}

						if( AccountService.user && AccountService.user.prefs && AccountService.user.prefs[ 'notification-desktop-support-all' ] === false ){
							return;
						}

						if (d.id_support == service.scope.viewTicket) {
							//App.playAudio('support-message-recieved');
						} else {
							//App.playAudio('support-message-new');
						}

						NotificationService.notify(d.name, d.body, null, function() {
							try{
								if( document.getElementById('support-chat-box') ){
									document.getElementById('support-chat-box').focus();
								}
							}catch(e){}

						});

					})
					.on( 'sms_status', function( d ){

						console.debug('Recieved sms status update: ', d);

						updateSmsStatus( d );
					} )

			}

		} else {
			//service.socket.close();
			//service.socket = null;
		}
	});

	var updateSmsStatus = function( d ){
		var id_support_message = d.id_support_message;
		var status = d.status;
		var element = $( '#support-message-status-' + id_support_message );
		if( element && element.length ){
			element.attr( 'title', 'Status: ' + status );
		}
	}

	$rootScope.$watch('account.user.prefs["notification-desktop-support-all"]', function(e, value) {
		if (value == '1') {
			console.debug('Subscribing to all tickets');
			service.socket.emit('event.subscribe', 'tickets');
		} else {
			console.debug('Unsubscribing to all tickets');
			service.socket.emit('event.unsubscribe', 'tickets');
		}
	});

	service.send = function(message, add_as_note, callback) {
		var add_as_note = ( add_as_note ? true : false );
		var guid = App.guid();
		console.log('guid',guid);
		if( !service.sideInfo.id_support ){
			return;
		}
		TicketService.message( {
			id_support: service.sideInfo.id_support,
			body: message,
			guid: guid,
			note: add_as_note
		},
		// callback
		function(d) {
			if(!d.status){
				d.status = 'queued';
			}

			for ( var x in service.sideInfo.data.messages ) {
				if ( service.sideInfo.data.messages[x].guid == guid ) {
					d.guid = guid;
					service.sideInfo.data.messages[x] = d;
					notified.push( d.id_support_message );
					break;
				}
			}
		});
		if( callback ){
			callback()
		} else {
			service.scope.$apply(function() {
				console.log('no callback');
				service.sideInfo.add_message( { body: message, name: AccountService.user.firstName, timestamp: new Date().getTime(), sending: true, guid: guid, status: 'queued' } );
			});
		}
		$rootScope.$broadcast( 'replyFinished' );
	};

	service.scroll = function(instant) {
		setTimeout(function() {
			if( $('.support-chat-contents-scroll').length ){
				$('.support-chat-contents-scroll').stop(true,false).animate({
					scrollTop: $('.support-chat-contents-scroll')[0].scrollHeight
				}, instant ? 0 : 800);
			}
		}, 100);
	};

	/*
	var typingTimer;

	service.typing = function(val) {
		return;
		if (!service.isTyping) {
			service.isTyping = true;
			service.websocket.send({
				type: 'ticket.typing.start'
			});

		} else {
			if (!val) {
				service.isTyping = false;
				service.websocket.send({
					type: 'ticket.typing.stop'
				});
			}
		}

		if (typingTimer) {
			clearTimeout(typingTimer);
		}
		typingTimer = setTimeout(function() {
			if (service.isTyping) {
				service.isTyping = false;
				service.websocket.send({
					type: 'ticket.typing.stop'
				});
			}
		}, 5000);
	};
	*/

	return service;
});
<commit_msg>Notify on all support tickets still notifies me when it is off #7651<commit_after>NGApp.factory('TicketViewService', function($rootScope, $resource, $routeParams, $timeout, NotificationService, AccountService, SocketService, MainNavigationService, TicketService) {
	var service = {
		isTyping: false,
		id_support: false,
		messages: [],
		socket: SocketService.socket
	};

	service.sideInfo = { id_support: null };

	service.sideInfo.setTicket = function( id_support ){
		service.sideInfo.id_support = id_support;
		service.sideInfo.reset();
	}

	service.sideInfo.reset = function(){
		service.sideInfo.data = { id_support: service.sideInfo.id_support, messages: [], page: 0, loaded: 0, total: null, has_more: false };
		service._private = { first_load: true, current_scroll: 0, could_load: true };
		service.sideInfo.update_controller();
		setTimeout( function(){
			service.sideInfo.update_controller();
		} )
	};

	service.sideInfo.force_first_page = function(){
		service.sideInfo.reset();
		service.sideInfo.load();
	}

	service.sideInfo.scroll = function( action ){
		var container = $('.support-chat-contents-scroll');
		switch( action ){
			case 'begin':
				$timeout( function(){
					container.stop( true, false ).animate( { scrollTop: container[0].scrollHeight }, 0 );
					service.sideInfo.release();
				}, 300 );
				break;
			case 'current':
				service._private.current_scroll = container[0].scrollHeight;
				break;
			default:
				var position = action;
				setTimeout(function() {
					var finalScroll = container[0].scrollHeight;
					var goToScroll = finalScroll - service._private.current_scroll;
					container[0].scrollTop = goToScroll;
					service.sideInfo.release();
				}, 100 );
				break;
		}
	}

	service.sideInfo.release = function(){
		setTimeout(function() { service._private.could_load = true }, 500 );
	}

	service.sideInfo.add_message = function( message ){
		service.sideInfo.data.messages.push( message );
		service.sideInfo.data.total++;
		service.sideInfo.data.loaded++;
		service.sideInfo.update_controller();
		service.sideInfo.scroll( 'begin' );
	}

	service.sideInfo.load_ticket_page = function(){
		if( service.sideInfo.id_support ){
			MainNavigationService.link( '/ticket/' + service.sideInfo.id_support );
		}
	}

	service.sideInfo.update_controller = function(){
		$rootScope.$broadcast( 'triggerTicketInfoUpdated', service.sideInfo.data );
	}

	service.sideInfo.load = function(){

		if( service.sideInfo.id_support ){
			if( !service._private.could_load ){
				return false;
			}
			if( service.sideInfo.data.total !== null && service.sideInfo.data.loaded >= service.sideInfo.data.total ){
				return;
			}
			service._private.could_load = false;

			service.sideInfo.data.page++;

			var params = { id_support: service.sideInfo.id_support, page: service.sideInfo.data.page };

			TicketService.side_info( params, function( data ){
				service.sideInfo.scroll( 'current' );
				service.sideInfo.data.pexcard = data.pexcard;
				service.sideInfo.data.restaurant = data.restaurant;
				service.sideInfo.data.order = data.order;
				service.sideInfo.data.total = data.messages.total;
				var messages = [];
				for( x in data.messages.list ){
					if( data.messages.list[ x ].id_support_message ){
						messages.push( data.messages.list[ x ] );
					}
				}
				for( x in service.sideInfo.data.messages ){
					if( service.sideInfo.data.messages[ x ].id_support_message ){
						messages.push( service.sideInfo.data.messages[ x ] );
					}
				}
				service.sideInfo.data.messages = messages;
				service.sideInfo.data.loaded = service.sideInfo.data.messages.length;
				service.sideInfo.data.has_more = ( service.sideInfo.data.loaded >= service.sideInfo.data.total ) ? false : true;
				service.sideInfo.update_controller();
				if( service._private.first_load ){
					service.sideInfo.scroll( 'begin' );
					service._private.first_load = false;
					service.sideInfo.load_ticket_page();
					$rootScope.$broadcast( 'triggerSideViewTicket', {} );
				} else {
					service.sideInfo.scroll();
				}
			} );
			return true;
		}
		return false;
	};

	service.setViewTicket = function(id) {
		service.scope.viewTicket = id;
		service.sideInfo.setTicket( id );
	};

	$rootScope.$on('triggerViewTicket', function(e, ticket) {

		if( service.scope.viewTicket && service.scope.ticket &&
				service.scope.viewTicket.id_support == ticket.id_support &&
				service.scope.ticket.id_support == ticket.id_support ){
			service.sideInfo.force_first_page();
			return;
		}

		NotificationService.check();

		service.scope.viewTicket = ticket;
		service.scope.ticket = ticket;

		if (service.scope.viewTicket) {
			if( typeof service.scope.viewTicket == 'object' ){
				var id_support = service.scope.viewTicket.id_support
			} else {
				var id_support = service.scope.viewTicket;
			}
			service.socket.emit('event.subscribe', 'ticket.' + id_support);
		}
	});

	var notified  = new Array();

	$rootScope.$on('userAuthUpdated', function(e, data) {

		if (AccountService.user && AccountService.user.id_admin) {

			service.socket.emit('event.subscribe', 'user.preference.' + AccountService.user.id_admin );

			service.socket.on('user.preference', function(payload) {
				console.log('on: payload',payload);
				AccountService.user.prefs[payload.key] = payload.value;
			});

			SocketService.listen( 'user.preference.' + AccountService.user.id_admin, $rootScope).on( 'user.preference', function( payload ){
				console.log('listen: payload',payload);
				AccountService.user.prefs[payload.key] = payload.value;
			} )

			if (AccountService.isSupport) {

				service.socket.emit('event.subscribe', 'ticket.update');

				SocketService.listen('ticket.update', $rootScope).on( 'change_ticket_status', function(){
					$rootScope.$broadcast( 'updateSideTickets' );
					$rootScope.$broadcast( 'updateHeartbeat' );
				} )

				SocketService.listen('tickets', $rootScope)
					.on('message', function(d) {
						console.debug('Recieved chat message: ', d);

						if (notified.indexOf(d.id_support_message) > -1) {
							console.log('already notified ', d.id_support_message);
							return;
						}

						notified.push(d.id_support_message);

						// update the chat room
						console.log('service.sideInfo.id_support',service.sideInfo.id_support);
						console.log('d.id_support',d.id_support);
						if( service.sideInfo.id_support && d.id_support && parseInt( service.sideInfo.id_support ) != parseInt( d.id_support ) ){
							console.log('force_first_page??');
							// service.sideInfo.force_first_page();
						}

						// https://github.com/crunchbutton/crunchbutton/issues/7579#issuecomment-172934677
						if (d.from == 'rep' ) {
							return;
						}

						if( AccountService.user && AccountService.user.prefs && AccountService.user.prefs[ 'notification-desktop-support-all' ] === false ){
							return;
						}

						if (d.id_support == service.scope.viewTicket) {
							//App.playAudio('support-message-recieved');
						} else {
							//App.playAudio('support-message-new');
						}

						NotificationService.notify(d.name, d.body, null, function() {
							try{
								if( document.getElementById('support-chat-box') ){
									document.getElementById('support-chat-box').focus();
								}
							}catch(e){}

						});

					})
					.on( 'sms_status', function( d ){

						console.debug('Recieved sms status update: ', d);

						updateSmsStatus( d );
					} )

			}

		} else {
			//service.socket.close();
			//service.socket = null;
		}
	});

	var updateSmsStatus = function( d ){
		var id_support_message = d.id_support_message;
		var status = d.status;
		var element = $( '#support-message-status-' + id_support_message );
		if( element && element.length ){
			element.attr( 'title', 'Status: ' + status );
		}
	}

	$rootScope.$watch('account.user.prefs["notification-desktop-support-all"]', function(e, value) {
		if (value == '1') {
			console.debug('Subscribing to all tickets');
			service.socket.emit('event.subscribe', 'tickets');
		} else {
			console.debug('Unsubscribing to all tickets');
			service.socket.emit('event.unsubscribe', 'tickets');
		}
	});

	service.send = function(message, add_as_note, callback) {
		var add_as_note = ( add_as_note ? true : false );
		var guid = App.guid();
		console.log('guid',guid);
		if( !service.sideInfo.id_support ){
			return;
		}
		TicketService.message( {
			id_support: service.sideInfo.id_support,
			body: message,
			guid: guid,
			note: add_as_note
		},
		// callback
		function(d) {
			if(!d.status){
				d.status = 'queued';
			}

			for ( var x in service.sideInfo.data.messages ) {
				if ( service.sideInfo.data.messages[x].guid == guid ) {
					d.guid = guid;
					service.sideInfo.data.messages[x] = d;
					notified.push( d.id_support_message );
					break;
				}
			}
		});
		if( callback ){
			callback()
		} else {
			service.scope.$apply(function() {
				console.log('no callback');
				service.sideInfo.add_message( { body: message, name: AccountService.user.firstName, timestamp: new Date().getTime(), sending: true, guid: guid, status: 'queued' } );
			});
		}
		$rootScope.$broadcast( 'replyFinished' );
	};

	service.scroll = function(instant) {
		setTimeout(function() {
			if( $('.support-chat-contents-scroll').length ){
				$('.support-chat-contents-scroll').stop(true,false).animate({
					scrollTop: $('.support-chat-contents-scroll')[0].scrollHeight
				}, instant ? 0 : 800);
			}
		}, 100);
	};

	/*
	var typingTimer;

	service.typing = function(val) {
		return;
		if (!service.isTyping) {
			service.isTyping = true;
			service.websocket.send({
				type: 'ticket.typing.start'
			});

		} else {
			if (!val) {
				service.isTyping = false;
				service.websocket.send({
					type: 'ticket.typing.stop'
				});
			}
		}

		if (typingTimer) {
			clearTimeout(typingTimer);
		}
		typingTimer = setTimeout(function() {
			if (service.isTyping) {
				service.isTyping = false;
				service.websocket.send({
					type: 'ticket.typing.stop'
				});
			}
		}, 5000);
	};
	*/

	return service;
});
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { connect } from 'react-redux';

import { TYPE_TO_CLASS } from '../../utils/constants';

class LogTable extends Component {
	renderLogTableRows() {
		return this.props.logs.map(log => {
			return (
				<tr key={`log-item-${log.time}`} className={TYPE_TO_CLASS[log.type]}>
					{log.time}
					{log.type}
					{log.message}

			);
		});
	}

	render() {
		if(this.props.selected.application.id === '') {
			return (

Welcome to the SocketIO Log Viewer

Select an application in the sidebar to view logs in real time.

			);
		}

		return (

Logs

										Time
										Type
										Message

									{this.renderLogTableRows()}

		);
	}
}

function mapStateToProps(state) {
	return {
		logs: state.logs[state.selected.application.id] || [],
		selected: state.selected
	};
}

export default connect(
	mapStateToProps,
	null
)(LogTable);
<commit_msg>Render the log time in a friendlier format.<commit_after>import React, { Component } from 'react';
import { connect } from 'react-redux';

import { TYPE_TO_CLASS } from '../../utils/constants';

class LogTable extends Component {
	renderLogTableRows() {
		return this.props.logs.map(log => {
			return (
				<tr key={`log-item-${log.time}`} className={TYPE_TO_CLASS[log.type]}>
					{(new Date(log.time)).toLocaleString()}
					{log.type}
					{log.message}

			);
		});
	}

	render() {
		if(this.props.selected.application.id === '') {
			return (

Welcome to the SocketIO Log Viewer

Select an application in the sidebar to view logs in real time.

			);
		}

		return (

Logs

										Time
										Type
										Message

									{this.renderLogTableRows()}

		);
	}
}

function mapStateToProps(state) {
	return {
		logs: state.logs[state.selected.application.id] || [],
		selected: state.selected
	};
}

export default connect(
	mapStateToProps,
	null
)(LogTable);
<|endoftext|>


<commit_before>var fs               = require('fs')
,   util             = require('util')
,   events           = require('events')
,   moment           = require('moment')
,   Playlist         = require('./api/Playlist')
,   Melted           = require('./api/Melted')
,   Media            = require('./api/Media')
,   ScheduledMedia   = require('./api/ScheduledMedia')
,   StatusClip       = require('./api/StatusClip')
,   mvcp_server      = require('./drivers/mvcp/mvcp-driver')
,   playlists_driver = require('./drivers/playlists/playlists-driver')
,   status_driver    = require('./drivers/status/pubsub')
,   utils            = require('./utils')
,   config           = require('mbc-common').config.Mosto.General
,   _                = require('underscore')
,   heartbeats        = require('./heartbeats')
,   models           = require('./models/Mosto')
;

function mosto(customConfig) {

    /** CONFIGURATION */
    this.config = customConfig || config;
    this.server = undefined;
    this.server_started = false;
    this.pl_driver = undefined;
    this.status_driver = undefined;

    /* MODULES */
    this.heartbeats = undefined;
    this.playlists = undefined;

    events.EventEmitter.call(this);
}

util.inherits(mosto, events.EventEmitter);

mosto.prototype.inTimeWindow = function(obj) {
    // expects obj.start and obj.end to exist and be moment()s
    return (obj.end > this.timeWindow.start && obj.start < this.timeWindow.end);
}

mosto.prototype.initDriver = function() {
    var self = this;
    console.log("mbc-mosto: [INFO] Initializing playlists driver");

    this.pl_driver.on('create', function(playlist) {
        var now = moment();

        if(!self.inTimeWindow(playlist))
            return;

        self.playlists.addPlaylist(playlist);
    });

    this.pl_driver.on('update', function(playlist) {
        if(!self.inTimeWindow(playlist))
            return self.playlists.removePlaylist(playlist);
        return self.playlists.addPlaylist(playlist);
    });

    this.pl_driver.on('delete', function(playlist) {
        return self.playlists.removePlaylist(playlist);
    });

    self.pl_driver.start(self.timeWindow);
};

mosto.prototype.stopDriver = function() {

    console.log("mbc-mosto: [INFO] Stopping playlists driver");

    this.pl_driver.stop();

    this.pl_driver.removeAllListeners("create");
    this.pl_driver.removeAllListeners("update");
    this.pl_driver.removeAllListeners("delete");
};

mosto.prototype.startMvcpServer = function(callback) {
    var self = this;
    var result = self.server.initServer();
    result.then(function() {
        console.log("mbc-mosto: [INFO] MVCP server started");
        self.server_started = true;
        if (callback !== undefined) {
            callback();
        }
    }, function(err) {
        var e = new Error("mbc-mosto: [ERROR] Error starting MVCP server: " + err + ".\nRetrying in 2 seconds...");
        console.error(e);
        setTimeout(function() {
            self.startMvcpServer(callback);
        }, 2000);
    });
};

mosto.prototype.initHeartbeats = function() {
    var self = this;

    console.log("mbc-mosto: [INFO] Initializing heartbeats");

    self.heartbeats.on('frameStatus', function(status) {
        self.status_driver.setStatusClip(StatusClip(
            status.media.id,
            status.media.get('playlist_order'),
            status.media.playlist.id,
            status.media.get('fps'),
            status.frame,
            status.media.get('length')
        ));
    });
    self.heartbeats.on('clipStatus', function(media) {
        var melted_medias = self.playlists.get('melted_medias');
        var playlists = self.playlists.get('playlists');
        var status = {
            clip:{
                previous: undefined,
                current: undefined,
                next: undefined
            },
            show: {
                previous: undefined,
                current: undefined,
                next: undefined
            },
            position: 0,
            clips: melted_medias.toJSON(),
            status: null,
        };
        /* clips */
        var index = melted_medias.indexOf(media);
        status.clip.current = media.toJSON();
        if( index > 0 )
            status.clip.previous = melted_medias.at(index-1).toJSON();
        if( index < melted_medias.length - 1 )
            status.clip.next = melted_medias.at(index+1).toJSON();
        /* shows */
        var playlist = playlists.find(function(pl) {
            return pl.indexOf(media) >= 0;
        });
        index = playlists.indexOf(playlist);
        status.show.current = playlist.toJSON();
        if( index > 0 )
            status.show.previous = playlists.at(index-1).toJSON();
        if( index < playlists.length - 1 )
            status.show.next = playlists.at(index+1).toJSON();
        self.status_driver.setStatus(status);
    });

    self.heartbeats.on("forceCheckout", function(window) {
        self.timeWindow = window;
        self.fetchPlaylists(window);
    });

    self.heartbeats.init();
};

mosto.prototype.fetchPlaylists = function(window) {
    var self = this;
    self.pl_driver.getPlaylists(window, function(playlists) {
        playlists.forEach(function(playlist) {
            self.playlists.get("playlists").add(playlist, {merge: true});
        });
        self.playlists.save();
    });
};

mosto.prototype.getModelPlaylistFromApiPlaylist = function(playlist) {
    //TODO: Remove this function and change driver to send Caspa Models
    var playlistJson   = {};
    playlistJson.name  = playlist.name;
    playlistJson.start = moment(playlist.startDate);
    playlistJson.end   = moment(playlist.endDate);
    playlistJson.id    = playlist.id;

    var start = playlistJson.start;
    var medias = new Mosto.MediaCollection();
    playlist.medias.forEach(function(media) {
        var mediaJson = {};
        mediaJson.playlist_order = media.orig_order;
        mediaJson.name           = media.name;
        mediaJson.type           = media.type;
        mediaJson.file           = media.file;
        //TODO: ASSUMING LENGTH COMES IN SOME KIND OF DATE FORMAT THAT CAN BE USED BY MOMENT
        mediaJson.length         = (moment(media.length) / 1000) * media.fps;
        mediaJson.fps            = media.fps;
        mediaJson.start          = start;
        mediaJson.end            = start + moment(media.length);
        mediaJson.id             = media.id;

        var mostoMedia = new Mosto.Media(mediaJson);
        medias.add(mostoMedia);

        start = start + moment(media.length);
    });

    playlistJson.medias = medias;

    return new Mosto.Playlist(playlistJson);
};

mosto.prototype.stopHeartbeats = function() {
    var self = this;

    console.log("mbc-mosto: [INFO] Stopping heartbeats");

    self.heartbeats.stop();

    self.heartbeats.removeAllListeners("frameStatus");
    self.heartbeats.removeAllListeners("clipStatus");
};

mosto.prototype.init = function( melted, callback) {
    console.log("mbc-mosto: [INFO] Init mbc-mosto... ") ;
    var self = this;
    /*
     * inicializar los drivers
     * inicializar los modelos backbone
     * inicializar los heartbeats
     * linkear los eventos entre pl driver y backbone driver
     * linkear heartbeat con status driver
     */
    function startall() {
        self.server        = new mvcp_server(self.config.mvcp_server);
        console.log("mbc-mosto: [INFO] MVCP Server instantiated: " + self.server.uuid);
        self.pl_driver     = new playlists_driver(self.config.playlist_server);
        self.status_driver = new status_driver();
        self.playlists     = models.Playlists();
        self.heartbeats    = new heartbeats();

        self.timeWindow = { start: moment(), end: moment().add(4, 'hours') };

        self.initDriver();
        self.initHeartbeats();

        self.startMvcpServer( function() {
            //TODO: See why this brakes everything :(
            self.fetchPlaylists({from: moment(), to: moment().add(4, 'hours')});
            if (callback) callback();
        } );
    }

    function check_and_start() {
        Melted.is_running(function(running) {
            if (!running) {
                Melted.start(function(pid) {
                    Melted.setup( undefined, undefined, function(result) {
                        startall();
                    });
                });
            } else {
                startall();
            }
        });
    };

    if (melted!==undefined) {
        Melted = melted;
        check_and_start();
    }
    else
        Melted.take( check_and_start );

};

mosto.prototype.finish = function(callback) {
    console.log("mbc-mosto: [INFO] Finish mbc-mosto... ") ;
    this.stopDriver();
    this.stopHeartbeats();
    Melted.stop(function(pid) {
        setTimeout( function() {
            Melted.leave();
            if (callback) callback();
        }, 1000 );
    });
};

exports = module.exports = function(customConfig) {
    var mosto_server = new mosto(customConfig);
    return mosto_server;
};
/*

  mosto tiene que:

  * inicializar melted
  * inicializar los drivers
  * inicializar los modelos backbone
  * inicializar los heartbeats
  * linkear los eventos entre pl driver y backbone driver
  * linkear heartbeat con status driver

  */
<commit_msg>if the searched object is not identical, indexOf fails so just search if the id is present<commit_after>var fs               = require('fs')
,   util             = require('util')
,   events           = require('events')
,   moment           = require('moment')
,   Playlist         = require('./api/Playlist')
,   Melted           = require('./api/Melted')
,   Media            = require('./api/Media')
,   ScheduledMedia   = require('./api/ScheduledMedia')
,   StatusClip       = require('./api/StatusClip')
,   mvcp_server      = require('./drivers/mvcp/mvcp-driver')
,   playlists_driver = require('./drivers/playlists/playlists-driver')
,   status_driver    = require('./drivers/status/pubsub')
,   utils            = require('./utils')
,   config           = require('mbc-common').config.Mosto.General
,   _                = require('underscore')
,   heartbeats        = require('./heartbeats')
,   models           = require('./models/Mosto')
;

function mosto(customConfig) {

    /** CONFIGURATION */
    this.config = customConfig || config;
    this.server = undefined;
    this.server_started = false;
    this.pl_driver = undefined;
    this.status_driver = undefined;

    /* MODULES */
    this.heartbeats = undefined;
    this.playlists = undefined;

    events.EventEmitter.call(this);
}

util.inherits(mosto, events.EventEmitter);

mosto.prototype.inTimeWindow = function(obj) {
    // expects obj.start and obj.end to exist and be moment()s
    return (obj.end > this.timeWindow.start && obj.start < this.timeWindow.end);
}

mosto.prototype.initDriver = function() {
    var self = this;
    console.log("mbc-mosto: [INFO] Initializing playlists driver");

    this.pl_driver.on('create', function(playlist) {
        var now = moment();

        if(!self.inTimeWindow(playlist))
            return;

        self.playlists.addPlaylist(playlist);
    });

    this.pl_driver.on('update', function(playlist) {
        if(!self.inTimeWindow(playlist))
            return self.playlists.removePlaylist(playlist);
        return self.playlists.addPlaylist(playlist);
    });

    this.pl_driver.on('delete', function(playlist) {
        return self.playlists.removePlaylist(playlist);
    });

    self.pl_driver.start(self.timeWindow);
};

mosto.prototype.stopDriver = function() {

    console.log("mbc-mosto: [INFO] Stopping playlists driver");

    this.pl_driver.stop();

    this.pl_driver.removeAllListeners("create");
    this.pl_driver.removeAllListeners("update");
    this.pl_driver.removeAllListeners("delete");
};

mosto.prototype.startMvcpServer = function(callback) {
    var self = this;
    var result = self.server.initServer();
    result.then(function() {
        console.log("mbc-mosto: [INFO] MVCP server started");
        self.server_started = true;
        if (callback !== undefined) {
            callback();
        }
    }, function(err) {
        var e = new Error("mbc-mosto: [ERROR] Error starting MVCP server: " + err + ".\nRetrying in 2 seconds...");
        console.error(e);
        setTimeout(function() {
            self.startMvcpServer(callback);
        }, 2000);
    });
};

mosto.prototype.initHeartbeats = function() {
    var self = this;

    console.log("mbc-mosto: [INFO] Initializing heartbeats");

    self.heartbeats.on('frameStatus', function(status) {
        self.status_driver.setStatusClip(StatusClip(
            status.media.id,
            status.media.get('playlist_order'),
            status.media.playlist.id,
            status.media.get('fps'),
            status.frame,
            status.media.get('length')
        ));
    });
    self.heartbeats.on('clipStatus', function(media) {
        var melted_medias = self.playlists.get('melted_medias');
        var playlists = self.playlists.get('playlists');
        var status = {
            clip:{
                previous: undefined,
                current: undefined,
                next: undefined
            },
            show: {
                previous: undefined,
                current: undefined,
                next: undefined
            },
            position: 0,
            clips: melted_medias.toJSON(),
            status: null,
        };
        /* clips */
        var index = melted_medias.indexOf(media);
        status.clip.current = media.toJSON();
        if( index > 0 )
            status.clip.previous = melted_medias.at(index-1).toJSON();
        if( index < melted_medias.length - 1 )
            status.clip.next = melted_medias.at(index+1).toJSON();
        /* shows */
        var playlist = playlists.find(function(pl) {
            return pl.get('medias').get(media.id);
        });
        index = playlists.indexOf(playlist);
        status.show.current = playlist.toJSON();
        if( index > 0 )
            status.show.previous = playlists.at(index-1).toJSON();
        if( index < playlists.length - 1 )
            status.show.next = playlists.at(index+1).toJSON();
        self.status_driver.setStatus(status);
    });

    self.heartbeats.on("forceCheckout", function(window) {
        self.timeWindow = window;
        self.fetchPlaylists(window);
    });

    self.heartbeats.init();
};

mosto.prototype.fetchPlaylists = function(window) {
    var self = this;
    self.pl_driver.getPlaylists(window, function(playlists) {
        playlists.forEach(function(playlist) {
            self.playlists.get("playlists").add(playlist, {merge: true});
        });
        self.playlists.save();
    });
};

mosto.prototype.getModelPlaylistFromApiPlaylist = function(playlist) {
    //TODO: Remove this function and change driver to send Caspa Models
    var playlistJson   = {};
    playlistJson.name  = playlist.name;
    playlistJson.start = moment(playlist.startDate);
    playlistJson.end   = moment(playlist.endDate);
    playlistJson.id    = playlist.id;

    var start = playlistJson.start;
    var medias = new Mosto.MediaCollection();
    playlist.medias.forEach(function(media) {
        var mediaJson = {};
        mediaJson.playlist_order = media.orig_order;
        mediaJson.name           = media.name;
        mediaJson.type           = media.type;
        mediaJson.file           = media.file;
        //TODO: ASSUMING LENGTH COMES IN SOME KIND OF DATE FORMAT THAT CAN BE USED BY MOMENT
        mediaJson.length         = (moment(media.length) / 1000) * media.fps;
        mediaJson.fps            = media.fps;
        mediaJson.start          = start;
        mediaJson.end            = start + moment(media.length);
        mediaJson.id             = media.id;

        var mostoMedia = new Mosto.Media(mediaJson);
        medias.add(mostoMedia);

        start = start + moment(media.length);
    });

    playlistJson.medias = medias;

    return new Mosto.Playlist(playlistJson);
};

mosto.prototype.stopHeartbeats = function() {
    var self = this;

    console.log("mbc-mosto: [INFO] Stopping heartbeats");

    self.heartbeats.stop();

    self.heartbeats.removeAllListeners("frameStatus");
    self.heartbeats.removeAllListeners("clipStatus");
};

mosto.prototype.init = function( melted, callback) {
    console.log("mbc-mosto: [INFO] Init mbc-mosto... ") ;
    var self = this;
    /*
     * inicializar los drivers
     * inicializar los modelos backbone
     * inicializar los heartbeats
     * linkear los eventos entre pl driver y backbone driver
     * linkear heartbeat con status driver
     */
    function startall() {
        self.server        = new mvcp_server(self.config.mvcp_server);
        console.log("mbc-mosto: [INFO] MVCP Server instantiated: " + self.server.uuid);
        self.pl_driver     = new playlists_driver(self.config.playlist_server);
        self.status_driver = new status_driver();
        self.playlists     = models.Playlists();
        self.heartbeats    = new heartbeats();

        self.timeWindow = { start: moment(), end: moment().add(4, 'hours') };

        self.initDriver();
        self.initHeartbeats();

        self.startMvcpServer( function() {
            //TODO: See why this brakes everything :(
            self.fetchPlaylists({from: moment(), to: moment().add(4, 'hours')});
            if (callback) callback();
        } );
    }

    function check_and_start() {
        Melted.is_running(function(running) {
            if (!running) {
                Melted.start(function(pid) {
                    Melted.setup( undefined, undefined, function(result) {
                        startall();
                    });
                });
            } else {
                startall();
            }
        });
    };

    if (melted!==undefined) {
        Melted = melted;
        check_and_start();
    }
    else
        Melted.take( check_and_start );

};

mosto.prototype.finish = function(callback) {
    console.log("mbc-mosto: [INFO] Finish mbc-mosto... ") ;
    this.stopDriver();
    this.stopHeartbeats();
    Melted.stop(function(pid) {
        setTimeout( function() {
            Melted.leave();
            if (callback) callback();
        }, 1000 );
    });
};

exports = module.exports = function(customConfig) {
    var mosto_server = new mosto(customConfig);
    return mosto_server;
};
/*

  mosto tiene que:

  * inicializar melted
  * inicializar los drivers
  * inicializar los modelos backbone
  * inicializar los heartbeats
  * linkear los eventos entre pl driver y backbone driver
  * linkear heartbeat con status driver

  */
<|endoftext|>