<commit_before>/*jslint node:true*/
"use strict";

var
    Duplex = require('stream').Duplex,
    Transform = require('stream').Transform,
    Readable = require('stream').Readable,
    fs = require('graceful-fs'),
    path = require('path'),
    util = require('util'),
    gutil = require('gulp-util'),
    watchify = require('watchify'),
    browserify = require('browserify'),
    copy = require('shallow-copy'),
    gcolors = gutil.colors,
    PluginError = gutil.PluginError;

/**
 * Simple function to make a readable time stamp
 * @returns {string}
 */
function makeTimeString() {
    var date = new Date(),
        minutes = String(date.getMinutes()),
        seconds = String(date.getSeconds());

    minutes = (minutes.length === 1) ? '0' + minutes : minutes;
    seconds = (seconds.length === 1) ? '0' + seconds : seconds;

    return date.getHours() + ':' + minutes + ':' + seconds;
}

/**
 * @type AbstractBundleStream
 * @extends Stream.Transform
 * @param options
 * @constructor
 */
function AbstractBundleStream(options) {
    Transform.call(this, { objectMode: true });

    this.verbose = (typeof options.verbose === 'boolean') ? options.verbose : true;

    this._skipUpdateError = (typeof options.skipUpdateError === 'boolean') ? options.skipUpdateError : true;
}

util.inherits(AbstractBundleStream, Transform);

/**
 * Create a new gutil.File to represent the bundle
 * @param {gutil.File} srcFile
 * @param {string} source
 * @returns {gutil.File}
 * @private
 */
AbstractBundleStream.prototype._createBundleFile = function (srcFile, source) {
    return new gutil.File({
        contents: new Buffer(source),
        base: srcFile.base,
        cwd: srcFile.cwd,
        path: srcFile.path
    });
}

/**
 * @type GulpWatchify.ReBundle
 *
 * Used to create a simple stream that
 * rebundles main files
 *
 * @param bundler
 * @constructor
 */
function ReBundle (bundlerInstance, options) {
    AbstractBundleStream.call(this, options);
    this._bundlerInstance = bundlerInstance;

    this.on('end', function () {
        gutil.log(gcolors.green('======================='));
        gutil.log(gcolors.cyan(makeTimeString()), gcolors.green('Successfully updated bundles for with changed dependencies'));
        gutil.log(gcolors.white("---------------"));
        gutil.log(gcolors.white("Watching..."));
    });
}

util.inherits(ReBundle, AbstractBundleStream);

ReBundle.prototype._transform = function (srcFile, encoding, done) {
    var self = this;

    this._bundlerInstance.bundle(function (err, source) {
        if (err) {
            if (!self._skipUpdateError) {
                done(new PluginError('gulp-watchify', err));
                return;
            }
            gutil.log(gcolors.cyan(makeTimeString()), gcolors.red('Encountered Error, failed to create bundle for "'), gcolors.magenta(srcFile.path), '"');
            gutil.log(err);
            done();
            return;
        }

        var file = self._createBundleFile(srcFile, source);

        self.push(file);
        if (self.verbose) {
            gutil.log(gcolors.cyan(makeTimeString()), '-> Successfully Re-Bundled changed file', srcFile.path);
        }
        done();
    });
    gutil.log(gcolors.cyan(makeTimeString()), '* Updating bundle for entry file :', srcFile.path);
};

function GulpWatchify(options) {
    options = options || {};

    AbstractBundleStream.call(this, options);

    /**
     * If watching should be turned on or not. If false, watchify is switched
     * out for browserify.
     * @type {options.watch|*}
     */
    this.watch = (typeof options.watch === 'boolean') ? options.watch : true;

    /**
     * User can optionally turn off caching watchify dependencies if they choose
     * @type {boolean}
     */
    options.primeCache = (typeof options.primeCache === 'boolean') ? options.primeCache : true;

    /**
     * User can optionally turn of sharing watchers want to. Sharing watchers is done
     * to stop multiple watcher instances being created on the same files. If no, this
     * may cause node to throw maximum listener warnings
     * @type {boolean}
     */
    options.shareWatchers = (typeof options.shareWatchers === 'boolean') ? options.shareWatchers : true;

    /**
     * Options to pass to the bundler when it is created
     * @type {[type]}
     */
    this.bundlerOptions = options.bundlerOptions || {};

    /**
     * If you want to show a warning instead of throwing an error
     * @type {boolean}
     */
    this._rebundle = (typeof options.rebundle === 'boolean') ? options.rebundle : true;

    /**
     * The bundler function to use to create common js bundles
     * @memberOf GulpWatchify#
     * @type {object}
     */
    this._bundler = options.Bundler;

    if (!this._bundler) {
        this._bundler = options.watch ? watchify : browserify;
    }

    /**
     * Transforms to add to the bundler instance
     */
    this._transforms = options.transforms || [];

    if (options.primeCache) {
        /**
         * Used to store all dependencies so that they can be shared
         * among watchify instances to speed up bundling
         * @memberOf GulpWachify#
         * @type {Object}
         */
        this._depsCache = {};

        /**
         * Used to store watchify packages so that they can be
         * shared among watchify instances to speed up building
         */
        this._packageCache = {};
    }

    if (options.shareWatchers) {
        this._watchers = {};
    }

    /**
    * Path to destination directory or file.
    * @type {string}
    */
    this._bundlers = options.bundlers || {};

    this.on('end', function () {
        if (this.verbose) {
            gutil.log(gcolors.green("============================="));
            gutil.log(gcolors.green("Successfully Wrote JS Bundles"));
            if (this.watch) {
                gutil.log(gcolors.white("---------------"));
                gutil.log(gcolors.white("Watching..."));
            }
        }
    });

}

// GulpWatchify should inherit from the abstract pseudoclass
util.inherits(GulpWatchify, AbstractBundleStream);

/**
 * Pass through newer files only.
 * @param {File} srcFile A vinyl file.
 * @param {string} encoding Encoding (ignored).
 * @param {function(Error, File)} done Callback.
 */
GulpWatchify.prototype._transform = function (srcFile, encoding, done) {
    if (!srcFile || !srcFile.path) {
        done(new PluginError('gulp-watchify', 'Expected a source file with a path'));
        return;
    }

    var self = this,
        opts = copy(this.bundlerOptions),
        bundler;

    opts.cache = this._depsCache;
    opts.pkgCache = this._packageCache;
    opts.packageCache = this._packageCache;
    opts.watchers = this._watchers;

    bundler = this._bundler(opts);

    bundler.add(srcFile);

    bundler.on('watch', function (watcher) {
        watcher.setMaxListeners(50);
    });

    bundler.on('update', function () {
        self.emit('update', srcFile, bundler);
        self._handleUpdate(srcFile, bundler)
    })

    this._bundlers[srcFile.path] = bundler;

    this._transforms.forEach(function (tr) {
        bundler.transform(tr.opts, tr.tr);
    });

    function firstBundleCallback(err, source) {
        if (err) {
            done(new PluginError('gulp-watchify', err));
            return;
        }

        var file = self._createBundleFile(srcFile, source);

        self.push(file);
        if (self.verbose) {
            gutil.log(gcolors.cyan(makeTimeString()), gcolors.green('-> Successfully bundled file"'), gcolors.magenta(srcFile.path), gcolors.green('"'));
        }
        done();
    }

    bundler.bundle(firstBundleCallback);

    if (this.verbose) {
        gutil.log(gcolors.cyan(makeTimeString()), '*Bundling file "', gcolors.magenta(srcFile.path), '"...');
    }

};

/**
 * Buffer update events through a ReBundle stream. If
 * the _rebundleDelay (ms) time passes with no update events,
 * the stream closes
 * @param srcFile
 * @param bundler
 * @returns {GulpWatchify}
 * @private
 */
GulpWatchify.prototype._handleUpdate = function (srcFile, bundler) {
    if (!this._rebundle) {
        return this;
    }
    var self = this,
        rebundle = self._rebundleStream;

    clearTimeout(self._rebundleTimeout)

    if (!rebundle) {
        gutil.log('_rebundleStream definition');
        rebundle = self._rebundleStream = new ReBundle(bundler, {
            verbose: self.verbose,
            skipUpdateError: self._skipUpdateError
        });
        self.emit('rebundle', rebundle);
    }

    self._rebundleStream.write(srcFile);

    self._rebundleTimeout = setTimeout(function () {
        rebundle.push(null);
        self._rebundleStream = null;
    }, self._rebundleDelay || 400);
}

/**
 * Remove references to buffered files.
 * @param {function(Error)} done Callback.
 */
GulpWatchify.prototype._flush = function (done) {
    done();
};

/**
 * Takes source files and passes through a bundled file. If
 * watch option is true, listens for updates and passes an
 * update event. If 'rebundle' is true, then on an update
 * will create a transform stream and that you can read
 * bundles through the 'rebundle' event.
 * @param {string} dest Path to destination directory or file.
 * @return {Newer} A transform stream.
 */
module.exports = function (options) {
    return new GulpWatchify(options);
};<commit_msg>fix semicolons<commit_after>/*jslint node:true*/
"use strict";

var
    Duplex = require('stream').Duplex,
    Transform = require('stream').Transform,
    Readable = require('stream').Readable,
    fs = require('graceful-fs'),
    path = require('path'),
    util = require('util'),
    gutil = require('gulp-util'),
    watchify = require('watchify'),
    browserify = require('browserify'),
    copy = require('shallow-copy'),
    gcolors = gutil.colors,
    PluginError = gutil.PluginError;

/**
 * Simple function to make a readable time stamp
 * @returns {string}
 */
function makeTimeString() {
    var date = new Date(),
        minutes = String(date.getMinutes()),
        seconds = String(date.getSeconds());

    minutes = (minutes.length === 1) ? '0' + minutes : minutes;
    seconds = (seconds.length === 1) ? '0' + seconds : seconds;

    return date.getHours() + ':' + minutes + ':' + seconds;
}

