<commit_before>var crypto = require('crypto');
var events = require('events');
var querystring = require('qs');
var url = require('url');
var APIError = require('./apierror');

function CloudStackClient (options) {
    for (attribute in options) {
        this[attribute] = options[attribute];
    }

    if (!this.http) {
        if (this.serverURL && this.serverURL.indexOf('https') === 0) {
            this.http = require('https');
        } else {
            this.http = require('http');
        }
    }
}

CloudStackClient.prototype.execute = function (cmd, params, callback) {
    params['command'] = cmd;
    params['response'] = 'json';
    params['apiKey'] = this.apiKey;
    params['signature'] = this.calculateSignature(params);
    callback = callback || function () {};

    var apiURL = url.parse(this.serverURL)
    apiURL.path += querystring.stringify(params, { arrayFormat: 'indices' }).replace('+', '%20').replace('details%5B0%5D', 'details\[0\]');

    var handleResponse = function (res) {
        var data = '';
        res.on('data', function (chunk) { data += chunk; });
        res.once('end', function () {
            res.removeAllListeners('close');
            if (res.statusCode == 200) {
                try {
                    return callback(null, JSON.parse(data));
                } catch (err) {
                    return callback(err);
                }
            } else {
                //CS 2.2 returns X-Description header
                if (res.headers['X-Description']) {
                    return callback(new APIError(res.statusCode, res.headers['X-Description']));
                }

                //CS 4.2 uses JSON response.
                if (data) {
                    try {
                        var json = JSON.parse(data);
                        var message = '';
                        var respName = cmd.toLowerCase() + 'response';

                        if (json[respName] && json[respName].errortext) {
                            message = json[respName].errortext;
                        }
                    }
                    catch (e) {
                        console.warn(e.message);
                    }
                }

                return callback(new APIError(res.statusCode, message));
            }
        });
        res.once('close', function (err) {
            return callback(err);
        });
    };

    var req = this.http.get(apiURL, handleResponse);
    req.once('error', function (err) {
        return callback(err);
    });
};

CloudStackClient.prototype.executeAsync = function (cmd, params, callback) {
    var that = this;
    var pollingTime = this.pollingTime || 1000;
    var pollingNumber = this.pollingNumber || 180;

    this.execute(cmd, params, function (err, response) {
        if (err) return callback(err);

        var jobid = response[Object.keys(response)[0]].jobid;
        var queryJobResult = function (polled) {
            that.execute('queryAsyncJobResult', { jobid: jobid }, function (error, jobresult) {
                if (error) return callback(error);

                jobresult = jobresult.queryasyncjobresultresponse;
                if (jobresult.jobstatus === 1 || jobresult.jobstatus === 2) {
                    if (jobresult.jobstatus === 1) return callback(null, response);
                    return callback(new APIError(jobresult.jobresultcode));
                }

                if (polled >= pollingNumber) return callback(new Error('Timeout'));

                setTimeout(queryJobResult, pollingTime, ++polled);
            });
        };

        setTimeout(queryJobResult, pollingTime, 0);
    });
}

CloudStackClient.prototype.calculateSignature = function (queryDict) {
    var hmac = crypto.createHmac('sha1', this.secretKey);
    var orderedQuery = querystring.stringify(queryDict, { arrayFormat: 'indices' }).replace('details%5B0%5D', 'details\[0\]').split('&').sort().join('&').toLowerCase();
    hmac.update(orderedQuery);
    return hmac.digest('base64');
};

module.exports = CloudStackClient;
<commit_msg>Removing unnecessary formatting of querystring<commit_after>var crypto = require('crypto');
var events = require('events');
var querystring = require('qs');
var url = require('url');
var APIError = require('./apierror');

function CloudStackClient (options) {
    for (attribute in options) {
        this[attribute] = options[attribute];
    }

    if (!this.http) {
        if (this.serverURL && this.serverURL.indexOf('https') === 0) {
            this.http = require('https');
        } else {
            this.http = require('http');
        }
    }
}

CloudStackClient.prototype.execute = function (cmd, params, callback) {
    params['command'] = cmd;
    params['response'] = 'json';
    params['apiKey'] = this.apiKey;
    params['signature'] = this.calculateSignature(params);
    callback = callback || function () {};

    var apiURL = url.parse(this.serverURL)
    apiURL.path += querystring.stringify(params, { arrayFormat: 'indices' });

    var handleResponse = function (res) {
        var data = '';
        res.on('data', function (chunk) { data += chunk; });
        res.once('end', function () {
            res.removeAllListeners('close');
            if (res.statusCode == 200) {
                try {
                    return callback(null, JSON.parse(data));
                } catch (err) {
                    return callback(err);
                }
            } else {
                //CS 2.2 returns X-Description header
                if (res.headers['X-Description']) {
                    return callback(new APIError(res.statusCode, res.headers['X-Description']));
                }

                //CS 4.2 uses JSON response.
                if (data) {
                    try {
                        var json = JSON.parse(data);
                        var message = '';
                        var respName = cmd.toLowerCase() + 'response';

                        if (json[respName] && json[respName].errortext) {
                            message = json[respName].errortext;
                        }
                    }
                    catch (e) {
                        console.warn(e.message);
                    }
                }

                return callback(new APIError(res.statusCode, message));
            }
        });
        res.once('close', function (err) {
            return callback(err);
        });
    };

    var req = this.http.get(apiURL, handleResponse);
    req.once('error', function (err) {
        return callback(err);
    });
};

CloudStackClient.prototype.executeAsync = function (cmd, params, callback) {
    var that = this;
    var pollingTime = this.pollingTime || 1000;
    var pollingNumber = this.pollingNumber || 180;

    this.execute(cmd, params, function (err, response) {
        if (err) return callback(err);

        var jobid = response[Object.keys(response)[0]].jobid;
        var queryJobResult = function (polled) {
            that.execute('queryAsyncJobResult', { jobid: jobid }, function (error, jobresult) {
                if (error) return callback(error);

                jobresult = jobresult.queryasyncjobresultresponse;
                if (jobresult.jobstatus === 1 || jobresult.jobstatus === 2) {
                    if (jobresult.jobstatus === 1) return callback(null, response);
                    return callback(new APIError(jobresult.jobresultcode));
                }

                if (polled >= pollingNumber) return callback(new Error('Timeout'));

                setTimeout(queryJobResult, pollingTime, ++polled);
            });
        };

        setTimeout(queryJobResult, pollingTime, 0);
    });
}

CloudStackClient.prototype.calculateSignature = function (queryDict) {
    var hmac = crypto.createHmac('sha1', this.secretKey);
    var orderedQuery = querystring.stringify(queryDict, { arrayFormat: 'indices' }).split('&').sort().join('&').toLowerCase();
    hmac.update(orderedQuery);
    return hmac.digest('base64');
};

module.exports = CloudStackClient;
<|endoftext|>


<commit_before>/**
 * Created by Liara Anna Maria Rørvig on 24/07/2017.
 */
'use strict';

const Colours = require( '../../lib/Colours.json' );
const Command = require( '../command.js' );
const dot     = require( 'dot-object' );

const NAME          = 'psn';
const HELP_SIMPLE   = 'Shows a small info message';
const HELP_DETAILED = 'Shows a small info message';
const CAN_RUN       = undefined;

class CommandPSN extends Command {
    constructor() {
        super( {
            name          : NAME,
            help_simple   : HELP_SIMPLE,
            help_detailed : HELP_DETAILED,
            can_run       : CAN_RUN
        } );
    }

    invoke( parameters, message, bot ) {
        let command_handler = ( resolve, reject ) => {
            let username = parameters[ 0 ];

            bot.psn.getProfile( parameters[ 0 ], ( error, profile_data ) => {
                if ( error ) {
                    console.log( error );

                    return reject( {
                        error   : true,
                        message : error.toString()
                    } );
                }

                let onlineId  = profile_data.onlineId;
                let panelBgc  = profile_data.panelBgc;
                let colour    = panelBgc ? parseInt( panelBgc.slice( panelBgc.length - 6 ), 16 ) : Colours.pink;
                let reactions = [
                    {
                        emoji : '🏆',
                        data  : {
                            action : 'GetTrophies',
                            data   : username
                        }
                    }
                ];
                let embed     = {
                    title       : onlineId + '\'s Profile',
                    description : profile_data.aboutMe,
                    color       : colour,
                    fields      : [
                        {
                            name   : 'Region',
                            value  : profile_data.region,
                            inline : true
                        },
                        {
                            name   : 'PS Plus',
                            value  : profile_data.plus === 1 ? 'Yes' : 'No',
                            inline : true
                        },
                        {
                            name   : 'Level',
                            value  : profile_data.trophySummary.level,
                            inline : true
                        },
                        {
                            name   : 'Progress',
                            value  : `${profile_data.trophySummary.progress}%`,
                            inline : true
                        },
                        {
                            name : 'Platinum', value : profile_data.trophySummary.earnedTrophies.platinum, inline : true
                        }, {
                            name : 'Gold', value : profile_data.trophySummary.earnedTrophies.gold, inline : true
                        }, {
                            name : 'Silver', value : profile_data.trophySummary.earnedTrophies.silver, inline : true
                        }, {
                            name : 'Bronze', value : profile_data.trophySummary.earnedTrophies.bronze, inline : true
                        }
                    ],
                    author      : {
                        name     : 'PlayStation Network',
                        url      : 'https://www.playstation.com/en-ca/network/',
                        icon_url : 'https://media.playstation.com/is/image/SCEA/navigation_home_ps-logo-us?$Icon$'
                    },
                    thumbnail   : {
                        url : dot.pick( 'personalDetail.profilePictureUrl', profile_data ) || profile_data.avatarUrl
                    }
                };

                if ( profile_data.relation === 'friend' ) {
                    embed.fields.push( {
                        name : 'Friends', value : 'Yes', inline : true
                    } );

                    reactions.push( {
                        emoji : '➗',
                        data  : {
                            action : 'RemoveFriend',
                            data   : username
                        }
                    } );
                } else if ( profile_data.relation === 'no relationship' || profile_data.relation === 'friend of friends' ) {
                    embed.fields.push( {
                        name : 'Friends', value : 'No', inline : true
                    } );

                    reactions.push( {
                        emoji : '➕',
                        data  : {
                            action : 'AddFriend',
                            data   : username
                        }
                    } );
                }

                if ( dot.pick( 'presence.primaryInfo.onlineStatus', profile_data ) === 'offline' ) {
                    embed.fields.push( {
                        name : 'Status', value : 'Offline', inline : true
                    } );
                    embed.fields.push( {
                        name   : 'Last Seen',
                        value  : new Date( profile_data.presence.primaryInfo.lastOnlineDate ).toUTCString(),
                        inline : true
                    } );
                } else if ( dot.pick( 'presence.primaryInfo.onlineStatus', profile_data ) === 'online' ) {
                    embed.fields.push( {
                        name : 'Status', value : 'Online', inline : true
                    } );
                }

                if ( dot.pick( 'presence.primaryInfo.gameTitleInfo.titleName', profile_data ) ) {
                    embed.fields.push( {
                        name   : 'Currently Playing',
                        value  : dot.pick( 'presence.primaryInfo.gameTitleInfo.titleName', profile_data ),
                        inline : true
                    } );
                }

                let response = {
                    error     : false,
                    message   : {
                        embed : embed
                    },
                    reactions : reactions,
                    no_delete : true
                };

                return resolve( response );
            } );
        };

        let promise = new Promise( command_handler );

        return promise;
    }

