<commit_before>import Declaration from './declaration';
import tokenizer   from './tokenize';
import Comment     from './comment';
import AtRule      from './at-rule';
import Root        from './root';
import Rule        from './rule';

export default class Parser {

    constructor(input) {
        this.input = input;

        this.pos       = 0;
        this.root      = new Root();
        this.current   = this.root;
        this.spaces    = '';
        this.semicolon = false;

        this.root.source = { input };
    }

    tokenize() {
        this.tokens = tokenizer(this.input);
    }

    loop() {
        let token;
        while ( this.pos < this.tokens.length ) {
            token = this.tokens[this.pos];

            switch ( token[0] ) {
            case 'word':
            case ':':
                this.word();
                break;

            case '}':
                this.end(token);
                break;

            case 'comment':
                this.comment(token);
                break;

            case 'at-word':
                this.atrule(token);
                break;

            case '{':
                this.emptyRule(token);
                break;

            default:
                this.spaces += token[1];
                break;
            }

            this.pos += 1;
        }
        this.endFile();
    }

    comment(token) {
        let node = new Comment();
        this.init(node, token[2], token[3]);
        node.source.end = { line: token[4], column: token[5] };

        let text = token[1].slice(2, -2);
        if ( text.match(/^\s*$/) ) {
            node.text      = '';
            node.raw.left  = text;
            node.raw.right = '';
        } else {
            let match = text.match(/^(\s*)([^]*[^\s])(\s*)$/);
            node.text      = match[2];
            node.raw.left  = match[1];
            node.raw.right = match[3];
        }
    }

    emptyRule(token) {
        let node = new Rule();
        this.init(node, token[2], token[3]);
        node.selector = '';
        node.raw.between = '';
        this.current = node;
    }

    word() {
        let token;
        let end      = false;
        let type     = null;
        let colon    = false;
        let bracket  = null;
        let brackets = 0;

        let start = this.pos;
        this.pos += 1;
        while ( this.pos < this.tokens.length ) {
            token = this.tokens[this.pos];
            type  = token[0];

            if ( type === '(' ) {
                if ( !bracket ) bracket = token;
                brackets += 1;

            } else if ( brackets === 0 ) {
                if ( type === ';' ) {
                    if ( colon ) {
                        this.decl(this.tokens.slice(start, this.pos + 1));
                        return;
                    } else {
                        break;
                    }

                } else if ( type === '{' ) {
                    this.rule(this.tokens.slice(start, this.pos + 1));
                    return;

                } else if ( type === '}' ) {
                    this.pos -= 1;
                    end = true;
                    break;

                } else if ( type === ':' ) {
                    colon = true;
                }

            } else if ( type === ')' ) {
                brackets -= 1;
                if ( brackets === 0 ) bracket = null;
            }

            this.pos += 1;
        }
        if ( this.pos === this.tokens.length ) {
            this.pos -= 1;
            end = true;
        }

        if ( brackets > 0 ) this.unclosedBracket(bracket);

        if ( end && colon ) {
            while ( this.pos > start ) {
                token = this.tokens[this.pos][0];
                if ( token !== 'space' && token !== 'comment' ) break;
                this.pos -= 1;
            }
            this.decl(this.tokens.slice(start, this.pos + 1));
            return;
        }

        this.unknownWord(start);
    }

    rule(tokens) {
        tokens.pop();

        let node = new Rule();
        this.init(node, tokens[0][2], tokens[0][3]);

        node.raw.between = this.spacesFromEnd(tokens);
        this.raw(node, 'selector', tokens);
        this.current = node;
    }

    decl(tokens) {
        let node = new Declaration();
        this.init(node);

        let last = tokens[tokens.length - 1];
        if ( last[0] === ';' ) {
            this.semicolon = true;
            tokens.pop();
        }
        if ( last[4] ) {
            node.source.end = { line: last[4], column: last[5] };
        } else {
            node.source.end = { line: last[2], column: last[3] };
        }

        while ( tokens[0][0] !== 'word' ) {
            node.raw.before += tokens.shift()[1];
        }
        node.source.start = { line: tokens[0][2], column: tokens[0][3] };

        node.prop = tokens.shift()[1];
        node.raw.between = '';

        let token;
        while ( tokens.length ) {
            token = tokens.shift();

            if ( token[0] === ':' ) {
                node.raw.between += token[1];
                break;
            } else if ( token[0] !== 'space' && token[0] !== 'comment' ) {
                this.unknownDecl(node, token);
            } else {
                node.raw.between += token[1];
            }
        }

        if ( node.prop[0] === '_' || node.prop[0] === '*' ) {
            node.raw.before += node.prop[0];
            node.prop = node.prop.slice(1);
        }
        node.raw.between += this.spacesFromStart(tokens);
        this.precheckMissedSemicolon(tokens);

        for ( let i = tokens.length - 1; i > 0; i-- ) {
            token = tokens[i];
            if ( token[1] === '!important' ) {
                node.important = true;
                let string = this.stringFrom(tokens, i);
                string = this.spacesFromEnd(tokens) + string;
                if ( string !== ' !important' ) node.raw.important = string;
                break;

            } else if (token[1] === 'important') {
                let cache = tokens.slice(0);
                let str   = '';
                for ( let j = i; j > 0; j-- ) {
                    let type = cache[j][0];
                    if ( str.trim().indexOf('!') === 0 && type !== 'space' ) {
                        break;
                    }
                    str = cache.pop()[1] + str;
                }
                if ( str.trim().indexOf('!') === 0 ) {
                    node.important = true;
                    node.raw.important = str;
                    tokens = cache;
                }
            }

            if ( token[0] !== 'space' && token[0] !== 'comment' ) {
                break;
            }
        }

        this.raw(node, 'value', tokens);

        if ( node.value.indexOf(':') !== -1 ) this.checkMissedSemicolon(tokens);
    }

    atrule(token) {
        let node  = new AtRule();
        node.name = token[1].slice(1);
        if ( node.name === '' ) {
            this.unnamedAtrule(node, token);
        }
        this.init(node, token[2], token[3]);

        let last   = false;
        let open   = false;
        let params = [];

        this.pos += 1;
        while ( this.pos < this.tokens.length ) {
            token = this.tokens[this.pos];

            if ( token[0] === ';' ) {
                node.source.end = { line: token[2], column: token[3] };
                this.semicolon = true;
                break;
            } else if ( token[0] === '{' ) {
                open = true;
                break;
            } else {
                params.push(token);
            }

            this.pos += 1;
        }
        if ( this.pos === this.tokens.length ) {
            last = true;
        }

        node.raw.between = this.spacesFromEnd(params);
        if ( params.length ) {
            node.raw.afterName = this.spacesFromStart(params);
            this.raw(node, 'params', params);
            if ( last ) {
                token = params[params.length - 1];
                node.source.end  = { line: token[4], column: token[5] };
                this.spaces      = node.raw.between;
                node.raw.between = '';
            }
        } else {
            node.raw.afterName = '';
            node.params    = '';
        }

        if ( open ) {
            node.nodes  = [];
            this.current = node;
        }
    }

    end(token) {
        if ( this.current.nodes && this.current.nodes.length ) {
            this.current.raw.semicolon = this.semicolon;
        }
        this.semicolon = false;

        this.current.raw.after = (this.current.raw.after || '') + this.spaces;
        this.spaces = '';

        if ( this.current.parent ) {
            this.current.source.end = { line: token[2], column: token[3] };
            this.current = this.current.parent;
        } else {
            this.unexpectedClose(token);
        }
    }

    endFile() {
        if ( this.current.parent ) this.unclosedBlock();

        if ( this.current.nodes && this.current.nodes.length ) {
            this.current.raw.semicolon = this.semicolon;
        }
        this.current.raw.after = (this.current.raw.after || '') + this.spaces;

        while ( this.current.parent ) {
            this.current = this.current.parent;
            this.current.raw.after = '';
        }
    }

    // Helpers

    init(node, line, column) {
        this.current.push(node);

        node.source = { start: { line, column }, input: this.input };
        node.raw.before = this.spaces;
        this.spaces = '';
        if ( node.type !== 'comment' ) this.semicolon = false;
    }

    raw(node, prop, tokens) {
        let token;
        let value = '';
        let clean = true;
        for ( token of tokens ) {
            if ( token[0] === 'comment' ) {
                clean = false;
            } else {
                value += token[1];
            }
        }
        if ( !clean ) {
            let origin = '';
            for ( token of tokens ) origin += token[1];
            node.raw[prop] = { value, raw: origin };
        }
        node[prop] = value;
    }

    spacesFromEnd(tokens) {
        let next;
        let spaces = '';
        while ( tokens.length ) {
            next = tokens[tokens.length - 1][0];
            if ( next !== 'space' && next !== 'comment' ) break;
            spaces += tokens.pop()[1];
        }
        return spaces;
    }

    spacesFromStart(tokens) {
        let next;
        let spaces = '';
        while ( tokens.length ) {
            next = tokens[0][0];
            if ( next !== 'space' && next !== 'comment' ) break;
            spaces += tokens.shift()[1];
        }
        return spaces;
    }

    stringFrom(tokens, from) {
        let result = '';
        for ( let i = from; i < tokens.length; i++ ) {
            result += tokens[i][1];
        }
        tokens.splice(from, tokens.length - from);
        return result;
    }

    colon(tokens) {
        let brackets = 0;
        let token, type, prev;
        for ( let i = 0; i < tokens.length; i++ ) {
            token = tokens[i];
            type  = token[0];

            if ( type === '(' ) {
                brackets += 1;
            } else if ( type === ')' ) {
                brackets -= 0;
            } else if ( brackets === 0 && type === ':' ) {
                if ( !prev ) {
                    this.doubleColon(token);
                    continue;
                } else if ( prev[0] === 'word' && prev[1] === 'progid' ) {
                    continue;
                } else {
                    return i;
                }
            }

            prev = token;
        }
        return false;
    }

    // Errors

    unknownDecl(node, token) {
        throw this.input.error('Unknown word', token[2], token[3]);
    }

    unclosedBracket(bracket) {
        throw this.input.error('Unclosed bracket', bracket[2], bracket[3]);
    }

    unknownWord(start) {
        let token = this.tokens[start];
        throw this.input.error('Unknown word', token[2], token[3]);
    }

    unexpectedClose(token) {
        throw this.input.error('Unexpected }', token[2], token[3]);
    }

    unclosedBlock() {
        let pos = this.current.source.start;
        throw this.input.error('Unclosed block', pos.line, pos.column);
    }

    doubleColon(token) {
        throw this.input.error('Double colon', token[2], token[3]);
    }

    unnamedAtrule(node, token) {
        throw this.input.error('At-rule without name', token[2], token[3]);
    }

    precheckMissedSemicolon() { }