/**
 * @type AbstractBundleStream
 * @extends Stream.Transform
 * @param options
 * @constructor
 */
function AbstractBundleStream(options) {
    Transform.call(this, { objectMode: true });

    this.verbose = (typeof options.verbose === 'boolean') ? options.verbose : true;

    this._skipUpdateError = (typeof options.skipUpdateError === 'boolean') ? options.skipUpdateError : true;
}

util.inherits(AbstractBundleStream, Transform);

/**
 * Create a new gutil.File to represent the bundle
 * @param {gutil.File} srcFile
 * @param {string} source
 * @returns {gutil.File}
 * @private
 */
AbstractBundleStream.prototype._createBundleFile = function (srcFile, source) {
    return new gutil.File({
        contents: new Buffer(source),
        base: srcFile.base,
        cwd: srcFile.cwd,
        path: srcFile.path
    });
};

/**
 * @type GulpWatchify.ReBundle
 *
 * Used to create a simple stream that
 * rebundles main files
 *
 * @param bundler
 * @constructor
 */
function ReBundle(bundlerInstance, options) {
    AbstractBundleStream.call(this, options);
    this._bundlerInstance = bundlerInstance;

    this.on('end', function () {
        gutil.log(gcolors.green('======================='));
        gutil.log(gcolors.cyan(makeTimeString()), gcolors.green('Successfully updated bundles for with changed dependencies'));
        gutil.log(gcolors.white("---------------"));
        gutil.log(gcolors.white("Watching..."));
    });
}

util.inherits(ReBundle, AbstractBundleStream);

ReBundle.prototype._transform = function (srcFile, encoding, done) {
    var self = this;

    this._bundlerInstance.bundle(function (err, source) {
        if (err) {
            if (!self._skipUpdateError) {
                done(new PluginError('gulp-watchify', err));
                return;
            }
            gutil.log(gcolors.cyan(makeTimeString()), gcolors.red('Encountered Error, failed to create bundle for "'), gcolors.magenta(srcFile.path), '"');
            gutil.log(err);
            done();
            return;
        }

        var file = self._createBundleFile(srcFile, source);

        self.push(file);
        if (self.verbose) {
            gutil.log(gcolors.cyan(makeTimeString()), '-> Successfully Re-Bundled changed file', srcFile.path);
        }
        done();
    });
    gutil.log(gcolors.cyan(makeTimeString()), '* Updating bundle for entry file :', srcFile.path);
};

function GulpWatchify(options) {
    options = options || {};

    AbstractBundleStream.call(this, options);

    /**
     * If watching should be turned on or not. If false, watchify is switched
     * out for browserify.
     * @type {options.watch|*}
     */
    this.watch = (typeof options.watch === 'boolean') ? options.watch : true;

    /**
     * User can optionally turn off caching watchify dependencies if they choose
     * @type {boolean}
     */
    options.primeCache = (typeof options.primeCache === 'boolean') ? options.primeCache : true;

    /**
     * User can optionally turn of sharing watchers want to. Sharing watchers is done
     * to stop multiple watcher instances being created on the same files. If no, this
     * may cause node to throw maximum listener warnings
     * @type {boolean}
     */
    options.shareWatchers = (typeof options.shareWatchers === 'boolean') ? options.shareWatchers : true;

    /**
     * Options to pass to the bundler when it is created
     * @type {[type]}
     */
    this.bundlerOptions = options.bundlerOptions || {};

    /**
     * If you want to show a warning instead of throwing an error
     * @type {boolean}
     */
    this._rebundle = (typeof options.rebundle === 'boolean') ? options.rebundle : true;

    /**
     * The bundler function to use to create common js bundles
     * @memberOf GulpWatchify#
     * @type {object}
     */
    this._bundler = options.Bundler;

    if (!this._bundler) {
        this._bundler = options.watch ? watchify : browserify;
    }

    /**
     * Transforms to add to the bundler instance
     */
    this._transforms = options.transforms || [];

    if (options.primeCache) {
        /**
         * Used to store all dependencies so that they can be shared
         * among watchify instances to speed up bundling
         * @memberOf GulpWachify#
         * @type {Object}
         */
        this._depsCache = {};

        /**
         * Used to store watchify packages so that they can be
         * shared among watchify instances to speed up building
         */
        this._packageCache = {};
    }

    if (options.shareWatchers) {
        this._watchers = {};
    }

    /**
    * Path to destination directory or file.
    * @type {string}
    */
    this._bundlers = options.bundlers || {};

    this.on('end', function () {
        if (this.verbose) {
            gutil.log(gcolors.green("============================="));
            gutil.log(gcolors.green("Successfully Wrote JS Bundles"));
            if (this.watch) {
                gutil.log(gcolors.white("---------------"));
                gutil.log(gcolors.white("Watching..."));
            }
        }
    });

}

// GulpWatchify should inherit from the abstract pseudoclass
util.inherits(GulpWatchify, AbstractBundleStream);

/**
 * Pass through newer files only.
 * @param {File} srcFile A vinyl file.
 * @param {string} encoding Encoding (ignored).
 * @param {function(Error, File)} done Callback.
 */
GulpWatchify.prototype._transform = function (srcFile, encoding, done) {
    if (!srcFile || !srcFile.path) {
        done(new PluginError('gulp-watchify', 'Expected a source file with a path'));
        return;
    }

    var self = this,
        opts = copy(this.bundlerOptions),
        bundler;

    opts.cache = this._depsCache;
    opts.pkgCache = this._packageCache;
    opts.packageCache = this._packageCache;
    opts.watchers = this._watchers;

    bundler = this._bundler(opts);

    bundler.add(srcFile);

    bundler.on('watch', function (watcher) {
        watcher.setMaxListeners(50);
    });

    bundler.on('update', function () {
        self.emit('update', srcFile, bundler);
        self._handleUpdate(srcFile, bundler);
    });

    this._bundlers[srcFile.path] = bundler;

    this._transforms.forEach(function (tr) {
        bundler.transform(tr.opts, tr.tr);
    });

    function firstBundleCallback(err, source) {
        if (err) {
            done(new PluginError('gulp-watchify', err));
            return;
        }

        var file = self._createBundleFile(srcFile, source);

        self.push(file);
        if (self.verbose) {
            gutil.log(gcolors.cyan(makeTimeString()), gcolors.green('-> Successfully bundled file"'), gcolors.magenta(srcFile.path), gcolors.green('"'));
        }
        done();
    }

    bundler.bundle(firstBundleCallback);

    if (this.verbose) {
        gutil.log(gcolors.cyan(makeTimeString()), '*Bundling file "', gcolors.magenta(srcFile.path), '"...');
    }

};

/**
 * Buffer update events through a ReBundle stream. If
 * the _rebundleDelay (ms) time passes with no update events,
 * the stream closes
 * @param srcFile
 * @param bundler
 * @returns {GulpWatchify}
 * @private
 */
GulpWatchify.prototype._handleUpdate = function (srcFile, bundler) {
    if (!this._rebundle) {
        return this;
    }
    var self = this,
        rebundle = self._rebundleStream;

    clearTimeout(self._rebundleTimeout);

    if (!rebundle) {
        gutil.log('_rebundleStream definition');
        rebundle = self._rebundleStream = new ReBundle(bundler, {
            verbose: self.verbose,
            skipUpdateError: self._skipUpdateError
        });
        self.emit('rebundle', rebundle);
    }

    self._rebundleStream.write(srcFile);

    self._rebundleTimeout = setTimeout(function () {
        rebundle.push(null);
        self._rebundleStream = null;
    }, self._rebundleDelay || 400);
};

/**
 * Remove references to buffered files.
 * @param {function(Error)} done Callback.
 */
GulpWatchify.prototype._flush = function (done) {
    done();
};

/**
 * Takes source files and passes through a bundled file. If
 * watch option is true, listens for updates and passes an
 * update event. If 'rebundle' is true, then on an update
 * will create a transform stream and that you can read
 * bundles through the 'rebundle' event.
 * @param {string} dest Path to destination directory or file.
 * @return {Newer} A transform stream.
 */
module.exports = function (options) {
    return new GulpWatchify(options);
};<|endoftext|>


<commit_before>const http = require('http');
const express = require('express');
const bodyParser = require('body-parser');
const app = express();

app.use(bodyParser.json());

const recipepuppyHost = 'http://www.recipepuppy.com/api/?q=';

app.get('/dummyget', function (req, res) {
    res.setHeader('Content-Type', 'application/json');
    res.send(JSON.stringify({ 'speech': 'dummy speech', 'displayText': 'dummy get works!' }));
});

app.post('/recipe', function (req, res) {
    console.log(req);
    let recipeitem = '';
    if (req.body.result.parameters['recipeitem']) {
        recipeitem = req.body.result.parameters['recipeitem'];
        console.log('Finding recipe for : ' + recipeitem);
    }

    callRecipePuppy(recipeitem).then((output) => {
        res.setHeader('Content-Type', 'application/json');
        res.send(JSON.stringify({ 'speech': output, 'displayText': output }));
    })
        .catch((error) => {
            res.setHeader('Content-Type', 'application/json');
            res.send(JSON.stringify({ 'speech': error, 'displayText': error }));
        });
});

function callRecipePuppy(recipeitem) {
    return new Promise((resolve, reject) => {
        http.get(recipepuppyHost + recipeitem, (res) => {
            let body = '';
            res.on('data', (d) => body += d);
            res.on('end', () => {
                let jO = JSON.parse(body);
                let firstItem = jO.results[0];

                let output = firstItem.title + " using " + firstItem.href;
                resolve(output);
            });

            res.on('error', (error) => {
                reject(error);
            });
        });
    });
}

app.listen((process.env.PORT || 5000), function () {
    console.log("Server listening");
});<commit_msg>generic hook<commit_after>const http = require('http');
const express = require('express');
const bodyParser = require('body-parser');
const app = express();