    reactionRemoveGetTrophies( parameters, message, bot ) {
        let username = parameters;
        bot.psn.getUserTrophies( 0, 10, username, ( error, trophies ) => {
            if ( error ) {
                return message.channel.send( error.toString() );
            }

            trophies = trophies.trophyTitles;

            trophies.forEach( trophy => {
                let title       = trophy.trophyTitleName;
                let description = trophy.trophyTitleDetail;
                let thumbnail   = trophy.trophyTitleIconUrl;
                let platform    = trophy.trophyTitlePlatfrom || 'Unknown';
                let colour      = Colours.pink;

                let embed = {
                    title       : title,
                    description : description,
                    color       : colour,
                    author      : {
                        name     : 'PlayStation Network',
                        url      : 'https://www.playstation.com/en-ca/network/',
                        icon_url : 'https://media.playstation.com/is/image/SCEA/navigation_home_ps-logo-us?$Icon$'
                    },
                    thumbnail   : {
                        url : thumbnail
                    },
                    fields      : [
                        {
                            name   : 'Platform',
                            value  : platform,
                            inline : true
                        }
                    ]
                };

                message.channel.send( {
                    embed : embed
                } );
            } );
        } );
    }

    reactionRemoveAddFriend( parameters, message, bot ) {
        let username = parameters;

        bot.psn.sendFriendRequest( username, '', ( error ) => {
            if ( error ) {
                return message.channel.send( error.toString() );
            }

            message.channel.send( 'Asynchronous Bot> Friend Request Sent' );
        } );
    }

    reactionRemoveRemoveFriend( parameters, message, bot ) {
        let username = parameters;

        bot.psn.removeFriend( username, ( error ) => {
            if ( error ) {
                return message.channel.send( error.toString() );
            }

            message.channel.send( 'Asynchronous Bot> Friend Removed' );
        } );
    }
}

module.exports = CommandPSN;<commit_msg>Fixed the help messages<commit_after>/**
 * Created by Liara Anna Maria Rørvig on 24/07/2017.
 */
'use strict';

const Colours = require( '../../lib/Colours.json' );
const Command = require( '../command.js' );
const dot     = require( 'dot-object' );

const NAME          = 'psn';
const HELP_SIMPLE   = 'Get\'s a user from PSN';
const HELP_DETAILED = 'Get\'s and displays you information about a user you want to lookup';
const CAN_RUN       = undefined;

class CommandPSN extends Command {
    constructor() {
        super( {
            name          : NAME,
            help_simple   : HELP_SIMPLE,
            help_detailed : HELP_DETAILED,
            can_run       : CAN_RUN
        } );
    }

    invoke( parameters, message, bot ) {
        let command_handler = ( resolve, reject ) => {
            let username = parameters[ 0 ];

            bot.psn.getProfile( parameters[ 0 ], ( error, profile_data ) => {
                if ( error ) {
                    console.log( error );

                    return reject( {
                        error   : true,
                        message : error.toString()
                    } );
                }

                let onlineId  = profile_data.onlineId;
                let panelBgc  = profile_data.panelBgc;
                let colour    = panelBgc ? parseInt( panelBgc.slice( panelBgc.length - 6 ), 16 ) : Colours.pink;
                let reactions = [
                    {
                        emoji : '🏆',
                        data  : {
                            action : 'GetTrophies',
                            data   : username
                        }
                    }
                ];
                let embed     = {
                    title       : onlineId + '\'s Profile',
                    description : profile_data.aboutMe,
                    color       : colour,
                    fields      : [
                        {
                            name   : 'Region',
                            value  : profile_data.region,
                            inline : true
                        },
                        {
                            name   : 'PS Plus',
                            value  : profile_data.plus === 1 ? 'Yes' : 'No',
                            inline : true
                        },
                        {
                            name   : 'Level',
                            value  : profile_data.trophySummary.level,
                            inline : true
                        },
                        {
                            name   : 'Progress',
                            value  : `${profile_data.trophySummary.progress}%`,
                            inline : true
                        },
                        {
                            name : 'Platinum', value : profile_data.trophySummary.earnedTrophies.platinum, inline : true
                        }, {
                            name : 'Gold', value : profile_data.trophySummary.earnedTrophies.gold, inline : true
                        }, {
                            name : 'Silver', value : profile_data.trophySummary.earnedTrophies.silver, inline : true
                        }, {
                            name : 'Bronze', value : profile_data.trophySummary.earnedTrophies.bronze, inline : true
                        }
                    ],
                    author      : {
                        name     : 'PlayStation Network',
                        url      : 'https://www.playstation.com/en-ca/network/',
                        icon_url : 'https://media.playstation.com/is/image/SCEA/navigation_home_ps-logo-us?$Icon$'
                    },
                    thumbnail   : {
                        url : dot.pick( 'personalDetail.profilePictureUrl', profile_data ) || profile_data.avatarUrl
                    }
                };

                if ( profile_data.relation === 'friend' ) {
                    embed.fields.push( {
                        name : 'Friends', value : 'Yes', inline : true
                    } );

                    reactions.push( {
                        emoji : '➗',
                        data  : {
                            action : 'RemoveFriend',
                            data   : username
                        }
                    } );
                } else if ( profile_data.relation === 'no relationship' || profile_data.relation === 'friend of friends' ) {
                    embed.fields.push( {
                        name : 'Friends', value : 'No', inline : true
                    } );

                    reactions.push( {
                        emoji : '➕',
                        data  : {
                            action : 'AddFriend',
                            data   : username
                        }
                    } );
                }

                if ( dot.pick( 'presence.primaryInfo.onlineStatus', profile_data ) === 'offline' ) {
                    embed.fields.push( {
                        name : 'Status', value : 'Offline', inline : true
                    } );
                    embed.fields.push( {
                        name   : 'Last Seen',
                        value  : new Date( profile_data.presence.primaryInfo.lastOnlineDate ).toUTCString(),
                        inline : true
                    } );
                } else if ( dot.pick( 'presence.primaryInfo.onlineStatus', profile_data ) === 'online' ) {
                    embed.fields.push( {
                        name : 'Status', value : 'Online', inline : true
                    } );
                }

                if ( dot.pick( 'presence.primaryInfo.gameTitleInfo.titleName', profile_data ) ) {
                    embed.fields.push( {
                        name   : 'Currently Playing',
                        value  : dot.pick( 'presence.primaryInfo.gameTitleInfo.titleName', profile_data ),
                        inline : true
                    } );
                }

                let response = {
                    error     : false,
                    message   : {
                        embed : embed
                    },
                    reactions : reactions,
                    no_delete : true
                };

                return resolve( response );
            } );
        };

        let promise = new Promise( command_handler );

        return promise;
    }

    reactionRemoveGetTrophies( parameters, message, bot ) {
        let username = parameters;
        bot.psn.getUserTrophies( 0, 10, username, ( error, trophies ) => {
            if ( error ) {
                return message.channel.send( error.toString() );
            }

            trophies = trophies.trophyTitles;

            trophies.forEach( trophy => {
                let title       = trophy.trophyTitleName;
                let description = trophy.trophyTitleDetail;
                let thumbnail   = trophy.trophyTitleIconUrl;
                let platform    = trophy.trophyTitlePlatfrom || 'Unknown';
                let colour      = Colours.pink;

                let embed = {
                    title       : title,
                    description : description,
                    color       : colour,
                    author      : {
                        name     : 'PlayStation Network',
                        url      : 'https://www.playstation.com/en-ca/network/',
                        icon_url : 'https://media.playstation.com/is/image/SCEA/navigation_home_ps-logo-us?$Icon$'
                    },
                    thumbnail   : {
                        url : thumbnail
                    },
                    fields      : [
                        {
                            name   : 'Platform',
                            value  : platform,
                            inline : true
                        }
                    ]
                };

                message.channel.send( {
                    embed : embed
                } );
            } );
        } );
    }

    reactionRemoveAddFriend( parameters, message, bot ) {
        let username = parameters;

        bot.psn.sendFriendRequest( username, '', ( error ) => {
            if ( error ) {
                return message.channel.send( error.toString() );
            }

            message.channel.send( 'Asynchronous Bot> Friend Request Sent' );
        } );
    }

    reactionRemoveRemoveFriend( parameters, message, bot ) {
        let username = parameters;

        bot.psn.removeFriend( username, ( error ) => {
            if ( error ) {
                return message.channel.send( error.toString() );
            }

            message.channel.send( 'Asynchronous Bot> Friend Removed' );
        } );
    }
}

module.exports = CommandPSN;<|endoftext|>


<commit_before>var Stream = require('stream');

// writable
exports.Event = function (options) {

    options = options || {};
    if (typeof options.objectMode === 'undefined') {
        options.objectMode = true;
    }

    var sequence = Stream.Duplex({
        readableObjectMode: options.objectMode ? true : false,
        writableObjectMode: options.objectMode ? true : false
    });

    // writable
    sequence._write = function (chunk, enc, next) {

        if (!sequence.seq) {
            return sequence.emit('error', new Error('Stream._write: Sequence not set.'));
        }

        sequence._next = next;
        sequence._pos = -1;
        sequence.next(null, chunk);
    };

    // readable
    sequence._read = function () {
        if (sequence.seq) {
            sequence.uncork();
        }
    };

    // next haninstancedler
    sequence.next = function (err, data) {

        // handle steam end (null) | (false, null)
        if (err === null && (data === null || data === undefined)) {
            sequence.end();
            return;
        }

        // set error flag and reset position (err)
        if (!sequence._err && err) {
            sequence._err = true;
            sequence._pos = 0;
            data = err;
        }

        // get handler
        // TODO handle no handler (reset position if no handler is found?)
        var handler = sequence.seq[0][++sequence._pos];
        if (!handler) {

            // emit error to reader
            if (sequence._err) {
                sequence._next(data || new Error('Error stream: no data.'));
                return;
            }

            // auto convert to non-object streams
            if (!options.objectMode &&
                typeof data !== 'string' &&
                data.constructor !== Buffer
            ) {
                try {
                    data = JSON.stringify(data);
                } catch (err)  {
                    sequence._next(err);
                    return;
                }
            }

            // write to linked stream
            if (sequence.linked) {

                // push also to response
                if (sequence.seq[1][0] === '|') {
                    sequence.push(data);
                }

                // handle back pressure
                if (!sequence.linked.write(data)) {
                    sequence.linked.once('drain', sequence._next);
                    return;
                }

            // push data to readable
            } else if ((!sequence.push(data))) {

                // buffer all writes
                sequence.cork()
            }

            // request the next data chunk
            sequence._next();
            return;
        }

        // call next data handler
        handler[0].call(handler[2], sequence.next.bind(sequence), handler[1], data);
    };

    return sequence;
};
<commit_msg>correct access<commit_after>var Stream = require('stream');