    checkMissedSemicolon(tokens) {
        let colon = this.colon(tokens);
        if ( colon === false ) return;

        let founded = 0;
        let token;
        for ( let j = colon - 1; j >= 0; j-- ) {
            token = tokens[j];
            if ( token[0] !== 'space' ) {
                founded += 1;
                if ( founded === 2 ) break;
            }
        }
        throw this.input.error('Missed semicolon', token[2], token[3]);
    }

}
<commit_msg>More clear precheckMissedSemicolon<commit_after>import Declaration from './declaration';
import tokenizer   from './tokenize';
import Comment     from './comment';
import AtRule      from './at-rule';
import Root        from './root';
import Rule        from './rule';

export default class Parser {

    constructor(input) {
        this.input = input;

        this.pos       = 0;
        this.root      = new Root();
        this.current   = this.root;
        this.spaces    = '';
        this.semicolon = false;

        this.root.source = { input };
    }

    tokenize() {
        this.tokens = tokenizer(this.input);
    }

    loop() {
        let token;
        while ( this.pos < this.tokens.length ) {
            token = this.tokens[this.pos];

            switch ( token[0] ) {
            case 'word':
            case ':':
                this.word();
                break;

            case '}':
                this.end(token);
                break;

            case 'comment':
                this.comment(token);
                break;

            case 'at-word':
                this.atrule(token);
                break;

            case '{':
                this.emptyRule(token);
                break;

            default:
                this.spaces += token[1];
                break;
            }

            this.pos += 1;
        }
        this.endFile();
    }

    comment(token) {
        let node = new Comment();
        this.init(node, token[2], token[3]);
        node.source.end = { line: token[4], column: token[5] };

        let text = token[1].slice(2, -2);
        if ( text.match(/^\s*$/) ) {
            node.text      = '';
            node.raw.left  = text;
            node.raw.right = '';
        } else {
            let match = text.match(/^(\s*)([^]*[^\s])(\s*)$/);
            node.text      = match[2];
            node.raw.left  = match[1];
            node.raw.right = match[3];
        }
    }

    emptyRule(token) {
        let node = new Rule();
        this.init(node, token[2], token[3]);
        node.selector = '';
        node.raw.between = '';
        this.current = node;
    }

    word() {
        let token;
        let end      = false;
        let type     = null;
        let colon    = false;
        let bracket  = null;
        let brackets = 0;

        let start = this.pos;
        this.pos += 1;
        while ( this.pos < this.tokens.length ) {
            token = this.tokens[this.pos];
            type  = token[0];

            if ( type === '(' ) {
                if ( !bracket ) bracket = token;
                brackets += 1;

            } else if ( brackets === 0 ) {
                if ( type === ';' ) {
                    if ( colon ) {
                        this.decl(this.tokens.slice(start, this.pos + 1));
                        return;
                    } else {
                        break;
                    }

                } else if ( type === '{' ) {
                    this.rule(this.tokens.slice(start, this.pos + 1));
                    return;

                } else if ( type === '}' ) {
                    this.pos -= 1;
                    end = true;
                    break;

                } else if ( type === ':' ) {
                    colon = true;
                }

            } else if ( type === ')' ) {
                brackets -= 1;
                if ( brackets === 0 ) bracket = null;
            }

            this.pos += 1;
        }
        if ( this.pos === this.tokens.length ) {
            this.pos -= 1;
            end = true;
        }

        if ( brackets > 0 ) this.unclosedBracket(bracket);

        if ( end && colon ) {
            while ( this.pos > start ) {
                token = this.tokens[this.pos][0];
                if ( token !== 'space' && token !== 'comment' ) break;
                this.pos -= 1;
            }
            this.decl(this.tokens.slice(start, this.pos + 1));
            return;
        }

        this.unknownWord(start);
    }

    rule(tokens) {
        tokens.pop();

        let node = new Rule();
        this.init(node, tokens[0][2], tokens[0][3]);

        node.raw.between = this.spacesFromEnd(tokens);
        this.raw(node, 'selector', tokens);
        this.current = node;
    }

    decl(tokens) {
        let node = new Declaration();
        this.init(node);

        let last = tokens[tokens.length - 1];
        if ( last[0] === ';' ) {
            this.semicolon = true;
            tokens.pop();
        }
        if ( last[4] ) {
            node.source.end = { line: last[4], column: last[5] };
        } else {
            node.source.end = { line: last[2], column: last[3] };
        }

        while ( tokens[0][0] !== 'word' ) {
            node.raw.before += tokens.shift()[1];
        }
        node.source.start = { line: tokens[0][2], column: tokens[0][3] };

        node.prop = tokens.shift()[1];
        node.raw.between = '';

        let token;
        while ( tokens.length ) {
            token = tokens.shift();

            if ( token[0] === ':' ) {
                node.raw.between += token[1];
                break;
            } else if ( token[0] !== 'space' && token[0] !== 'comment' ) {
                this.unknownDecl(node, token);
            } else {
                node.raw.between += token[1];
            }
        }

        if ( node.prop[0] === '_' || node.prop[0] === '*' ) {
            node.raw.before += node.prop[0];
            node.prop = node.prop.slice(1);
        }
        node.raw.between += this.spacesFromStart(tokens);
        this.precheckMissedSemicolon(tokens);

        for ( let i = tokens.length - 1; i > 0; i-- ) {
            token = tokens[i];
            if ( token[1] === '!important' ) {
                node.important = true;
                let string = this.stringFrom(tokens, i);
                string = this.spacesFromEnd(tokens) + string;
                if ( string !== ' !important' ) node.raw.important = string;
                break;

            } else if (token[1] === 'important') {
                let cache = tokens.slice(0);
                let str   = '';
                for ( let j = i; j > 0; j-- ) {
                    let type = cache[j][0];
                    if ( str.trim().indexOf('!') === 0 && type !== 'space' ) {
                        break;
                    }
                    str = cache.pop()[1] + str;
                }
                if ( str.trim().indexOf('!') === 0 ) {
                    node.important = true;
                    node.raw.important = str;
                    tokens = cache;
                }
            }

            if ( token[0] !== 'space' && token[0] !== 'comment' ) {
                break;
            }
        }

        this.raw(node, 'value', tokens);

        if ( node.value.indexOf(':') !== -1 ) this.checkMissedSemicolon(tokens);
    }

    atrule(token) {
        let node  = new AtRule();
        node.name = token[1].slice(1);
        if ( node.name === '' ) {
            this.unnamedAtrule(node, token);
        }
        this.init(node, token[2], token[3]);

        let last   = false;
        let open   = false;
        let params = [];

        this.pos += 1;
        while ( this.pos < this.tokens.length ) {
            token = this.tokens[this.pos];

            if ( token[0] === ';' ) {
                node.source.end = { line: token[2], column: token[3] };
                this.semicolon = true;
                break;
            } else if ( token[0] === '{' ) {
                open = true;
                break;
            } else {
                params.push(token);
            }

            this.pos += 1;
        }
        if ( this.pos === this.tokens.length ) {
            last = true;
        }

        node.raw.between = this.spacesFromEnd(params);
        if ( params.length ) {
            node.raw.afterName = this.spacesFromStart(params);
            this.raw(node, 'params', params);
            if ( last ) {
                token = params[params.length - 1];
                node.source.end  = { line: token[4], column: token[5] };
                this.spaces      = node.raw.between;
                node.raw.between = '';
            }
        } else {
            node.raw.afterName = '';
            node.params    = '';
        }

        if ( open ) {
            node.nodes  = [];
            this.current = node;
        }
    }

    end(token) {
        if ( this.current.nodes && this.current.nodes.length ) {
            this.current.raw.semicolon = this.semicolon;
        }
        this.semicolon = false;

        this.current.raw.after = (this.current.raw.after || '') + this.spaces;
        this.spaces = '';

        if ( this.current.parent ) {
            this.current.source.end = { line: token[2], column: token[3] };
            this.current = this.current.parent;
        } else {
            this.unexpectedClose(token);
        }
    }

    endFile() {
        if ( this.current.parent ) this.unclosedBlock();

        if ( this.current.nodes && this.current.nodes.length ) {
            this.current.raw.semicolon = this.semicolon;
        }
        this.current.raw.after = (this.current.raw.after || '') + this.spaces;

        while ( this.current.parent ) {
            this.current = this.current.parent;
            this.current.raw.after = '';
        }
    }

    // Helpers

    init(node, line, column) {
        this.current.push(node);

        node.source = { start: { line, column }, input: this.input };
        node.raw.before = this.spaces;
        this.spaces = '';
        if ( node.type !== 'comment' ) this.semicolon = false;
    }

    raw(node, prop, tokens) {
        let token;
        let value = '';
        let clean = true;
        for ( token of tokens ) {
            if ( token[0] === 'comment' ) {
                clean = false;
            } else {
                value += token[1];
            }
        }
        if ( !clean ) {
            let origin = '';
            for ( token of tokens ) origin += token[1];
            node.raw[prop] = { value, raw: origin };
        }
        node[prop] = value;
    }

    spacesFromEnd(tokens) {
        let next;
        let spaces = '';
        while ( tokens.length ) {
            next = tokens[tokens.length - 1][0];
            if ( next !== 'space' && next !== 'comment' ) break;
            spaces += tokens.pop()[1];
        }
        return spaces;
    }

    spacesFromStart(tokens) {
        let next;
        let spaces = '';
        while ( tokens.length ) {
            next = tokens[0][0];
            if ( next !== 'space' && next !== 'comment' ) break;
            spaces += tokens.shift()[1];
        }
        return spaces;
    }

    stringFrom(tokens, from) {
        let result = '';
        for ( let i = from; i < tokens.length; i++ ) {
            result += tokens[i][1];
        }
        tokens.splice(from, tokens.length - from);
        return result;
    }

    colon(tokens) {
        let brackets = 0;
        let token, type, prev;
        for ( let i = 0; i < tokens.length; i++ ) {
            token = tokens[i];
            type  = token[0];

            if ( type === '(' ) {
                brackets += 1;
            } else if ( type === ')' ) {
                brackets -= 0;
            } else if ( brackets === 0 && type === ':' ) {
                if ( !prev ) {
                    this.doubleColon(token);
                    continue;
                } else if ( prev[0] === 'word' && prev[1] === 'progid' ) {
                    continue;
                } else {
                    return i;
                }
            }

            prev = token;
        }
        return false;
    }

    // Errors

    unknownDecl(node, token) {
        throw this.input.error('Unknown word', token[2], token[3]);
    }

    unclosedBracket(bracket) {
        throw this.input.error('Unclosed bracket', bracket[2], bracket[3]);
    }

    unknownWord(start) {
        let token = this.tokens[start];
        throw this.input.error('Unknown word', token[2], token[3]);
    }