app.use(bodyParser.json());

const recipepuppyHost = 'http://www.recipepuppy.com/api/?q=';

app.get('/dummyget', function (req, res) {
    res.setHeader('Content-Type', 'application/json');
    res.send(JSON.stringify({ 'speech': 'dummy speech', 'displayText': 'dummy get works!' }));
});

app.post('/webhook', function (req, res) {
    if (req.body.result.parameters['recipeitem']) {
        recipeitem = req.body.result.parameters['recipeitem'];
        callRecipePuppy(recipeitem)
            .then((output) => {
                res.setHeader('Content-Type', 'application/json');
                res.send(JSON.stringify({ 'speech': output, 'displayText': output }));
            })
            .catch((error) => {
                res.setHeader('Content-Type', 'application/json');
                res.send(JSON.stringify({ 'speech': error, 'displayText': error }));
            });
    } else {
        res.setHeader('Content-Type', 'application/json');
        res.send(JSON.stringify({ 'speech': "No Proper hook found", 'displayText': "No Proper hook found" }));
    }
});

function callRecipePuppy(recipeitem) {
    return new Promise((resolve, reject) => {
        http.get(recipepuppyHost + recipeitem, (res) => {
            let body = '';
            res.on('data', (d) => body += d);
            res.on('end', () => {
                let jO = JSON.parse(body);
                let firstItem = jO.results[0];

                let output = firstItem.title + " using " + firstItem.href;
                resolve(output);
            });

            res.on('error', (error) => {
                reject(error);
            });
        });
    });
}

app.listen((process.env.PORT || 5000), function () {
    console.log("Server listening");
});<|endoftext|>


<commit_before>module.exports = require('./lib/main.js');<commit_msg>some changes<commit_after>module.exports = require('./lib/main.js');
<|endoftext|>


<commit_before>
// Setup basic express server
var express = require('express');
var app = express();
var crypto = require('crypto');
var server = require('http').createServer(app);
var io = require('socket.io')(server);
var port = process.env.PORT || 3000;
var config = require('./config');
var Room = require('./js/room');
var helper = require('./js/helper');
var command = require('./js/command');

// Check for config availability

if (!config) {
  throw new Error("No configuration available. See config.example.js for example.");
}

// Auth module
require('socketio-auth')(io, {
  authenticate: authenticate,
  postAuthenticate: postAuthenticate,
  timeout: 1000
});

/**
 * Процедура аутентификации пользователя
 *
 */
function authenticate(data, callback) {
  var userData = data.user;
  var md5 = crypto.createHash('md5');
  var salt = config.salt;
  var result = false;

  if (md5.update(JSON.stringify(userData)).update(salt).digest('hex') === data.hash) {
    result = true;
  }

  return callback(null, result);
}

/**
 * Обработка авторизационных данных после успешной аутентификации
 *
 */
function postAuthenticate(socket, data) {
  var userData = data.user;

  socket.user = userData;
  socket.username = userData.name;
}

server.listen(port, function () {
  console.log('Server listening at port %d', port);
});

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

// Chatrooms
var rooms = {};

io.on('connection', function (socket) {
  var addedUser = false;

  // when the client emits 'new message', this listens and executes
  socket.on('new message', function (data) {
    // if message starts with '/' — we're not sending it to everyone, we're processing it in unique way
    if (command.isCommand(data)) {
      command.processCommand(data, socket);
    } else {
      var hash = crypto.createHash('md5');
      hash = hash.update(data).digest('hex');

      rooms[socket.room].addToHistory(socket.user, data);

      io.to(socket.room).emit('new message', {
        user: socket.user,
        message: data,
        room: socket.room,
        id: hash
      });
    }
  });

  // when the client emits 'add user', this listens and executes
  socket.on('add user', function (data) {
    // normilize room name
    var room = helper.roomNameNormilize(data.room);

    // check access
    if (room.length === 0 || !helper.checkRoomAuthorization(room, config.salt, data.roomHash)) {
        socket.emit('auth failed').disconnect('wrong room');
        return false;
    }

    // Prevent reading from multiple rooms
    if (socket.room) {
      socket.leave(socket.room);
      rooms[socket.room].removeUser(socket.user.id);
      socket.broadcast.to(socket.room).emit('user left', {
        user: socket.user,
        numUsers: rooms[socket.room].getUsersCount(),
        users: rooms[socket.room].getUsers()
      });
    }

    socket.room = room;

    // If not authorized
    if (socket.user == undefined) {
        socket.emit('auth failed').disconnect('wrong user data');
        return false;
    }

    // Init room
    if (rooms[room] == undefined) {
        rooms[room] = new Room(room);

    }

    rooms[room].users[socket.user.id] = socket.user;
    ++rooms[room].numUsers;

    addedUser = true;

    socket.join(room);

    socket.emit('login', {
      numUsers: rooms[room].getUsersCount(),
      users: rooms[room].getUsers()
    });

    // echo to room that a person has connected
    socket.broadcast.to(socket.room).emit('user joined', {
      user: socket.user,
      numUsers: rooms[room].getUsersCount(),
      users: rooms[room].getUsers()
    });

    // History of last messages in chat
    rooms[socket.room].sendHistory(socket);
  });

  // when the client emits 'typing', we broadcast it to others
  socket.on('typing', function () {
    socket.broadcast.to(socket.room).emit('typing', {
      user: socket.user
    });
  });

  // when the client emits 'stop typing', we broadcast it to others
  socket.on('stop typing', function () {
    socket.broadcast.to(socket.room).emit('stop typing', {
      user: socket.user
    });
  });

  // when the user disconnects.. perform this
  socket.on('disconnect', function () {
    if (addedUser) {
      rooms[socket.room].removeUser(socket.user.id);

      // echo to the room that this client has left
      socket.broadcast.to(socket.room).emit('user left', {
        user: socket.user,
        numUsers: rooms[socket.room].getUsersCount(),
        users: rooms[socket.room].getUsers()
      });
    }
  });
});
<commit_msg>Fix #15 with socket.room check<commit_after>
// Setup basic express server
var express = require('express');
var app = express();
var crypto = require('crypto');
var server = require('http').createServer(app);
var io = require('socket.io')(server);
var port = process.env.PORT || 3000;
var config = require('./config');
var Room = require('./js/room');
var helper = require('./js/helper');
var command = require('./js/command');

// Check for config availability

if (!config) {
  throw new Error("No configuration available. See config.example.js for example.");
}

// Auth module
require('socketio-auth')(io, {
  authenticate: authenticate,
  postAuthenticate: postAuthenticate,
  timeout: 1000
});

/**
 * Процедура аутентификации пользователя
 *
 */
function authenticate(data, callback) {
  var userData = data.user;
  var md5 = crypto.createHash('md5');
  var salt = config.salt;
  var result = false;

  if (md5.update(JSON.stringify(userData)).update(salt).digest('hex') === data.hash) {
    result = true;
  }

  return callback(null, result);
}

/**
 * Обработка авторизационных данных после успешной аутентификации
 *
 */
function postAuthenticate(socket, data) {
  var userData = data.user;

  socket.user = userData;
  socket.username = userData.name;
}

server.listen(port, function () {
  console.log('Server listening at port %d', port);
});

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

// Chatrooms
var rooms = {};

io.on('connection', function (socket) {
  var addedUser = false;

  // when the client emits 'new message', this listens and executes
  socket.on('new message', function (data) {
    if (socket.room) {
      // if message starts with '/' — we're not sending it to everyone, we're processing it in unique way
      if (command.isCommand(data)) {
        command.processCommand(data, socket);
      } else {
        var hash = crypto.createHash('md5');
        hash = hash.update(data).digest('hex');

        rooms[socket.room].addToHistory(socket.user, data);

        io.to(socket.room).emit('new message', {
          user: socket.user,
          message: data,
          room: socket.room,
          id: hash
        });
      }
    }
  });

  // when the client emits 'add user', this listens and executes
  socket.on('add user', function (data) {
    // normilize room name
    var room = helper.roomNameNormilize(data.room);

    // check access
    if (room.length === 0 || !helper.checkRoomAuthorization(room, config.salt, data.roomHash)) {
        socket.emit('auth failed').disconnect('wrong room');
        return false;
    }

    // Prevent reading from multiple rooms
    if (socket.room) {
      socket.leave(socket.room);
      rooms[socket.room].removeUser(socket.user.id);
      socket.broadcast.to(socket.room).emit('user left', {
        user: socket.user,
        numUsers: rooms[socket.room].getUsersCount(),
        users: rooms[socket.room].getUsers()
      });
    }

    socket.room = room;

    // If not authorized
    if (socket.user == undefined) {
        socket.emit('auth failed').disconnect('wrong user data');
        return false;
    }

    // Init room
    if (rooms[room] == undefined) {
        rooms[room] = new Room(room);

    }

    rooms[room].users[socket.user.id] = socket.user;
    ++rooms[room].numUsers;

    addedUser = true;

    socket.join(room);

    socket.emit('login', {
      numUsers: rooms[room].getUsersCount(),
      users: rooms[room].getUsers()
    });

    // echo to room that a person has connected
    socket.broadcast.to(socket.room).emit('user joined', {
      user: socket.user,
      numUsers: rooms[room].getUsersCount(),
      users: rooms[room].getUsers()
    });

    // History of last messages in chat
    rooms[socket.room].sendHistory(socket);
  });

  // when the client emits 'typing', we broadcast it to others
  socket.on('typing', function () {
    if (socket.room) {
      socket.broadcast.to(socket.room).emit('typing', {
        user: socket.user
      });
    }
  });

  // when the client emits 'stop typing', we broadcast it to others
  socket.on('stop typing', function () {
    if (socket.room){
      socket.broadcast.to(socket.room).emit('stop typing', {
        user: socket.user
      });
    }
  });

  // when the user disconnects.. perform this
  socket.on('disconnect', function () {
    if (socket.room) {
      rooms[socket.room].removeUser(socket.user.id);

      // echo to the room that this client has left
      socket.broadcast.to(socket.room).emit('user left', {
        user: socket.user,
        numUsers: rooms[socket.room].getUsersCount(),
        users: rooms[socket.room].getUsers()
      });
    }
  });
});
<|endoftext|>