// writable
exports.Event = function (options) {

    options = options || {};
    if (typeof options.objectMode === 'undefined') {
        options.objectMode = true;
    }

    var sequence = Stream.Duplex({
        readableObjectMode: options.objectMode ? true : false,
        writableObjectMode: options.objectMode ? true : false
    });

    // writable
    sequence._write = function (chunk, enc, next) {

        if (!sequence.seq) {
            return sequence.emit('error', new Error('Stream._write: Sequence not set.'));
        }

        sequence._next = next;
        sequence._pos = -1;
        sequence.next(null, chunk);
    };

    // readable
    sequence._read = function () {
        if (sequence.seq) {
            sequence.uncork();
        }
    };

    // next haninstancedler
    sequence.next = function (err, data) {

        // handle steam end (null) | (false, null)
        if (err === null && (data === null || data === undefined)) {
            sequence.end();
            return;
        }

        // set error flag and reset position (err)
        if (!sequence._err && err) {
            sequence._err = true;
            sequence._pos = 0;
            data = err;
        }

        // get handler
        var handler = sequence.seq[++sequence._pos];
        if (!handler) {

            // emit error to reader
            if (sequence._err) {
                sequence._next(data || new Error('Error stream: no data.'));
                return;
            }

            // auto convert to non-object streams
            if (!options.objectMode &&
                typeof data !== 'string' &&
                data.constructor !== Buffer
            ) {
                try {
                    data = JSON.stringify(data);
                } catch (err)  {
                    sequence._next(err);
                    return;
                }
            }

            // write to linked stream
            if (sequence.linked) {

                // push also to response
                if (sequence.seq[1][0] === '|') {
                    sequence.push(data);
                }

                // handle back pressure
                if (!sequence.linked.write(data)) {
                    sequence.linked.once('drain', sequence._next);
                    return;
                }

            // push data to readable
            } else if ((!sequence.push(data))) {

                // buffer all writes
                sequence.cork()
            }

            // request the next data chunk
            sequence._next();
            return;
        }

        // call next data handler
        handler[0].call(handler[2], sequence.next.bind(sequence), handler[1], data);
    };

    return sequence;
};
<|endoftext|>


<commit_before>import {Model, BasicModel, ObjectModel, _isPrivate, _isConstant, _original} from "./object-model.js"
import ArrayModel from "./array-model.js"
import SetModel from "./set-model.js"
import MapModel from "./map-model.js"
import FunctionModel from "./function-model.js"
import {getProto, is, isArray, isFunction, isPlainObject, mapProps} from "./helpers.js"

const styles = {
	list: `list-style-type: none; padding: 0; margin: 0;`,
	listItem: `padding: 0 0 0 1em;`,
	model: `color: #3e999f;`,
	sealedModel: `color: #3e999f; font-weight: bold`,
	instance: `color: #718c00; font-style: italic`,
	function: `color: #4271AE`,
	string: `color: #C41A16`,
	number: `color: #1C00CF`,
	boolean: `color: #AA0D91`,
	property: `color: #8959a8`,
	private: `color: #C19ED8`,
	constant: `color: #8959a8; font-weight: bold`,
	privateConstant: `color: #C19ED8; font-weight: bold`,
	null: `color: #8e908c`,
	undeclared: `color: #C0C0C0;`,
	proto: `color: #B871BD; font-style: italic`
};

const getModel = (instance) => {
	if (instance === undefined || instance === null)
		return null

	let proto = getProto(instance);
	if (!proto || !proto.constructor || !is(Model, proto.constructor))
		return null

	return proto.constructor
}

const span = (style, ...children) => ["span", {style}, ...children]

const format = (x, config={}) => {
	if (x === null || x === undefined)
		return span(styles.null, ""+x);

	if (typeof x === "boolean")
		return span(styles.boolean, x);

	if (typeof x === "number")
		return span(styles.number, x);

	if (typeof x === "string")
		return span(styles.string, `"${x}"`);

	if (isArray(x) && config.isModelDefinition) {
		let def = [];
		if (x.length === 1) x.push(undefined, null);
		for (let i = 0; i < x.length; i++) {
			def.push(format(x[i], config))
			if (i < x.length - 1) def.push(' or ')
		}
		return span('', ...def)
	}

	if (isPlainObject(x))
		return formatObject(x, getModel(x), config)

	if (isFunction(x) && !is(Model, x) && config.isModelDefinition)
		return span(styles.function, x.name || x.toString());

	return ['object', {object: x, config}]
}

const formatObject = (o, model, config) => span('',
	'{',
	['ol', {style: styles.list}, ...mapProps(o, prop =>
		['li', {style: styles.listItem}, span(styles.property, prop), ': ', format(o[prop], config) ])
	],
	'}'
)

const formatModel = model => {
	const parts = [],
	      cfg = { isModelDefinition: true },
	      def = model.definition;

	if(is(BasicModel, model )) parts.push(format(def, cfg))
	if(is(ArrayModel, model)) parts.push("Array of ", format(def, cfg))
	if(is(SetModel, model)) parts.push("Set of ", format(def, cfg))
	if(is(MapModel, model)) parts.push("Map of ", format(def.key, cfg), " : ", format(def.value, cfg))
	if(is(FunctionModel, model)){
		parts.push("Function(")
		def.arguments.forEach((arg, i) => {
			parts.push(format(arg, cfg), i < def.arguments.length - 1 ? ", " : ")");
		})
		if ("return" in def) parts.push(" => ", format(def.return, cfg))
	}

	if(model.assertions.length > 0){
		parts.push("\n(assertions: ")
		model.assertions.forEach((f, i) => {
			parts.push(['object', { object: f }], i < model.assertions.length - 1 ? ", " : ")");
		})
	}

	return parts
}

const ModelFormatter = {
	header(x, config = {}) {
		if(is(ObjectModel, x))
			return span(x.sealed ? styles.sealedModel : styles.model, x.name)

		if (is(Model, x)) {
			return span(styles.model, ...formatModel(x))
		}

		if (config.isModelDefinition && isPlainObject(x))
			return format(x, config)

		return null;
	},
	hasBody(x) {
		return is(ObjectModel, x)
	},
	body(model) {
		return span('',
			'{',
			['ol', {style: styles.list}, ...mapProps(model.definition, prop => {
				let isPrivate = model[_isPrivate](prop),
				    isConstant = model[_isConstant](prop),
				    hasDefault = model.prototype.hasOwnProperty(prop),
				    style = styles.property;

				if(isPrivate) {
					style = isConstant ? styles.privateConstant : styles.private
				} else if(isConstant) {
					style = styles.constant
				}

				return ['li', {style: styles.listItem},
					span(style, prop), ': ', format(model.definition[prop], { isModelDefinition: true }),
					hasDefault ? span(styles.proto, ' = ', format(model.prototype[prop])) : ''
				]
			}) ],
			'}'
		)
	}
}

const ModelInstanceFormatter = {
	header(x, config = {}) {
		if (config.isInstanceProperty && isPlainObject(x)) {
			return format(x, config)
		}

		let model = getModel(x);
		if(is(Model, model)){
			let parts = is(ObjectModel, model) ? [model.name] : [['object', { object: x[_original] }], ` (${model.name})`];
			return span(styles.instance, ...parts)
		}

		return null;
	},
	hasBody(x) {
		return x && is(ObjectModel, getModel(x))
	},
	body(x) {
		const model = getModel(x)
		const o = x[_original] || x;
		return span('',
			'{',
			['ol', {style: styles.list}, ...mapProps(o, prop => {
				let isPrivate = model[_isPrivate](prop),
				    isConstant = model[_isConstant](prop),
				    isDeclared = prop in model.definition,
				    style = styles.property;

				if(!isDeclared) {
					style = styles.undeclared
				} else if(isPrivate) {
					style = isConstant ? styles.privateConstant : styles.private
				} else if(isConstant) {
					style = styles.constant
				}

				return ['li', {style: styles.listItem},
					span(style, prop), ': ', format(o[prop], { isInstanceProperty: true })
				]
			}),
				['li', {style: styles.listItem},
					span(styles.proto, '__proto__', ': ', ['object', {object: getProto(x)}])
				],
			],
			'}'
		)
	}
}

if (typeof window !== "undefined") {
	window.devtoolsFormatters = (window.devtoolsFormatters || [])
		.concat(ModelFormatter, ModelInstanceFormatter);
}<commit_msg>devtool formatter: fixes<commit_after>import {Model, BasicModel, ObjectModel, _isPrivate, _isConstant, _original} from "./object-model.js"
import ArrayModel from "./array-model.js"
import SetModel from "./set-model.js"
import MapModel from "./map-model.js"
import FunctionModel from "./function-model.js"
import {getProto, is, isArray, isFunction, isPlainObject, mapProps} from "./helpers.js"

const styles = {
	list: `list-style-type: none; padding: 0; margin: 0;`,
	listItem: `padding: 0 0 0 1em;`,
	model: `color: #3e999f;`,
	sealedModel: `color: #3e999f; font-weight: bold`,
	instance: `color: #718c00; font-style: italic`,
	function: `color: #4271AE`,
	string: `color: #C41A16`,
	number: `color: #1C00CF`,
	boolean: `color: #AA0D91`,
	property: `color: #8959a8`,
	private: `color: #C19ED8`,
	constant: `color: #8959a8; font-weight: bold`,
	privateConstant: `color: #C19ED8; font-weight: bold`,
	null: `color: #8e908c`,
	undeclared: `color: #C0C0C0;`,
	proto: `color: #B871BD; font-style: italic`
};

const getModel = (instance) => {
	if (instance === undefined || instance === null)
		return null

	let proto = getProto(instance);
	if (!proto || !proto.constructor || !is(Model, proto.constructor))
		return null

	return proto.constructor
}

const span = (style, ...children) => ["span", {style}, ...children]

const format = (x, config={}) => {
	if (x === null || x === undefined)
		return span(styles.null, ""+x);

	if (typeof x === "boolean")
		return span(styles.boolean, x);

	if (typeof x === "number")
		return span(styles.number, x);

	if (typeof x === "string")
		return span(styles.string, `"${x}"`);

	if (isArray(x) && config.isModelDefinition) {
		let def = [];
		if (x.length === 1) x.push(undefined, null);
		for (let i = 0; i < x.length; i++) {
			def.push(format(x[i], config))
			if (i < x.length - 1) def.push(' or ')
		}
		return span('', ...def)
	}

	if (isPlainObject(x))
		return formatObject(x, getModel(x), config)

	if (isFunction(x) && !is(Model, x) && config.isModelDefinition)
		return span(styles.function, x.name || x.toString());

	return ['object', {object: x, config}]
}

const formatObject = (o, model, config) => span('',
	'{',
	['ol', {style: styles.list}, ...mapProps(o, prop =>
		['li', {style: styles.listItem}, span(styles.property, prop), ': ', format(o[prop], config) ])
	],
	'}'
)

const formatModel = model => {
	const parts = [],
	      cfg = { isModelDefinition: true },
	      def = model.definition,
	      formatList = (list, map) => list.reduce((r, e) => [...r, map(e), ", "], []).slice(0, 2 * list.length - 1);

	if(is(BasicModel, model )) parts.push(format(def, cfg))
	if(is(ArrayModel, model)) parts.push("Array of ", format(def, cfg))
	if(is(SetModel, model)) parts.push("Set of ", format(def, cfg))
	if(is(MapModel, model)) parts.push("Map of ", format(def.key, cfg), " : ", format(def.value, cfg))
	if(is(FunctionModel, model)){
		parts.push("Function(", ...formatList(def.arguments, arg => format(arg, cfg)), ")")
		if ("return" in def) parts.push(" => ", format(def.return, cfg))
	}

	if(model.assertions.length > 0){
		parts.push("\n(assertions: ", ...formatList(model.assertions, f => ['object', { object: f }]), ")")
	}

	return span(styles.model, ...parts)
}