    unexpectedClose(token) {
        throw this.input.error('Unexpected }', token[2], token[3]);
    }

    unclosedBlock() {
        let pos = this.current.source.start;
        throw this.input.error('Unclosed block', pos.line, pos.column);
    }

    doubleColon(token) {
        throw this.input.error('Double colon', token[2], token[3]);
    }

    unnamedAtrule(node, token) {
        throw this.input.error('At-rule without name', token[2], token[3]);
    }

    precheckMissedSemicolon(tokens) {
        // Hook for Safe Parser
        tokens;
    }

    checkMissedSemicolon(tokens) {
        let colon = this.colon(tokens);
        if ( colon === false ) return;

        let founded = 0;
        let token;
        for ( let j = colon - 1; j >= 0; j-- ) {
            token = tokens[j];
            if ( token[0] !== 'space' ) {
                founded += 1;
                if ( founded === 2 ) break;
            }
        }
        throw this.input.error('Missed semicolon', token[2], token[3]);
    }

}
<|endoftext|>


<commit_before>var config           = require('mbc-common').config.Mosto.HeartBeats,
    util             = require('util'),
    events           = require('events'),
    Mosto            = require('./models/Mosto'),
    moment           = require('moment'),
    mvcp_server      = require('./drivers/mvcp/mvcp-driver'),
    utils            = require('./utils');

function heartbeats(customConfig) {
    //THIS MODULE ASSUMES MELTED ALWAYS HAS THE SAME CLIPS AS MELTED_MEDIAS
    //AND THAT THEY ARE IN THE SAME ORDER
    defaults = {
        gc_interval: 1000 * 60 * 60,
        sync_interval: 250,
        mvcp_server: "melted"
    };
    this.config = customConfig || config || defaults;

    this.melted_medias = Mosto.Playlists.get('melted_medias');

    // TODO: Listen to Mosto.Media#change:playing
    this.current_media = false;

    this.timers = {
        gc: undefined,
        sy: undefined
    };

    this.server = new mvcp_server(this.config.mvcp_server);
}

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

heartbeats.prototype.init = function() {
    this.startMvcpServer(this.initTimers);
};

heartbeats.prototype.initTimers = function() {
    var self = this;
    this.timers.gc = setInterval(function() {
        self.executeGc();
    }, self.config.gc_interval);
    this.timers.sy = setInterval(function() {
        self.syncMelted();
    }, self.config.sync_interval);
};

heartbeats.prototype.clear = function() {
    clearInterval(this.timers.gc);
    clearInterval(this.timers.sy);
};

heartbeats.prototype.executeGc = function() {
    var self = this;
    console.log("[HEARTBEAT-GC] Started Garbage Collector");
    var timeLimit = moment().subtract('hours', 1);
    var oldMedias = self.melted_medias.filter(function(media) {
        return moment(media.get('end')) < timeLimit;
    });
    if (oldMedias) {
        oldMedias.forEach(function(media) {
            self.melted_medias.remove(media);
        });
        self.melted_medias.save();
    }
    console.log("[HEARTBEAT-GC] Finished Garbage Collector: " + oldMedias.length + " removed.");
};

heartbeats.prototype.sendStatus = function() {
    var self = this;
    console.log("[HEARTBEAT-FS] Started Status");
    var media = getExpectedMedia();
    if (!self.current_media)
        self.current_media = media;
    if (media.get("id").toString() !== self.current_media.get("id").toString()) {
        self.emit("clipStatus", media);
        self.current_media = media;
    }
    self.emit("frameStatus", media.current_frame);
    console.log("[HEARTBEAT-FS] Finished Status");
};

heartbeats.prototype.getExpectedMedia = function() {
    var self = this;
    var now = moment();
    var expected = {};
    var media = self.melted_medias.find(function(media) {
        return moment(media.end) >= now;
    });
    if (media) {
        var elapsed = now - moment(media.start);
        var frame = elapsed / media.fps;
        expected.media = media;
        expected.frame = frame;
        return expected;
    } else {
        throw new Error("[HEARTBEAT-SY] Could not find expected clip!");
    }
};

heartbeats.prototype.syncMelted = function() {
    console.log("[HEARTBEAT-SY] Start Sync");
    var self = this;
    self.server.getServerStatus(function(meltedStatus) {
        if (meltedStatus.status !== "playing") {
            self.handleError(new Error("[HEARTBEAT-SY] Melted is not playing!"));
        } else {
            try {
                var expected = self.getExpectedMedia();
                var meltedClip = meltedStatus.clip;
                if (expected.media.get("id").toString() !== meltedClip.id.toString()) {
                    var index = self.melted_medias.indexOf(expected.media);
                    var frames = 9999;
                    var mediaAbove = self.melted_medias.at(index - 1);
                    if (mediaAbove.get("id").toString() === meltedClip.id.toString()) {
                        frames = meltedClip.length - meltedClip.currentFrame + expected.frame;
                    } else {
                        var mediaBelow = self.melted_medias.at(index + 1);
                        if (mediaBelow.get("id").toString() === meltedClip.id.toString()) {
                            var length = undefined;
                            if (!isNaN(parseFloat(expected.media.length)) && isFinite(expected.media.length)) {
                                //length is numeric (frames)
                                length = expected.media.length;
                            } else {
                                length = utils.convertTimeToFrames(expected.media.length, exptected.media.fps);
                            }
                            frames = meltedClip.currentFrame + (length - expected.frame);
                        }
                    }
                    if (frames > expected.media.fps)
                        self.fixMelted(expected);
                } else if (Math.abs(meltedClip.currentFrame - expected.frame) > expected.media.fps) {
                    self.fixMelted(expected);
                } else {
                    self.sendStatus();
                }
            } catch(err) {
                self.handleError(error);
            }
        }
    }, self.handleError);
    console.log("[HEARTBEAT-SY] Finish Sync");
};

heartbeats.prototype.fixMelted = function(expected) {
    console.error("[HEARTBEAT-SY] Melted is out of sync!");
    self.server.goto(expected.media.actual_order, expected.frame, self.sendStatus, self.handleError);
};

heartbeats.prototype.handleError =  function(error) {
    console.error(error);
    //FORCING LIST TO SYNC, SHOULD CHECK MELTED PLAYLIST, FIX IT AND START PLAYING
    self.melted_medias.save();
};

exports = module.exports = function(customConfig) {
    util.inherits(heartbeats, events.EventEmitter);
    var hb = new heartbeats(customConfig);
    return hb;
};<commit_msg>Moved to timeouts and semaphore<commit_after>var config           = require('mbc-common').config.Mosto.HeartBeats,
    util             = require('util'),
    events           = require('events'),
    Mosto            = require('./models/Mosto'),
    moment           = require('moment'),
    mvcp_server      = require('./drivers/mvcp/mvcp-driver'),
    utils            = require('./utils');

function heartbeats(customConfig) {
    //THIS MODULE ASSUMES MELTED ALWAYS HAS THE SAME CLIPS AS MELTED_MEDIAS
    //AND THAT THEY ARE IN THE SAME ORDER
    defaults = {
        gc_interval: 1000 * 60 * 60,
        sync_interval: 250,
        mvcp_server: "melted"
    };
    this.config = customConfig || config || defaults;

    this.melted_medias = Mosto.Playlists.get('melted_medias');

    // TODO: Listen to Mosto.Media#change:playing
    this.current_media = false;

    this.stop_timers = false;

    this.server = new mvcp_server(this.config.mvcp_server);
}

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

heartbeats.prototype.init = function() {
    this.startMvcpServer(this.initTimers);
};

heartbeats.prototype.initTimers = function() {
    var self = this;
    self.scheduleGc();
    self.scheduleSync();
};

heartbeats.prototype.scheduleGc = function() {
    var self = this;
    if (!self.stop_timers) {
        setTimeout(function() {
            self.executeGc();
        }, self.config.gc_interval);
    }
};

heartbeats.prototype.scheduleSync = function() {
    var self = this;
    if (!self.stop_timers) {
        setTimeout(function() {
            self.melted_medias.take(self.syncMelted.bind(self));
        }, self.config.sync_interval);
    }
};

heartbeats.prototype.stop = function() {
    this.stop_timers = true;
};

heartbeats.prototype.executeGc = function() {
    var self = this;
    console.log("[HEARTBEAT-GC] Started Garbage Collector");
    var timeLimit = moment().subtract('hours', 1);
    var oldMedias = self.melted_medias.filter(function(media) {
        return moment(media.get('end')) < timeLimit;
    });
    if (oldMedias) {
        oldMedias.forEach(function(media) {
            self.melted_medias.remove(media);
        });
        self.melted_medias.save();
    }
    self.scheduleGc();
    console.log("[HEARTBEAT-GC] Finished Garbage Collector: " + oldMedias.length + " removed.");
};

heartbeats.prototype.sendStatus = function() {
    var self = this;
    console.log("[HEARTBEAT-FS] Started Status");
    var media = getExpectedMedia();
    if (!self.current_media)
        self.current_media = media;
    if (media.get("id").toString() !== self.current_media.get("id").toString()) {
        self.emit("clipStatus", media);
        self.current_media = media;
    }
    self.emit("frameStatus", media.current_frame);
    console.log("[HEARTBEAT-FS] Finished Status");
};

heartbeats.prototype.getExpectedMedia = function() {
    var self = this;
    var now = moment();
    var expected = {};
    var media = self.melted_medias.find(function(media) {
        return moment(media.end) >= now;
    });
    if (media) {
        var elapsed = now - moment(media.start);
        var frame = elapsed / media.fps;
        expected.media = media;
        expected.frame = frame;
        return expected;
    } else {
        throw new Error("[HEARTBEAT-SY] Could not find expected clip!");
    }
};

heartbeats.prototype.syncMelted = function() {
    console.log("[HEARTBEAT-SY] Start Sync");
    var self = this;
    self.server.getServerStatus(function(meltedStatus) {
        if (meltedStatus.status !== "playing") {
            self.handleError(new Error("[HEARTBEAT-SY] Melted is not playing!"));
        } else {
            try {
                var expected = self.getExpectedMedia();
                var meltedClip = meltedStatus.clip;
                if (expected.media.get("id").toString() !== meltedClip.id.toString()) {
                    var index = self.melted_medias.indexOf(expected.media);
                    var frames = 9999;
                    var mediaAbove = self.melted_medias.at(index - 1);
                    if (mediaAbove.get("id").toString() === meltedClip.id.toString()) {
                        frames = meltedClip.length - meltedClip.currentFrame + expected.frame;
                    } else {
                        var mediaBelow = self.melted_medias.at(index + 1);
                        if (mediaBelow.get("id").toString() === meltedClip.id.toString()) {
                            var length = undefined;
                            if (!isNaN(parseFloat(expected.media.length)) && isFinite(expected.media.length)) {
                                //length is numeric (frames)
                                length = expected.media.length;
                            } else {
                                length = utils.convertTimeToFrames(expected.media.length, exptected.media.fps);
                            }
                            frames = meltedClip.currentFrame + (length - expected.frame);
                        }
                    }
                    if (frames > expected.media.fps)
                        self.fixMelted(expected);
                } else if (Math.abs(meltedClip.currentFrame - expected.frame) > expected.media.fps) {
                    self.fixMelted(expected);
                } else {
                    self.sendStatus();
                }
            } catch(err) {
                self.handleError(error);
            }
        }
    }, self.handleError);
    self.scheduleSync();
    self.melted_medias.leave();
    console.log("[HEARTBEAT-SY] Finish Sync");
};