<commit_before>'use strict'

const plugin = require('./package'),
  Noble = require('noble'),
  EventEmitter = require('events').EventEmitter
var Homebridge, Service, Characteristic

module.exports = function(homebridge) {
  console.log("Homebridge API version: " + homebridge.version)

  Homebridge = homebridge
  Service = homebridge.hap.Service
  Characteristic = homebridge.hap.Characteristic

  homebridge.registerAccessory(plugin.name, "satellite-fan", FanLightAccessory)
}

function trimAddress(address) {
  return address.toLowerCase().replace(/:/g, "")
}

function trimUUID(uuid) {
  return uuid.toLowerCase().replace(/:/g, "").replace(/-/g, "")
}

// MARK: -

class FanRequest {

  writeInto(buffer) {
    throw new TypeError('Must override method')
  }

  toPrefixedBuffer(prefix) {
    var buffer
    if (prefix > 0) {
      buffer = new Buffer(13)
      buffer.writeUInt8(prefix)
      this.writeInto(buffer.slice(1))
    } else {
      buffer = new Buffer(12)
      this.writeInto(buffer)
    }

    const checksum = buffer.slice(0, buffer.length - 1).reduce(function(a, b){
      return a + b
    }, 0) & 255

    buffer.writeUInt8(checksum, buffer.length - 1)
    return buffer
  }

}

class FanGetStateRequest extends FanRequest {

  writeInto(buffer) {
    buffer.fill(0)
    buffer.writeUInt8(160)
  }

}

Math.clamp = function(number, min, max) {
  return Math.max(min, Math.min(number, max))
}

class FanUpdateLightRequest extends FanRequest {

  constructor(isOn, level) {
    super()
    this.on = isOn ? 1 : 0
    this.level = Math.clamp(level, 0, 100)
  }

  writeInto(buffer) {
    buffer.fill(0)
    buffer.writeUInt8(161)
    buffer.writeUInt8(255, 4)
    buffer.writeUInt8(100, 5)
    buffer.writeUInt8((this.on << 7) | this.level, 6)
    buffer.fill(255, 7, 10)
  }

}

class FanUpdateLevelRequest extends FanRequest {

  constructor(level) {
    super()
    this.level = Math.clamp(level, 0, 3)
  }

  writeInto(buffer) {
    buffer.fill(0)
    buffer.writeUInt8(161)
    buffer.writeUInt8(this.level, 4)
    buffer.fill(255, 5, 10)
  }

}

class FanResponse {

  static fromPrefixedBuffer(prefix, buffer) {
    if (prefix > 0) {
      buffer = buffer.slice(1)
    }

    if (buffer.readUInt8(0) != 176) { return null }
    const response = new FanResponse()

    const windVelocity = buffer.readUInt8(2)
    response.supportsFanReversal = (windVelocity & 0b00100000) != 0
    response.maximumFanLevel     =  windVelocity & 0b00011111

    const currentWindVelocity = buffer.readUInt8(4)
    response.isFanReversed     = (currentWindVelocity & 0b10000000) != 0
    response.fanLevel          =  currentWindVelocity & 0b00011111

    const currentBrightness = buffer.readUInt8(6)
    response.lightIsOn       = (currentBrightness & 0b10000000) != 0
    response.lightBrightness = (currentBrightness & 0b01111111)

    return response
  }

}

// MARK: -

class FanLightAccessory extends EventEmitter {

  constructor (log, config) {
    super()

    this.onDiscover = this.onDiscover.bind(this)

    this.log = log
    this.name = config.name || "Ceiling Fan"
    if (!config.address) {
      throw new Error(this.prefix + " Missing mandatory config 'address'")
    }
    this.address = trimAddress(config.address)
    if (!config.ble) {
      throw new Error(this.prefix + " Missing mandatory config 'ble'")
    }
    this.manufacturerPrefix = config.ble.prefix || 0
    if (!config.ble.serviceUUID) {
      throw new Error(this.prefix + " Missing mandatory config 'ble.serviceUUID'")
    }
    this.serviceUUID = trimUUID(config.ble.serviceUUID)
    if (!config.ble.writeCharacteristicUUID) {
      throw new Error(this.prefix + " Missing mandatory config 'ble.writeCharacteristicUUID'")
    }
    this.writeCharacteristicUUID = trimUUID(config.ble && config.ble.writeCharacteristicUUID)
    this.writeCharacteristic = null
    if (!config.ble.notifyCharacteristicUUID) {
      throw new Error(this.prefix + " Missing mandatory config 'ble.notifyCharacteristicUUID'")
    }
    this.notifyCharacteristicUUID = trimUUID(config.ble && config.ble.notifyCharacteristicUUID)
    this.notifyCharacteristic = null

    this.informationService = this.makeInformationService(config)
    this.fanService = this.makeFanService(config)
    this.lightService = this.makeLightService(config)

    this.maximumFanLevel = 3

    Homebridge.on('didFinishLaunching', this.onDidFinishLaunching.bind(this))
  }

  identify (callback) {
    this.log('Device identified!')
    callback()
  }

  startDiscovering() {
    this.log.debug('Start discovery')

    Noble.on('scanStop', function() {
      setTimeout(function() {
        Noble.startScanning([], true)
      }, 2500)
    })

    Noble.on('discover', this.onDiscover)
    Noble.startScanning([], true)
    this.log.debug('discover count ', Noble.listenerCount('discover'))
  }

  stopDiscovering() {
    Noble.removeListener('discover', this.onDiscover)
    if (Noble.listenerCount('discover') == 0) {
      Noble.removeAllListeners('scanStop')
    }
  }

  sendCommand(command, callback) {
    if (!this.notifyCharacteristic || !this.writeCharacteristic) {
      this.log.info('waiting on connect...')
      this.once('ready', function() {
        this.sendCommand(command, callback)
      }.bind(this))
      return
    }

    const buffer = command.toPrefixedBuffer(this.manufacturerPrefix)
    this.log.debug('will send', this.manufacturerPrefix, buffer)
    this.writeCharacteristic.write(buffer, false, callback)
  }

  sendUpdateStateRequest() {
    this.log.info('coalesced update request')
    const command = new FanGetStateRequest()
    this.sendCommand(command, function(error){
      if (!error) { return }
      this.emit('updateState', error)
    }.bind(this))
  }

  // MARK: -

  onDidFinishLaunching() {
    this.log.info("Received did finish launching")
    Noble.on('stateChange', this.onAdapterChange.bind(this))
  }

  onAdapterChange(state) {
    Noble.removeAllListeners('scanStop')
    Noble.stopScanning()

    if (state != 'poweredOn') {
      this.log.debug("Stopped scanning: " + state)
      return
    }

    this.startDiscovering()
  }

  onDiscover(peripheral) {
    if (trimAddress(peripheral.address) !== this.address || (this.writeCharacteristic && this.notifyCharacteristic)) {
      this.log.debug("Ignoring " + peripheral.address + " (RSSI " + peripheral.rssi + "dB)")
      return
    }

    this.log.debug("Found " + peripheral.address + " (RSSI " + peripheral.rssi + "dB)")
    this.stopDiscovering()
    peripheral.connect(function(error) {
      this.onConnect(error, peripheral)
    }.bind(this))
  }

  onConnect(error, peripheral) {
    if (error) {
      this.log.error("Connecting to " + peripheral.address + " failed: " + error)
      this.onDisconnect(error, peripheral)
      return
    }
    this.log.debug("Connected to " + peripheral.address)

    peripheral.discoverSomeServicesAndCharacteristics([ this.serviceUUID ], [ this.writeCharacteristicUUID, this.notifyCharacteristicUUID ], this.onDiscoverCharacteristics.bind(this));
    peripheral.once('disconnect', function(error) {
      this.onDisconnect(error, peripheral)
    }.bind(this))
  }

  onDisconnect(error, peripheral) {
    if (this.writeCharacteristic) {
      this.writeCharacteristic.removeAllListeners('set')
    }
    this.writeCharacteristic = null

    if (this.notifyCharacteristic) {
      this.notifyCharacteristic.unsubscribe(null)
      this.notifyCharacteristic.removeAllListeners('data')
    }
    this.notifyCharacteristic = null

    peripheral.removeAllListeners()

    this.log.info("Disconnected")

    this.startDiscovering()

    if (this.listenerCount('updateState') != 0) {
      this.sendUpdateStateRequest()
    }
  }

  onDiscoverCharacteristics(error, services, characteristics) {
    if (error || characteristics.count < 2) {
      this.log.error(this.prefix, "Discover services failed: " + error)
      return
    }

    const writeCharacteristic = characteristics[0],
      notifyCharacteristic = characteristics[1]

    notifyCharacteristic.on('data', this.onNotify.bind(this))
    notifyCharacteristic.subscribe(function (error) {
      if (error) {
        this.log.warn("Subscribe to notify characteristic failed")
      }

      this.writeCharacteristic = writeCharacteristic
      this.notifyCharacteristic = notifyCharacteristic

      this.log.info("Ready")
      this.emit('ready')
    }.bind(this))
  }

  onNotify(data, isNotification) {
    const response = FanResponse.fromPrefixedBuffer(this.manufacturerPrefix, data)
    if (!response) { return }
    this.log.debug('received fan state')

    this.maximumFanLevel = response.maximumFanLevel
    this.fanService.getCharacteristic(Characteristic.On).updateValue(response.fanLevel != 0)
    this.fanService.getCharacteristic(Characteristic.RotationSpeed).updateValue(Math.ceil((response.fanLevel / response.maximumFanLevel) * 100))
    this.lightService.getCharacteristic(Characteristic.On).updateValue(response.lightIsOn)
    this.lightService.getCharacteristic(Characteristic.Brightness).updateValue(response.lightBrightness)

    this.emit('updateState', null)
  }