const ModelFormatter = {
	header(x, config = {}) {
		if(is(ObjectModel, x))
			return span(x.sealed ? styles.sealedModel : styles.model, x.name)

		if (is(Model, x)) {
			return formatModel(x)
		}

		if (config.isModelDefinition && isPlainObject(x))
			return format(x, config)

		return null;
	},
	hasBody(x) {
		return is(ObjectModel, x)
	},
	body(model) {
		return span('',
			'{',
			['ol', {style: styles.list}, ...mapProps(model.definition, prop => {
				let isPrivate = model[_isPrivate](prop),
				    isConstant = model[_isConstant](prop),
				    hasDefault = model.prototype.hasOwnProperty(prop),
				    style = styles.property;

				if(isPrivate) {
					style = isConstant ? styles.privateConstant : styles.private
				} else if(isConstant) {
					style = styles.constant
				}

				return ['li', {style: styles.listItem},
					span(style, prop), ': ', format(model.definition[prop], { isModelDefinition: true }),
					hasDefault ? span(styles.proto, ' = ', format(model.prototype[prop])) : ''
				]
			}) ],
			'}'
		)
	}
}

const ModelInstanceFormatter = {
	header(x, config = {}) {
		if (config.isInstanceProperty && isPlainObject(x)) {
			return format(x, config)
		}

		let model = getModel(x);
		if(is(Model, model)){
			let parts = is(ObjectModel, model) ? [model.name] : [['object', { object: x[_original] }], ` (${model.name})`];
			return span(styles.instance, ...parts)
		}

		return null;
	},
	hasBody(x) {
		return x && is(ObjectModel, getModel(x))
	},
	body(x) {
		const model = getModel(x)
		const o = x[_original] || x;
		return span('',
			'{',
			['ol', {style: styles.list}, ...mapProps(o, prop => {
				let isPrivate = model[_isPrivate](prop),
				    isConstant = model[_isConstant](prop),
				    isDeclared = prop in model.definition,
				    style = styles.property;

				if(!isDeclared) {
					style = styles.undeclared
				} else if(isPrivate) {
					style = isConstant ? styles.privateConstant : styles.private
				} else if(isConstant) {
					style = styles.constant
				}

				return ['li', {style: styles.listItem},
					span(style, prop), ': ', format(o[prop], { isInstanceProperty: true })
				]
			}),
				['li', {style: styles.listItem},
					span(styles.proto, '__proto__', ': ', ['object', {object: getProto(x)}])
				],
			],
			'}'
		)
	}
}

if (typeof window !== "undefined") {
	window.devtoolsFormatters = (window.devtoolsFormatters || [])
		.concat(ModelFormatter, ModelInstanceFormatter);
}<|endoftext|>


<commit_before>/*
 * Modified version of http://github.com/desandro/imagesloaded v2.1.1
 * MIT License. by Paul Irish et al.
 */

var BLANK = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=='

function loaded(image, callback) {
    "use strict";
    var src, old;

    if (!image.nodeName) {
        return callback(new Error('First argument must be an image element'));
    }

    if (image.nodeName.toLowerCase() !== 'img') {
        return callback(new Error('Element supplied is not an image'));
    }
    if (image.src && image.complete && image.naturalWidth !== undefined) {
        return callback(null, true);
    }

    old = !image.addEventListener;

    function bindEvent(element, event, attach, cb) {
        if (!element.addEventListener) {
            element[(attach ? 'attachEvent' : 'detachEvent')]('on' + event, cb);
        } else {
            element[(attach ? 'addEventListener' : 'removeEventListener')](event, cb);
        }
    }

    function onload() {
        bindEvent(image, 'load', false, onload);
        bindEvent(image, 'error', false, onload);

        callback(null, false);
    }

    bindEvent(image, 'load', true, onload);
    bindEvent(image, 'error', true, onload);

    if (image.readyState || image.complete) {
        src = image.src;
        image.src = BLANK;
        image.src = src;
    }
}

module.exports = loaded;<commit_msg>removed unneeded code<commit_after>/*
 * Modified version of http://github.com/desandro/imagesloaded v2.1.1
 * MIT License. by Paul Irish et al.
 */

var BLANK = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=='

function loaded(image, callback) {
    "use strict";
    var src, old;

    if (!image.nodeName) {
        return callback(new Error('First argument must be an image element'));
    }

    if (image.nodeName.toLowerCase() !== 'img') {
        return callback(new Error('Element supplied is not an image'));
    }
    if (image.src && image.complete && image.naturalWidth !== undefined) {
        return callback(null, true);
    }

    function bindEvent(element, event, attach, cb) {
        if (!element.addEventListener) {
            element[(attach ? 'attachEvent' : 'detachEvent')]('on' + event, cb);
        } else {
            element[(attach ? 'addEventListener' : 'removeEventListener')](event, cb);
        }
    }

    function onload() {
        bindEvent(image, 'load', false, onload);
        bindEvent(image, 'error', false, onload);

        callback(null, false);
    }

    bindEvent(image, 'load', true, onload);
    bindEvent(image, 'error', true, onload);

    if (image.readyState || image.complete) {
        src = image.src;
        image.src = BLANK;
        image.src = src;
    }
}

module.exports = loaded;<|endoftext|>


<commit_before>'use strict';
var QEmitter = require('qemitter'),
    util = require('util'),
    path = require('path'),

    _ = require('lodash'),
    chalk = require('chalk'),
    ScreenShooter = require('./screen-shooter'),
    Tester = require('./tester'),
    Config = require('./config'),
    Suite = require('./suite'),
    suiteUtils = require('./suite-util'),

    pathUtils = require('./path-utils'),
    publicApi = require('./public-api'),
    RunnnerStats = require('./stats'),
    RunnerEvents = require('./constants/runner-events'),

    exposeTestsApi = require('./tests-api'),

    GeminiError = require('./errors/gemini-error'),
    Image = require('./image'),

    DEFAULT_CFG_NAME = '.gemini.yml',
    DEFAULT_SPECS_DIR = 'gemini';

function requireWithNoCache(moduleName) {
    var result = require(moduleName);
    delete require.cache[moduleName];
    return result;
}

function Gemini(config, overrides) {
    QEmitter.call(this);
    config = config || DEFAULT_CFG_NAME;
    this.config = new Config(config, overrides);
    var _this = this;

    require('./plugins').load(_this, this.config);

    function getDefaultSpecsDir() {
        return path.join(_this.config.system.projectRoot, DEFAULT_SPECS_DIR);
    }

    function executeRunner(runnerInstance, paths, options) {
        if (!options) {
            //if there are only two arguments, they are
            //(runnerInstance, options) and paths are
            //the default.
            options = paths;
            paths = [getDefaultSpecsDir()];
        }
        options = options || {};
        options.reporters = options.reporters || [];

        var envBrowsers = process.env.GEMINI_BROWSERS? process.env.GEMINI_BROWSERS.split(',') : null;
        options.browsers = options.browsers || envBrowsers;

        if (options.browsers) {
            areAllBrowsersFromConfig(options.browsers, _this.browserIds);
            runnerInstance.setTestBrowsers(options.browsers);
        }

        return _this.readTests(paths)
            .then(function(rootSuite) {
                return _this.emitAndWait(RunnerEvents.START_RUNNER, runnerInstance)
                    .thenResolve(rootSuite);
            })
            .then(function(rootSuite) {
                var suites = suiteUtils.flattenSuites(rootSuite);

                if (options.grep) {
                    suites = suites.filter(function(suite) {
                        return options.grep.test(suite.fullName);
                    });
                }

                options.reporters.forEach(_.partial(applyReporter, runnerInstance));

                var stats = new RunnnerStats(runnerInstance);

                return runnerInstance.run(suites)
                    .thenResolve(stats.get());
            })
            .then(function(data) {
                return _this.emitAndWait(RunnerEvents.END_RUNNER, runnerInstance, data)
                    .thenResolve(data);
            });
    }

    function areAllBrowsersFromConfig(browsers, browsersFromConfig) {
        browsers.forEach(function(name) {
            if (browsersFromConfig.indexOf(name) === -1) {
                throw new GeminiError('Unknown browser id: ' + name,
                    'Use one of the browser ids specified in config file: ' +
                    browsersFromConfig.join(', '));
            }
        });
    }

    this.readTests = function(paths) {
        paths = paths || [getDefaultSpecsDir()];
        return pathUtils.expandPaths(paths)
            .then(function(expanded) {
                var rootSuite = Suite.create('');
                exposeTestsApi(publicApi, rootSuite);

                expanded.forEach(requireWithNoCache);
                return rootSuite;
            });
    };

    this.gather = function(paths, options) {
        return executeRunner(
            new ScreenShooter(this.config),
            paths,
            options);
    };

    this.test = function(paths, options) {
        return executeRunner(
            new Tester(this.config, {tempDir: options.tempDir}),
            paths,
            options
        );
    };

    this.getScreenshotPath = function(suite, stateName, browserId) {
        return this.config.getScreenshotPath(suite, stateName, browserId);
    };

    this.buildDiff = util.deprecate(function(referencePath, currentPath, diffPath) {
            return Image.buildDiff({
                reference: referencePath,
                current: currentPath,
                diff: diffPath,
                diffColor: this.config.diffColor,
                strictComparison: this.config.strictComparison,
                tolerance: this.config.tolerance
            });
        }, 'gemini.buildDiff' + chalk.red(' is deprecated.\n') +
        'Use ' + chalk.green('testResult.saveDiffTo(path)') +
        ' in ' + chalk.green('endTest') + ' event handler for more accurate diff'
    );

    this.getBrowserCapabilites = function(browserId) {
        return this.config.browsers[browserId];
    };

    Object.defineProperty(this, 'browserIds', {
        enumerable: true,
        get: function() {
            return this.config.getBrowserIds();
        }
    });
}

// Gemini needs to inherit from QEmitter rather then
// EventEmmiter because it needs to allow `startRunner`
// event handler delay the execution of tests
util.inherits(Gemini, QEmitter);

function applyReporter(runner, reporter) {
    if (typeof reporter === 'string') {
        try {
            reporter = require('./reporters/' + reporter);
        } catch (e) {
            if (e.code === 'MODULE_NOT_FOUND') {
                throw new Error('No such reporter: ' + reporter);
            }
            throw e;
        }
    }
    if (typeof reporter !== 'function') {
        throw new TypeError('Reporter must be a string or a function');
    }

    reporter(runner);
}

module.exports = Gemini;
<commit_msg>Fix public APIs required by GUI<commit_after>'use strict';
var QEmitter = require('qemitter'),
    util = require('util'),
    path = require('path'),

    _ = require('lodash'),
    chalk = require('chalk'),
    ScreenShooter = require('./screen-shooter'),
    Tester = require('./tester'),
    Config = require('./config'),
    Suite = require('./suite'),
    suiteUtils = require('./suite-util'),

    pathUtils = require('./path-utils'),
    publicApi = require('./public-api'),
    RunnnerStats = require('./stats'),
    RunnerEvents = require('./constants/runner-events'),

    exposeTestsApi = require('./tests-api'),

    GeminiError = require('./errors/gemini-error'),
    Image = require('./image'),

    DEFAULT_CFG_NAME = '.gemini.yml',
    DEFAULT_SPECS_DIR = 'gemini';