heartbeats.prototype.fixMelted = function(expected) {
    console.error("[HEARTBEAT-SY] Melted is out of sync!");
    self.server.goto(expected.media.actual_order, expected.frame, self.sendStatus, self.handleError);
};

heartbeats.prototype.handleError =  function(error) {
    console.error(error);
    //FORCING LIST TO SYNC, SHOULD CHECK MELTED PLAYLIST, FIX IT AND START PLAYING
    self.melted_medias.save();
};

exports = module.exports = function(customConfig) {
    util.inherits(heartbeats, events.EventEmitter);
    var hb = new heartbeats(customConfig);
    return hb;
};<|endoftext|>


<commit_before>(function (env) {
    "use strict";

    env.ddg_spice_get_events = function(api_result){
        if (!api_result || api_result.error) {
            return Spice.failed('get_events');
        }

        DDG.require('maps', function(){
        DDG.require('moment.js', function(){
            Spice.add({
                id: "get_events",
                name: "Events",
                model: "Place",
                view: "Places",
                signal: "high",
                data: api_result.events,
                meta: {
                    sourceName: "GetEvents",
                    sourceUrl: 'http://getevents.co',
                    itemType: "Upcoming Events",
                    formattedSourceName: 'More at GetEvents',
                    snippetChars: 110
                },
                normalize: function(item){

                    // exclude events that have already ended
                    if ( moment(item.end_date).isBefore($.now()) ){
                        return null;
                    }
                    return {
                        data_front: {
                            showPin: true,
                            title: item.name,
                            venue: item.venue,
                            image: item.image_large_url,
                            altSubtitle: formatSubtitleString(item),

                            footer_content: Spice.get_events.foot_front,

                            footLines: '4',
                            titleClass: 'tile__title--3 tx--16 tx--bold mg--none',
                            altSubClass: 'tx--13 tx-clr--grey'
                        },
                        data_back: {
                            title: item.name,
                            url: buildUrl(item.id),
                            description: item.description ? DDG.strip_html(item.description) : 'No description available.',

                            footer_content: Spice.get_events.foot_back,

                            titleClass: 'tile__title--1 tx--16 tx--bold'
                        },
                        city: item.venue.city,
                        place: item.venue.name,
                        lat: item.venue.lat,
                        lon: item.venue.lng
                    };
                },
                templates: {
                    group: 'places',
                    item: 'basic_flipping_item',
                    variants: {
                        tileSnippet: 'large'
                    },
                    elClass: {
                        tileSnippet: 'tx-clr--slate-light tx--13'
                    }
                }
            });
        });
        });

    function buildUrl(id) {
        return 'https://getevents.co/events/preview/'+id;
    }

    function checkFullDay(s, e) {
        var dates = getStartEnd(s, e);
        if ((dates.plain_start.format('H:mm')=='0:00') && (dates.plain_end.format('H:mm')=='23:59')) {
            return true;
        }
        return false;
    }

    function getStartEnd(s, e) {
        var start = moment(s).utc(),
            dates = {
                start: start.format('MMM D'),
                end:   null,
                hours: null,
                plain_start: start
            };

        if (e.length && moment(e).isAfter(start) ){
            var end = moment(e).utc(),
                diff = start.diff(end, 'days');

            // Check if event ends same day
            // if so, display timespan
            if (diff > 0) {
                dates.end = end.format('MMM D');
            } else {
                dates.hours = {
                    start: start.format('ha'),
                    end: end.format('ha')
                };
            }
            dates.plain_end = end;
        }

        return dates;
    }

    function formatSubtitleString(obj) {
        var startEnd = getStartEnd(obj.start_date, obj.end_date),
            fullDay = checkFullDay(obj.start_date, obj.end_date),
            startStr = startEnd.start || '';

        if (fullDay) {
            return startStr + ', All Day';
        } else {
            if (startStr && startEnd.hours) {
                startStr += ', ' + startEnd.hours.start + '-' + startEnd.hours.end;
            }
            if (startStr && startEnd.end) {
                startStr += '-' + startEnd.end;
            }
        }

        return startStr;
    }

   };
}(this));
<commit_msg>migrate get_events to async require<commit_after>(function (env) {
    "use strict";

    env.ddg_spice_get_events = function(api_result){
        if (!api_result || api_result.error) {
            return Spice.failed('get_events');
        }

        DDG.require(['maps', 'moment.js'], function(){
            Spice.add({
                id: "get_events",
                name: "Events",
                model: "Place",
                view: "Places",
                signal: "high",
                data: api_result.events,
                meta: {
                    sourceName: "GetEvents",
                    sourceUrl: 'http://getevents.co',
                    itemType: "Upcoming Events",
                    formattedSourceName: 'More at GetEvents',
                    snippetChars: 110
                },
                normalize: function(item){

                    // exclude events that have already ended
                    if ( moment(item.end_date).isBefore($.now()) ){
                        return null;
                    }
                    return {
                        data_front: {
                            showPin: true,
                            title: item.name,
                            venue: item.venue,
                            image: item.image_large_url,
                            altSubtitle: formatSubtitleString(item),

                            footer_content: Spice.get_events.foot_front,

                            footLines: '4',
                            titleClass: 'tile__title--3 tx--16 tx--bold mg--none',
                            altSubClass: 'tx--13 tx-clr--grey'
                        },
                        data_back: {
                            title: item.name,
                            url: buildUrl(item.id),
                            description: item.description ? DDG.strip_html(item.description) : 'No description available.',

                            footer_content: Spice.get_events.foot_back,

                            titleClass: 'tile__title--1 tx--16 tx--bold'
                        },
                        city: item.venue.city,
                        place: item.venue.name,
                        lat: item.venue.lat,
                        lon: item.venue.lng
                    };
                },
                templates: {
                    group: 'places',
                    item: 'basic_flipping_item',
                    variants: {
                        tileSnippet: 'large'
                    },
                    elClass: {
                        tileSnippet: 'tx-clr--slate-light tx--13'
                    }
                }
            });
        });

    function buildUrl(id) {
        return 'https://getevents.co/events/preview/'+id;
    }

    function checkFullDay(s, e) {
        var dates = getStartEnd(s, e);
        if ((dates.plain_start.format('H:mm')=='0:00') && (dates.plain_end.format('H:mm')=='23:59')) {
            return true;
        }
        return false;
    }

    function getStartEnd(s, e) {
        var start = moment(s).utc(),
            dates = {
                start: start.format('MMM D'),
                end:   null,
                hours: null,
                plain_start: start
            };

        if (e.length && moment(e).isAfter(start) ){
            var end = moment(e).utc(),
                diff = start.diff(end, 'days');

            // Check if event ends same day
            // if so, display timespan
            if (diff > 0) {
                dates.end = end.format('MMM D');
            } else {
                dates.hours = {
                    start: start.format('ha'),
                    end: end.format('ha')
                };
            }
            dates.plain_end = end;
        }

        return dates;
    }

    function formatSubtitleString(obj) {
        var startEnd = getStartEnd(obj.start_date, obj.end_date),
            fullDay = checkFullDay(obj.start_date, obj.end_date),
            startStr = startEnd.start || '';

        if (fullDay) {
            return startStr + ', All Day';
        } else {
            if (startStr && startEnd.hours) {
                startStr += ', ' + startEnd.hours.start + '-' + startEnd.hours.end;
            }
            if (startStr && startEnd.end) {
                startStr += '-' + startEnd.end;
            }
        }

        return startStr;
    }

   };
}(this));
<|endoftext|>


<commit_before>'use strict'

var url = require('url')
var util = require('util')
var stringify = require('fast-safe-stringify')
var objectAssign = require('object-assign')
var cookie = require('cookie')
var stackman = require('stackman')()

exports._MAX_HTTP_BODY_CHARS = 2048 // expose for testing purposes

var mysqlErrorMsg = /(ER_[A-Z_]+): /

exports.parseMessage = function (message, payload) {
  if (typeof message === 'object' && message !== null) {
    // if `captureError` is parsed an object instead of a string we except
    // it to be in the format of `{ message: '...', params: [] }` and it will
    // be used as `param_message`.
    if (message.message) {
      payload.param_message = message.message
      message = util.format.apply(this, [message.message].concat(message.params))
    } else {
      message = util.inspect(message)
    }
  }

  payload.message = message
}

exports.parseError = function (err, payload, cb) {
  stackman(err, function (stack) {
    setMessage(payload, err)
    setException(payload, err)
    setStacktrace(payload, stack.frames)
    setCulprit(payload, stack.frames)
    setModule(payload)
    setExtraProperties(payload, stack.properties)
    cb(payload)
  })
}

exports.getHTTPContextFromRequest = function (req, opts) {
  if (!opts) opts = {}

  var protocol = req.socket.encrypted ? 'https' : 'http'
  var host = req.headers.host || '<no host>'
  var path = req.originalUrl || req.url
  var context = {
    method: req.method,
    url: protocol + '://' + host + path,
    query_string: url.parse(path).query,
    headers: objectAssign({}, req.headers),
    secure: !!req.socket.encrypted,
    remote_host: req.socket.remoteAddress
  }

  if (req.headers['user-agent']) context.user_agent = req.headers['user-agent']

  if (typeof context.headers.cookie === 'string') {
    context.cookies = cookie.parse(context.headers.cookie)
    delete context.headers.cookie
  }

  var contentLength = parseInt(req.headers['content-length'], 10)
  var transferEncoding = req.headers['transfer-encoding']
  var chunked = typeof transferEncoding === 'string' && transferEncoding.toLowerCase() === 'chunked'
  var body = req.json || req.body || req.payload
  var haveBody = body && (chunked || contentLength > 0)

  if (haveBody) {
    if (opts.body) {
      var bodyStr = typeof body === 'string' ? body : stringify(body)
      if (bodyStr.length > exports._MAX_HTTP_BODY_CHARS) {
        body = bodyStr.slice(0, exports._MAX_HTTP_BODY_CHARS)
      }
      context.data = body
    } else {
      context.data = '[REDACTED]'
    }
  }

  return context
}