  // MARK: -

  getNextValueForFanState(characteristic, callback) {
    const shouldSend = this.listenerCount('updateState') == 0

    this.once('updateState', function(error){
      if (error) {
        callback(error, null)
      } else {
        callback(null, characteristic.value)
      }
    })

    if (shouldSend) {
      this.sendUpdateStateRequest()
    }
  }

  getFanOn(callback) {
    this.getNextValueForFanState(this.fanService.getCharacteristic(Characteristic.On), callback)
  }

  setFanOn(newValue, callback) {
    const level = newValue ? this.fanService.getCharacteristic(Characteristic.RotationSpeed).value : 0
    this.log.info('Fan on to level ' + newValue)
    this.fanService.getCharacteristic(Characteristic.On).updateValue(newValue)

    const command = new FanUpdateLevelRequest(level)
    this.sendCommand(command, callback)
  }

  getFanRotationSpeed(callback) {
    this.getNextValueForFanState(this.fanService.getCharacteristic(Characteristic.RotationSpeed), callback)
  }

  setFanRotationSpeed(newValue, callback) {
    const level = Math.ceil(newValue * (this.maximumFanLevel / 100))
    this.log.info('Fan speed: ' + level)
    this.fanService.getCharacteristic(Characteristic.RotationSpeed).updateValue(newValue)

    const command = new FanUpdateLevelRequest(level)
    this.sendCommand(command, callback)
  }

  getLightOn(callback) {
    this.getNextValueForFanState(this.lightService.getCharacteristic(Characteristic.On), callback)
  }

  setLightOn(newValue, callback) {
    this.log.info('Light on: ' + newValue)
    this.lightService.getCharacteristic(Characteristic.On).updateValue(newValue)

    const brightness = this.lightService.getCharacteristic(Characteristic.Brightness).value
    this.log.debug('Using current brightness ' + brightness)

    const command = new FanUpdateLightRequest(newValue, brightness)
    this.sendCommand(command, callback)
  }

  getLightBrightness(callback) {
    this.getNextValueForFanState(this.lightService.getCharacteristic(Characteristic.Brightness), callback)
  }

  setLightBrightness(newValue, callback) {
    this.log.info('Light brightness: ' + newValue)
    this.lightService.getCharacteristic(Characteristic.Brightness).updateValue(newValue)

    const isOn = this.lightService.getCharacteristic(Characteristic.On).value
    const command = new FanUpdateLightRequest(isOn, newValue)
    this.sendCommand(command, callback)
  }

  // MARK: -

  makeInformationService(config) {
    const service = new Service.AccessoryInformation()

    service
      .setCharacteristic(Characteristic.Manufacturer, config.device && config.device.manufacturer)
      .setCharacteristic(Characteristic.Model, config.device && config.device.model)
      .setCharacteristic(Characteristic.SerialNumber, config.device && config.device.serial)
      .setCharacteristic(Characteristic.FirmwareRevision, (config.device && config.device.revision) || plugin.version)

    return service
  }

  makeFanService(config) {
    const service = new Service.Fan(this.name)

    service.getCharacteristic(Characteristic.On)
      .on('get', this.getFanOn.bind(this))
      .on('set', this.setFanOn.bind(this))

    service.getCharacteristic(Characteristic.RotationSpeed)
      .setProps({
        maxValue: 99,
        minStep: 33
      })
      .on('get', this.getFanRotationSpeed.bind(this))
      .on('set', this.setFanRotationSpeed.bind(this))

    return service
  }

  makeLightService(config) {
    const service = new Service.Lightbulb(this.name)

    service.getCharacteristic(Characteristic.On)
      .on('get', this.getLightOn.bind(this))
      .on('set', this.setLightOn.bind(this))

    service.getCharacteristic(Characteristic.Brightness)
      .on('get', this.getLightBrightness.bind(this))
      .on('set', this.setLightBrightness.bind(this))

    return service
  }

  getServices () {
    return [ this.informationService, this.fanService, this.lightService ]
  }

}
<commit_msg>Restore stop to scanning in addition to Noble's likely stop on connect<commit_after>'use strict'

const plugin = require('./package'),
  Noble = require('noble'),
  EventEmitter = require('events').EventEmitter
var Homebridge, Service, Characteristic

module.exports = function(homebridge) {
  console.log("Homebridge API version: " + homebridge.version)

  Homebridge = homebridge
  Service = homebridge.hap.Service
  Characteristic = homebridge.hap.Characteristic

  homebridge.registerAccessory(plugin.name, "satellite-fan", FanLightAccessory)
}

function trimAddress(address) {
  return address.toLowerCase().replace(/:/g, "")
}

function trimUUID(uuid) {
  return uuid.toLowerCase().replace(/:/g, "").replace(/-/g, "")
}

// MARK: -

class FanRequest {

  writeInto(buffer) {
    throw new TypeError('Must override method')
  }

  toPrefixedBuffer(prefix) {
    var buffer
    if (prefix > 0) {
      buffer = new Buffer(13)
      buffer.writeUInt8(prefix)
      this.writeInto(buffer.slice(1))
    } else {
      buffer = new Buffer(12)
      this.writeInto(buffer)
    }

    const checksum = buffer.slice(0, buffer.length - 1).reduce(function(a, b){
      return a + b
    }, 0) & 255

    buffer.writeUInt8(checksum, buffer.length - 1)
    return buffer
  }

}

class FanGetStateRequest extends FanRequest {

  writeInto(buffer) {
    buffer.fill(0)
    buffer.writeUInt8(160)
  }

}

Math.clamp = function(number, min, max) {
  return Math.max(min, Math.min(number, max))
}

class FanUpdateLightRequest extends FanRequest {

  constructor(isOn, level) {
    super()
    this.on = isOn ? 1 : 0
    this.level = Math.clamp(level, 0, 100)
  }

  writeInto(buffer) {
    buffer.fill(0)
    buffer.writeUInt8(161)
    buffer.writeUInt8(255, 4)
    buffer.writeUInt8(100, 5)
    buffer.writeUInt8((this.on << 7) | this.level, 6)
    buffer.fill(255, 7, 10)
  }

}

class FanUpdateLevelRequest extends FanRequest {

  constructor(level) {
    super()
    this.level = Math.clamp(level, 0, 3)
  }

  writeInto(buffer) {
    buffer.fill(0)
    buffer.writeUInt8(161)
    buffer.writeUInt8(this.level, 4)
    buffer.fill(255, 5, 10)
  }

}

class FanResponse {

  static fromPrefixedBuffer(prefix, buffer) {
    if (prefix > 0) {
      buffer = buffer.slice(1)
    }

    if (buffer.readUInt8(0) != 176) { return null }
    const response = new FanResponse()

    const windVelocity = buffer.readUInt8(2)
    response.supportsFanReversal = (windVelocity & 0b00100000) != 0
    response.maximumFanLevel     =  windVelocity & 0b00011111

    const currentWindVelocity = buffer.readUInt8(4)
    response.isFanReversed     = (currentWindVelocity & 0b10000000) != 0
    response.fanLevel          =  currentWindVelocity & 0b00011111

    const currentBrightness = buffer.readUInt8(6)
    response.lightIsOn       = (currentBrightness & 0b10000000) != 0
    response.lightBrightness = (currentBrightness & 0b01111111)

    return response
  }

}

// MARK: -

class FanLightAccessory extends EventEmitter {

  constructor (log, config) {
    super()

    this.onDiscover = this.onDiscover.bind(this)

    this.log = log
    this.name = config.name || "Ceiling Fan"
    if (!config.address) {
      throw new Error(this.prefix + " Missing mandatory config 'address'")
    }
    this.address = trimAddress(config.address)
    if (!config.ble) {
      throw new Error(this.prefix + " Missing mandatory config 'ble'")
    }
    this.manufacturerPrefix = config.ble.prefix || 0
    if (!config.ble.serviceUUID) {
      throw new Error(this.prefix + " Missing mandatory config 'ble.serviceUUID'")
    }
    this.serviceUUID = trimUUID(config.ble.serviceUUID)
    if (!config.ble.writeCharacteristicUUID) {
      throw new Error(this.prefix + " Missing mandatory config 'ble.writeCharacteristicUUID'")
    }
    this.writeCharacteristicUUID = trimUUID(config.ble && config.ble.writeCharacteristicUUID)
    this.writeCharacteristic = null
    if (!config.ble.notifyCharacteristicUUID) {
      throw new Error(this.prefix + " Missing mandatory config 'ble.notifyCharacteristicUUID'")
    }
    this.notifyCharacteristicUUID = trimUUID(config.ble && config.ble.notifyCharacteristicUUID)
    this.notifyCharacteristic = null

    this.informationService = this.makeInformationService(config)
    this.fanService = this.makeFanService(config)
    this.lightService = this.makeLightService(config)

    this.maximumFanLevel = 3

    Homebridge.on('didFinishLaunching', this.onDidFinishLaunching.bind(this))
  }

  identify (callback) {
    this.log('Device identified!')
    callback()
  }

  startDiscovering() {
    this.log.debug('Start discovery')

    Noble.on('scanStop', function() {
      setTimeout(function() {
        Noble.startScanning([], true)
      }, 2500)
    })

    Noble.on('discover', this.onDiscover)
    Noble.startScanning([], true)
    this.log.debug('discover count ', Noble.listenerCount('discover'))
  }

  stopDiscovering() {
    Noble.removeListener('discover', this.onDiscover)
    if (Noble.listenerCount('discover') == 0) {
      Noble.removeAllListeners('scanStop')
      Noble.stopScanning()
    }
  }