function requireWithNoCache(moduleName) {
    var result = require(moduleName);
    delete require.cache[moduleName];
    return result;
}

function Gemini(config, overrides) {
    QEmitter.call(this);
    config = config || DEFAULT_CFG_NAME;
    this.config = new Config(config, overrides);
    var _this = this;

    require('./plugins').load(_this, this.config);

    function getDefaultSpecsDir() {
        return path.join(_this.config.system.projectRoot, DEFAULT_SPECS_DIR);
    }

    function executeRunner(runnerInstance, paths, options) {
        if (!options) {
            //if there are only two arguments, they are
            //(runnerInstance, options) and paths are
            //the default.
            options = paths;
            paths = [getDefaultSpecsDir()];
        }
        options = options || {};
        options.reporters = options.reporters || [];

        var envBrowsers = process.env.GEMINI_BROWSERS? process.env.GEMINI_BROWSERS.split(',') : null;
        options.browsers = options.browsers || envBrowsers;

        if (options.browsers) {
            areAllBrowsersFromConfig(options.browsers, _this.browserIds);
            runnerInstance.setTestBrowsers(options.browsers);
        }

        return _this.readTests(paths)
            .then(function(rootSuite) {
                return _this.emitAndWait(RunnerEvents.START_RUNNER, runnerInstance)
                    .thenResolve(rootSuite);
            })
            .then(function(rootSuite) {
                var suites = suiteUtils.flattenSuites(rootSuite);

                if (options.grep) {
                    suites = suites.filter(function(suite) {
                        return options.grep.test(suite.fullName);
                    });
                }

                options.reporters.forEach(_.partial(applyReporter, runnerInstance));

                var stats = new RunnnerStats(runnerInstance);

                return runnerInstance.run(suites)
                    .thenResolve(stats.get());
            })
            .then(function(data) {
                return _this.emitAndWait(RunnerEvents.END_RUNNER, runnerInstance, data)
                    .thenResolve(data);
            });
    }

    function areAllBrowsersFromConfig(browsers, browsersFromConfig) {
        browsers.forEach(function(name) {
            if (browsersFromConfig.indexOf(name) === -1) {
                throw new GeminiError('Unknown browser id: ' + name,
                    'Use one of the browser ids specified in config file: ' +
                    browsersFromConfig.join(', '));
            }
        });
    }

    this.readTests = function(paths) {
        paths = paths || [getDefaultSpecsDir()];
        return pathUtils.expandPaths(paths)
            .then(function(expanded) {
                var rootSuite = Suite.create('');
                exposeTestsApi(publicApi, rootSuite);

                expanded.forEach(requireWithNoCache);
                return rootSuite;
            });
    };

    this.gather = function(paths, options) {
        return executeRunner(
            new ScreenShooter(this.config),
            paths,
            options);
    };

    this.test = function(paths, options) {
        return executeRunner(
            new Tester(this.config, {tempDir: options.tempDir}),
            paths,
            options
        );
    };

    this.getScreenshotPath = function(suite, stateName, browserId) {
        return this.config.forBrowser(browserId).getScreenshotPath(suite, stateName);
    };

    this.buildDiff = util.deprecate(function(referencePath, currentPath, diffPath) {
            return Image.buildDiff({
                reference: referencePath,
                current: currentPath,
                diff: diffPath,
                diffColor: this.config.diffColor,
                strictComparison: this.config.strictComparison,
                tolerance: this.config.tolerance
            });
        }, 'gemini.buildDiff' + chalk.red(' is deprecated.\n') +
        'Use ' + chalk.green('testResult.saveDiffTo(path)') +
        ' in ' + chalk.green('endTest') + ' event handler for more accurate diff'
    );

    this.getBrowserCapabilites = function(browserId) {
        return this.config.forBrowser(browserId).desiredCapabilities;
    };

    Object.defineProperty(this, 'browserIds', {
        enumerable: true,
        get: function() {
            return this.config.getBrowserIds();
        }
    });
}

// Gemini needs to inherit from QEmitter rather then
// EventEmmiter because it needs to allow `startRunner`
// event handler delay the execution of tests
util.inherits(Gemini, QEmitter);

function applyReporter(runner, reporter) {
    if (typeof reporter === 'string') {
        try {
            reporter = require('./reporters/' + reporter);
        } catch (e) {
            if (e.code === 'MODULE_NOT_FOUND') {
                throw new Error('No such reporter: ' + reporter);
            }
            throw e;
        }
    }
    if (typeof reporter !== 'function') {
        throw new TypeError('Reporter must be a string or a function');
    }

    reporter(runner);
}

module.exports = Gemini;
<|endoftext|>


<commit_before>/* Copyright (C) 2009-2015:
   Gabes Jean, naparuba@gmail.com
   Gerhard Lausser, Gerhard.Lausser@consol.de
   Gregory Starck, g.starck@gmail.com
   Hartmut Goebel, h.goebel@goebel-consult.de
   Andreas Karfusehr, andreas@karfusehr.de
   Frederic Mohier, frederic.mohier@gmail.com

   This file is part of Shinken.

   Shinken is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   Shinken is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with Shinken.  If not, see <http://www.gnu.org/licenses/>.
*/

var custom_logs=false;