exports.getUserContextFromRequest = function (req) {
  var user = req.user || (req.auth && req.auth.credentials) || req.session
  if (!user) return

  var context = {}

  if (typeof user.authenticated === 'boolean') {
    context.is_authenticated = user.authenticated
  }

  if (typeof user.id === 'string' || typeof user.id === 'number') {
    context.id = user.id
  } else if (typeof user._id === 'string' || typeof user._id === 'number') {
    context.id = user._id
  }

  if (typeof user.username === 'string') {
    context.username = user.username
  } else if (typeof user.name === 'string') {
    context.username = user.name
  }

  if (typeof user.email === 'string') {
    context.email = user.email
  }

  return context
}

exports.parseCallsite = function (callsite) {
  var filename = callsite.getFileName()
  var frame = {
    filename: callsite.getRelativeFileName() || '',
    lineno: callsite.getLineNumber(),
    function: callsite.getFunctionNameSanitized(),
    in_app: callsite.isApp()
  }
  if (!Number.isFinite(frame.lineno)) frame.lineno = 0 // this should be an int, but sometimes it's not?!
  if (filename) frame.abs_path = filename

  if ('context' in callsite) {
    frame.pre_context = callsite.context.pre
    frame.context_line = callsite.context.line
    frame.post_context = callsite.context.post
  }

  return frame
}

function setMessage (payload, err) {
  // If the message have already been set, for instance when calling
  // `captureError` with a string or an object-literal, just return without
  // overwriting it
  if (payload.message) return
  // TODO: Normally err.name is just "Error", maybe we should omit it in that case?
  payload.message = err.name + ': ' + (err.message || '<no message>')
}

function setException (payload, err) {
  var type = err.name

  // Force `err.message` to a string because we've seen a case where it for
  // some reason wasn't (even though `err` was a proper Error object). See
  // https://github.com/opbeat/opbeat-node/issues/130 for details.
  var msg = String(err.message)

  // To provide better grouping of mysql errors that happens after the async
  // boundery, we modify to exception type to include the custom mysql error
  // type (e.g. ER_PARSE_ERROR)
  var match = msg.match(mysqlErrorMsg)
  if (match) type += ': ' + match[1]

  payload.exception = {
    type: type,
    value: msg
  }
}

function setStacktrace (payload, frames) {
  if (!frames) return
  payload.stacktrace = {
    frames: frames.map(exports.parseCallsite)
  }
}

// Default `culprit` to the top of the stack or the highest `in_app` frame if such exists
function setCulprit (payload, frames) {
  if (payload.culprit) return // skip if user provided a custom culprit
  if (!frames || !frames.length) return
  var filename = frames[0].getRelativeFileName()
  var fnName = frames[0].getFunctionNameSanitized()
  for (var n = 0, l = frames.length; n < l; n++) {
    if (frames[n].in_app) {
      filename = frames[n].getRelativeFileName()
      fnName = frames[n].getFunctionNameSanitized()
      break
    }
  }
  payload.culprit = filename ? fnName + ' (' + filename + ')' : fnName
}

function setModule (payload) {
  if (!payload.stacktrace) return
  var frame = payload.stacktrace.frames[0]
  if (!frame || frame.in_app) return
  var match = frame.filename.match(/node_modules\/([^/]*)/)
  if (!match) return
  payload.exception.module = match[1]
}

function setExtraProperties (payload, properties) {
  var keys = Object.keys(properties || {})
  if (!keys.length) return
  payload.extra = payload.extra || {}

  // handle if user gives us stuff like { extra: 404 }
  if (typeof payload.extra !== 'object') payload.extra = { value: String(payload.extra) }

  keys.forEach(function (key) {
    if (key in payload.extra) return
    payload.extra[key] = properties[key]
  })
}
<commit_msg>chore: code cleanup<commit_after>'use strict'

var url = require('url')
var util = require('util')
var stringify = require('fast-safe-stringify')
var objectAssign = require('object-assign')
var cookie = require('cookie')
var stackman = require('stackman')()

exports._MAX_HTTP_BODY_CHARS = 2048 // expose for testing purposes

var mysqlErrorMsg = /(ER_[A-Z_]+): /

exports.parseMessage = function (message, payload) {
  if (typeof message === 'object' && message !== null) {
    // if `captureError` is parsed an object instead of a string we except
    // it to be in the format of `{ message: '...', params: [] }` and it will
    // be used as `param_message`.
    if (message.message) {
      payload.param_message = message.message
      message = util.format.apply(this, [message.message].concat(message.params))
    } else {
      message = util.inspect(message)
    }
  }

  payload.message = message
}

exports.parseError = function (err, payload, cb) {
  stackman(err, function (stack) {
    setMessage(payload, err)
    setException(payload, err)
    setStacktrace(payload, stack.frames)
    setCulprit(payload, stack.frames)
    setModule(payload)
    setExtraProperties(payload, stack.properties)
    cb(payload)
  })
}

exports.getHTTPContextFromRequest = function (req, opts) {
  if (!opts) opts = {}

  var protocol = req.socket.encrypted ? 'https' : 'http'
  var host = req.headers.host || '<no host>'
  var path = req.originalUrl || req.url
  var context = {
    method: req.method,
    url: protocol + '://' + host + path,
    query_string: url.parse(path).query,
    headers: objectAssign({}, req.headers),
    secure: !!req.socket.encrypted,
    remote_host: req.socket.remoteAddress
  }

  if (req.headers['user-agent']) context.user_agent = req.headers['user-agent']

  if (typeof context.headers.cookie === 'string') {
    context.cookies = cookie.parse(context.headers.cookie)
    delete context.headers.cookie
  }

  var contentLength = parseInt(req.headers['content-length'], 10)
  var transferEncoding = req.headers['transfer-encoding']
  var chunked = typeof transferEncoding === 'string' && transferEncoding.toLowerCase() === 'chunked'
  var body = req.json || req.body || req.payload
  var haveBody = body && (chunked || contentLength > 0)

  if (haveBody) {
    if (opts.body) {
      var bodyStr = typeof body === 'string' ? body : stringify(body)
      if (bodyStr.length > exports._MAX_HTTP_BODY_CHARS) {
        body = bodyStr.slice(0, exports._MAX_HTTP_BODY_CHARS)
      }
      context.data = body
    } else {
      context.data = '[REDACTED]'
    }
  }

  return context
}

exports.getUserContextFromRequest = function (req) {
  var user = req.user || (req.auth && req.auth.credentials) || req.session
  if (!user) return

  var context = {}

  if (typeof user.authenticated === 'boolean') {
    context.is_authenticated = user.authenticated
  }

  if (typeof user.id === 'string' || typeof user.id === 'number') {
    context.id = user.id
  } else if (typeof user._id === 'string' || typeof user._id === 'number') {
    context.id = user._id
  }

  if (typeof user.username === 'string') {
    context.username = user.username
  } else if (typeof user.name === 'string') {
    context.username = user.name
  }

  if (typeof user.email === 'string') {
    context.email = user.email
  }

  return context
}

exports.parseCallsite = function (callsite) {
  var filename = callsite.getFileName()
  var frame = {
    filename: callsite.getRelativeFileName() || '',
    lineno: callsite.getLineNumber(),
    function: callsite.getFunctionNameSanitized(),
    in_app: callsite.isApp()
  }
  if (!Number.isFinite(frame.lineno)) frame.lineno = 0 // this should be an int, but sometimes it's not?!
  if (filename) frame.abs_path = filename

  if ('context' in callsite) {
    frame.pre_context = callsite.context.pre
    frame.context_line = callsite.context.line
    frame.post_context = callsite.context.post
  }

  return frame
}

function setMessage (payload, err) {
  // If the message have already been set, for instance when calling
  // `captureError` with a string or an object-literal, just return without
  // overwriting it
  if (payload.message) return
  // TODO: Normally err.name is just "Error", maybe we should omit it in that case?
  payload.message = err.name + ': ' + (err.message || '<no message>')
}

function setException (payload, err) {
  var type = err.name

  // Force `err.message` to a string because we've seen a case where it for
  // some reason wasn't (even though `err` was a proper Error object). See
  // https://github.com/opbeat/opbeat-node/issues/130 for details.
  var msg = String(err.message)

  // To provide better grouping of mysql errors that happens after the async
  // boundery, we modify to exception type to include the custom mysql error
  // type (e.g. ER_PARSE_ERROR)
  var match = msg.match(mysqlErrorMsg)
  if (match) type += ': ' + match[1]

  payload.exception = {
    type: type,
    value: msg
  }
}

function setStacktrace (payload, frames) {
  if (!frames) return
  payload.stacktrace = {
    frames: frames.map(exports.parseCallsite)
  }
}

// Default `culprit` to the top of the stack or the highest `in_app` frame if
// such exists
function setCulprit (payload, frames) {
  if (payload.culprit) return // skip if user provided a custom culprit
  if (!frames || !frames.length) return
  var filename = frames[0].getRelativeFileName()
  var fnName = frames[0].getFunctionNameSanitized()
  for (var n = 0, l = frames.length; n < l; n++) {
    if (frames[n].in_app) {
      filename = frames[n].getRelativeFileName()
      fnName = frames[n].getFunctionNameSanitized()
      break
    }
  }
  payload.culprit = filename ? fnName + ' (' + filename + ')' : fnName
}

function setModule (payload) {
  if (!payload.stacktrace) return
  var frame = payload.stacktrace.frames[0]
  if (!frame || frame.in_app) return
  var match = frame.filename.match(/node_modules\/([^/]*)/)
  if (!match) return
  payload.exception.module = match[1]
}

function setExtraProperties (payload, properties) {
  var keys = Object.keys(properties || {})
  if (!keys.length) return
  payload.extra = payload.extra || {}

  // handle if user gives us stuff like { extra: 404 }
  if (typeof payload.extra !== 'object') payload.extra = { value: String(payload.extra) }

  keys.forEach(function (key) {
    if (key in payload.extra) return
    payload.extra[key] = properties[key]
  })
}
<|endoftext|>


<commit_before>'use strict';

var angular = require('angular');
var calendarUtils = require('calendar-utils');