  sendCommand(command, callback) {
    if (!this.notifyCharacteristic || !this.writeCharacteristic) {
      this.log.info('waiting on connect...')
      this.once('ready', function() {
        this.sendCommand(command, callback)
      }.bind(this))
      return
    }

    const buffer = command.toPrefixedBuffer(this.manufacturerPrefix)
    this.log.debug('will send', this.manufacturerPrefix, buffer)
    this.writeCharacteristic.write(buffer, false, callback)
  }

  sendUpdateStateRequest() {
    this.log.info('coalesced update request')
    const command = new FanGetStateRequest()
    this.sendCommand(command, function(error){
      if (!error) { return }
      this.emit('updateState', error)
    }.bind(this))
  }

  // MARK: -

  onDidFinishLaunching() {
    this.log.info("Received did finish launching")
    Noble.on('stateChange', this.onAdapterChange.bind(this))
  }

  onAdapterChange(state) {
    Noble.removeAllListeners('scanStop')
    Noble.stopScanning()

    if (state != 'poweredOn') {
      this.log.debug("Stopped scanning: " + state)
      return
    }

    this.startDiscovering()
  }

  onDiscover(peripheral) {
    if (trimAddress(peripheral.address) !== this.address || (this.writeCharacteristic && this.notifyCharacteristic)) {
      this.log.debug("Ignoring " + peripheral.address + " (RSSI " + peripheral.rssi + "dB)")
      return
    }

    this.log.debug("Found " + peripheral.address + " (RSSI " + peripheral.rssi + "dB)")
    this.stopDiscovering()
    peripheral.connect(function(error) {
      this.onConnect(error, peripheral)
    }.bind(this))
  }

  onConnect(error, peripheral) {
    if (error) {
      this.log.error("Connecting to " + peripheral.address + " failed: " + error)
      this.onDisconnect(error, peripheral)
      return
    }
    this.log.debug("Connected to " + peripheral.address)

    peripheral.discoverSomeServicesAndCharacteristics([ this.serviceUUID ], [ this.writeCharacteristicUUID, this.notifyCharacteristicUUID ], this.onDiscoverCharacteristics.bind(this));
    peripheral.once('disconnect', function(error) {
      this.onDisconnect(error, peripheral)
    }.bind(this))
  }

  onDisconnect(error, peripheral) {
    if (this.writeCharacteristic) {
      this.writeCharacteristic.removeAllListeners('set')
    }
    this.writeCharacteristic = null

    if (this.notifyCharacteristic) {
      this.notifyCharacteristic.unsubscribe(null)
      this.notifyCharacteristic.removeAllListeners('data')
    }
    this.notifyCharacteristic = null

    peripheral.removeAllListeners()

    this.log.info("Disconnected")

    this.startDiscovering()

    if (this.listenerCount('updateState') != 0) {
      this.sendUpdateStateRequest()
    }
  }

  onDiscoverCharacteristics(error, services, characteristics) {
    if (error || characteristics.count < 2) {
      this.log.error(this.prefix, "Discover services failed: " + error)
      return
    }

    const writeCharacteristic = characteristics[0],
      notifyCharacteristic = characteristics[1]

    notifyCharacteristic.on('data', this.onNotify.bind(this))
    notifyCharacteristic.subscribe(function (error) {
      if (error) {
        this.log.warn("Subscribe to notify characteristic failed")
      }

      this.writeCharacteristic = writeCharacteristic
      this.notifyCharacteristic = notifyCharacteristic

      this.log.info("Ready")
      this.emit('ready')
    }.bind(this))
  }

  onNotify(data, isNotification) {
    const response = FanResponse.fromPrefixedBuffer(this.manufacturerPrefix, data)
    if (!response) { return }
    this.log.debug('received fan state')

    this.maximumFanLevel = response.maximumFanLevel
    this.fanService.getCharacteristic(Characteristic.On).updateValue(response.fanLevel != 0)
    this.fanService.getCharacteristic(Characteristic.RotationSpeed).updateValue(Math.ceil((response.fanLevel / response.maximumFanLevel) * 100))
    this.lightService.getCharacteristic(Characteristic.On).updateValue(response.lightIsOn)
    this.lightService.getCharacteristic(Characteristic.Brightness).updateValue(response.lightBrightness)

    this.emit('updateState', null)
  }

  // MARK: -

  getNextValueForFanState(characteristic, callback) {
    const shouldSend = this.listenerCount('updateState') == 0

    this.once('updateState', function(error){
      if (error) {
        callback(error, null)
      } else {
        callback(null, characteristic.value)
      }
    })

    if (shouldSend) {
      this.sendUpdateStateRequest()
    }
  }

  getFanOn(callback) {
    this.getNextValueForFanState(this.fanService.getCharacteristic(Characteristic.On), callback)
  }

  setFanOn(newValue, callback) {
    const level = newValue ? this.fanService.getCharacteristic(Characteristic.RotationSpeed).value : 0
    this.log.info('Fan on to level ' + newValue)
    this.fanService.getCharacteristic(Characteristic.On).updateValue(newValue)

    const command = new FanUpdateLevelRequest(level)
    this.sendCommand(command, callback)
  }

  getFanRotationSpeed(callback) {
    this.getNextValueForFanState(this.fanService.getCharacteristic(Characteristic.RotationSpeed), callback)
  }

  setFanRotationSpeed(newValue, callback) {
    const level = Math.ceil(newValue * (this.maximumFanLevel / 100))
    this.log.info('Fan speed: ' + level)
    this.fanService.getCharacteristic(Characteristic.RotationSpeed).updateValue(newValue)

    const command = new FanUpdateLevelRequest(level)
    this.sendCommand(command, callback)
  }

  getLightOn(callback) {
    this.getNextValueForFanState(this.lightService.getCharacteristic(Characteristic.On), callback)
  }

  setLightOn(newValue, callback) {
    this.log.info('Light on: ' + newValue)
    this.lightService.getCharacteristic(Characteristic.On).updateValue(newValue)

    const brightness = this.lightService.getCharacteristic(Characteristic.Brightness).value
    this.log.debug('Using current brightness ' + brightness)

    const command = new FanUpdateLightRequest(newValue, brightness)
    this.sendCommand(command, callback)
  }

  getLightBrightness(callback) {
    this.getNextValueForFanState(this.lightService.getCharacteristic(Characteristic.Brightness), callback)
  }

  setLightBrightness(newValue, callback) {
    this.log.info('Light brightness: ' + newValue)
    this.lightService.getCharacteristic(Characteristic.Brightness).updateValue(newValue)

    const isOn = this.lightService.getCharacteristic(Characteristic.On).value
    const command = new FanUpdateLightRequest(isOn, newValue)
    this.sendCommand(command, callback)
  }

  // MARK: -

  makeInformationService(config) {
    const service = new Service.AccessoryInformation()

    service
      .setCharacteristic(Characteristic.Manufacturer, config.device && config.device.manufacturer)
      .setCharacteristic(Characteristic.Model, config.device && config.device.model)
      .setCharacteristic(Characteristic.SerialNumber, config.device && config.device.serial)
      .setCharacteristic(Characteristic.FirmwareRevision, (config.device && config.device.revision) || plugin.version)

    return service
  }

  makeFanService(config) {
    const service = new Service.Fan(this.name)

    service.getCharacteristic(Characteristic.On)
      .on('get', this.getFanOn.bind(this))
      .on('set', this.setFanOn.bind(this))

    service.getCharacteristic(Characteristic.RotationSpeed)
      .setProps({
        maxValue: 99,
        minStep: 33
      })
      .on('get', this.getFanRotationSpeed.bind(this))
      .on('set', this.setFanRotationSpeed.bind(this))

    return service
  }

  makeLightService(config) {
    const service = new Service.Lightbulb(this.name)

    service.getCharacteristic(Characteristic.On)
      .on('get', this.getLightOn.bind(this))
      .on('set', this.setLightOn.bind(this))

    service.getCharacteristic(Characteristic.Brightness)
      .on('get', this.getLightBrightness.bind(this))
      .on('set', this.setLightBrightness.bind(this))

    return service
  }

  getServices () {
    return [ this.informationService, this.fanService, this.lightService ]
  }

}
<|endoftext|>