// when we show a custom view tab, we lazy load it :D
function show_custom_view(elt){
   var hname = elt.data('element');
   var cvname = elt.data('name');
   var cvconf = elt.data('conf');
   if (custom_logs) console.debug('Request for loading custom view: ', cvname, ' for ', hname, ', configuration: ', cvconf);

   var _t = new Date().getTime();
   $("#cv"+cvname+"_"+cvconf+" .panel-body").load('/cv/'+cvname+'/'+hname+'/'+cvconf+'?_='+_t, function(response, status, xhr) {
      if (status == "error") {
         // Let us try with default host view ... we never know :-)
         // First rename tab ...
         $("#tab-cv-"+cvname+"-"+cvconf)
            .data('name', 'host')
            .data('conf', 'replace')
            .attr('id', 'tab-cv-host-replace')
            .children('a')
            .attr('href', '#cvhost_replace');
         $("#cv"+cvname+"_"+cvconf)
            .data('name', 'host')
            .data('conf', 'replace')
            .attr('id', 'cvhost_replace');
         $("#cvhost_replace .panel-body").load('/cv/host/'+hname+'/replace?_='+_t, function(response, status, xhr) {
            if (status == "error") {
               $('#cvhost_replace').html('
Sorry but there really was an error: ' + xhr.status + ' ' + xhr.statusText+'
');
            } else {
               // Panel container height is updated with the custom view height ...
               $("#cvhost_replace .panel-body").each(function() {
                  $(this).css('height', $('#cvhost_replace').height() + "px");
               });
               $('#cvhost_replace').prepend('
Replacing the custom view \''+cvname+'\' that was not found ... hope it will help anyway :-)
');
               if (custom_logs) console.debug('Loaded custom view (after error): ', 'cvhost_replace');
            }
         });
      } else {
         // Panel container height is updated with the custom view height ...
         $("#cv"+cvname+"_"+cvconf+" .panel-body").each(function() {
            $(this).css('height', $('#cv_'+cvname+"_"+cvconf).height() + "px");
         });
         if (custom_logs) console.debug('Loaded custom view: ', cvname+"_"+cvconf);
      }
   });
}

function reload_custom_view(elt){
   var hname = elt.data('element');
   var cvname = elt.data('name');
   var cvconf = elt.data('conf');

   // Be sure to remove the panel from already loaded panels, else it won't load
   delete _already_loaded[cvname+cvconf];
   show_custom_view(elt);
}<commit_msg>#252 - remove alerting message<commit_after>/* Copyright (C) 2009-2015:
   Gabes Jean, naparuba@gmail.com
   Gerhard Lausser, Gerhard.Lausser@consol.de
   Gregory Starck, g.starck@gmail.com
   Hartmut Goebel, h.goebel@goebel-consult.de
   Andreas Karfusehr, andreas@karfusehr.de
   Frederic Mohier, frederic.mohier@gmail.com

   This file is part of Shinken.

   Shinken is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   Shinken is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with Shinken.  If not, see <http://www.gnu.org/licenses/>.
*/

var custom_logs=false;

// when we show a custom view tab, we lazy load it :D
function show_custom_view(elt){
   var hname = elt.data('element');
   var cvname = elt.data('name');
   var cvconf = elt.data('conf');
   if (custom_logs) console.debug('Request for loading custom view: ', cvname, ' for ', hname, ', configuration: ', cvconf);

   var _t = new Date().getTime();
   $("#cv"+cvname+"_"+cvconf+" .panel-body").load('/cv/'+cvname+'/'+hname+'/'+cvconf+'?_='+_t, function(response, status, xhr) {
      if (status == "error") {
         // Let us try with default host view ... we never know :-)
         // First rename tab ...
         $("#tab-cv-"+cvname+"-"+cvconf)
            .data('name', 'host')
            .data('conf', 'replace')
            .attr('id', 'tab-cv-host-replace')
            .children('a')
            .attr('href', '#cvhost_replace');
         $("#cv"+cvname+"_"+cvconf)
            .data('name', 'host')
            .data('conf', 'replace')
            .attr('id', 'cvhost_replace');
         $("#cvhost_replace .panel-body").load('/cv/host/'+hname+'/replace?_='+_t, function(response, status, xhr) {
            if (status == "error") {
               $('#cvhost_replace').html('
Sorry but there really was an error: ' + xhr.status + ' ' + xhr.statusText+'
');
            } else {
               // Panel container height is updated with the custom view height ...
               $("#cvhost_replace .panel-body").each(function() {
                  $(this).css('height', $('#cvhost_replace').height() + "px");
               });
               $('#cvhost_replace').prepend('
Replacing the custom view \''+cvname+'\' that was not found ... hope it will help anyway :-)
');
               setTimeout(function(){
                  $('#cvhost_replace div.alert').remove();
               }, 10000);
               if (custom_logs) console.debug('Loaded custom view (after error): ', 'cvhost_replace');
            }
         });
      } else {
         // Panel container height is updated with the custom view height ...
         $("#cv"+cvname+"_"+cvconf+" .panel-body").each(function() {
            $(this).css('height', $('#cv_'+cvname+"_"+cvconf).height() + "px");
         });
         if (custom_logs) console.debug('Loaded custom view: ', cvname+"_"+cvconf);
      }
   });
}

function reload_custom_view(elt){
   var hname = elt.data('element');
   var cvname = elt.data('name');
   var cvconf = elt.data('conf');

   // Be sure to remove the panel from already loaded panels, else it won't load
   delete _already_loaded[cvname+cvconf];
   show_custom_view(elt);
}<|endoftext|>


<commit_before>var tokTypes = require("babel").acorn.tokTypes;
var traverse = require("babel").traverse;
var t        = require("babel").types;

exports.toToken = function (token) {
  var type = token.type;

  if (type === tokTypes.name) {
    token.type = "Identifier";
  } else if (type === tokTypes.semi || type === tokTypes.comma || type === tokTypes.parenL || type === tokTypes.parenR || type === tokTypes.braceL || type === tokTypes.braceR) {
    token.type = "Punctuator";
    token.value = type.type;
  }

  return token;
};

exports.toAST = function (ast) {
  traverse(ast, astTransformVisitor);
};

var astTransformVisitor = {
  noScope: true,
  enter: function (node) {
    if (t.isSpreadProperty(node)) {
      node.type = "Property";
      node.kind = "init";
      node.computed = false;
      node.key = node.value = node.argument;
      delete node.argument;
    }

    if (t.isClassProperty(node)) {
      // eslint doesn't like these
      this.remove();
    }

    if (t.isImportBatchSpecifier(node)) {
      // ImportBatchSpecifier<name> => ImportNamespaceSpecifier<id>
      node.type = "ImportNamespaceSpecifier";
      node.id = node.name;
      delete node.name;
    }

    // JSX

    if (t.isJSXIdentifier(node)) {
      if (node.name === "this" && t.isReferenced(node, parent)) {
        return t.thisExpression();
      } else {
        node.type = "Identifier";
      }
    }

    if (t.isJSXMemberExpression(node)) {
      node.type = "MemberExpression";
    }

    // functions

    if (t.isFunction(node)) {
      node.defaults = [];
      node.params = node.params.map(function (param) {
        if (t.isAssignmentPattern(param)) {
          node.defaults.push(param.right);
          return param.left;
        } else {
          if (t.isRestElement(param)) param = param.argument;
          node.defaults.push(null);
          return param;
        }
      });
    }

    if (t.isArrowFunctionExpression(node)) {
      node.type = "FunctionExpression";
    }
  }
};
<commit_msg>turn class declarations into variable declarations - fixes babel/babel-eslint#8<commit_after>var tokTypes = require("babel").acorn.tokTypes;
var traverse = require("babel").traverse;
var t        = require("babel").types;

exports.toToken = function (token) {
  var type = token.type;

  if (type === tokTypes.name) {
    token.type = "Identifier";
  } else if (type === tokTypes.semi || type === tokTypes.comma || type === tokTypes.parenL || type === tokTypes.parenR || type === tokTypes.braceL || type === tokTypes.braceR) {
    token.type = "Punctuator";
    token.value = type.type;
  }

  return token;
};

exports.toAST = function (ast) {
  traverse(ast, astTransformVisitor);
};

var astTransformVisitor = {
  noScope: true,
  enter: function (node) {
    if (t.isSpreadProperty(node)) {
      node.type = "Property";
      node.kind = "init";
      node.computed = false;
      node.key = node.value = node.argument;
      delete node.argument;
    }

    if (t.isImportBatchSpecifier(node)) {
      // ImportBatchSpecifier<name> => ImportNamespaceSpecifier<id>
      node.type = "ImportNamespaceSpecifier";
      node.id = node.name;
      delete node.name;
    }

    // classes

    if (t.isClassDeclaration(node)) {
      return t.variableDeclaration("let", [
        t.variableDeclarator(node.id, node)
      ]);
    }

    if (t.isClassProperty(node)) {
      // eslint doesn't like these
      this.remove();
    }

    // JSX

    if (t.isJSXIdentifier(node)) {
      if (node.name === "this" && t.isReferenced(node, parent)) {
        return t.thisExpression();
      } else {
        node.type = "Identifier";
      }
    }

    if (t.isJSXMemberExpression(node)) {
      node.type = "MemberExpression";
    }

    // functions

    if (t.isFunction(node)) {
      node.defaults = [];
      node.params = node.params.map(function (param) {
        if (t.isAssignmentPattern(param)) {
          node.defaults.push(param.right);
          return param.left;
        } else {
          if (t.isRestElement(param)) param = param.argument;
          node.defaults.push(null);
          return param;
        }
      });
    }

    if (t.isArrowFunctionExpression(node)) {
      node.type = "FunctionExpression";
    }
  }
};
<|endoftext|>


<commit_before>/**
 * HTTP client.
 */

'use strict';

/**
 * Module dependencies.
 */

var events = require('events');
var http = require('http');
var https = require('https');
var stream = require('stream');
var url = require('url');
var util = require('util');

var constants = require('./constants');
var errors = require('./errors');
var meta = require('../package.json');
var utils = require('./utils');

/**
 * Client
 */

function Client(opts) {
  if (!(this instanceof Client)) {
    return new Client(opts);
  }

  events.EventEmitter.call(this);

  opts = opts || {};

  if (typeof opts === 'string') {
    opts = { baseUrl: opts };
  }

  if (!opts.baseUrl) {
    throw errors.Validation('baseUrl required');
  }

  if (!(opts.baseUrl instanceof url.Url)) {
    if (typeof opts.baseUrl !== 'string') {
      throw errors.Validation('baseUrl must be a string: ' + opts.baseUrl);
    }

    opts.baseUrl = url.parse(opts.baseUrl);
  }

  opts.baseUrl.path = opts.baseUrl.pathname;
  opts.baseUrl = utils.pick(opts.baseUrl,
    'auth', 'hostname', 'path', 'port', 'protocol');

  if (opts.baseUrl.path === '/') {
    opts.baseUrl.path = '';
  } else if (opts.baseUrl.path[opts.baseUrl.path.length - 1] === '/') {
    throw errors.Validation('baseUrl must not end with a forward slash');
  }

  opts.headers = utils.mergeHeaders(opts.headers);
  opts.tags = opts.tags || [];

  if (opts.name && !~opts.tags.indexOf(opts.name)) {
    opts.tags = opts.tags.concat(opts.name);
  }

  opts.encoders = utils.merge(constants.ENCODERS, opts.encoders);
  opts.decoders = utils.merge(constants.DECODERS, opts.decoders);

  this._opts = opts;
  this._exts = {};
}

util.inherits(Client, events.EventEmitter);

/**
 * Add information to error
 */

Client.prototype._err = function(err, opts) {
  if (!err) return err;

  if (!(err instanceof Error)) err = new Error(err);

  if (opts && opts.name) {
    err.message = util.format('%s: %s', opts.name, err.message);
  }

  if (this._opts.name) {
    err.message = util.format('%s: %s', this._opts.name, err.message);
  }

  return err;
};

/**
 * Register an extension
 */

Client.prototype._ext = function(eventName, callback) {
  if (!eventName || typeof eventName !== 'string') {
    throw this._err(errors.Validation('extension eventName required'));
  }

  if (typeof callback !== 'function') {
    throw this._err(errors.Validation('extension callback required'));
  }

  if (!this._exts[eventName]) this._exts[eventName] = [];

  this._exts[eventName].push(callback);
};

/**
 * Register a plugin
 */

Client.prototype._plugin = function(plugin, options) {
  if (!plugin) {
    throw this._err(errors.Validation('plugin required'));
  }

  if (typeof plugin.register !== 'function') {
    throw this._err(errors.Validation('plugin must have register function'));
  }

  var attributes = plugin.register.attributes;

  if (!attributes) {
    throw this._err(errors.Validation('plugin attributes required'));
  }

  if (!attributes.name) {
    throw this._err(errors.Validation('plugin attributes name required'));
  }

  if (!attributes.version) {
    throw this._err(errors.Validation('plugin attributes version required'));
  }

  return plugin.register(this, options || {});
};

/**
 * Log request events
 */

Client.prototype._log = function(tags, data) {
  return this.emit('log', tags, data);
};

/**
 * Encode
 */

Client.prototype._encode = function(mime, value) {
  if (!this._opts.encoders[mime]) {
    throw errors.Codec('unknown encoder: ' + mime);
  }

  try {
    return this._opts.encoders[mime](value);
  } catch (err) {
    err.message = 'encode (' + mime + ') failed: ' + err.message;
    throw errors.Codec(err);
  }
};

/**
 * Decode
 */

Client.prototype._decode = function(mime, value) {
  if (!this._opts.decoders[mime]) {
    throw errors.Codec('unknown decoder: ' + mime);
  }

  try {
    return this._opts.decoders[mime](value);
  } catch (err) {
    err.message = 'decode (' + mime + ') failed: ' + err.message;
    throw errors.Codec(err);
  }
};

/**
 * Push ext list
 */

Client.prototype.__push = function(request, name) {
  if (this._exts[name]) {
    request._stack.push.apply(request._stack, this._exts[name]);
  }

  if (request.opts && request.opts.exts && request.opts.exts[name]) {
    if (Array.isArray(request.opts.exts[name])) {
      request._stack.push.apply(request._stack, request.opts.exts[name]);
    } else {
      request._stack.push(request.opts.exts[name]);
    }
  }
};

/**
 * Run request pipeline
 */

Client.prototype._request = function(opts) {
  var self = this;

  var request;

  if (this.__request) {
    request = this.__request;
    opts = request.opts;
    self = request._client;
  } else {
    request = {
      _args: Array.prototype.slice.call(arguments),
      _client: this,
      opts: opts,
      state: {},
    };

    if (!opts) opts = request.opts = {};

    if (request._args.length > 1) {
      request._callback = request._args[request._args.length - 1];
    } else {
      return self.emit('error', self._err(
        errors.Validation('callback required'), opts));
    }

    // if ctx is an event emitter we use it to abort requests when done is
    // emitted
    if (opts.ctx instanceof events.EventEmitter) {
      request.ctx = opts.ctx;
    }

    // combine global and request tags
    opts.tags = (opts.tags || []).concat(self._opts.tags);

    // inject request name into tags if not already defined
    if (opts.name && !~opts.tags.indexOf(opts.name)) {
      opts.tags.push(opts.name);
    }

    if (!opts.headers) opts.headers = {};
    if (!opts.params) opts.params = {};
    if (!opts.query) opts.query = {};

    // restart request
    request.retry = function() {
      if (request._retryable === false) {
        throw errors.Validation('request is not retryable');
      }

      self._log(['papi', 'request', 'retry'].concat(request.opts.tags));

      delete request.body;
      delete request.err;
      delete request.req;
      delete request.res;
      delete request.transport;

      self._request.call({ __request: request });
    };

    request._stack = [];

    request._stack.push(self.__create);

    self.__push(request, 'onRequest');

    request._stack.push(self.__execute);

    self.__push(request, 'onResponse');

    request._stack.push.apply(
      request._stack,
      request._args.slice(1, request._args.length - 1)
    );
  }

  var i = 0;
  function next(err) {
    if (err) return request._callback(self._err(err, opts));

    // middlware can call next(false, args...) to stop middleware
    if (err === false) {
      return request._callback.apply(null,
        Array.prototype.slice.call(arguments, 1));
    }

    var fn = request._stack[i++];
    if (fn) {
      fn.call(self, request, next);
    } else {
      request._callback.call(self, self._err(request.err, opts), request.res);
    }
  }

  next();
};

/**
 * Create HTTP request
 */

Client.prototype.__create = function(request, next) {
  var self = this;

  var opts = request.opts;
  var path = opts.path;

  if (typeof path !== 'string') {
    return next(errors.Validation('path required'));
  }

  var headers = utils.mergeHeaders(self._opts.headers, opts.headers);

  // path
  try {
    path = path.replace(/\{(\w+)\}/g, function(src, dst) {
      if (!opts.params.hasOwnProperty(dst)) {
        throw errors.Validation('missing param: ' + dst);
      }

      var part = opts.params[dst] || '';

      // optionally disable param encoding
      return part.encode === false && part.toString ?
        part.toString() : encodeURIComponent(part);
    });
  } catch (err) {
    return next(err);
  }

  // query
  if (!utils.isEmpty(opts.query)) {
    try {
      path += '?' + self._encode('application/x-www-form-urlencoded',
                                 opts.query).toString();
    } catch (err) {
      return next(err);
    }
  }

  // body
  if (opts.body !== undefined) {
    var mime = constants.MIME_ALIAS[opts.type] ||
      headers['content-type'] ||
      constants.MIME_ALIAS[self._opts.type];

    var isFunction = typeof opts.body === 'function';

    if (isFunction) {
      try {
        request.body = opts.body();
      } catch (err) {
        return next(err);
      }
    } else {
      request.body = opts.body;
    }

    var isBuffer = Buffer.isBuffer(request.body);
    var isStream = request.body instanceof stream.Readable;

    if (!isBuffer && !isStream && !mime) {
      return next(errors.Validation('type required'));
    }

    if (!isBuffer && !isStream) {
      if (self._opts.encoders[mime]) {
        try {
          request.body = this._encode(mime, request.body);
        } catch (err) {
          return next(err);
        }
      } else {
        return next(errors.Codec('type is unknown: ' + mime));
      }
    }

    if (!headers['content-type'] && mime) {
      headers['content-type'] = mime + '; charset=' + constants.CHARSET;
    }

    if (isStream) {
      if (!isFunction) request._retryable = false;
    } else {
      headers['content-length'] = request.body.length;
    }
  } else if (!~constants.EXCLUDE_CONTENT_LENGTH.indexOf(opts.method)) {
    headers['content-length'] = 0;
  }

  // response pipe
  if (opts.pipe) {
    var isPipeFunction = typeof opts.pipe === 'function';

    if (isPipeFunction) {
      try {
        request.pipe = opts.pipe();
      } catch (err) {
        return next(err);
      }
    } else {
      request.pipe = opts.pipe;

      request._retryable = false;
    }

    if (!(request.pipe instanceof stream.Writable)) {
      return next(errors.Validation('pipe must be a writable stream'));
    }
  }

  // build http.request options
  request.req = utils.merge(
    utils.pick(self._opts, constants.CLIENT_OPTIONS),
    utils.pick(self._opts.baseUrl, 'auth', 'hostname', 'port', 'path'),
    utils.pick(opts, constants.REQUEST_OPTIONS),
    { headers: headers }
  );

  // append request path to baseUrl
  request.req.path += path;

  // pick http transport
  if (self._opts.baseUrl.protocol === 'https:') {
    request.transport = https;
    if (!request.req.port) request.req.port = 443;
  } else {
    request.transport = http;
    if (!request.req.port) request.req.port = 80;
  }

  if (request.req.auth === null) delete request.req.auth;

  next();
};

/**
 * Execute HTTP request
 */

Client.prototype.__execute = function(request, next) {
  var self = this;

  if (request.ctx) {
    if (request.ctx.canceled === true) {
      return next(errors.Validation('ctx already canceled'));
    } else if (request.ctx.finished === true) {
      return next(errors.Validation('ctx already finished'));
    }
  }

  var done = false;

  var opts = request.opts;

  var abort;
  var timeoutId;
  var timeout = opts.hasOwnProperty('timeout') ?
    opts.timeout : self._opts.timeout;

  self._log(['papi', 'request'].concat(opts.tags), request.req);

  var req = request.transport.request(request.req);

  var userAgent = req.getHeader('user-agent');

  if (userAgent === undefined) {
    req.setHeader('user-agent', 'papi/' + meta.version);
  } else if (userAgent === null) {
    req.removeHeader('user-agent');
  }

  req.on('error', function(err) {
    self._log(['papi', 'request', 'error'].concat(opts.tags), err);

    if (done) return;
    done = true;

    if (abort) request.ctx.removeListener('cancel', abort);
    if (timeoutId) clearTimeout(timeoutId);

    request.err = err;
    next();
  });

  if (request.ctx) {
    abort = function() {
      req.abort();
      req.emit('error', errors.Abort('request aborted'));
    };

    request.ctx.once('cancel', abort);
  }

  // set request and absolute timeout
  if (timeout && timeout > 0) {
    timeoutId = setTimeout(function() {
      req.emit('timeout');
      req.abort();
    }, timeout);

    req.setTimeout(timeout);
  }

  req.on('timeout', function(err) {
    self._log(['papi', 'request', 'error', 'timeout'].concat(opts.tags));
    if (err) {
      err = errors.Timeout(err);
    } else {
      err = errors.Timeout('request timed out (' + timeout + 'ms)');
    }
    req.emit('error', err);
  });

  req.on('response', function(res) {
    var chunks = [];
    var bodyLength = 0;

    self._log(['papi', 'response'].concat(opts.tags), {
      method: opts.method,
      path: req.path,
      statusCode: res.statusCode,
      headers: res.headers,
      remoteAddress: res.connection.remoteAddress,
      remotePort: res.connection.remotePort,
    });

    request.res = res;

    if (request.pipe) {
      res.pipe(request.pipe);
    } else {
      res.on('data', function(chunk) {
        chunks.push(chunk);
        bodyLength += chunk.length;
      });
    }

    res.on('end', function() {
      if (done) return;
      done = true;

      if (abort) request.ctx.removeListener('cancel', abort);
      if (timeoutId) clearTimeout(timeoutId);

      // body content mime
      var mime;

      // decode body
      if (bodyLength) {
        res.body = Buffer.concat(chunks, bodyLength);

        // don't decode if user explicitly asks for buffer
        if (!opts.buffer) {
          mime = (res.headers['content-type'] || '').split(';')[0].trim();

          if (self._opts.decoders[mime]) {
            try {
              res.body = self._decode(mime, res.body);
            } catch (err) {
              request.err = err;
              return next();
            }
          }
        }
      }

      // any non-200 is consider an error
      if (Math.floor(res.statusCode / 100) !== 2) {
        var err = errors.Response();

        if (res.body && mime === 'text/plain' && res.body.length < 80) {
          err.message = res.body;
        }

        if (!err.message) {
          if (http.STATUS_CODES[res.statusCode]) {
            err.message = http.STATUS_CODES[res.statusCode].toLowerCase();
          } else {
            err.message = 'request failed: ' + res.statusCode;
          }
        }

        request.err = err;
      }

      next();
    });
  });

  if (request.body instanceof stream.Readable) {
    request.body.pipe(req);
  } else {
    req.end(request.body);
  }
};

/**
 * Shortcuts
 */

constants.METHODS.forEach(function(method) {
  var reqMethod = method.toUpperCase();

  Client.prototype['_' + method] = function(opts) {
    var args;

    if (typeof opts === 'string') {
      opts = { path: opts, method: reqMethod };

      args = Array.prototype.slice.call(arguments);
      args[0] = opts;

      return this._request.apply(this, args);
    } else if (!opts) {
      args = Array.prototype.slice.call(arguments);
      args[0] = {};

      return this._request.apply(this, args);
    }

    opts.method = reqMethod;

    return this._request.apply(this, arguments);
  };
});

/**
 * Module exports.
 */

exports.Client = Client;
<commit_msg>Support streaming to express response<commit_after>/**
 * HTTP client.
 */

'use strict';

/**
 * Module dependencies.
 */

var events = require('events');
var http = require('http');
var https = require('https');
var stream = require('stream');
var url = require('url');
var util = require('util');

var constants = require('./constants');
var errors = require('./errors');
var meta = require('../package.json');
var utils = require('./utils');

/**
 * Client
 */

function Client(opts) {
  if (!(this instanceof Client)) {
    return new Client(opts);
  }

  events.EventEmitter.call(this);

  opts = opts || {};

  if (typeof opts === 'string') {
    opts = { baseUrl: opts };
  }

  if (!opts.baseUrl) {
    throw errors.Validation('baseUrl required');
  }

  if (!(opts.baseUrl instanceof url.Url)) {
    if (typeof opts.baseUrl !== 'string') {
      throw errors.Validation('baseUrl must be a string: ' + opts.baseUrl);
    }

    opts.baseUrl = url.parse(opts.baseUrl);
  }

  opts.baseUrl.path = opts.baseUrl.pathname;
  opts.baseUrl = utils.pick(opts.baseUrl,
    'auth', 'hostname', 'path', 'port', 'protocol');

  if (opts.baseUrl.path === '/') {
    opts.baseUrl.path = '';
  } else if (opts.baseUrl.path[opts.baseUrl.path.length - 1] === '/') {
    throw errors.Validation('baseUrl must not end with a forward slash');
  }

  opts.headers = utils.mergeHeaders(opts.headers);
  opts.tags = opts.tags || [];

  if (opts.name && !~opts.tags.indexOf(opts.name)) {
    opts.tags = opts.tags.concat(opts.name);
  }

  opts.encoders = utils.merge(constants.ENCODERS, opts.encoders);
  opts.decoders = utils.merge(constants.DECODERS, opts.decoders);

  this._opts = opts;
  this._exts = {};
}

util.inherits(Client, events.EventEmitter);

/**
 * Add information to error
 */

Client.prototype._err = function(err, opts) {
  if (!err) return err;

  if (!(err instanceof Error)) err = new Error(err);

  if (opts && opts.name) {
    err.message = util.format('%s: %s', opts.name, err.message);
  }

  if (this._opts.name) {
    err.message = util.format('%s: %s', this._opts.name, err.message);
  }

  return err;
};

/**
 * Register an extension
 */

Client.prototype._ext = function(eventName, callback) {
  if (!eventName || typeof eventName !== 'string') {
    throw this._err(errors.Validation('extension eventName required'));
  }

  if (typeof callback !== 'function') {
    throw this._err(errors.Validation('extension callback required'));
  }

  if (!this._exts[eventName]) this._exts[eventName] = [];

  this._exts[eventName].push(callback);
};

/**
 * Register a plugin
 */

Client.prototype._plugin = function(plugin, options) {
  if (!plugin) {
    throw this._err(errors.Validation('plugin required'));
  }

  if (typeof plugin.register !== 'function') {
    throw this._err(errors.Validation('plugin must have register function'));
  }

  var attributes = plugin.register.attributes;

  if (!attributes) {
    throw this._err(errors.Validation('plugin attributes required'));
  }

  if (!attributes.name) {
    throw this._err(errors.Validation('plugin attributes name required'));
  }

  if (!attributes.version) {
    throw this._err(errors.Validation('plugin attributes version required'));
  }

  return plugin.register(this, options || {});
};

/**
 * Log request events
 */

Client.prototype._log = function(tags, data) {
  return this.emit('log', tags, data);
};

/**
 * Encode
 */

Client.prototype._encode = function(mime, value) {
  if (!this._opts.encoders[mime]) {
    throw errors.Codec('unknown encoder: ' + mime);
  }

  try {
    return this._opts.encoders[mime](value);
  } catch (err) {
    err.message = 'encode (' + mime + ') failed: ' + err.message;
    throw errors.Codec(err);
  }
};

/**
 * Decode
 */

Client.prototype._decode = function(mime, value) {
  if (!this._opts.decoders[mime]) {
    throw errors.Codec('unknown decoder: ' + mime);
  }

  try {
    return this._opts.decoders[mime](value);
  } catch (err) {
    err.message = 'decode (' + mime + ') failed: ' + err.message;
    throw errors.Codec(err);
  }
};

/**
 * Push ext list
 */

Client.prototype.__push = function(request, name) {
  if (this._exts[name]) {
    request._stack.push.apply(request._stack, this._exts[name]);
  }

  if (request.opts && request.opts.exts && request.opts.exts[name]) {
    if (Array.isArray(request.opts.exts[name])) {
      request._stack.push.apply(request._stack, request.opts.exts[name]);
    } else {
      request._stack.push(request.opts.exts[name]);
    }
  }
};

/**
 * Run request pipeline
 */

Client.prototype._request = function(opts) {
  var self = this;

  var request;

  if (this.__request) {
    request = this.__request;
    opts = request.opts;
    self = request._client;
  } else {
    request = {
      _args: Array.prototype.slice.call(arguments),
      _client: this,
      opts: opts,
      state: {},
    };

    if (!opts) opts = request.opts = {};

    if (request._args.length > 1) {
      request._callback = request._args[request._args.length - 1];
    } else {
      return self.emit('error', self._err(
        errors.Validation('callback required'), opts));
    }

    // if ctx is an event emitter we use it to abort requests when done is
    // emitted
    if (opts.ctx instanceof events.EventEmitter) {
      request.ctx = opts.ctx;
    }

    // combine global and request tags
    opts.tags = (opts.tags || []).concat(self._opts.tags);

    // inject request name into tags if not already defined
    if (opts.name && !~opts.tags.indexOf(opts.name)) {
      opts.tags.push(opts.name);
    }

    if (!opts.headers) opts.headers = {};
    if (!opts.params) opts.params = {};
    if (!opts.query) opts.query = {};

    // restart request
    request.retry = function() {
      if (request._retryable === false) {
        throw errors.Validation('request is not retryable');
      }

      self._log(['papi', 'request', 'retry'].concat(request.opts.tags));

      delete request.body;
      delete request.err;
      delete request.req;
      delete request.res;
      delete request.transport;

      self._request.call({ __request: request });
    };

    request._stack = [];

    request._stack.push(self.__create);

    self.__push(request, 'onRequest');

    request._stack.push(self.__execute);

    self.__push(request, 'onResponse');

    request._stack.push.apply(
      request._stack,
      request._args.slice(1, request._args.length - 1)
    );
  }

  var i = 0;
  function next(err) {
    if (err) return request._callback(self._err(err, opts));

    // middlware can call next(false, args...) to stop middleware
    if (err === false) {
      return request._callback.apply(null,
        Array.prototype.slice.call(arguments, 1));
    }

    var fn = request._stack[i++];
    if (fn) {
      fn.call(self, request, next);
    } else {
      request._callback.call(self, self._err(request.err, opts), request.res);
    }
  }

  next();
};

/**
 * Create HTTP request
 */

Client.prototype.__create = function(request, next) {
  var self = this;

  var opts = request.opts;
  var path = opts.path;

  if (typeof path !== 'string') {
    return next(errors.Validation('path required'));
  }

  var headers = utils.mergeHeaders(self._opts.headers, opts.headers);

  // path
  try {
    path = path.replace(/\{(\w+)\}/g, function(src, dst) {
      if (!opts.params.hasOwnProperty(dst)) {
        throw errors.Validation('missing param: ' + dst);
      }

      var part = opts.params[dst] || '';

      // optionally disable param encoding
      return part.encode === false && part.toString ?
        part.toString() : encodeURIComponent(part);
    });
  } catch (err) {
    return next(err);
  }

  // query
  if (!utils.isEmpty(opts.query)) {
    try {
      path += '?' + self._encode('application/x-www-form-urlencoded',
                                 opts.query).toString();
    } catch (err) {
      return next(err);
    }
  }

  // body
  if (opts.body !== undefined) {
    var mime = constants.MIME_ALIAS[opts.type] ||
      headers['content-type'] ||
      constants.MIME_ALIAS[self._opts.type];

    var isFunction = typeof opts.body === 'function';

    if (isFunction) {
      try {
        request.body = opts.body();
      } catch (err) {
        return next(err);
      }
    } else {
      request.body = opts.body;
    }

    var isBuffer = Buffer.isBuffer(request.body);
    var isStream = request.body instanceof stream.Readable;

    if (!isBuffer && !isStream && !mime) {
      return next(errors.Validation('type required'));
    }

    if (!isBuffer && !isStream) {
      if (self._opts.encoders[mime]) {
        try {
          request.body = this._encode(mime, request.body);
        } catch (err) {
          return next(err);
        }
      } else {
        return next(errors.Codec('type is unknown: ' + mime));
      }
    }

    if (!headers['content-type'] && mime) {
      headers['content-type'] = mime + '; charset=' + constants.CHARSET;
    }

    if (isStream) {
      if (!isFunction) request._retryable = false;
    } else {
      headers['content-length'] = request.body.length;
    }
  } else if (!~constants.EXCLUDE_CONTENT_LENGTH.indexOf(opts.method)) {
    headers['content-length'] = 0;
  }

  // response pipe
  if (opts.pipe) {
    var isPipeFunction = typeof opts.pipe === 'function';

    if (isPipeFunction) {
      try {
        request.pipe = opts.pipe();
      } catch (err) {
        return next(err);
      }
    } else {
      request.pipe = opts.pipe;

      request._retryable = false;
    }

    if (!(
        (request.pipe instanceof stream.Writable) ||
        (request.pipe instanceof http.ServerResponse)
    )) {
      return next(errors.Validation('pipe must be a writable stream'));
    }
  }

  // build http.request options
  request.req = utils.merge(
    utils.pick(self._opts, constants.CLIENT_OPTIONS),
    utils.pick(self._opts.baseUrl, 'auth', 'hostname', 'port', 'path'),
    utils.pick(opts, constants.REQUEST_OPTIONS),
    { headers: headers }
  );

  // append request path to baseUrl
  request.req.path += path;

  // pick http transport
  if (self._opts.baseUrl.protocol === 'https:') {
    request.transport = https;
    if (!request.req.port) request.req.port = 443;
  } else {
    request.transport = http;
    if (!request.req.port) request.req.port = 80;
  }

  if (request.req.auth === null) delete request.req.auth;

  next();
};

/**
 * Execute HTTP request
 */

Client.prototype.__execute = function(request, next) {
  var self = this;

  if (request.ctx) {
    if (request.ctx.canceled === true) {
      return next(errors.Validation('ctx already canceled'));
    } else if (request.ctx.finished === true) {
      return next(errors.Validation('ctx already finished'));
    }
  }

  var done = false;

  var opts = request.opts;

  var abort;
  var timeoutId;
  var timeout = opts.hasOwnProperty('timeout') ?
    opts.timeout : self._opts.timeout;

  self._log(['papi', 'request'].concat(opts.tags), request.req);

  var req = request.transport.request(request.req);

  var userAgent = req.getHeader('user-agent');

  if (userAgent === undefined) {
    req.setHeader('user-agent', 'papi/' + meta.version);
  } else if (userAgent === null) {
    req.removeHeader('user-agent');
  }

  req.on('error', function(err) {
    self._log(['papi', 'request', 'error'].concat(opts.tags), err);

    if (done) return;
    done = true;

    if (abort) request.ctx.removeListener('cancel', abort);
    if (timeoutId) clearTimeout(timeoutId);

    request.err = err;
    next();
  });

  if (request.ctx) {
    abort = function() {
      req.abort();
      req.emit('error', errors.Abort('request aborted'));
    };

    request.ctx.once('cancel', abort);
  }

  // set request and absolute timeout
  if (timeout && timeout > 0) {
    timeoutId = setTimeout(function() {
      req.emit('timeout');
      req.abort();
    }, timeout);

    req.setTimeout(timeout);
  }

  req.on('timeout', function(err) {
    self._log(['papi', 'request', 'error', 'timeout'].concat(opts.tags));
    if (err) {
      err = errors.Timeout(err);
    } else {
      err = errors.Timeout('request timed out (' + timeout + 'ms)');
    }
    req.emit('error', err);
  });

  req.on('response', function(res) {
    var chunks = [];
    var bodyLength = 0;

    self._log(['papi', 'response'].concat(opts.tags), {
      method: opts.method,
      path: req.path,
      statusCode: res.statusCode,
      headers: res.headers,
      remoteAddress: res.connection.remoteAddress,
      remotePort: res.connection.remotePort,
    });

    request.res = res;

    if (request.pipe) {
      res.pipe(request.pipe);
    } else {
      res.on('data', function(chunk) {
        chunks.push(chunk);
        bodyLength += chunk.length;
      });
    }

    res.on('end', function() {
      if (done) return;
      done = true;

      if (abort) request.ctx.removeListener('cancel', abort);
      if (timeoutId) clearTimeout(timeoutId);

      // body content mime
      var mime;

      // decode body
      if (bodyLength) {
        res.body = Buffer.concat(chunks, bodyLength);

        // don't decode if user explicitly asks for buffer
        if (!opts.buffer) {
          mime = (res.headers['content-type'] || '').split(';')[0].trim();

          if (self._opts.decoders[mime]) {
            try {
              res.body = self._decode(mime, res.body);
            } catch (err) {
              request.err = err;
              return next();
            }
          }
        }
      }

      // any non-200 is consider an error
      if (Math.floor(res.statusCode / 100) !== 2) {
        var err = errors.Response();

        if (res.body && mime === 'text/plain' && res.body.length < 80) {
          err.message = res.body;
        }

        if (!err.message) {
          if (http.STATUS_CODES[res.statusCode]) {
            err.message = http.STATUS_CODES[res.statusCode].toLowerCase();
          } else {
            err.message = 'request failed: ' + res.statusCode;
          }
        }

        request.err = err;
      }

      next();
    });
  });

  if (request.body instanceof stream.Readable) {
    request.body.pipe(req);
  } else {
    req.end(request.body);
  }
};

/**
 * Shortcuts
 */

constants.METHODS.forEach(function(method) {
  var reqMethod = method.toUpperCase();

  Client.prototype['_' + method] = function(opts) {
    var args;

    if (typeof opts === 'string') {
      opts = { path: opts, method: reqMethod };

      args = Array.prototype.slice.call(arguments);
      args[0] = opts;

      return this._request.apply(this, args);
    } else if (!opts) {
      args = Array.prototype.slice.call(arguments);
      args[0] = {};

      return this._request.apply(this, args);
    }

    opts.method = reqMethod;

    return this._request.apply(this, arguments);
  };
});

/**
 * Module exports.
 */

exports.Client = Client;
<|endoftext|>


<commit_before>import uploadReport from '../uploadReport';

export default async function runCommand(sha, config) {
  const {
    apiKey,
    apiSecret,
    endpoint,
    hooks: {
      run,
    },
  } = config;
  const snaps = await run(config);
  console.log(`Uploading report for ${sha}...`);
  await uploadReport({
    snaps,
    sha,
    endpoint,
    apiKey,
    apiSecret,
  });
  console.log(`Done ${sha}`);
}
<commit_msg>Add link to single report after `happo run`<commit_after>import uploadReport from '../uploadReport';

export default async function runCommand(sha, config) {
  const {
    apiKey,
    apiSecret,
    endpoint,
    hooks: {
      run,
    },
  } = config;
  const snaps = await run(config);
  console.log(`Uploading report for ${sha}...`);
  await uploadReport({
    snaps,
    sha,
    endpoint,
    apiKey,
    apiSecret,
  });
  console.log(`View results at ${endpoint}/report?q=${sha}`);
  console.log(`Done ${sha}`);
}
<|endoftext|>