angular
  .module('mwl.calendar')
  .controller('MwlCalendarHourListCtrl', function($scope, $attrs, moment, calendarHelper) {
    var vm = this;

    function updateDays() {

      vm.dayViewSplit = parseInt(vm.dayViewSplit);
      var dayStart = (vm.dayViewStart || '00:00').split(':');
      var dayEnd = (vm.dayViewEnd || '23:59').split(':');
      vm.hourGrid = calendarUtils.getDayViewHourGrid({
        viewDate: $attrs.dayWidth ? moment(vm.viewDate).startOf('week').toDate() : moment(vm.viewDate).toDate(),
        hourSegments: 60 / vm.dayViewSplit,
        dayStart: {
          hour: dayStart[0],
          minute: dayStart[1]
        },
        dayEnd: {
          hour: dayEnd[0],
          minute: dayEnd[1]
        }
      });

    }

    var originalLocale = moment.locale();

    $scope.$on('calendar.refreshView', function() {

      if (originalLocale !== moment.locale()) {
        originalLocale = moment.locale();
        updateDays();
      }

    });

    $scope.$watchGroup([
      'vm.dayViewStart',
      'vm.dayViewEnd',
      'vm.dayViewSplit',
      'vm.viewDate'
    ], function() {
      updateDays();
    });

    vm.eventDropped = function(event, date) {
      var newStart = moment(date);
      var newEnd = calendarHelper.adjustEndDateFromStartDiff(event.startsAt, newStart, event.endsAt);

      vm.onEventTimesChanged({
        calendarEvent: event,
        calendarDate: date,
        calendarNewEventStart: newStart.toDate(),
        calendarNewEventEnd: newEnd ? newEnd.toDate() : null
      });
    };

    vm.getClickedDate = function(baseDate, minutes, days) {
      return moment(baseDate).clone().add(minutes, 'minutes').add(days || 0, 'days').toDate();
    };

    vm.onDragSelectStart = function(date, dayIndex) {
      if (!vm.dateRangeSelect) {
        vm.dateRangeSelect = {
          active: true,
          startDate: date,
          endDate: date,
          dayIndex: dayIndex
        };
      }
    };

    vm.onDragSelectMove = function(date) {
      if (vm.dateRangeSelect) {
        vm.dateRangeSelect.endDate = date;
      }
    };

    vm.onDragSelectEnd = function(date) {
      vm.dateRangeSelect.endDate = date;
      if (vm.dateRangeSelect.endDate > vm.dateRangeSelect.startDate) {
        vm.onDateRangeSelect({calendarRangeStartDate: vm.dateRangeSelect.startDate, calendarRangeEndDate: vm.dateRangeSelect.endDate});
      }
      delete vm.dateRangeSelect;
    };

  })
  .directive('mwlCalendarHourList', function() {

    return {
      restrict: 'E',
      template: '

',
      controller: 'MwlCalendarHourListCtrl as vm',
      scope: {
        viewDate: '=',
        dayViewStart: '=',
        dayViewEnd: '=',
        dayViewSplit: '=',
        dayWidth: '=?',
        onTimespanClick: '=',
        onDateRangeSelect: '=',
        onEventTimesChanged: '=',
        customTemplateUrls: '=?'
      },
      bindToController: true
    };

  });
<commit_msg>fix(onDateRangeSelect): fix behaviour after bug was introduced in previous commit<commit_after>'use strict';

var angular = require('angular');
var calendarUtils = require('calendar-utils');

angular
  .module('mwl.calendar')
  .controller('MwlCalendarHourListCtrl', function($scope, $attrs, moment, calendarHelper) {
    var vm = this;

    function updateDays() {

      vm.dayViewSplit = parseInt(vm.dayViewSplit);
      var dayStart = (vm.dayViewStart || '00:00').split(':');
      var dayEnd = (vm.dayViewEnd || '23:59').split(':');
      vm.hourGrid = calendarUtils.getDayViewHourGrid({
        viewDate: $attrs.dayWidth ? moment(vm.viewDate).startOf('week').toDate() : moment(vm.viewDate).toDate(),
        hourSegments: 60 / vm.dayViewSplit,
        dayStart: {
          hour: dayStart[0],
          minute: dayStart[1]
        },
        dayEnd: {
          hour: dayEnd[0],
          minute: dayEnd[1]
        }
      });

    }

    var originalLocale = moment.locale();

    $scope.$on('calendar.refreshView', function() {

      if (originalLocale !== moment.locale()) {
        originalLocale = moment.locale();
        updateDays();
      }

    });

    $scope.$watchGroup([
      'vm.dayViewStart',
      'vm.dayViewEnd',
      'vm.dayViewSplit',
      'vm.viewDate'
    ], function() {
      updateDays();
    });

    vm.eventDropped = function(event, date) {
      var newStart = moment(date);
      var newEnd = calendarHelper.adjustEndDateFromStartDiff(event.startsAt, newStart, event.endsAt);

      vm.onEventTimesChanged({
        calendarEvent: event,
        calendarDate: date,
        calendarNewEventStart: newStart.toDate(),
        calendarNewEventEnd: newEnd ? newEnd.toDate() : null
      });
    };

    vm.getClickedDate = function(baseDate, minutes, days) {
      return moment(baseDate).clone().startOf('hour').add(minutes, 'minutes').add(days || 0, 'days').toDate();
    };

    vm.onDragSelectStart = function(date, dayIndex) {
      if (!vm.dateRangeSelect) {
        vm.dateRangeSelect = {
          active: true,
          startDate: date,
          endDate: date,
          dayIndex: dayIndex
        };
      }
    };

    vm.onDragSelectMove = function(date) {
      if (vm.dateRangeSelect) {
        vm.dateRangeSelect.endDate = date;
      }
    };

    vm.onDragSelectEnd = function(date) {
      vm.dateRangeSelect.endDate = date;
      if (vm.dateRangeSelect.endDate > vm.dateRangeSelect.startDate) {
        vm.onDateRangeSelect({calendarRangeStartDate: vm.dateRangeSelect.startDate, calendarRangeEndDate: vm.dateRangeSelect.endDate});
      }
      delete vm.dateRangeSelect;
    };

  })
  .directive('mwlCalendarHourList', function() {

    return {
      restrict: 'E',
      template: '

',
      controller: 'MwlCalendarHourListCtrl as vm',
      scope: {
        viewDate: '=',
        dayViewStart: '=',
        dayViewEnd: '=',
        dayViewSplit: '=',
        dayWidth: '=?',
        onTimespanClick: '=',
        onDateRangeSelect: '=',
        onEventTimesChanged: '=',
        customTemplateUrls: '=?'
      },
      bindToController: true
    };

  });
<|endoftext|>


<commit_before>/**
 * @author xiaojue
 * @email designsor@gmail.com
 * @fileoverview 对应bin文件的command指令实现
 */
var forever = require('forever');
var os = require('os');
var exec = require('child_process').exec;
var spawn = require('child_process').spawn;
var sys = os.platform();
var logger = require('./log/logger');
var http = require("http");
var Path = require("path");
var config = require('./sysconfig');
var fs = require('fs');
var isWin = (/^win/).test(sys) ? true: false;
var logFile = Path.resolve(__dirname, '../fdserver.log');
var fdurl = 'http://local.fd.server/';

if (!fs.existsSync(logFile)) {
	fs.writeFileSync(logFile, '');
}

var foreverOptions = {
	uid: 'fdserver',
	command: 'node',
	logFile: logFile
};

function openUrl(url) {
	switch (sys) {
		case "darwin":
			exec('open ' + url);
			break;
		case "win32":
			exec('start ' + url);
			break;
		default:
			spawn('xdg-open', [url]);
			break;
	}
}

var masterScript = Path.join(__dirname, 'master.js');

var fdserver = {
	commanders: {
		start: {
			description: 'start the fd-server service',
			exec: function() {
				forever.start(masterScript, foreverOptions).on('start', function() {
					logger.warn('fds start successed , you can use startDeamon to start with daemon');
					openUrl(fdurl);
				});
			}
		},
		startDaemon: {
			description: 'start with daemon',
			exec: function() {
				if (isWin) {
					logger.error('windows system not supported start daemon');
				} else {
					function _start() {
						var child = forever.startDaemon(masterScript, foreverOptions);
						logger.info('log file '+foreverOptions.logFile);
						logger.info('open http://local.fd.server/ to check the server!');
					}
					forever.stop(foreverOptions.uid).on('stop', _start).on('error', _start);
				}
			}
		},
		stop: {
			description: 'stop the fd-server service',
			exec: function() {
				if (isWin) {
					logger.error('windows system not supported stop');
				} else {
					forever.stop(foreverOptions.uid).on('stop', function() {
						logger.info('fds stop successed');
					}).on('error', function(err) {
						logger.error(err.message);
					});
				}
			}
		},
		restart: {
			description: 'restart the fd-server service',
			exec: function() {
				if (isWin) {
					logger.info('windows system not supported restart');
				} else {
					forever.restart('fdserver').on('restart', function() {
						logger.info('fds restart successed');
					}).on('error', function(err) {
						logger.error(err.message);
					});
				}
			}
		}
	}
};

module.exports = fdserver;

<commit_msg>增加延迟处理，等待服务启动<commit_after>/**
 * @author xiaojue
 * @email designsor@gmail.com
 * @fileoverview 对应bin文件的command指令实现
 */
var forever = require('forever');
var os = require('os');
var exec = require('child_process').exec;
var spawn = require('child_process').spawn;
var sys = os.platform();
var logger = require('./log/logger');
var http = require("http");
var Path = require("path");
var config = require('./sysconfig');
var fs = require('fs');
var isWin = (/^win/).test(sys) ? true: false;
var logFile = Path.resolve(__dirname, '../fdserver.log');
var fdurl = 'http://local.fd.server/';

if (!fs.existsSync(logFile)) {
	fs.writeFileSync(logFile, '');
}

var foreverOptions = {
	uid: 'fdserver',
	command: 'node',
	logFile: logFile
};

function openUrl(url) {
	setTimeout(function() {
		switch (sys) {
		case "darwin":
			exec('open ' + url);
			break;
		case "win32":
			exec('start ' + url);
			break;
		default:
			spawn('xdg-open', [url]);
			break;
		}
	},
	1000);
}

var masterScript = Path.join(__dirname, 'master.js');

var fdserver = {
	commanders: {
		start: {
			description: 'start the fd-server service',
			exec: function() {
				forever.start(masterScript, foreverOptions).on('start', function() {
					logger.warn('fds start successed , you can use startDeamon to start with daemon');
					openUrl(fdurl);
				});
			}
		},
		startDaemon: {
			description: 'start with daemon',
			exec: function() {
				if (isWin) {
					logger.error('windows system not supported start daemon');
				} else {
					function _start() {
						var child = forever.startDaemon(masterScript, foreverOptions);
						logger.info('log file ' + foreverOptions.logFile);
						logger.info('open http://local.fd.server/ to check the server!');
						openUrl(fdurl);
					}
					forever.stop(foreverOptions.uid).on('stop', _start).on('error', _start);
				}
			}
		},
		stop: {
			description: 'stop the fd-server service',
			exec: function() {
				if (isWin) {
					logger.error('windows system not supported stop');
				} else {
					forever.stop(foreverOptions.uid).on('stop', function() {
						logger.info('fds stop successed');
					}).on('error', function(err) {
						logger.error(err.message);
					});
				}
			}
		},
		restart: {
			description: 'restart the fd-server service',
			exec: function() {
				if (isWin) {
					logger.info('windows system not supported restart');
				} else {
					forever.restart('fdserver').on('restart', function() {
						logger.info('fds restart successed');
					}).on('error', function(err) {
						logger.error(err.message);
					});
				}
			}
		}
	}
};