<commit_before>(function() {
    var isNodejs = (typeof module === 'object' && module.exports);

    function Rohrpost(options) {
        var that = this;

        var connectionUrl = options.connectionUrl;

        // Simple exponential backoff for reconnects
        var backoffIntervalInitial = 500;
        var backoffIntervalStop = 60000;
        var backoffInterval = 0;

        var closing = false;
        that.open = false;
        var openEventHasBeenEmitted = false;

        // Create a random sessionId
        var sessionId = Math.random().toString(36).substr(2);

        var socketConnection;

        // This will be used whilst a reconnect or similar is going on.
        // Should be empty most of the time
        var messageQueue = [];

        /*******************
         * Public interface
         *******************/

        /**
         * This method sends a payload to a specific topic. Should this socket
         * not be allowed to send to this topic the message will be silently
         * dropped.
         */
        that.publish = function(topic, data) {
            var payload = {
                'topic': topic,
                'data': data
            };
            if (that.open) {
                send(payload);
            } else {
                messageQueue.push(payload);
            }
        };

        that.close = function() {
            closing = true;
            socketConnection.close();
        }

        that.log = {
            info: createDefaultLogger('info'),
            debug: createDefaultLogger('debug'),
            warn: createDefaultLogger('warn'),
            error: console.error
        }

        /*******************
         * Private methods
         *******************/

        /**
         * This function connects to the server. To do so it first does a
         * request to figure out which host/port to connect to and
         * then tries to connect via SockJS. Should something go wrong,
         * try again.
         */
        function connect() {
            makeRequest(connectionUrl, function(err, connectionUrl) {
                if (err) {
                    if (err.status == 404 || err.status == 0) {
                        reconnectAfterError();
                    } else {
                        console.error('uncaught error', err);
                    }
                    return;
                }

                if (!connectionUrl) {
                    throw "Couldn't get connection URL from " + connectionUrl;
                }

                function onOpen() {
                    socketConnection.send(sessionId);
                    backoffInterval = 0; // Reset backoff interval
                }

                if (isNodejs) {
                    var wsUrl = connectionUrl.replace(/^http/, 'ws') + '/websocket';
                    var Websocket = require('ws');
                    socketConnection = new Websocket(wsUrl);
                    socketConnection.on('open', onOpen);
                    socketConnection.on('close', onClose);
                    socketConnection.on('message', onMessage);
                    socketConnection.on('error', onError);
                    that.log.debug("Connect to websocket via " + wsUrl);
                } else {
                    socketConnection = new SockJS(connectionUrl);
                    socketConnection.onopen = onOpen
                    socketConnection.onmessage = onMessage;
                    socketConnection.onclose = onClose;
                    that.log.debug("Connect to sockjs via " + connectionUrl);
                }
            });
        }

        /**
         * This method should be called after an unexpected disconnect of
         * in case of an unsuccessful ajax request. Uses exponential backoff
         * to avoid flooding the server with reconnect requests
         */
        function reconnectAfterError() {
            if (backoffInterval < backoffIntervalStop) {
                // try and avoid the thundering herd
                var timeout = Math.random() * backoffInterval;
                setTimeout(function() {
                    if (backoffInterval == 0) {
                        backoffInterval = backoffIntervalInitial;
                    } else {
                        backoffInterval *= 2;
                    }
                    connect();
                }, timeout);
                that.log.info('Connection lost. Attempting to reconnect in %d ms', timeout);
            } else {
                that.log.warn('Couldn\'t reconnect. Giving up');
            }
        }

        /**
         * This should be called after a connection to the server has been
         * (re-)established to send all messages that have been queued in
         * the meantime.
         */
        function flushMessageQueue() {
            while(messageQueue.length > 0) {
                send(messageQueue.pop());
            }
        }

        /**
         * Close events are a bit more complicated, since some of them
         * are expected (e.g. we know how to handle them) but others are
         * unexptected. We should always attempt to handle close events
         * as gracefully as possible, without disturbing user experience
         * unnecessarily.
         */
        function onClose(e) {
            that.open = false;

            if (closing) {
                that.log.debug('Closed connection');
                return;
            }
            if (e.code == 100) {
                // The client is asked to reconnect.
                connect();
            } else if (!e.wasClean) {
                // Something bad happened
                reconnectAfterError();
            } else {
                // If we end up here we should analyse the error and write a
                // custom handler for it.
                that.log.error('Unhandled close event', e);
            }
        }

        /**
         * This gets called when we receive a raw message from the socket.
         * We need to unwrap and emit it.
         */
        function onMessage(message) {
            var data;
            if (isNodejs) {
                data = message;
            } else {
                data = message.data;
            }
            if (!that.open) {
                if (data == 'ok:' + sessionId) {
                    that.open = true;
                    flushMessageQueue();

                    // only emit 'open' once
                    if (!openEventHasBeenEmitted) {
                        openEventHasBeenEmitted = true;
                        that.emit('open');
                    }
                } else {
                    that.log.warn('error', 'Handshake was not successful (' + data + ' != ok:' + sessionId + ')');
                }
            } else {
                try {
                    var payload = JSON.parse(data);
                    that.log.debug('[Received message]', payload);
                    that.emit(payload.topic, payload.data);
                } catch (err) {
                    that.log.error(err, data);
                }
            }

        }

        function onError(error) {
            that.log.error(error);
            that.open = false;
            reconnectAfterError();
        }

        /**
         * This is helper function that sends a raw json object over the
         * wire.
         */

        function send(rawObject) {
            that.log.debug('[Send message]', rawObject);
            socketConnection.send(JSON.stringify(rawObject));
        }

        /**
         * This function is used to fill the this.log object above
         */
        function createDefaultLogger(level) {
            if (console[level]) return console[level];
            return console.log;
        }

        /***************
         * Constructor
         ***************/
        connect();
    }

    /**
     * We need some sort of EventEmitter. In case of Nodejs we can
     * use the build-in one, for the browser we need to use a custom
     * one.
     */
    if (isNodejs) {
        EventEmitter = require('events').EventEmitter;
    } else {
        /*!
         * EventEmitter v4.2.3 - git.io/ee
         * Oliver Caldwell
         * MIT license
         * @preserve
         */
        !function(){"use strict";function t(){}function r(t,n){for(var e=t.length;e--;)if(t[e].listener===n)return e;return-1}function n(e){return function(){return this[e].apply(this,arguments)}}var e=t.prototype;e.getListeners=function(n){var r,e,t=this._getEvents();if("object"==typeof n){r={};for(e in t)t.hasOwnProperty(e)&&n.test(e)&&(r[e]=t[e])}else r=t[n]||(t[n]=[]);return r},e.flattenListeners=function(t){var e,n=[];for(e=0;e<t.length;e+=1)n.push(t[e].listener);return n},e.getListenersAsObject=function(n){var e,t=this.getListeners(n);return t instanceof Array&&(e={},e[n]=t),e||t},e.addListener=function(i,e){var t,n=this.getListenersAsObject(i),s="object"==typeof e;for(t in n)n.hasOwnProperty(t)&&-1===r(n[t],e)&&n[t].push(s?e:{listener:e,once:!1});return this},e.on=n("addListener"),e.addOnceListener=function(e,t){return this.addListener(e,{listener:t,once:!0})},e.once=n("addOnceListener"),e.defineEvent=function(e){return this.getListeners(e),this},e.defineEvents=function(t){for(var e=0;e<t.length;e+=1)this.defineEvent(t[e]);return this},e.removeListener=function(i,s){var n,e,t=this.getListenersAsObject(i);for(e in t)t.hasOwnProperty(e)&&(n=r(t[e],s),-1!==n&&t[e].splice(n,1));return this},e.off=n("removeListener"),e.addListeners=function(e,t){return this.manipulateListeners(!1,e,t)},e.removeListeners=function(e,t){return this.manipulateListeners(!0,e,t)},e.manipulateListeners=function(r,t,i){var e,n,s=r?this.removeListener:this.addListener,o=r?this.removeListeners:this.addListeners;if("object"!=typeof t||t instanceof RegExp)for(e=i.length;e--;)s.call(this,t,i[e]);else for(e in t)t.hasOwnProperty(e)&&(n=t[e])&&("function"==typeof n?s.call(this,e,n):o.call(this,e,n));return this},e.removeEvent=function(n){var e,r=typeof n,t=this._getEvents();if("string"===r)delete t[n];else if("object"===r)for(e in t)t.hasOwnProperty(e)&&n.test(e)&&delete t[e];else delete this._events;return this},e.emitEvent=function(r,o){var e,i,t,s,n=this.getListenersAsObject(r);for(t in n)if(n.hasOwnProperty(t))for(i=n[t].length;i--;)e=n[t][i],e.once===!0&&this.removeListener(r,e.listener),s=e.listener.apply(this,o||[]),s===this._getOnceReturnValue()&&this.removeListener(r,e.listener);return this},e.trigger=n("emitEvent"),e.emit=function(e){var t=Array.prototype.slice.call(arguments,1);return this.emitEvent(e,t)},e.setOnceReturnValue=function(e){return this._onceReturnValue=e,this},e._getOnceReturnValue=function(){return this.hasOwnProperty("_onceReturnValue")?this._onceReturnValue:!0},e._getEvents=function(){return this._events||(this._events={})},"function"==typeof define&&define.amd?define(function(){return t}):"object"==typeof module&&module.exports?module.exports=t:this.EventEmitter=t}.call(this);
    }

    /**
     * We also need to make one HTTP(s) request.
     */
    var makeRequest = null;
    if (isNodejs) {
        var request = require('request');
        makeRequest = function(url, callback) {
            request(url, function (err, response, body) {
                if (!err && response.statusCode == 200) {
                    callback(null, body)
                } else {
                    callback(err);
                }
            });
        }
    } else {
        makeRequest = function(url, callback){
            var xmlhttp;
            // compatible with IE7+, Firefox, Chrome, Opera, Safari
            xmlhttp = new XMLHttpRequest();
            xmlhttp.onreadystatechange = function(){
                if (xmlhttp.readyState == 4) {
                    if (xmlhttp.status == 200){
                        callback(null, xmlhttp.responseText);
                    } else {
                        callback(xmlhttp);
                    }
                }
            }
            xmlhttp.open("GET", url, true);
            xmlhttp.send();
        }
    }

    // Extend object
    if (isNodejs) {
        require('util').inherits(Rohrpost, EventEmitter);
    } else {
        Rohrpost.prototype = new EventEmitter();
        Rohrpost.prototype.constructor = Rohrpost;
    }

    // Expose the class either via AMD, CommonJS or the global object
    if (isNodejs){
        module.exports = Rohrpost;
    }
    else {
        this.Rohrpost = Rohrpost;
    }

})(this);
<commit_msg>Bugfix the bugfix<commit_after>(function() {
    var isNodejs = (typeof module === 'object' && module.exports);

    function Rohrpost(options) {
        var that = this;

        var connectionUrl = options.connectionUrl;

        // Simple exponential backoff for reconnects
        var backoffIntervalInitial = 500;
        var backoffIntervalStop = 60000;
        var backoffInterval = 0;

        var closing = false;
        that.open = false;
        var openEventHasBeenEmitted = false;

        // Create a random sessionId
        var sessionId = Math.random().toString(36).substr(2);

        var socketConnection;

        // This will be used whilst a reconnect or similar is going on.
        // Should be empty most of the time
        var messageQueue = [];

        /*******************
         * Public interface
         *******************/

        /**
         * This method sends a payload to a specific topic. Should this socket
         * not be allowed to send to this topic the message will be silently
         * dropped.
         */
        that.publish = function(topic, data) {
            var payload = {
                'topic': topic,
                'data': data
            };
            if (that.open) {
                send(payload);
            } else {
                messageQueue.push(payload);
            }
        };

        that.close = function() {
            closing = true;
            socketConnection.close();
        }

        that.log = {
            info: createDefaultLogger('info'),
            debug: createDefaultLogger('debug'),
            warn: createDefaultLogger('warn'),
            error: console.error
        }

        /*******************
         * Private methods
         *******************/

        /**
         * This function connects to the server. To do so it first does a
         * request to figure out which host/port to connect to and
         * then tries to connect via SockJS. Should something go wrong,
         * try again.
         */
        function connect() {
            makeRequest(connectionUrl, function(err, connectionUrl) {
                if (err) {
                    if (err.status == 404 || err.status == 0) {
                        reconnectAfterError();
                    } else {
                        console.error('uncaught error', err);
                    }
                    return;
                }

                if (!connectionUrl) {
                    throw "Couldn't get connection URL from " + connectionUrl;
                }

                function onOpen() {
                    socketConnection.send(sessionId);
                    backoffInterval = 0; // Reset backoff interval
                }

                if (isNodejs) {
                    var wsUrl = connectionUrl.replace(/^http/, 'ws') + '/websocket';
                    var Websocket = require('ws');
                    socketConnection = new Websocket(wsUrl);
                    socketConnection.on('open', onOpen);
                    socketConnection.on('close', onClose);
                    socketConnection.on('message', onMessage);
                    socketConnection.on('error', onError);
                    that.log.debug("Connect to websocket via " + wsUrl);
                } else {
                    socketConnection = new SockJS(connectionUrl);
                    socketConnection.onopen = onOpen
                    socketConnection.onmessage = onMessage;
                    socketConnection.onclose = onClose;
                    that.log.debug("Connect to sockjs via " + connectionUrl);
                }
            });
        }

        /**
         * This method should be called after an unexpected disconnect of
         * in case of an unsuccessful ajax request. Uses exponential backoff
         * to avoid flooding the server with reconnect requests
         */
        function reconnectAfterError() {
            if (backoffInterval < backoffIntervalStop) {
                // try and avoid the thundering herd
                var timeout = Math.random() * backoffInterval;
                setTimeout(function() {
                    if (backoffInterval == 0) {
                        backoffInterval = backoffIntervalInitial;
                    } else {
                        backoffInterval *= 2;
                    }
                    connect();
                }, timeout);
                that.log.info('Connection lost. Attempting to reconnect in %d ms', timeout);
            } else {
                that.log.warn('Couldn\'t reconnect. Giving up');
            }
        }

        /**
         * This should be called after a connection to the server has been
         * (re-)established to send all messages that have been queued in
         * the meantime.
         */
        function flushMessageQueue() {
            while(messageQueue.length > 0) {
                send(messageQueue.pop());
            }
        }

        /**
         * Close events are a bit more complicated, since some of them
         * are expected (e.g. we know how to handle them) but others are
         * unexptected. We should always attempt to handle close events
         * as gracefully as possible, without disturbing user experience
         * unnecessarily.
         */
        function onClose(e) {
            that.open = false;

            if (closing) {
                that.log.debug('Closed connection');
                return;
            }
            if (e.code == 100) {
                // The client is asked to reconnect.
                connect();
            } else if (!e.wasClean) {
                // Something bad happened
                reconnectAfterError();
            } else {
                // If we end up here we should analyse the error and write a
                // custom handler for it.
                that.log.error('Unhandled close event', e);
            }
        }

        /**
         * This gets called when we receive a raw message from the socket.
         * We need to unwrap and emit it.
         */
        function onMessage(message) {
            var data;
            if (isNodejs) {
                data = message;
            } else {
                data = message.data;
            }
            if (!that.open) {
                if (data == 'ok:' + sessionId) {
                    that.open = true;
                    flushMessageQueue();

                    // only emit 'open' once
                    if (!openEventHasBeenEmitted) {
                        openEventHasBeenEmitted = true;
                        that.emit('open');
                    }
                } else {
                    that.log.warn('error', 'Handshake was not successful (' + data + ' != ok:' + sessionId + ')');
                }
            } else {
                try {
                    var payload = JSON.parse(data);
                    that.log.debug('[Received message]', payload);
                    that.emit(payload.topic, payload.data);
                } catch (err) {
                    that.log.error(err, data);
                }
            }

        }

        function onError(error) {
            that.log.error(error);
            that.open = false;
            reconnectAfterError();
        }

        /**
         * This is helper function that sends a raw json object over the
         * wire.
         */

        function send(rawObject) {
            that.log.debug('[Send message]', rawObject);
            socketConnection.send(JSON.stringify(rawObject));
        }

        /**
         * This function is used to fill the this.log object above
         */
        function createDefaultLogger(level) {
            return function () {
                if (console[level]) return console[level];
                return console.log;
	    };
        }

        /***************
         * Constructor
         ***************/
        connect();
    }

    /**
     * We need some sort of EventEmitter. In case of Nodejs we can
     * use the build-in one, for the browser we need to use a custom
     * one.
     */
    if (isNodejs) {
        EventEmitter = require('events').EventEmitter;
    } else {
        /*!
         * EventEmitter v4.2.3 - git.io/ee
         * Oliver Caldwell
         * MIT license
         * @preserve
         */
        !function(){"use strict";function t(){}function r(t,n){for(var e=t.length;e--;)if(t[e].listener===n)return e;return-1}function n(e){return function(){return this[e].apply(this,arguments)}}var e=t.prototype;e.getListeners=function(n){var r,e,t=this._getEvents();if("object"==typeof n){r={};for(e in t)t.hasOwnProperty(e)&&n.test(e)&&(r[e]=t[e])}else r=t[n]||(t[n]=[]);return r},e.flattenListeners=function(t){var e,n=[];for(e=0;e<t.length;e+=1)n.push(t[e].listener);return n},e.getListenersAsObject=function(n){var e,t=this.getListeners(n);return t instanceof Array&&(e={},e[n]=t),e||t},e.addListener=function(i,e){var t,n=this.getListenersAsObject(i),s="object"==typeof e;for(t in n)n.hasOwnProperty(t)&&-1===r(n[t],e)&&n[t].push(s?e:{listener:e,once:!1});return this},e.on=n("addListener"),e.addOnceListener=function(e,t){return this.addListener(e,{listener:t,once:!0})},e.once=n("addOnceListener"),e.defineEvent=function(e){return this.getListeners(e),this},e.defineEvents=function(t){for(var e=0;e<t.length;e+=1)this.defineEvent(t[e]);return this},e.removeListener=function(i,s){var n,e,t=this.getListenersAsObject(i);for(e in t)t.hasOwnProperty(e)&&(n=r(t[e],s),-1!==n&&t[e].splice(n,1));return this},e.off=n("removeListener"),e.addListeners=function(e,t){return this.manipulateListeners(!1,e,t)},e.removeListeners=function(e,t){return this.manipulateListeners(!0,e,t)},e.manipulateListeners=function(r,t,i){var e,n,s=r?this.removeListener:this.addListener,o=r?this.removeListeners:this.addListeners;if("object"!=typeof t||t instanceof RegExp)for(e=i.length;e--;)s.call(this,t,i[e]);else for(e in t)t.hasOwnProperty(e)&&(n=t[e])&&("function"==typeof n?s.call(this,e,n):o.call(this,e,n));return this},e.removeEvent=function(n){var e,r=typeof n,t=this._getEvents();if("string"===r)delete t[n];else if("object"===r)for(e in t)t.hasOwnProperty(e)&&n.test(e)&&delete t[e];else delete this._events;return this},e.emitEvent=function(r,o){var e,i,t,s,n=this.getListenersAsObject(r);for(t in n)if(n.hasOwnProperty(t))for(i=n[t].length;i--;)e=n[t][i],e.once===!0&&this.removeListener(r,e.listener),s=e.listener.apply(this,o||[]),s===this._getOnceReturnValue()&&this.removeListener(r,e.listener);return this},e.trigger=n("emitEvent"),e.emit=function(e){var t=Array.prototype.slice.call(arguments,1);return this.emitEvent(e,t)},e.setOnceReturnValue=function(e){return this._onceReturnValue=e,this},e._getOnceReturnValue=function(){return this.hasOwnProperty("_onceReturnValue")?this._onceReturnValue:!0},e._getEvents=function(){return this._events||(this._events={})},"function"==typeof define&&define.amd?define(function(){return t}):"object"==typeof module&&module.exports?module.exports=t:this.EventEmitter=t}.call(this);
    }

    /**
     * We also need to make one HTTP(s) request.
     */
    var makeRequest = null;
    if (isNodejs) {
        var request = require('request');
        makeRequest = function(url, callback) {
            request(url, function (err, response, body) {
                if (!err && response.statusCode == 200) {
                    callback(null, body)
                } else {
                    callback(err);
                }
            });
        }
    } else {
        makeRequest = function(url, callback){
            var xmlhttp;
            // compatible with IE7+, Firefox, Chrome, Opera, Safari
            xmlhttp = new XMLHttpRequest();
            xmlhttp.onreadystatechange = function(){
                if (xmlhttp.readyState == 4) {
                    if (xmlhttp.status == 200){
                        callback(null, xmlhttp.responseText);
                    } else {
                        callback(xmlhttp);
                    }
                }
            }
            xmlhttp.open("GET", url, true);
            xmlhttp.send();
        }
    }

    // Extend object
    if (isNodejs) {
        require('util').inherits(Rohrpost, EventEmitter);
    } else {
        Rohrpost.prototype = new EventEmitter();
        Rohrpost.prototype.constructor = Rohrpost;
    }

    // Expose the class either via AMD, CommonJS or the global object
    if (isNodejs){
        module.exports = Rohrpost;
    }
    else {
        this.Rohrpost = Rohrpost;
    }

})(this);
<|endoftext|>