module.exports = fdserver;

<|endoftext|>


<commit_before>'use strict';

var extglob = require('extglob');
var nanomatch = require('nanomatch');
var regexNot = require('regex-not');
var toRegex = require('to-regex');

/**
 * Characters to use in negation regex (we want to "not" match
 * characters that are matched by other parsers)
 */

var cached;
var TEXT = '([!@*?+]?\\(|\\)|\\[:?(?=.*?:?\\])|:?\\]|[*+?!^$.\\\\/])+';
var not = textRegex(TEXT);

module.exports = function(micromatch) {
  var parsers = micromatch.parser.parsers;

  micromatch.use(nanomatch.parsers);
  var escape = parsers.escape;
  var slash = parsers.slash;
  var qmark = parsers.qmark;
  var plus = parsers.plus;
  var star = parsers.star;
  var dot = parsers.dot;

  micromatch.use(extglob.parsers);
  micromatch.parser
    .use(function() {
      // override "notRegex" used in nanomatch
      this.notRegex = /^\!+(?!\()/;
    })
    .capture('escape', escape)
    .capture('slash', slash)
    .capture('qmark', qmark)
    .capture('star', star)
    .capture('plus', plus)
    .capture('dot', dot)

   /**
     * Override `text` parser
     */

    .capture('text', function() {
      if (this.isInside('bracket')) return;
      var pos = this.position();
      var m = this.match(not);
      if (!m || !m[0]) return;

      // escape brackets and regex boundary characters
      var val = m[0].replace(/([[\]^$])/g, '\\$1');
      return pos({
        type: 'text',
        val: val
      });
    });
};

/**
 * Create text regex
 */

function textRegex(pattern) {
  if (cached) return cached;
  var opts = {contains: true, strictClose: false};
  var not = regexNot.create(pattern, opts);
  var re = toRegex('(?:[\\^]|\\\\|' + not + ')', {strictClose: false});
  return (cached = re);
}
<commit_msg>minor code organization<commit_after>'use strict';

var define = require('define-property');
var extglob = require('extglob');
var nanomatch = require('nanomatch');
var regexNot = require('regex-not');
var toRegex = require('to-regex');
var cached;
var not;

/**
 * Characters to use in negation regex (we want to "not" match
 * characters that are matched by other parsers)
 */

var TEXT = '([!@*?+]?\\(|\\)|\\[:?(?=.*?:?\\])|:?\\]|[*+?!^$.\\\\/])+';
var createNotRegex = function(opts) {
  return not || (not = textRegex(TEXT));
};

/**
 * Parsers
 */

module.exports = function(snapdragon) {
  var parsers = snapdragon.parser.parsers;

  // register nanomatch parsers
  snapdragon.use(nanomatch.parsers);

  // get references to some specific nanomatch parsers before they
  // are overridden by the extglob and/or parsers
  var escape = parsers.escape;
  var slash = parsers.slash;
  var qmark = parsers.qmark;
  var plus = parsers.plus;
  var star = parsers.star;
  var dot = parsers.dot;

  // register extglob parsers
  snapdragon.use(extglob.parsers);

  // custom micromatch parsers
  snapdragon.parser
    .use(function() {
      // override "notRegex" created in nanomatch parser
      this.notRegex = /^\!+(?!\()/;
    })
    // reset the referenced parsers
    .capture('escape', escape)
    .capture('slash', slash)
    .capture('qmark', qmark)
    .capture('star', star)
    .capture('plus', plus)
    .capture('dot', dot)

    /**
     * Override `text` parser
     */

    .capture('text', function() {
      if (this.isInside('bracket')) return;
      var pos = this.position();
      var m = this.match(createNotRegex(this.options));
      if (!m || !m[0]) return;

      // escape regex boundary characters and simple brackets
      var val = m[0].replace(/([[\]^$])/g, '\\$1');

      return pos({
        type: 'text',
        val: val
      });
    });
};

/**
 * Create text regex
 */

function textRegex(pattern) {
  if (cached) return cached;
  var notStr = regexNot.create(pattern, {contains: true, strictClose: false});
  var prefix = '(?:[\\^]|\\\\|';
  var re = toRegex(prefix + notStr + ')', {strictClose: false});
  return (cached = re);
}
<|endoftext|>


<commit_before>/*!
 * Copyright 2014 Apereo Foundation (AF) Licensed under the
 * Educational Community License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may
 * obtain a copy of the License at
 *
 *     http://opensource.org/licenses/ECL-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an "AS IS"
 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

require(['jquery','oae.core'], function($, oae) {

    // Set the page title
    oae.api.util.setBrowserTitle('__MSG__ACCESS_DENIED__');

    if (oae.data.me.anon) {

        // When only a single external institutional authentication strategy (`cas`, `googleApps`, `shibboleth`)
        // is enabled, the Sign In button should send the user directly to the sign in page. Alternatively, the sign
        // in dropdown in the top navigation should be enabled when clicking the Sign In button
        var enabledStrategies = oae.api.authentication.getEnabledStrategies();
        var singleInstitutionalAuth = null;
        if (_.keys(enabledStrategies).length === 1 && _.contains(['cas', 'googleApps', 'shibboleth'], _.keys(enabledStrategies)[0])) {
            singleInstitutionalAuth = _.values(enabledStrategies)[0];
        }

        // After signing in, the user should be redirected to the redirect target encoded in the URL
        var redirectUrl = $.url().param('url') || '/me';

        oae.api.util.template().render($('#error-signin-template'), {
            'singleInstitutionalAuth': singleInstitutionalAuth,
            'redirectUrl': redirectUrl
        }, $('#error-signin-container'));

        // Trigger the sign in dropdown when the user clicks the sign in dropdown trigger
        $(document).on('click', '#error-signin-dropdown', function(ev) {
            $('#topnavigation-signin').click();
            return false;
        });
    }

});
<commit_msg>Self review on authentication redirect<commit_after>/*!
 * Copyright 2014 Apereo Foundation (AF) Licensed under the
 * Educational Community License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may
 * obtain a copy of the License at
 *
 *     http://opensource.org/licenses/ECL-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an "AS IS"
 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

require(['jquery','oae.core'], function($, oae) {

    // Set the page title
    oae.api.util.setBrowserTitle('__MSG__ACCESS_DENIED__');

    if (oae.data.me.anon) {

        // When only a single external institutional authentication strategy (`cas`, `googleApps`, `shibboleth`)
        // is enabled, the Sign In button should send the user directly to the sign in page. Alternatively, the sign
        // in dropdown in the top navigation should be opened when clicking the Sign In button
        var enabledStrategies = oae.api.authentication.getEnabledStrategies();
        var singleInstitutionalAuth = null;
        if (_.keys(enabledStrategies).length === 1 && _.contains(['cas', 'googleApps', 'shibboleth'], _.keys(enabledStrategies)[0])) {
            singleInstitutionalAuth = _.values(enabledStrategies)[0];
        }

        // After signing in, the user should be redirected to the redirect target encoded in the URL
        var redirectUrl = $.url().param('url') || '/me';

        oae.api.util.template().render($('#error-signin-template'), {
            'singleInstitutionalAuth': singleInstitutionalAuth,
            'redirectUrl': redirectUrl
        }, $('#error-signin-container'));

        // Trigger the sign in dropdown when the user clicks the sign in dropdown trigger
        $(document).on('click', '#error-signin-dropdown', function(ev) {
            $('#topnavigation-signin').click();
            return false;
        });
    }

});
<|endoftext|>


<commit_before>exports.broadcastList = function(io, applicationName) {
    var listData = {
        serverStartTime: easyrtc.serverStartTime,
        connections: {}
    };

    //
    // needed by the rooms demo. Don't remove.
    //
    if( applicationName === 'default' ) return;

    // Form list of current connections
    for (var key in easyrtc.connections) {
        if (easyrtc.connections.hasOwnProperty(key) && easyrtc.connections[key].applicationName == applicationName) {
            listData.connections[key] = easyrtc.connections[key];
        }
    };

    // Broadcast list of current connections
    for (var key in listData.connections) {
        io.sockets.socket(key).json.emit('easyrtcCmd',{
            msgType:easyrtcCfg.cmdMsgType.list,
            msgData:listData,
            serverTime: Date.now()
        });
    };
};

exports.onSocketConnection = function(io, socket, connectionEasyRtcId) {
    var winston = require('winston');
    var logServer = winston.loggers.get('easyrtcServer');

    easyrtc.connections[connectionEasyRtcId]={
        easyrtcid: connectionEasyRtcId,
        applicationName: easyrtcCfg.defaultApplicationName,
        clientConnectTime: Date.now()
    };

    // Immediatly send the easyrtcid and application
    logServer.debug('easyRTC: Socket [' + socket.id + '] command sent', {
        label: 'easyrtc',
        easyrtcid:connectionEasyRtcId,
        data: {
            applicationName: easyrtc.connections[connectionEasyRtcId].applicationName,
            iceConfig: {"iceServers": easyrtcCfg.iceServers}
    }});
    socket.json.emit( easyrtcCfg.cmdPacketType, {
        msgType:easyrtcCfg.cmdMsgType.token,
        easyrtcid: connectionEasyRtcId,
        applicationName: easyrtc.connections[connectionEasyRtcId].applicationName,
        iceConfig: {"iceServers": easyrtcCfg.iceServers},
        serverTime: Date.now()
    });

    // Send the connection list to current connection, then broadcast to all others
    exports.broadcastList(io,easyrtc.connections[connectionEasyRtcId].applicationName);
}

exports.onSocketMessage = function(io, socket, connectionEasyRtcId, msg) {
    // Messages must have a targetId and a msgType. This section should be hardened.
    if (msg.targetId) {
        var winston = require('winston');
        var logServer = winston.loggers.get('easyrtcServer');
        logServer.debug('easyRTC: Socket [' + msg.targetId+ '] sending message from [' + connectionEasyRtcId + ']', {
            label: 'easyrtc',
            easyrtcid:msg.targetId,
            data:msg
        });
        io.sockets.socket(msg.targetId).json.send({
            msgType:msg.msgType,
            senderId:connectionEasyRtcId,
            msgData:msg.msgData,
            serverTime: Date.now()
        });
    }
}

exports.onSocketDisconnect = function(io, socket, connectionEasyRtcId) {
    // console.log('easyRTC: Socket disconnected: ' + connectionEasyRtcId);
    var previousApplicationName = easyrtc.connections[connectionEasyRtcId].applicationName;

    // Remove connection from the map
    delete easyrtc.connections[connectionEasyRtcId];
    // Broadcast new list to all others
    exports.broadcastList(io, previousApplicationName);
}

exports.onEasyRtcCmd = function(io, socket, connectionEasyRtcId, msg) {
    var winston = require('winston');
    var logServer = winston.loggers.get('easyrtcServer');

    // Messages with a targetId get forwarded on. This section should be hardened.
    if (msg.targetId) {
        logServer.debug('easyRTC: Socket [' + msg.targetId + '] command sent from [' + socket.id + ']', {
            label: 'easyrtc',
            easyrtcid:msg.targetId,
            data: {
                msgType:msg.msgType,
                senderId:connectionEasyRtcId,
                msgData:msg.msgData,
        }});
        io.sockets.socket(msg.targetId).json.emit('easyrtcCmd',{
            msgType:msg.msgType,
            senderId:connectionEasyRtcId,
            msgData:msg.msgData,
            serverTime: Date.now()
        });
    }
    // easyrtc server-side user configuration options are set here.
    if (msg.msgType == "setUserCfg") {
        setUserCfg(connectionEasyRtcId, msg.msgData);

        if (msg.msgData.applicationName) {  // Set the application namespace
            easyrtc.connections[connectionEasyRtcId].applicationName = msg.msgData.applicationName;
            // console.log('easyRTC: Application Name Change: ' + connectionEasyRtcId + ' : ' + msg.msgData.applicationName);
            exports.broadcastList(io,easyrtc.connections[connectionEasyRtcId].applicationName);
        }

        logServer.debug('easyRTC: Socket [' + connectionEasyRtcId + '] updated user config info', {
            label: 'easyrtc',
            easyrtcid:connectionEasyRtcId,
            data: {
                msgType:'updatedUserCfg',
                msgData:easyrtc.connections[connectionEasyRtcId]
        }});

    }
}

function setUserCfg(easyrtcid, userCfg) {
        // TODO: Harden section with better variable checking
        if (userCfg.screenWidth) {      easyrtc.connections[easyrtcid].screenWidth =      userCfg.screenWidth;}
        if (userCfg.screenHeight) {     easyrtc.connections[easyrtcid].screenHeight =     userCfg.screenHeight;}
        if (userCfg.browserUserAgent) { easyrtc.connections[easyrtcid].browserUserAgent = userCfg.browserUserAgent;}
        if (userCfg.sharingAudio) {     easyrtc.connections[easyrtcid].sharingAudio =     userCfg.sharingAudio;}
        if (userCfg.sharingVideo) {     easyrtc.connections[easyrtcid].sharingVideo =     userCfg.sharingVideo;}
        if (userCfg.sharingData) {      easyrtc.connections[easyrtcid].sharingData =      userCfg.sharingData;}
        if (userCfg.windowWidth) {      easyrtc.connections[easyrtcid].windowWidth =      userCfg.windowWidth;}
        if (userCfg.windowHeight) {     easyrtc.connections[easyrtcid].windowHeight =     userCfg.windowHeight;}
        if (userCfg.cookieEnabled) {    easyrtc.connections[easyrtcid].cookieEnabled =    userCfg.cookieEnabled;}
        if (userCfg.language) {         easyrtc.connections[easyrtcid].language =         userCfg.language;}
        if (userCfg.nativeVideoWidth) { easyrtc.connections[easyrtcid].nativeVideoWidth = userCfg.nativeVideoWidth;}
        if (userCfg.nativeVideoHeight){ easyrtc.connections[easyrtcid].nativeVideoHeight= userCfg.nativeVideoHeight;}
        if (userCfg.connectionList) {
            easyrtc.connections[easyrtcid].connectionList = JSON.parse(JSON.stringify(userCfg.connectionList));
        }
}
<commit_msg>Added userName preserving to setUserCfg<commit_after>exports.broadcastList = function(io, applicationName) {
    var listData = {
        serverStartTime: easyrtc.serverStartTime,
        connections: {}
    };

    //
    // needed by the rooms demo. Don't remove.
    //
    if( applicationName === 'default' ) return;

    // Form list of current connections
    for (var key in easyrtc.connections) {
        if (easyrtc.connections.hasOwnProperty(key) && easyrtc.connections[key].applicationName == applicationName) {
            listData.connections[key] = easyrtc.connections[key];
        }
    };

    // Broadcast list of current connections
    for (var key in listData.connections) {
        io.sockets.socket(key).json.emit('easyrtcCmd',{
            msgType:easyrtcCfg.cmdMsgType.list,
            msgData:listData,
            serverTime: Date.now()
        });
    };
};

exports.onSocketConnection = function(io, socket, connectionEasyRtcId) {
    var winston = require('winston');
    var logServer = winston.loggers.get('easyrtcServer');

    easyrtc.connections[connectionEasyRtcId]={
        easyrtcid: connectionEasyRtcId,
        applicationName: easyrtcCfg.defaultApplicationName,
        clientConnectTime: Date.now()
    };

    // Immediatly send the easyrtcid and application
    logServer.debug('easyRTC: Socket [' + socket.id + '] command sent', {
        label: 'easyrtc',
        easyrtcid:connectionEasyRtcId,
        data: {
            applicationName: easyrtc.connections[connectionEasyRtcId].applicationName,
            iceConfig: {"iceServers": easyrtcCfg.iceServers}
    }});
    socket.json.emit( easyrtcCfg.cmdPacketType, {
        msgType:easyrtcCfg.cmdMsgType.token,
        easyrtcid: connectionEasyRtcId,
        applicationName: easyrtc.connections[connectionEasyRtcId].applicationName,
        iceConfig: {"iceServers": easyrtcCfg.iceServers},
        serverTime: Date.now()
    });

    // Send the connection list to current connection, then broadcast to all others
    exports.broadcastList(io,easyrtc.connections[connectionEasyRtcId].applicationName);
}

exports.onSocketMessage = function(io, socket, connectionEasyRtcId, msg) {
    // Messages must have a targetId and a msgType. This section should be hardened.
    if (msg.targetId) {
        var winston = require('winston');
        var logServer = winston.loggers.get('easyrtcServer');
        logServer.debug('easyRTC: Socket [' + msg.targetId+ '] sending message from [' + connectionEasyRtcId + ']', {
            label: 'easyrtc',
            easyrtcid:msg.targetId,
            data:msg
        });
        io.sockets.socket(msg.targetId).json.send({
            msgType:msg.msgType,
            senderId:connectionEasyRtcId,
            msgData:msg.msgData,
            serverTime: Date.now()
        });
    }
}

exports.onSocketDisconnect = function(io, socket, connectionEasyRtcId) {
    // console.log('easyRTC: Socket disconnected: ' + connectionEasyRtcId);
    var previousApplicationName = easyrtc.connections[connectionEasyRtcId].applicationName;

    // Remove connection from the map
    delete easyrtc.connections[connectionEasyRtcId];
    // Broadcast new list to all others
    exports.broadcastList(io, previousApplicationName);
}

exports.onEasyRtcCmd = function(io, socket, connectionEasyRtcId, msg) {
    var winston = require('winston');
    var logServer = winston.loggers.get('easyrtcServer');

    // Messages with a targetId get forwarded on. This section should be hardened.
    if (msg.targetId) {
        logServer.debug('easyRTC: Socket [' + msg.targetId + '] command sent from [' + socket.id + ']', {
            label: 'easyrtc',
            easyrtcid:msg.targetId,
            data: {
                msgType:msg.msgType,
                senderId:connectionEasyRtcId,
                msgData:msg.msgData,
        }});
        io.sockets.socket(msg.targetId).json.emit('easyrtcCmd',{
            msgType:msg.msgType,
            senderId:connectionEasyRtcId,
            msgData:msg.msgData,
            serverTime: Date.now()
        });
    }
    // easyrtc server-side user configuration options are set here.
    if (msg.msgType == "setUserCfg") {
        setUserCfg(connectionEasyRtcId, msg.msgData);

        if (msg.msgData.applicationName) {  // Set the application namespace
            easyrtc.connections[connectionEasyRtcId].applicationName = msg.msgData.applicationName;
            // console.log('easyRTC: Application Name Change: ' + connectionEasyRtcId + ' : ' + msg.msgData.applicationName);
            exports.broadcastList(io,easyrtc.connections[connectionEasyRtcId].applicationName);
        }

        logServer.debug('easyRTC: Socket [' + connectionEasyRtcId + '] updated user config info', {
            label: 'easyrtc',
            easyrtcid:connectionEasyRtcId,
            data: {
                msgType:'updatedUserCfg',
                msgData:easyrtc.connections[connectionEasyRtcId]
        }});

    }
}

function setUserCfg(easyrtcid, userCfg) {
        // TODO: Harden section with better variable checking
        if (userCfg.screenWidth) {      easyrtc.connections[easyrtcid].screenWidth =      userCfg.screenWidth;}
        if (userCfg.screenHeight) {     easyrtc.connections[easyrtcid].screenHeight =     userCfg.screenHeight;}
        if (userCfg.browserUserAgent) { easyrtc.connections[easyrtcid].browserUserAgent = userCfg.browserUserAgent;}
        if (userCfg.sharingAudio) {     easyrtc.connections[easyrtcid].sharingAudio =     userCfg.sharingAudio;}
        if (userCfg.sharingVideo) {     easyrtc.connections[easyrtcid].sharingVideo =     userCfg.sharingVideo;}
        if (userCfg.sharingData) {      easyrtc.connections[easyrtcid].sharingData =      userCfg.sharingData;}
        if (userCfg.userName) {         easyrtc.connections[easyrtcid].userName =         userCfg.userName;}
        if (userCfg.windowWidth) {      easyrtc.connections[easyrtcid].windowWidth =      userCfg.windowWidth;}
        if (userCfg.windowHeight) {     easyrtc.connections[easyrtcid].windowHeight =     userCfg.windowHeight;}
        if (userCfg.cookieEnabled) {    easyrtc.connections[easyrtcid].cookieEnabled =    userCfg.cookieEnabled;}
        if (userCfg.language) {         easyrtc.connections[easyrtcid].language =         userCfg.language;}
        if (userCfg.nativeVideoWidth) { easyrtc.connections[easyrtcid].nativeVideoWidth = userCfg.nativeVideoWidth;}
        if (userCfg.nativeVideoHeight){ easyrtc.connections[easyrtcid].nativeVideoHeight= userCfg.nativeVideoHeight;}
        if (userCfg.connectionList) {
            easyrtc.connections[easyrtcid].connectionList = JSON.parse(JSON.stringify(userCfg.connectionList));
        }
}
<|endoftext|>