<commit_before>const express = require('express')
const chalk = require('chalk')
const path = require('path')
const Promise = require('bluebird')
const _ = require('lodash')
const socketio = require('socket.io')
const http = require('http')
const bodyParser = require('body-parser')

const util = require('./util')

const setupSocket = function(app, skin) {
  const server = http.createServer(app)
  const io = socketio(server)

  io.on('connection', function(socket) {
    skin.logger.verbose('socket connected')

    socket.on('event', function(event) {
      skin.events.emit(event.name, event.data, 'client')
    })
  })

  skin.events.onAny(function(event, data, from) {
    if(from === 'client') {
      // we sent this ourselves
      return
    }
    io.emit('event', {
      name: event,
      data: data
    })
  })

  return server
}

const serveApi = function(app, skin) {

  app.post('/api/login', (req, res, next) => {
    const result = skin.login(req.body.user, req.body.password, req.ip)
    res.send(result)
  })

  app.get('/api/*', authenticationMiddleware(skin))

  app.get('/api/ping', (req, res, next) => {
    res.send('pong')
  })

  app.get('/api/modules', (req, res, next) => {
    const modules = _.map(skin.modules, (module) => {
      return {
        name: module.name,
        menuText: module.settings.menuText || module.name,
        menuIcon: module.settings.menuIcon || 'icon-puzzle'
      }
    })
    res.send(modules)
  })

  app.get('/api/logs', (req, res, next) => {
    const options = {
      from: new Date() - 7 * 24 * 60 * 60 * 1000,
      until: new Date(),
      limit: (req.query && req.query.limit) || 50,
      start: 0,
      order: 'desc',
      fields: ['message', 'level', 'timestamp']
    }
    skin.logger.query(options, (err, results) => {
      if (err) return console.log(err)
      res.send(results.file)
    })
  })

  app.get('/api/logs/archive', (req, res, next) => {
    skin.logger.archiveToFile()
    .then((archivePath) => {
      res.download(archivePath)
    })
  })
}

const serveStatic = function(app, skin) {
  app.use(express.static(path.join(__dirname, '../web/dist')))

  app.get('*', (req, res, next) => {
    if(/html/i.test(req.headers.accept)) {
      return res.sendFile(path.join(__dirname, '../web/dist/index.html'))
    }
    next()
  })

  if (util.isDeveloping) {
    return new Promise(function(resolve, reject) {
      // backup current working directory
      const cwd = process.cwd()
      skin.logger.verbose('compiling website...')
      try {
        process.chdir(path.join(__dirname, '../web'))
        const Tasks = require(path.join(__dirname, '../web/tasks'))
        const modules = _.map(_.values(skin.modules), (mod) => {
          return { name: mod.name, path: `${mod.root}/views/**.*` }
        })
        const gulp = Tasks({ modules, skipLogs: true })
        gulp.on('done', resolve)
        gulp.on('error', (err) => {
          skin.logger.error('Gulp error', err)
        })

        gulp.start('default')
      }
      catch (err) {
        reject(err)
      }
      finally {
        // restore initial working directory
        process.chdir(cwd);
      }
    })
  } else {
    return Promise.resolve()
  }
}

const authenticationMiddleware = (skin) => function(req, res, next) {
  if(!skin.requiresAuth) {
    return next()
  }

  if(skin.authenticate(req.headers.authorization)) {
    next()
  } else {
    res.status(401).location('/login').end()
  }
}

class WebServer {

  constructor({ skin }) {
    this.skin = skin
  }

  start() {
    const app = express()
    app.use(bodyParser.json())
    app.use(bodyParser.urlencoded({ extended: true }))

    // TODO Add proxy trusting config
    // app.enable('trust proxy')

    const server = setupSocket(app, this.skin)
    serveApi(app, this.skin)
    serveStatic(app, this.skin)

    .then (() => {
      server.listen(3000, () => { // TODO Port in config
        this.skin.logger.info('listening on port 3000')
      })
    })
  }

}

module.exports = WebServer;
<commit_msg>securing socket<commit_after>const express = require('express')
const chalk = require('chalk')
const path = require('path')
const Promise = require('bluebird')
const _ = require('lodash')
const socketio = require('socket.io')
const socketioJwt = require('socketio-jwt')
const http = require('http')
const bodyParser = require('body-parser')

const util = require('./util')

const setupSocket = function(app, skin) {
  const server = http.createServer(app)
  const io = socketio(server)

  if(skin.requiresAuth) {
    io.use(socketioJwt.authorize({
      secret: skin.getSecret(),
      handshake: true
    }))
  }

  io.on('connection', function(socket) {
    skin.logger.verbose('socket connected')

    socket.on('event', function(event) {
      skin.events.emit(event.name, event.data, 'client')
    })
  })

  skin.events.onAny(function(event, data, from) {
    if(from === 'client') {
      // we sent this ourselves
      return
    }
    io.emit('event', {
      name: event,
      data: data
    })
  })

  return server
}

const serveApi = function(app, skin) {

  app.post('/api/login', (req, res, next) => {
    const result = skin.login(req.body.user, req.body.password, req.ip)
    res.send(result)
  })

  app.get('/api/*', authenticationMiddleware(skin))

  app.get('/api/ping', (req, res, next) => {
    res.send('pong')
  })

  app.get('/api/modules', (req, res, next) => {
    const modules = _.map(skin.modules, (module) => {
      return {
        name: module.name,
        menuText: module.settings.menuText || module.name,
        menuIcon: module.settings.menuIcon || 'icon-puzzle'
      }
    })
    res.send(modules)
  })

  app.get('/api/logs', (req, res, next) => {
    const options = {
      from: new Date() - 7 * 24 * 60 * 60 * 1000,
      until: new Date(),
      limit: (req.query && req.query.limit) || 50,
      start: 0,
      order: 'desc',
      fields: ['message', 'level', 'timestamp']
    }
    skin.logger.query(options, (err, results) => {
      if (err) return console.log(err)
      res.send(results.file)
    })
  })

  app.get('/api/logs/archive', (req, res, next) => {
    skin.logger.archiveToFile()
    .then((archivePath) => {
      res.download(archivePath)
    })
  })
}

const serveStatic = function(app, skin) {
  app.use(express.static(path.join(__dirname, '../web/dist')))

  app.get('*', (req, res, next) => {
    if(/html/i.test(req.headers.accept)) {
      return res.sendFile(path.join(__dirname, '../web/dist/index.html'))
    }
    next()
  })

  if (util.isDeveloping) {
    return new Promise(function(resolve, reject) {
      // backup current working directory
      const cwd = process.cwd()
      skin.logger.verbose('compiling website...')
      try {
        process.chdir(path.join(__dirname, '../web'))
        const Tasks = require(path.join(__dirname, '../web/tasks'))
        const modules = _.map(_.values(skin.modules), (mod) => {
          return { name: mod.name, path: `${mod.root}/views/**.*` }
        })
        const gulp = Tasks({ modules, skipLogs: true })
        gulp.on('done', resolve)
        gulp.on('error', (err) => {
          skin.logger.error('Gulp error', err)
        })

        gulp.start('default')
      }
      catch (err) {
        reject(err)
      }
      finally {
        // restore initial working directory
        process.chdir(cwd);
      }
    })
  } else {
    return Promise.resolve()
  }
}

const authenticationMiddleware = (skin) => function(req, res, next) {
  if(!skin.requiresAuth) {
    return next()
  }

  if(skin.authenticate(req.headers.authorization)) {
    next()
  } else {
    res.status(401).location('/login').end()
  }
}

class WebServer {

  constructor({ skin }) {
    this.skin = skin
  }

  start() {
    const app = express()
    app.use(bodyParser.json())
    app.use(bodyParser.urlencoded({ extended: true }))

    // TODO Add proxy trusting config
    // app.enable('trust proxy')

    const server = setupSocket(app, this.skin)
    serveApi(app, this.skin)
    serveStatic(app, this.skin)

    .then (() => {
      server.listen(3000, () => { // TODO Port in config
        this.skin.logger.info('listening on port 3000')
      })
    })
  }

}

module.exports = WebServer;
<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2013 Chris Neave
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

var http = require('http'),
    url = require('url'),
    httpProxy = require('http-proxy'),
    FakeMinder = require(__dirname + '/../lib/fakeminder.js'),
    log = require(__dirname + '/../lib/logger');

module.exports.start = function(config_file) {
  if (!require('fs').existsSync(config_file)) {
    log.error('server', 'Config file %s does not exist', config_file);
    process.exit();
  }

  var fm = new FakeMinder(config_file, log);
  var port = fm.config.proxy().port;
  var upstreamApp = fm.config.upstreamApp('sample_target');

  var server = httpProxy.createServer(function(req, res, proxy) {
    fm.middleware(req, res, function() {
      log.http('server', '%s %s', req.method, req.url);

      proxy.proxyRequest(req, res, {
        host: upstreamApp.hostname,
        port: upstreamApp.port
      });
    });
  }).listen(port);

  log.info('server', 'FakeMinder listening on port ' + port);

  server.proxy.on('proxyError', function(err, req, res) {
    if (err.code === 'ECONNREFUSED') {
      log.error('server', 'Connection refused! Make sure the target application %s:%d is running', upstreamApp.hostname, upstreamApp.port);
    }
  });

  server.proxy.on('proxyResponse', function(req, res, response) {

  });
};
<commit_msg>Bad response codes are now logged more clearly<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2013 Chris Neave
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

var http = require('http'),
    url = require('url'),
    httpProxy = require('http-proxy'),
    FakeMinder = require(__dirname + '/../lib/fakeminder.js'),
    log = require(__dirname + '/../lib/logger'),
    util = require('util');

module.exports.start = function(config_file) {
  if (!require('fs').existsSync(config_file)) {
    log.error('#server', 'Config file %s does not exist', config_file);
    process.exit();
  }

  var fm = new FakeMinder(config_file, log);
  var port = fm.config.proxy().port;
  var upstreamApp = fm.config.upstreamApp('sample_target');

  var server = httpProxy.createServer(function(req, res, proxy) {
    fm.middleware(req, res, function() {

      proxy.proxyRequest(req, res, {
        host: upstreamApp.hostname,
        port: upstreamApp.port
      });
    });
  }).listen(port);

  log.info('#server', 'Listening on port ' + port);

  server.proxy.on('proxyError', function(err, req, res) {
    if (err.code === 'ECONNREFUSED') {
      log.error('#server', 'Connection refused! Make sure the target application %s:%d is running', upstreamApp.hostname, upstreamApp.port);
    }
  });

  server.proxy.on('proxyResponse', function(req, res, response) {
    var message = util.format('%s %s => %d', req.method, req.url, response.statusCode);

    if (response.statusCode >= 500) {
      log.error('#server', message);
    } else if (response.statusCode >= 400) {
      log.warn('#server', message);
    } else {
      log.verbose('#server', message);
    }
  });
};
<|endoftext|>


<commit_before>var nssocket = require('./nssocket');
var events = require('events');
var util = require('util');
var async = require('async');

var Request = require('./request');
var Response = require('./response');
var IllyriaZk = require('./illyria-zk');

/**
 * Illyria Server
 * @param options { host: ..., port: ... }
 * @param zkOptions { connectingString: ..., root: ..., prefix: ..., sessionTimeout: ..., spinDelay: ..., retries: ... }
 * @constructor
 */
function Server(options, zkOptions) {
    events.EventEmitter.call(this);

    var self = this;
    this.modules = { };
    this.tasks = [];

    this.options = options;
    this.zkOptions = zkOptions;

    this.zk = null;
    if(zkOptions !== undefined) {
        this.zk = new IllyriaZk(zkOptions.connectingString, zkOptions, zkOptions.root, zkOptions.prefix);
    }

    this.server = nssocket.createServer(options, function(socket) {
        var moduleNames = Object.keys(self.modules);
        moduleNames.forEach(function(moduleName) {
            var module = self.modules[moduleName];
            var methods = Object.keys(module);

            // add client count
            if(self.zk) {
                self.zk.clientConnected();
                socket.on('close', function() { self.zk.clientDisconnected(); });
            }

            methods.forEach(function(method) {
                socket.data(['*', 'call', moduleName, method], function() {
                    var params = [].slice.call(arguments);
                    params = params.length === 1 ? params[0] : { };
                    var msg_id = this.event[1];

                    var req = new Request(socket, params);
                    var res = new Response(socket, msg_id);
                    if(self.tasks.length > 0) {
                        async.mapSeries(self.tasks, function(task, done) {
                            task(req, res, done);
                        }, function() {
                            module[method](req, res);
                        });
                    } else {
                        module[method](req, res);
                    }
                });
            });
        });
    });
}

util.inherits(Server, events.EventEmitter);

/**
 * close server
 : @param callback
 */
Server.prototype.close = function(callback) {
    if(this.zk) this.zk.disconnect();
    this.server.close(callback);
};

/**
 * start listening on a certain port
 * @param [port]
 * @param [host]
 * @param cb
 */
Server.prototype.listen = function(port, host, cb) {
    if(typeof port === "function") {
        cb = port;
        port = this.options.port;
    } else {
        this.options.port = port;
    }

    if(typeof host === "function") {
        cb = host;
        host = this.options.host;
    } else {
        this.options.host = host;
    }

    if(undefined === host && this.zk) {
        throw new Error("You must specify your server host when you use Zookeeper.");
    }

    if(undefined === port) {
        throw new Error("You must specify a port when you use Zookeeper.");
    }

    var self = this;
    if(undefined === this.options.host) this.options.host = "0.0.0.0";

    this.server.listen(port, host, function() {
        if(!self.zkOptions) {
            return cb();
        }

        self.zk.setServerInformation(host, port);
        self.zk.connect(function(err, path) {
            console.log(path);
            if(err) {
                self.server.close();
                return cb(err);
            }

            cb();
        });
    });
};

/**
 * add a middleware to the chain
 * @param func
 */
Server.prototype.use = function(func) {
    this.tasks.push(func);
};

/**
 * Expose an router event
 * @param module
 */
Server.prototype.expose = function(module) {
    this.modules[module.name] = module.methods;
};

module.exports = {
    Server: Server,
    createServer: function(options, zkOptions) {
        var opts = options || { };
        opts.delimiter = opts.delimiter || '.';
        return new Server(opts, zkOptions);
    }
};
<commit_msg>fix a bug<commit_after>var nssocket = require('./nssocket');
var events = require('events');
var util = require('util');
var async = require('async');

var Request = require('./request');
var Response = require('./response');
var IllyriaZk = require('./illyria-zk');

/**
 * Illyria Server
 * @param options { host: ..., port: ... }
 * @param zkOptions { connectingString: ..., root: ..., prefix: ..., sessionTimeout: ..., spinDelay: ..., retries: ... }
 * @constructor
 */
function Server(options, zkOptions) {
    events.EventEmitter.call(this);

    var self = this;
    this.modules = { };
    this.tasks = [];

    this.options = options;
    this.zkOptions = zkOptions;

    this.zk = null;
    if(zkOptions !== undefined) {
        this.zk = new IllyriaZk(zkOptions.connectingString, zkOptions, zkOptions.root, zkOptions.prefix);
    }

    this.server = nssocket.createServer(options, function(socket) {
        var moduleNames = Object.keys(self.modules);
        moduleNames.forEach(function(moduleName) {
            var module = self.modules[moduleName];
            var methods = Object.keys(module);

            // add client count
            if(self.zk) {
                self.zk.clientConnected();
                socket.on('close', function() { self.zk.clientDisconnected(); });
            }

            methods.forEach(function(method) {
                socket.data(['*', 'call', moduleName, method], function() {
                    var params = [].slice.call(arguments);
                    params = params.length === 1 ? params[0] : { };
                    var msg_id = this.event[1];

                    var req = new Request(socket, params);
                    var res = new Response(socket, msg_id);
                    if(self.tasks.length > 0) {
                        async.mapSeries(self.tasks, function(task, done) {
                            task(req, res, done);
                        }, function() {
                            module[method](req, res);
                        });
                    } else {
                        module[method](req, res);
                    }
                });
            });
        });
    });
}

util.inherits(Server, events.EventEmitter);

/**
 * close server
 : @param callback
 */
Server.prototype.close = function(callback) {
    if(this.zk) this.zk.disconnect();
    this.server.close(callback);
};

/**
 * start listening on a certain port
 * @param [port]
 * @param [host]
 * @param cb
 */
Server.prototype.listen = function(port, host, cb) {
    if(typeof port === "function" || undefined === port) {
        cb = (undefined === port) ? cb : port;
        port = this.options.port;
    } else {
        this.options.port = port;
    }

    if(typeof host === "function" || undefined === host) {
        cb = (undefined === host) ? cb : host;
        host = this.options.host;
    } else {
        this.options.host = host;
    }

    if(undefined === host && this.zk) {
        throw new Error("You must specify your server host when you use Zookeeper.");
    }

    if(undefined === port) {
        throw new Error("You must specify a port when you use Zookeeper.");
    }

    var self = this;
    if(undefined === this.options.host) this.options.host = "0.0.0.0";

    this.server.listen(port, host, function() {
        if(!self.zkOptions) {
            return cb();
        }

        self.zk.setServerInformation(host, port);
        self.zk.connect(function(err, path) {
            console.log(path);
            if(err) {
                self.server.close();
                return cb(err);
            }

            cb();
        });
    });
};

/**
 * add a middleware to the chain
 * @param func
 */
Server.prototype.use = function(func) {
    this.tasks.push(func);
};

/**
 * Expose an router event
 * @param module
 */
Server.prototype.expose = function(module) {
    this.modules[module.name] = module.methods;
};

module.exports = {
    Server: Server,
    createServer: function(options, zkOptions) {
        var opts = options || { };
        opts.delimiter = opts.delimiter || '.';
        return new Server(opts, zkOptions);
    }
};
<|endoftext|>


<commit_before>var http = require('http');

module.exports = exports = new Server();

function Server() {

}

(function(){
  this.listen = function(port) {
    port = port|| process.env.PORT || 3000;

    http.createServer(function(req, res){

    }).listen(port, function() {
      console.log('The server is running on port: ' + port);
    });
  };

}).call(Server.prototype);

<commit_msg>finished<commit_after>var http = require('http');
var router = require(__dirname + '/router.js');

module.exports = exports = new Server();

function Server() {

}

(function(){
  this.listen = function(port) {
    port = port|| process.env.PORT || 3000;

    http.createServer(function(req, res){
      router.route(req, res);
    }).listen(port, function() {
      console.log('The server is running on port: ' + port);
    });
  };

}).call(Server.prototype);

<|endoftext|>


<commit_before>var
    _       = require('lodash'),
    assert  = require('assert'),
    bole    = require('bole'),
    Monitor = require('@npm/restify-monitor'),
    restify = require('restify'),
    Norch   = require('./norch')
    ;

var Server = module.exports = function Server(options)
{
    assert(options, 'you must pass an options object to the constructor');
    assert(options.name && _.isString(options.name), 'you must pass a name for this node');
    assert(options.metrics && _.isObject(options.metrics), 'you must pass a metrics emitter');

    this.options = options;
    this.logger  = bole(options.name);
    this.server  = restify.createServer(options);
    this.metrics = options.metrics;

    this.norch = new Norch(options);

    // middleware
    this.server.use(restify.bodyParser());
    Monitor(this.server, { logger: this.logger, metrics: this.metrics });

    this.server.post('/package', this.handlePackagePost.bind(this));
    this.server.del('/package/:id', this.handlePackageDelete.bind(this));
    this.server.get('/matches/:prefix', this.handleMatches.bind(this));
    this.server.get('/search/:terms', this.handleSearch.bind(this));

    this.server.on('after', this.emitMetrics.bind(this));
};

Server.prototype.server  = null;
Server.prototype.options = null;
Server.prototype.logger  = null;
Server.prototype.metrics = null;

Server.prototype.listen = function listen(port, host, callback)
{
    this.server.listen(port, host, callback);
};

Server.prototype.close = function close(callback)
{
    this.server.close();
    this.norch.close(callback);
};

Server.prototype.emitMetrics = function emitMetrics(request, response, route, error)
{
    if (response._time && this.metrics)
    {
        var latency = Date.now() - response._time;
        this.metrics.metric({ name: 'scorch.latency', value: latency });
    }
};

Server.prototype.handlePackagePost = function handlePackagePost(request, response, next)
{
    var self = this,
        package = request.body;

    if (!package || !package.id)
    {
        response.send(422);
        return next();
    }

    this.norch.add(package, function(err)
    {
        if (err)
        {
            response.json(500, err);
            return next();
        }
        response.json(201, { status: package.id + ' indexed' });
        next();
    });
};

Server.prototype.handlePackageDelete = function handlePackageDelete(request, response, next)
{
    var self = this,
        name = decodeURIComponent(request.params.id);

    this.norch.remove(name, function(err)
    {
        if (err)
        {
            response.json(500, err);
            return next();
        }
        response.json(204);
        next();
    });
};

Server.prototype.handleMatches = function handleMatches(request, response, next)
{
    var self = this,
        prefix = decodeURIComponent(request.params.prefix);

    this.norch.matches(prefix, function(err, matches)
    {
        if (err)
        {
            response.json(500, err);
            return next();
        }
        response.json(200, matches);
        next();
    });
};

Server.prototype.handleSearch = function handleSearch(request, response, next)
{
    var self = this,
        terms = decodeURIComponent(request.params.terms);

    var query =
    {
        // TODO a giant TODO
    };

    this.norch.search(query, function(err, results)
    {
        if (err)
        {
            response.json(500, err);
            return next();
        }
        response.json(200, results);
        next();
    });
};
<commit_msg>Aha, no wonder this was succeeding for me.<commit_after>var
    _       = require('lodash'),
    assert  = require('assert'),
    bole    = require('bole'),
    Monitor = require('@ceejbot/restify-monitor'),
    restify = require('restify'),
    Norch   = require('./norch')
    ;

var Server = module.exports = function Server(options)
{
    assert(options, 'you must pass an options object to the constructor');
    assert(options.name && _.isString(options.name), 'you must pass a name for this node');
    assert(options.metrics && _.isObject(options.metrics), 'you must pass a metrics emitter');

    this.options = options;
    this.logger  = bole(options.name);
    this.server  = restify.createServer(options);
    this.metrics = options.metrics;

    this.norch = new Norch(options);

    // middleware
    this.server.use(restify.bodyParser());
    Monitor(this.server, { logger: this.logger, metrics: this.metrics });

    this.server.post('/package', this.handlePackagePost.bind(this));
    this.server.del('/package/:id', this.handlePackageDelete.bind(this));
    this.server.get('/matches/:prefix', this.handleMatches.bind(this));
    this.server.get('/search/:terms', this.handleSearch.bind(this));

    this.server.on('after', this.emitMetrics.bind(this));
};

Server.prototype.server  = null;
Server.prototype.options = null;
Server.prototype.logger  = null;
Server.prototype.metrics = null;

Server.prototype.listen = function listen(port, host, callback)
{
    this.server.listen(port, host, callback);
};

Server.prototype.close = function close(callback)
{
    this.server.close();
    this.norch.close(callback);
};

Server.prototype.emitMetrics = function emitMetrics(request, response, route, error)
{
    if (response._time && this.metrics)
    {
        var latency = Date.now() - response._time;
        this.metrics.metric({ name: 'scorch.latency', value: latency });
    }
};

Server.prototype.handlePackagePost = function handlePackagePost(request, response, next)
{
    var self = this,
        package = request.body;

    if (!package || !package.id)
    {
        response.send(422);
        return next();
    }

    this.norch.add(package, function(err)
    {
        if (err)
        {
            response.json(500, err);
            return next();
        }
        response.json(201, { status: package.id + ' indexed' });
        next();
    });
};

Server.prototype.handlePackageDelete = function handlePackageDelete(request, response, next)
{
    var self = this,
        name = decodeURIComponent(request.params.id);

    this.norch.remove(name, function(err)
    {
        if (err)
        {
            response.json(500, err);
            return next();
        }
        response.json(204);
        next();
    });
};

Server.prototype.handleMatches = function handleMatches(request, response, next)
{
    var self = this,
        prefix = decodeURIComponent(request.params.prefix);

    this.norch.matches(prefix, function(err, matches)
    {
        if (err)
        {
            response.json(500, err);
            return next();
        }
        response.json(200, matches);
        next();
    });
};

Server.prototype.handleSearch = function handleSearch(request, response, next)
{
    var self = this,
        terms = decodeURIComponent(request.params.terms);

    var query =
    {
        // TODO a giant TODO
    };

    this.norch.search(query, function(err, results)
    {
        if (err)
        {
            response.json(500, err);
            return next();
        }
        response.json(200, results);
        next();
    });
};
<|endoftext|>


<commit_before>var util = require('util'),
    spawn = require('child_process').spawn,
    config = require('./config'),
    os = require('os'),
    escapeHtml = require('escape-html'),
    codeWriteSync = require('./util').codeWriteSync,
    temp = require('temp'),
    services = require('./services'),
    Promise = require('bluebird');

// used for indicating an error which is related to user code and not an application error.
// useful for when we run code compilation inside of this host process.
var CompileError = module.exports.CompileError = function(message) {
    this.name = "CompileError";
    this.message = message;
}
CompileError.prototype = Error.prototype;

//runs the strategy specified in opts and reports the results of the run, then runs the callback
module.exports.start = function start(opts, cb, strategies)
{
    opts.publish = function(){}

    if (opts.ably && opts.channel) {
        try {
            var ably = new require('ably').Rest(opts.ably);
            var channel = ably.channels.get(opts.channel);
            opts.publish = function(event, data) {
                if (event && data) {
                    channel.publish(event, data);
                }
            }
        } catch(e)
        {}
    }

    // in case anything needs to cleanup after itself
    opts.onCompleted = [];

    cb = cb || function (){};
    run(opts, strategies, function (buffer)
    {
        reportBuffer(opts, buffer, strategies);
        cb(buffer);

        opts.onCompleted.forEach(f => f(buffer));
    });
};

// given the options provided and a list of strategies on how to handle them, run will pick the
// appropriate strategy and execute it.
function run(opts, strategies, cb)
{
    function executer(params)
    {
        // if is error
        if (typeof params === 'string')
        {
            cb({stdout: [''], stderr: [params], wallTime: 0});
        }
        else if (params.stdout || params.stderr) {
            cb(params);
        }
        else
        {
            exec(opts, params.name, params.args, params.options, params.stdin, cb);
        }
    }

    // write the solution to a text file so that it can be inspected if need be
    codeWriteSync(null, opts.code || opts.solution, '/home/codewarrior/', 'solution.txt', true);

    var hasServices = opts.services && !!opts.services.length
    if (hasServices) opts.publish('status', 'Starting services...');

    // allow for language level modification of opts, such as services and shell
    if (strategies.modifyOpts) {
        strategies.modifyOpts(opts);
    }

    services.start(opts, function() {
        runShell(opts, function() {
            try {
                var strategy = opts.fixture ? strategies.testIntegration : strategies.solutionOnly;
                strategy(executer);
            }
            catch(ex) {
                executer(ex.stack || ex.toString());
            }
        });
    });
}

// handles running an optional shell value, which allows users to configure a shell script to be ran
// before the code executes
function runShell(opts, resolve) {

    // if a shell script is provided then run it now
    if (opts.shell) {
        opts.publish('status', 'Running setup scripts...');
        temp.track();
        var file = codeWriteSync('bash', `#!/bin/bash\n${opts.shell}`, temp.mkdirSync('bash'), 'shell.sh'),
            shellOpts = {
                timeout: 5000, // allow the shell script its own 5 sec timeout
                compiling: true // set to true so that the script doesn't exit
            };

        exec(shellOpts, 'bash', [file], {}, null, function(result) {
            opts.shellResult = result;
            resolve();
        });
    }
    else {
        resolve();
    }
}

function exec(opts, name, args, processOptions, processStdin, cb)
{
    opts.publish = opts.publish || function(){};

    opts.publish("status", "Running...");

    var args = args || [],
	      child = spawn(name, args, processOptions),
        buffer = {stdout: [], stderr: []},
        start = new Date(),
        finished = false,
        stdoutLength = 0,
        maxTime = opts.timeout || config.timeouts[opts.language] || config.timeouts.default

    function exit(reason)
    {
        if (!finished)
        {
            child.kill('SIGKILL');
            if (reason) buffer.stderr.push(reason + '\n');
            finished = true;
        }
        cb(buffer);
    }

    function cleanupFiles()
    {
        // cleanup temp files
        if (opts.tempFiles)
        {
            for (var i = 0, l = opts.tempFiles.length; i < l; i++)
            {
                fs.unlink(opts.tempFiles[i]);
            }
            delete opts.tempFiles;
        }
    }

    if (processStdin) child.stdin.write(processStdin);

    // Listen
    child.stdout.on('data', function (data)
    {
        if (!!data)
        {
            var text = data.toString();
            buffer.stdout.push(text);
            stdoutLength += text.length;
            opts.publish('stdout', text);
        }

        if (stdoutLength > 1200000)
        {
            buffer.status = 'max_buffer_reached';
            exit('Max Buffer reached: Too much information has been written to stdout.');
        }
    });

    child.stderr.on('data', function (data)
    {
        if (!!data) buffer.stderr.push(data.toString());
    });

    child.on('error', exit);

    // prevent the process from running for too long
    var timeout = setTimeout(function ()
    {
        if (!finished)
        {
            buffer.status = 'max_time_reached';
            exit('Process was terminated. It took longer than ' + maxTime + 'ms to complete');
        }
        process.exit(1);
    }, maxTime);

    var complete = function(code, signal) {
        if(finished) return;

        finished = true;
        buffer.exitCode = code;
        buffer.signalCode = signal;
        buffer.wallTime = new Date() - start;
        cleanupFiles();
        cb(buffer);

        // if we are within the run script
        if (!opts.compiling && (process.argv[1] || '').indexOf('/run') >= 0)
        {
            //ensure that node exits now, even if processes have forked off
            process.exit(0);
        }
    }

    child.on('exit', function (code, signal) {
        // wait the remaining time left to see if all stdio processes close
        // preferably we cleanup after 'exit' is called
        setTimeout(function() {
            complete(code, signal);
        }, getTimeLeft(timeout))
    });

    child.on('close', function (code, signal) {
        clearTimeout(timeout);
        complete(code, signal);
    });

    child.stdin.end();
}

// Get the time left in a set timeout
// http://stackoverflow.com/questions/3144711/find-the-time-left-in-a-settimeout
function getTimeLeft(timeout) {
    return Math.ceil((timeout._idleStart + timeout._idleTimeout - Date.now()) / 1000);
}

function reportBuffer(opts, buffer, strategies)
{
    if (buffer.stdout.join) buffer.stdout = buffer.stdout.join('');
    if (buffer.stderr.join) buffer.stderr = buffer.stderr.join('');

    if (strategies)
    {
        // added as a way to transform the buffer before any additional stdout/err specific processing is made.
        // useful for things like when you may want to split stdout into both stdout and stderr
        if (strategies.transformBuffer) {
            strategies.transformBuffer(buffer);
        }

        if (strategies.transformOutput)
        {
           buffer.stdout = strategies.transformOutput(opts);
        }

        // if there is an error, provide the ability to sanitize it. This is useful for when
        // output can be noisy.
        if (buffer.stderr && strategies.sanitizeStdErr)
        {
            buffer.stderr = strategies.sanitizeStdErr(buffer.stderr);
        }

        if (buffer.stdout && strategies.sanitizeStdOut)
        {
            buffer.stdout = strategies.sanitizeStdOut(buffer.stdout);
        }
    }

    // return the output of the shell call
    if (opts.shellResult) {
        buffer.shellResult = opts.shellResult;
    }

    // escape the stderr output after strategies have been run
    // and before the it is written to the process stream

    buffer.outputType = strategies.outputType || 'pre';

    if (opts.format == 'json')
    {
        var json = JSON.stringify(buffer);
        writeToStream(process.stdout, json, "\\n");
    }
    else
    {
        if (buffer.stdout) writeToStream(process.stdout, buffer.stdout, "\n")
        if (buffer.stderr) writeToStream(process.stderr, buffer.stderr, "\n")
        if (buffer.wallTime && opts.debug) {
            console.info(buffer.wallTime + 'ms');
        }
    }
}

// we need to chunk the data back out to handle strange inconsistency issues with large data.
// Ideally we could chunk based off of character count but for some reason chunking by line breaks
// is the only thing that is consistent.
function writeToStream(stream, data, linebreak) {
    data.split(linebreak).forEach((line, i, arr) => {
        // don't write a line break on the last line
        return stream.write(line.normalize() + (i != arr.length - 1 ? linebreak : ''))
    });
}
<commit_msg>updated exitSignal name<commit_after>var util = require('util'),
    spawn = require('child_process').spawn,
    config = require('./config'),
    os = require('os'),
    escapeHtml = require('escape-html'),
    codeWriteSync = require('./util').codeWriteSync,
    temp = require('temp'),
    services = require('./services'),
    Promise = require('bluebird');

// used for indicating an error which is related to user code and not an application error.
// useful for when we run code compilation inside of this host process.
var CompileError = module.exports.CompileError = function(message) {
    this.name = "CompileError";
    this.message = message;
}
CompileError.prototype = Error.prototype;

//runs the strategy specified in opts and reports the results of the run, then runs the callback
module.exports.start = function start(opts, cb, strategies)
{
    opts.publish = function(){}

    if (opts.ably && opts.channel) {
        try {
            var ably = new require('ably').Rest(opts.ably);
            var channel = ably.channels.get(opts.channel);
            opts.publish = function(event, data) {
                if (event && data) {
                    channel.publish(event, data);
                }
            }
        } catch(e)
        {}
    }

    // in case anything needs to cleanup after itself
    opts.onCompleted = [];

    cb = cb || function (){};
    run(opts, strategies, function (buffer)
    {
        reportBuffer(opts, buffer, strategies);
        cb(buffer);

        opts.onCompleted.forEach(f => f(buffer));
    });
};

// given the options provided and a list of strategies on how to handle them, run will pick the
// appropriate strategy and execute it.
function run(opts, strategies, cb)
{
    function executer(params)
    {
        // if is error
        if (typeof params === 'string')
        {
            cb({stdout: [''], stderr: [params], wallTime: 0});
        }
        else if (params.stdout || params.stderr) {
            cb(params);
        }
        else
        {
            exec(opts, params.name, params.args, params.options, params.stdin, cb);
        }
    }

    // write the solution to a text file so that it can be inspected if need be
    codeWriteSync(null, opts.code || opts.solution, '/home/codewarrior/', 'solution.txt', true);

    var hasServices = opts.services && !!opts.services.length
    if (hasServices) opts.publish('status', 'Starting services...');

    // allow for language level modification of opts, such as services and shell
    if (strategies.modifyOpts) {
        strategies.modifyOpts(opts);
    }

    services.start(opts, function() {
        runShell(opts, function() {
            try {
                var strategy = opts.fixture ? strategies.testIntegration : strategies.solutionOnly;
                strategy(executer);
            }
            catch(ex) {
                executer(ex.stack || ex.toString());
            }
        });
    });
}

// handles running an optional shell value, which allows users to configure a shell script to be ran
// before the code executes
function runShell(opts, resolve) {

    // if a shell script is provided then run it now
    if (opts.shell) {
        opts.publish('status', 'Running setup scripts...');
        temp.track();
        var file = codeWriteSync('bash', `#!/bin/bash\n${opts.shell}`, temp.mkdirSync('bash'), 'shell.sh'),
            shellOpts = {
                timeout: 5000, // allow the shell script its own 5 sec timeout
                compiling: true // set to true so that the script doesn't exit
            };

        exec(shellOpts, 'bash', [file], {}, null, function(result) {
            opts.shellResult = result;
            resolve();
        });
    }
    else {
        resolve();
    }
}

function exec(opts, name, args, processOptions, processStdin, cb)
{
    opts.publish = opts.publish || function(){};

    opts.publish("status", "Running...");

    var args = args || [],
	      child = spawn(name, args, processOptions),
        buffer = {stdout: [], stderr: []},
        start = new Date(),
        finished = false,
        stdoutLength = 0,
        maxTime = opts.timeout || config.timeouts[opts.language] || config.timeouts.default

    function exit(reason)
    {
        if (!finished)
        {
            child.kill('SIGKILL');
            if (reason) buffer.stderr.push(reason + '\n');
            finished = true;
        }
        cb(buffer);
    }

    function cleanupFiles()
    {
        // cleanup temp files
        if (opts.tempFiles)
        {
            for (var i = 0, l = opts.tempFiles.length; i < l; i++)
            {
                fs.unlink(opts.tempFiles[i]);
            }
            delete opts.tempFiles;
        }
    }

    if (processStdin) child.stdin.write(processStdin);

    // Listen
    child.stdout.on('data', function (data)
    {
        if (!!data)
        {
            var text = data.toString();
            buffer.stdout.push(text);
            stdoutLength += text.length;
            opts.publish('stdout', text);
        }

        if (stdoutLength > 1200000)
        {
            buffer.status = 'max_buffer_reached';
            exit('Max Buffer reached: Too much information has been written to stdout.');
        }
    });

    child.stderr.on('data', function (data)
    {
        if (!!data) buffer.stderr.push(data.toString());
    });

    child.on('error', exit);

    // prevent the process from running for too long
    var timeout = setTimeout(function ()
    {
        if (!finished)
        {
            buffer.status = 'max_time_reached';
            exit('Process was terminated. It took longer than ' + maxTime + 'ms to complete');
        }
        process.exit(1);
    }, maxTime);

    var complete = function(code, signal) {
        if(finished) return;

        finished = true;
        buffer.exitCode = code;
        buffer.exitSignal = signal;
        buffer.wallTime = new Date() - start;
        cleanupFiles();
        cb(buffer);

        // if we are within the run script
        if (!opts.compiling && (process.argv[1] || '').indexOf('/run') >= 0)
        {
            //ensure that node exits now, even if processes have forked off
            process.exit(0);
        }
    }

    child.on('exit', function (code, signal) {
        // wait the remaining time left to see if all stdio processes close
        // preferably we cleanup after 'exit' is called
        setTimeout(function() {
            complete(code, signal);
        }, getTimeLeft(timeout))
    });

    child.on('close', function (code, signal) {
        clearTimeout(timeout);
        complete(code, signal);
    });

    child.stdin.end();
}

// Get the time left in a set timeout
// http://stackoverflow.com/questions/3144711/find-the-time-left-in-a-settimeout
function getTimeLeft(timeout) {
    return Math.ceil((timeout._idleStart + timeout._idleTimeout - Date.now()) / 1000);
}

function reportBuffer(opts, buffer, strategies)
{
    if (buffer.stdout.join) buffer.stdout = buffer.stdout.join('');
    if (buffer.stderr.join) buffer.stderr = buffer.stderr.join('');

    if (strategies)
    {
        // added as a way to transform the buffer before any additional stdout/err specific processing is made.
        // useful for things like when you may want to split stdout into both stdout and stderr
        if (strategies.transformBuffer) {
            strategies.transformBuffer(buffer);
        }

        if (strategies.transformOutput)
        {
           buffer.stdout = strategies.transformOutput(opts);
        }

        // if there is an error, provide the ability to sanitize it. This is useful for when
        // output can be noisy.
        if (buffer.stderr && strategies.sanitizeStdErr)
        {
            buffer.stderr = strategies.sanitizeStdErr(buffer.stderr);
        }

        if (buffer.stdout && strategies.sanitizeStdOut)
        {
            buffer.stdout = strategies.sanitizeStdOut(buffer.stdout);
        }
    }

    // return the output of the shell call
    if (opts.shellResult) {
        buffer.shellResult = opts.shellResult;
    }

    // escape the stderr output after strategies have been run
    // and before the it is written to the process stream

    buffer.outputType = strategies.outputType || 'pre';

    if (opts.format == 'json')
    {
        var json = JSON.stringify(buffer);
        writeToStream(process.stdout, json, "\\n");
    }
    else
    {
        if (buffer.stdout) writeToStream(process.stdout, buffer.stdout, "\n")
        if (buffer.stderr) writeToStream(process.stderr, buffer.stderr, "\n")
        if (buffer.wallTime && opts.debug) {
            console.info(buffer.wallTime + 'ms');
        }
    }
}

// we need to chunk the data back out to handle strange inconsistency issues with large data.
// Ideally we could chunk based off of character count but for some reason chunking by line breaks
// is the only thing that is consistent.
function writeToStream(stream, data, linebreak) {
    data.split(linebreak).forEach((line, i, arr) => {
        // don't write a line break on the last line
        return stream.write(line.normalize() + (i != arr.length - 1 ? linebreak : ''))
    });
}
<|endoftext|>


<commit_before>"use strict";

//var http = require('http');
var websocket = require('websocket-stream');
var http = require('flowhttp');
var Stream = require('stream');
var sockets = {};
var events = {};

exports.demux = Demux;
function Demux (CoreInst, socket) {

    /*
      header:
      - instance name
      - event name
      - request id
      - code 0 (data), 1 (error), // 2 (end) ??, read
      body:
      - data
    */

   return Stream.Writable({
        write: function (chunk, enc, next) {

            // parse chunk
            try {
                chunk = JSON.parse(chunk.toString());
            } catch (err) {
                return next(err);
            }

            let instance = chunk[0];
            let eventNme = chunk[1];
            let streamId = instance + eventNme + chunk[2];
            let type = chunk[3];
            let data = chunk[4];

            if (
                chunk.length < 4 ||
                typeof instance !== 'string' || !instance ||
                typeof eventNme !== 'string' || !eventNme ||
                typeof streamId !== 'string' || !streamId ||
                typeof type !== 'number' || type < 0 || type > 2
            ) {
                let err = new Error('Socket.mux: Invalid frame.');
                return next(err);
            }

            // emit or get stream
            socket._streams = socket._streams || {};
            let event = socket._streams[streamId];
            if (!event) {
                let options = {
                    to: instance,
                    emit: eventNme,
                    id: chunk[2]
                };
                event = socket._streams[streamId] = CoreInst.flow(eventNme, options);

                event.on('error', function (err) {
                    // TODO send error frame
                    console.log('DEMUX EVENT ERROR:', err);
                });

                // TODO pipe to mux
                event.pipe(Mux(socket, options));
            }

            // write chunk to stream
            // TODO event needs to write to the next correct section!
            event.write(data);

            // message type end
            // message type error
            // message type data

            next();
        }
    });
}

function Mux (socket, options) {

    return Stream.Writable({
        objectMode: true,
        write: function (chunk, enc, next) {
            // create message
            // send message to socket
            if (socket._writableState.ended || !socket.writable) {
                return next();
            }

            let msg = [options.to, options.emit, options.id, 0, chunk];

            // TODO buffer writes until stream is open
            socket.write(JSON.stringify(msg));
            next();
        }
    });
}

function socketEnd () {
    var socket = this;
    for (var stream in socket._streams) {
        socket._streams[stream].end();
    }
    socket._streams = {};
}

exports.request = function (CoreInst, options) {

    var request;
    switch (options.net) {

        case '>':
        case 'ws':

            let host = options.host || window.location.host;
            let socket = sockets[host];
            if (!socket) {

                socket = websocket('wss://' + host);
                socket.pipe(Demux(CoreInst, socket));
                sockets[host] = socket;

                socket.on('close', socketEnd);
                socket.on('end', socketEnd);
                socket.on('error', function (err) {
                    for (var stream in socket._streams) {
                        socket._streams[stream].emit('error', err);
                    }
                });

                socket._streams = {};
            }

            options.id = 'truckenId';
            let streamId = options.to + options.emit + options.id;
            socket._streams[streamId] = options.subStream;
            request = Mux(socket, options);
            break;

        case '/':
        case 'http':

            /*
            var opts = {
                method: 'GET',
                path: '/' + instance._name + ':' + eventName.substr(1)
                headers: {},
                host: window.location.host,
                port: window.location.port
                responseType: 'response type to set on the underlying xhr object'
            };
            */

            let url = options.emit[0] === '/' || options.emit.indeOf('http') === 0 ? options.emit : '/' + options.to + ':' + options.emit

            // pipe http to flow event stream
            let method = options.method || (options.end ? 'get' : 'post');
            request = http[method](url);

            // collect all data for a classic request callback
            if (options.end) {
                var body = '';
                var error;
                request.on('response', function (res) {
                    if (res.statusCode !== 200) {
                        error = true;
                    }
                })
                request.on('data', function (chunk) {
                    body += chunk;
                })
                .on('error', function (err) {
                    body = err;
                })
                .on('end', function () {
                    if (error) {
                        error = body;
                        body = undefined;
                    }
                    options.end(error, body);
                });
                request.end();
            }
            break;

        default:
            // TODO error
            return;
    }

    return request;
}

exports.reset = function () {

    // close all websockets
    for (var socket in sockets) {
        sockets[socket].close();
    }

    // remove socket refs
    sockets = {};
};

<commit_msg>use native WebSocket for browser; buffer msg until socket is ready<commit_after>"use strict";

//var http = require('http');
var http = require('flowhttp');
var Stream = require('stream');
var sockets = {};
var events = {};

exports.demux = Demux;
function Demux (CoreInst) {

    /*
      header:
      - instance name
      - event name
      - request id
      - code 0 (data), 1 (error), // 2 (end) ??, read
      body:
      - data
    */
    return function (chunk) {

        var socket = chunk.target;
        chunk = chunk.data;

        // parse chunk
        try {
            chunk = JSON.parse(chunk.toString());
        } catch (err) {
            return next(err);
        }

        let instance = chunk[0];
        let eventNme = chunk[1];
        let streamId = instance + eventNme + chunk[2];
        let type = chunk[3];
        let data = chunk[4];

        if (
            chunk.length < 4 ||
            typeof instance !== 'string' || !instance ||
            typeof eventNme !== 'string' || !eventNme ||
            typeof streamId !== 'string' || !streamId ||
            typeof type !== 'number' || type < 0 || type > 2
        ) {
            let err = new Error('Socket.mux: Invalid frame.');
            // TODO create an error frame
            return;
        }

        // emit or get stream
        socket._streams = socket._streams || {};
        let event = socket._streams[streamId];
        if (!event) {
            let options = {
                to: instance,
                emit: eventNme,
                id: chunk[2]
            };
            event = socket._streams[streamId] = CoreInst.flow(eventNme, options);

            event.on('error', function (err) {
                // TODO send error frame
                console.log('DEMUX EVENT ERROR:', err);
            });

            // TODO pipe to mux
            event.pipe(Mux(socket, options));
        }

        // write chunk to stream
        // TODO event needs to write to the next correct section!
        event.write(data);

        // message type end
        // message type error
        // message type data
    };
}

function Mux (socket, options) {

    socket._msgBuf = socket._msgBuf || [];

    let streamId = options.to + options.emit + options.id;
    var mux = Stream.Writable({
        objectMode: true,
        write: function (chunk, enc, next) {
            // create message
            // send message to socket

            var msg;
            try {
                msg = JSON.stringify([options.to, options.emit, options.id, 0, chunk]);
            } catch (err) {
                msg = JSON.stringify([options.to, options.emit, options.id, 2, err.stack]);
            }

            if (socket.readyState !== 1) {
                socket._msgBuf.push(msg);
            } else {
                socket.send(msg);
            }

            next();
        }
    });

    socket._streams[streamId] = options.subStream;

    return mux;
}

function socketEnd () {
    var socket = this;
    for (var stream in socket._streams) {
        socket._streams[stream].end();
    }
    socket._streams = {};
}

exports.request = function (CoreInst, options) {

    var request;
    switch (options.net) {

        case '>':
        case 'ws':

            let host = options.host || window.location.host;
            let socket = sockets[host];
            if (!socket) {

                socket = new WebSocket('wss://' + host);
                sockets[host] = socket;

                socket.onopen = function () {
                    // write buffered messages
                    if (socket._msgBuf.length) {
                        socket._msgBuf.forEach(socket.send.bind(socket));
                        socket._msgBuf = [];
                    }
                };
                socket.onclose = socketEnd;
                socket.onend = socketEnd;
                socket.onerror = function (err) {
                    for (var stream in socket._streams) {
                        socket._streams[stream].emit('error', err);
                    }
                };
                socket.onmessage = Demux(CoreInst);

                socket._streams = {};
            }

            options.id = 'truckenId';

            // TODO save readable
            request = Mux(socket, options);
            break;

        case '/':
        case 'http':

            /*
            var opts = {
                method: 'GET',
                path: '/' + instance._name + ':' + eventName.substr(1)
                headers: {},
                host: window.location.host,
                port: window.location.port
                responseType: 'response type to set on the underlying xhr object'
            };
            */

            let url = options.emit[0] === '/' || options.emit.indeOf('http') === 0 ? options.emit : '/' + options.to + ':' + options.emit

            // pipe http to flow event stream
            let method = options.method || (options.end ? 'get' : 'post');
            request = http[method](url);

            // collect all data for a classic request callback
            if (options.end) {
                var body = '';
                var error;
                request.on('response', function (res) {
                    if (res.statusCode !== 200) {
                        error = true;
                    }
                })
                request.on('data', function (chunk) {
                    body += chunk;
                })
                .on('error', function (err) {
                    body = err;
                })
                .on('end', function () {
                    if (error) {
                        error = body;
                        body = undefined;
                    }
                    options.end(error, body);
                });
                request.end();
            }
            break;

        default:
            // TODO error
            return;
    }

    return request;
}

exports.reset = function () {

    // close all websockets
    for (var socket in sockets) {
        sockets[socket].close();
    }

    // remove socket refs
    sockets = {};
};

<|endoftext|>


<commit_before>"use strict";

/**
 * Text element without background.
 *
 * @class Text
 * @extends {Element}
 * @param {DOM} parent Parent element.
 */
function Text(parent)
{
	Element.call(this, parent);

	this.element.style.pointerEvents = "none";
	this.element.style.color = Editor.theme.textColor;
	this.element.style.display = "flex";

	//Span
	this.span = document.createElement("span");
	this.element.appendChild(this.span);

	//Text
	this.text = document.createTextNode("");
	this.span.appendChild(this.text);

	/**
	 * If set to true the text container will automatically fit the text size.
	 *
	 * @attribute fitContent
	 * @type {Boolean}
	 */
	this.fitContent = false;

	this.allowWordBreak(true);
	this.setVerticalAlignment(Text.CENTER);
	this.setAlignment(Text.CENTER);
}

Text.CENTER = 0;
Text.LEFT = 1;
Text.RIGHT = 2;
Text.TOP = 3;
Text.BOTTOM = 4;

Text.CLIP = 10;
Text.ELLIPSIS = 11;

Text.prototype = Object.create(Element.prototype);

/**
 * Enable of disable word breaking.
 *
 * @method allowWordBreak
 * @param {Boolean} line If true words can be breaked.
 */
Text.prototype.allowWordBreak = function(value)
{
	if(value === true)
	{
		this.span.style.whiteSpace = "pre";
		this.span.style.wordBreak = "break-word";
	}
	else
	{
		this.span.style.whiteSpace = "normal";
		this.span.style.wordBreak = "normal";
	}
};

/**
 * Set text.
 *
 * @method setText
 * @param {String} text Text.
 */
Text.prototype.setText = function(text)
{
	this.text.data = text;
};

/**
 * Set text border.
 *
 * @method setTextBorder
 * @param {Number} size Border size in pixels.
 * @param {String} color CSS Color.
 */
Text.prototype.setTextBorder = function(size, color)
{
	this.span.style.textShadow = "-" + size + "px 0 " + color + ", 0 " + size + "px " + color + ", " + size + "px 0 " + color + ", 0 -" + size + "px " + color;
};

/**
 * Set Text size, in pixels.
 *
 * @method setTextSize
 * @param {Number} size Size in pixel for this text element.
 */
Text.prototype.setTextSize = function(size)
{
	this.element.style.fontSize = size + "px";
};

/**
 * Set text color.
 *
 * @method setTextColor
 * @param {String} color Color code.
 */
Text.prototype.setTextColor = function(color)
{
	this.span.style.color = color;
};

/**
 * Set text overflow handling
 *
 * @method setOverflow
 * @param {Number} overflow
 */
Text.prototype.setOverflow = function(overflow)
{
	if(overflow === Text.ELLIPSIS)
	{
		this.span.style.whiteSpace = "nowrap";
		this.span.style.textOverflow = "ellipsis";
	}
	else
	{
		this.span.style.whiteSpace = "pre";
		this.span.style.textOverflow = "clip";
	}
};

/**
 * Set text horizontal alignment.
 *  - Text.CENTER
 *  - Text.LEFT
 *  - Text.RIGHT
 *
 * @method setAlignment
 * @param {Number} align Alingment mode.
 */
Text.prototype.setAlignment = function(align)
{
	if(align === Text.CENTER)
	{
		this.element.style.justifyContent = "center";
		this.element.style.textAlign = "center";
	}
	else if(align === Text.LEFT)
	{
		this.element.style.justifyContent = "flex-start";
		this.element.style.textAlign = "left";
	}
	else if(align === Text.RIGHT)
	{
		this.element.style.justifyContent = "flex-end";
		this.element.style.textAlign = "right";
	}
};

/**
 * Set text vertical alignment.
 *  - Text.CENTER
 *  - Text.TOP
 *  - Text.BOTTOM
 *
 * @method setVerticalAlignment
 * @param {Number} align Alingment mode.
 */
Text.prototype.setVerticalAlignment = function(align)
{
	if(align === Text.CENTER)
	{
		this.element.style.alignItems = "center";
	}
	else if(align === Text.TOP)
	{
 		this.element.style.alignItems = "flex-start";
	}
	else if(align === Text.BOTTOM)
	{
		this.element.style.alignItems = "flex-end";
	}
};

/**
 * Get size of the text inside of this component in px.
 *
 * @method measure
 * @return {THREE.Vector2} A vector with the size of the text.
 */
Text.prototype.measure = function()
{
 	return new THREE.Vector2(this.span.offsetWidth, this.span.offsetHeight);
};

/**
 * Set text internal margin in pixels.
 *
 * @method setMargin
 * @param {Number} margin Margin size in pixels.
 */
Text.prototype.setMargin = function(margin)
{
	this.span.style.margin = margin + "px";
};

Text.prototype.updateVisibility = function()
{
	this.element.style.visibility = this.visible ? "visible" : "hidden";
}

Text.prototype.updateSize = function()
{
	if(this.fitContent)
	{
		this.size.x = this.span.clientWidth;
		this.size.y = this.span.clientHeight;
	}

	Element.prototype.updateSize.call(this);
};<commit_msg>Derp<commit_after>"use strict";

/**
 * Text element without background.
 *
 * @class Text
 * @extends {Element}
 * @param {DOM} parent Parent element.
 */
function Text(parent)
{
	Element.call(this, parent);

	this.element.style.pointerEvents = "none";
	this.element.style.color = Editor.theme.textColor;
	this.element.style.display = "flex";

	//Span
	this.span = document.createElement("span");
	this.element.appendChild(this.span);

	//Text
	this.text = document.createTextNode("");
	this.span.appendChild(this.text);

	/**
	 * If set to true the text container will automatically fit the text size.
	 *
	 * @attribute fitContent
	 * @type {Boolean}
	 */
	this.fitContent = false;

	this.allowWordBreak(false);
	this.setVerticalAlignment(Text.CENTER);
	this.setAlignment(Text.CENTER);
}

Text.CENTER = 0;
Text.LEFT = 1;
Text.RIGHT = 2;
Text.TOP = 3;
Text.BOTTOM = 4;

Text.CLIP = 10;
Text.ELLIPSIS = 11;

Text.prototype = Object.create(Element.prototype);

/**
 * Enable of disable word breaking.
 *
 * @method allowWordBreak
 * @param {Boolean} line If true words can be breaked.
 */
Text.prototype.allowWordBreak = function(value)
{
	if(value === true)
	{
		this.span.style.whiteSpace = "normal";
		this.span.style.wordBreak = "break-word";
	}
	else
	{
		this.span.style.whiteSpace = "pre";
		this.span.style.wordBreak = "normal";
	}
};

/**
 * Set text.
 *
 * @method setText
 * @param {String} text Text.
 */
Text.prototype.setText = function(text)
{
	this.text.data = text;
};

/**
 * Set text border.
 *
 * @method setTextBorder
 * @param {Number} size Border size in pixels.
 * @param {String} color CSS Color.
 */
Text.prototype.setTextBorder = function(size, color)
{
	this.span.style.textShadow = "-" + size + "px 0 " + color + ", 0 " + size + "px " + color + ", " + size + "px 0 " + color + ", 0 -" + size + "px " + color;
};

/**
 * Set Text size, in pixels.
 *
 * @method setTextSize
 * @param {Number} size Size in pixel for this text element.
 */
Text.prototype.setTextSize = function(size)
{
	this.element.style.fontSize = size + "px";
};

/**
 * Set text color.
 *
 * @method setTextColor
 * @param {String} color Color code.
 */
Text.prototype.setTextColor = function(color)
{
	this.span.style.color = color;
};

/**
 * Set text overflow handling
 *
 * @method setOverflow
 * @param {Number} overflow
 */
Text.prototype.setOverflow = function(overflow)
{
	if(overflow === Text.ELLIPSIS)
	{
		this.span.style.whiteSpace = "nowrap";
		this.span.style.textOverflow = "ellipsis";
	}
	else
	{
		this.span.style.whiteSpace = "pre";
		this.span.style.textOverflow = "clip";
	}
};

/**
 * Set text horizontal alignment.
 *  - Text.CENTER
 *  - Text.LEFT
 *  - Text.RIGHT
 *
 * @method setAlignment
 * @param {Number} align Alingment mode.
 */
Text.prototype.setAlignment = function(align)
{
	if(align === Text.CENTER)
	{
		this.element.style.justifyContent = "center";
		this.element.style.textAlign = "center";
	}
	else if(align === Text.LEFT)
	{
		this.element.style.justifyContent = "flex-start";
		this.element.style.textAlign = "left";
	}
	else if(align === Text.RIGHT)
	{
		this.element.style.justifyContent = "flex-end";
		this.element.style.textAlign = "right";
	}
};

/**
 * Set text vertical alignment.
 *  - Text.CENTER
 *  - Text.TOP
 *  - Text.BOTTOM
 *
 * @method setVerticalAlignment
 * @param {Number} align Alingment mode.
 */
Text.prototype.setVerticalAlignment = function(align)
{
	if(align === Text.CENTER)
	{
		this.element.style.alignItems = "center";
	}
	else if(align === Text.TOP)
	{
 		this.element.style.alignItems = "flex-start";
	}
	else if(align === Text.BOTTOM)
	{
		this.element.style.alignItems = "flex-end";
	}
};

/**
 * Get size of the text inside of this component in px.
 *
 * @method measure
 * @return {THREE.Vector2} A vector with the size of the text.
 */
Text.prototype.measure = function()
{
 	return new THREE.Vector2(this.span.offsetWidth, this.span.offsetHeight);
};

/**
 * Set text internal margin in pixels.
 *
 * @method setMargin
 * @param {Number} margin Margin size in pixels.
 */
Text.prototype.setMargin = function(margin)
{
	this.span.style.margin = margin + "px";
};

Text.prototype.updateVisibility = function()
{
	this.element.style.visibility = this.visible ? "visible" : "hidden";
}

Text.prototype.updateSize = function()
{
	if(this.fitContent)
	{
		this.size.x = this.span.clientWidth;
		this.size.y = this.span.clientHeight;
	}

	Element.prototype.updateSize.call(this);
};<|endoftext|>


<commit_before>export default function isValid(error) {

}
<commit_msg>Add functionality to allow for arrays of error values<commit_after>export default function isValid(error) {
  if ( !Array.isArray(error) ) {
    return !error;
  }
  return error.reduce(( valid, errorValue ) => valid && !errorValue, true);
}
<|endoftext|>


<commit_before>var path      = require('path')
var assert    = require('assert')
var stat      = require('./stat')
var exists    = require('./exists')
// var revFile   = require('./revFile')
var readFile  = require('./readFile')
var writeFile = require('./writeFile')
var name      = require('../path/name')
var segments  = require('../path/segments')
var eachAsync = require('../async/iterate').map
var rename    = require('../string/placeholders')

// THIS NEEDS WORK!!
// options: flatten, preserve [dir structure], noclobber.
function copy (files, dest, opts, cb) {
  var defaults = {flatten: false, noclobber: false}

  if (typeof opts === 'function') {
    cb = opts
    opts = defaults
  } else {
    opts = opts || defaults
  }

  files = Array.isArray(files) ? files : [files]

  eachAsync(files, function (file, key, done) {
    readFile(file, function (err, data) {
      assert.ifError(err)

      var method
      var filepath
      var destination

      if (opts.flatten) method = 'last'
      else method = 'fromFirst'

      filepath = segments[method](file)
      destination = path.resolve(dest, filepath)

      if (opts.noclobber && exists(destination)) {
        if (stat(destination).content === stat(file).content) {
          return done(null, file)
        } else {
          // destination = revFile(destination)
          var fn = rename()(':dest/:file:bkup:ext')
          destination = fn({
            dest: name.dir(destination),
            file: name.file(destination),
            ext: name.ext(destination),
            bkup: 'hello'//something goes here
          })
        }
      }

      writeFile(destination, data, function (err) {
        assert.ifError(err)
        done(null, file)
      })
    })
  }, function (err, res) {
    assert.ifError(err)
    cb(res)
  })
}

function copySync (files, dest) {
  files = Array.isArray(files) ? files : [files]

  files.forEach(function (file) {
    var destFile = path.resolve(dest, segments.fromFirst(file))
    var content = readFile(file)
    writeFile(destFile, content)
  })
}

module.exports = copy
module.exports.sync = copySync
<commit_msg>update file/copy.js<commit_after>var path      = require('path')
var assert    = require('assert')
var stat      = require('./stat')
var exists    = require('./exists')
var readFile  = require('./readFile')
var writeFile = require('./writeFile')
var name      = require('../path/name')
var segments  = require('../path/segments')
var eachAsync = require('../async/iterate').map
var randHex   = require('../random/randHex')
var rename    = require('../string/placeholders')

// THIS STILL NEEDS WORK!!
// optional rename function or parent path segment instead of randHex.
// better error handling.
function copy (files, dest, opts, cb) {
  var defaults = {flatten: false, noclobber: false}

  if (typeof opts === 'function') {
    cb = opts
    opts = defaults
  } else {
    opts = opts || defaults
  }

  files = Array.isArray(files) ? files : [files]

  eachAsync(files, function (file, key, done) {
    readFile(file, function (err, data) {
      assert.ifError(err)

      var method
      var filepath
      var destination

      if (opts.flatten) method = 'last'
      else method = 'fromFirst'

      filepath = segments[method](file)
      destination = path.resolve(dest, filepath)

      if (opts.noclobber && exists(destination)) {
        if (stat(destination).content === stat(file).content) {
          return done(null, file)
        } else {
          var fn = rename()(':dest/:file:vers:ext')
          destination = fn({
            dest : name.dir(destination),
            file : name.file(destination),
            ext  : name.ext(destination),
            vers : '-' + randHex(4)
          })
        }
      }

      writeFile(destination, data, function (err) {
        assert.ifError(err)
        done(null, file)
      })
    })
  }, function (err, res) {
    assert.ifError(err)
    cb(res)
  })
}

function copySync (files, dest) {
  files = Array.isArray(files) ? files : [files]

  files.forEach(function (file) {
    var destFile = path.resolve(dest, segments.fromFirst(file))
    var content = readFile(file)
    writeFile(destFile, content)
  })
}

module.exports = copy
module.exports.sync = copySync
<|endoftext|>


<commit_before>import React from 'react';
import ReactDOM from 'react-dom';
import 'bootstrap/dist/css/bootstrap.css';
import './index.css';

import PropTypes from "prop-types";
import ReactAudioPlayer from 'react-audio-player';
import axios from "axios";

class AvailableDatalist extends React.Component {
  render() {
    console.log("props during render songs")
    console.log(this.props)
    var ret = []
    if(this.props.song_set[0] !== 0){
      ret = this.props.song_set.map(c =>

          {c.songName[0]}
          {c.album}
          {c.author}
          {c.date}

      )
    }else{
      ret[0] =
loading...

    }

    console.log("ret nach bearbeitung")
    console.log(ret)

    return(

            Songname
            Album
            Author
            Date

          {ret}

    )
  }
}

AvailableDatalist.propTypes = {
  song_set: PropTypes.array.isRequired
};

class SongBoard extends React.Component {
	state = {
	  song_set: [0:{name:"none", album:"none", author:"none"}]
	};

	componentDidMount() {
		console.log("current location")
		console.log(window.location.hostname)
	  axios
	  .get("http://" + window.location.hostname + ":8000/album/")
	  .then(response => {
	    const song_set = response.data.map(c => {

	      return{
	        songName: c.song_set.map(d => {return(d.name)}),
	        album: c.name,
	        author: c.author,
	        date: c.release_date
	      };
	    });

	    const newState = Object.assign({}, this.state, {
	          song_set: song_set
	    });
	    this.setState(newState);
	    console.log("state after api request")
	    console.log(this.state)

	  })
	  .catch(error => console.log(error));

	}

  render() {

    return(

							    <input type="radio" name="options" id="option1" autoComplete="off" defaultChecked />
										Song

							    <input type="radio" name="options" id="option2" autoComplete="off" />
										Album

	          <AvailableDatalist song_set={this.state.song_set}/> {/*songs={this.state.contacts}*/}

					playlist

    )
  }
}

class Player extends React.Component {
  render() {
    return(
      <ReactAudioPlayer
        src="my_audio_file.ogg"
        autoPlay
        controls
      />
    )
  }
}

class Site extends React.Component {
  render() {
    return (

          <Player />

          <SongBoard />

    )
  }
}

ReactDOM.render(
  <Site />,
  document.getElementById('root')
);
<commit_msg>bootstrap<commit_after>import React from 'react';
import ReactDOM from 'react-dom';
import 'bootstrap/dist/css/bootstrap.css';
import './index.css';

import PropTypes from "prop-types";
import ReactAudioPlayer from 'react-audio-player';
import axios from "axios";

class AvailableDatalist extends React.Component {
  render() {
    console.log("props during render songs")
    console.log(this.props)
    var ret = []
    if(this.props.song_set[0] !== 0){
      ret = this.props.song_set.map(c =>

          {c.songName[0]}
          {c.album}
          {c.author}
          {c.date}

      )
    }else{
      ret[0] =
loading...

    }

    console.log("ret nach bearbeitung")
    console.log(ret)

    return(

            Songname
            Album
            Author
            Date

          {ret}

    )
  }
}

AvailableDatalist.propTypes = {
  song_set: PropTypes.array.isRequired
};

class SongBoard extends React.Component {
	state = {
	  song_set: [0:{name:"none", album:"none", author:"none"}]
	};

	componentDidMount() {
		console.log("current location")
		console.log(window.location.hostname)
	  axios
	  .get("http://" + window.location.hostname + ":8000/album/")
	  .then(response => {
	    const song_set = response.data.map(c => {

	      return{
	        songName: c.song_set.map(d => {return(d.name)}),
	        album: c.name,
	        author: c.author,
	        date: c.release_date
	      };
	    });

	    const newState = Object.assign({}, this.state, {
	          song_set: song_set
	    });
	    this.setState(newState);
	    console.log("state after api request")
	    console.log(this.state)

	  })
	  .catch(error => console.log(error));

	}

  render() {

    return(

							    <input type="radio" name="options" id="option1" autoComplete="off" defaultChecked />
										Song

							    <input type="radio" name="options" id="option2" autoComplete="off" />
										Album

	          <AvailableDatalist song_set={this.state.song_set}/> {/*songs={this.state.contacts}*/}

						playlist

    )
  }
}

class Player extends React.Component {
  render() {
    return(
      <ReactAudioPlayer
        src="my_audio_file.ogg"
        autoPlay
        controls
      />
    )
  }
}

class Site extends React.Component {
  render() {
    return (

          <Player />

          <SongBoard />

    )
  }
}

ReactDOM.render(
  <Site />,
  document.getElementById('root')
);
<|endoftext|>


<commit_before>//*@public
/**
    The _enyo.ObjectController_ is a sophisticated proxy for
    underlying data. Other objects may observe or bind to its
    properties as if they were that of the underlying data object.
    This abstraction allows for the underlying data to be changed
    or modified without the other objects needing to rebind or
    be aware of the change. It can be subclassed to deal with
    specific data object implementations and special needs. This
    particular controller can handle native data hashes or any
    _enyo.Object_ or sub-kind. While data is being proxied by the
    controller, access to its properties should use the _get_ and
    _set_ method of the controller.
*/
enyo.kind({

    // ...........................
    // PUBLIC PROPERTIES

    //*@public
    name: "enyo.ObjectController",

    //*@public
    kind: "enyo.Controller",

    // ...........................
    // PROTECTED PROPERTIES

    //*@protected
    _getting: false,

    //*@protected
    _useData: false,

    //*@protected
    _listener: null,

    //*@protected
    _last: null,

    // ...........................
    // PUBLIC METHODS

    //*@public
    get: function (prop) {
        var ret;
        // it we are recursing we go straight to the default
        // or if the property is data - data is a reserved word
        // in this case otherwise we can't get a reference to the
        // object
        if (true === this._getting || "data" === prop) return this.inherited(arguments);
        // to avoid infinite recursion
        this._getting = true;
        if (false === (ret = this.getDataProperty(prop))) {
            ret = this.inherited(arguments);
        }
        this._getting = false;
        return ret;
    },

    //*@public
    set: function (prop, value) {
        if (!this.setDataProperty(prop, value)) {
            return this.inherited(arguments);
        }
    },

    //*@public
    /**
        This method is called by the object-controller's _set_ method
        to allow this portion to be overloaded cleanly in cases where
        there is a non-normative behavior required. Accepts the same
        parameters as the normal _set_ method but is expected to return
        a truthy/falsy value to indicate its success. The default behavior
        is to check to see if the _data_ property exists and if the property
        being set is a top-level property of that object. If not it returns
        false. Notification __is__ handled by this method to allow that
        behavior to be overloaded as well. It is responsible for determining
        the previous value and passing that to the notification method.
    */
    setDataProperty: function (prop, value) {
        var data = this.get("data");
        var always = this._useData;
        var prev;
        if (data && (true === always || data.hasOwnProperty(prop))) {
            // if the data object is a native object then we need to make
            // sure to fire our own notifications, we retrieve the previous
            // value early and setup the notification to fire once the new
            // value has been set
            if (true !== always) {
                prev = enyo.getPath.call(data, prop);
                this.stopNotifications();
                this.notifyObservers(prop, prev, value);
            }
            // if the object is an enyo object instance its notifications will
            // automatically fire
            enyo.setPath.call(data, prop, value);
            // if it is instead a native object, we have already queued the
            // the notification so this will flush it, otherwise, it will do
            // nothing
            this.startNotifications();
            return true;
        }
        // under any other circumstances return false
        return false;
    },

    //*@public
    /**
        This method is called by the object-controller's _get_ method
        to allow this portion to be overloaded cleanly in cases where
        there is a non-normative behavior required. Accepts the same
        parameters as the normal _get_ method but is expected to return
        a truthy/explicit-boolean-false value to indicate its success. The
        default behavior is to check to see if the _data_ property exists
        and if the requested property is a first-level property of the object.
        If it is it will return the value. The default getter of the object-
        controller will only execute if this method returns an explicit false.
    */
    getDataProperty: function (prop) {
        var data = this.get("data");
        var always = this._useData;
        if (data && (true === always || data.hasOwnProperty(prop))) {
            return enyo.getPath.call(data, prop);
        }
        // under any other circumstance return false explicitly
        return false;
    },

    //*@public
    /**
        Takes a string parameter and returns a boolean true|false
        depending on whether or not the parameter is an attribute
        of the data object. If no data is present it will always return
        false. If the object has its own _isAttribute_ method it will
        return the the execute method. For more complex implementations
        overload this method.
    */
    isAttribute: function (prop) {
        var data = this.get("data");
        // if the object exists and has its own isAttribute method
        // use that otherwise use our default
        if (data) {
            if ("function" === typeof data.isAttribute) {
                return data.isAttribute(prop);
            } else if (data.hasOwnProperty(prop)) {
                return true;
            }
        }
        return false;
    },

    //*@public
    releaseData: function (data) {
        var data = data || this.get("data");
        // we need to go ahead and double check that the data exists
        // and is a valid enyo object instance
        if (!data || !(data instanceof enyo.Object)) return;
        // reset our always flag
        this._useData = false;
        // if we had a listener registered on the previous data we
        // need to remove it
        if (this._listener) {
            data.removeObserver("*", this._listener);
        }
        // clear any reference
        this._last = null;
    },

    //*@public
    initData: function (data) {
        // if no data was passed in we try and grab the property
        // on our own
        var data = data || this.get("data");
        // we need to go ahead and double check that the data exists
        // and is a valid enyo object instance
        if (!data || !(data instanceof enyo.Object)) return;
        // ok lets go ahead and set our always flag
        this._useData = true;
        // register ourselves as a global listener on the object
        // via the special attribute '*'
        this._listener = data.addObserver("*", this.notifyObservers, this);
        // go ahead and setup our last reference for the future
        this._last = data;
    },

    // ...........................
    // PROTECTED METHODS

    //*@protected
    create: function () {
        this.inherited(arguments);
        this.dataDidChange();
    },

    //*@protected
    dataFindAndInstance: function (ctor, inst) {
        if (inst) {
            if (inst instanceof enyo.Object) {
                this.initData(inst);
            }
        }
    },

    //*@protected
    /**
        This method attempts to find the correct target(s) and
        notify them of any/all the possible properties to force
        them to synchronize to the current values.
    */
    notifyAll: function () {
        // we will try and trick our bindings into firing by simply
        // triggering all of our registered observers since at this
        // moment it is the only way to be sure we get all bindings
        // not just our dispatch targets or owner
        var observers = this.observers;
        var handlers;
        var prop;
        for (prop in observers) {
            if (!observers.hasOwnProperty(prop)) continue;
            if (false === this.isAttribute(prop)) continue;
            handlers = observers[prop];
            enyo.forEach(handlers, function (fn) {
                if ("function" === typeof fn) fn();
            }, this);
        }
    },

    // ...........................
    // OBSERVERS METHODS

    //*@protected
    /**
        This method is intended to fire only when the _data_ property is
        arbitrarily set on the object-controller.
    */
    dataDidChange: enyo.Observer(function () {
        if (this._last) this.releaseData(this._last);
        this.findAndInstance("data");
        this._last = this.get("data");
        this.notifyAll();
    }, "data")

});
<commit_msg>add sync method to only fire bound properties in the object controller<commit_after>//*@public
/**
    The _enyo.ObjectController_ is a sophisticated proxy for
    underlying data. Other objects may observe or bind to its
    properties as if they were that of the underlying data object.
    This abstraction allows for the underlying data to be changed
    or modified without the other objects needing to rebind or
    be aware of the change. It can be subclassed to deal with
    specific data object implementations and special needs. This
    particular controller can handle native data hashes or any
    _enyo.Object_ or sub-kind. While data is being proxied by the
    controller, access to its properties should use the _get_ and
    _set_ method of the controller.
*/
enyo.kind({

    // ...........................
    // PUBLIC PROPERTIES

    //*@public
    name: "enyo.ObjectController",

    //*@public
    kind: "enyo.Controller",

    // ...........................
    // PROTECTED PROPERTIES

    //*@protected
    _getting: false,

    //*@protected
    _useData: false,

    //*@protected
    _listener: null,

    //*@protected
    _last: null,

    // ...........................
    // PUBLIC METHODS

    //*@public
    get: function (prop) {
        var ret;
        // it we are recursing we go straight to the default
        // or if the property is data - data is a reserved word
        // in this case otherwise we can't get a reference to the
        // object
        if (true === this._getting || "data" === prop) return this.inherited(arguments);
        // to avoid infinite recursion
        this._getting = true;
        if (false === (ret = this.getDataProperty(prop))) {
            ret = this.inherited(arguments);
        }
        this._getting = false;
        return ret;
    },

    //*@public
    set: function (prop, value) {
        if (!this.setDataProperty(prop, value)) {
            return this.inherited(arguments);
        }
    },

    //*@public
    /**
        This method is called by the object-controller's _set_ method
        to allow this portion to be overloaded cleanly in cases where
        there is a non-normative behavior required. Accepts the same
        parameters as the normal _set_ method but is expected to return
        a truthy/falsy value to indicate its success. The default behavior
        is to check to see if the _data_ property exists and if the property
        being set is a top-level property of that object. If not it returns
        false. Notification __is__ handled by this method to allow that
        behavior to be overloaded as well. It is responsible for determining
        the previous value and passing that to the notification method.
    */
    setDataProperty: function (prop, value) {
        var data = this.get("data");
        var always = this._useData;
        var prev;
        if (data && (true === always || data.hasOwnProperty(prop))) {
            // if the data object is a native object then we need to make
            // sure to fire our own notifications, we retrieve the previous
            // value early and setup the notification to fire once the new
            // value has been set
            if (true !== always) {
                prev = enyo.getPath.call(data, prop);
                this.stopNotifications();
                this.notifyObservers(prop, prev, value);
            }
            // if the object is an enyo object instance its notifications will
            // automatically fire
            enyo.setPath.call(data, prop, value);
            // if it is instead a native object, we have already queued the
            // the notification so this will flush it, otherwise, it will do
            // nothing
            this.startNotifications();
            return true;
        }
        // under any other circumstances return false
        return false;
    },

    //*@public
    /**
        This method is called by the object-controller's _get_ method
        to allow this portion to be overloaded cleanly in cases where
        there is a non-normative behavior required. Accepts the same
        parameters as the normal _get_ method but is expected to return
        a truthy/explicit-boolean-false value to indicate its success. The
        default behavior is to check to see if the _data_ property exists
        and if the requested property is a first-level property of the object.
        If it is it will return the value. The default getter of the object-
        controller will only execute if this method returns an explicit false.
    */
    getDataProperty: function (prop) {
        var data = this.get("data");
        var always = this._useData;
        if (data && (true === always || data.hasOwnProperty(prop))) {
            return enyo.getPath.call(data, prop);
        }
        // under any other circumstance return false explicitly
        return false;
    },

    //*@public
    /**
        Takes a string parameter and returns a boolean true|false
        depending on whether or not the parameter is an attribute
        of the data object. If no data is present it will always return
        false. If the object has its own _isAttribute_ method it will
        return the the execute method. For more complex implementations
        overload this method.
    */
    isAttribute: function (prop) {
        var data = this.get("data");
        // if the object exists and has its own isAttribute method
        // use that otherwise use our default
        if (data) {
            if ("function" === typeof data.isAttribute) {
                return data.isAttribute(prop);
            } else if (data.hasOwnProperty(prop)) {
                return true;
            }
        }
        return false;
    },

    //*@public
    releaseData: function (data) {
        var data = data || this.get("data");
        // we need to go ahead and double check that the data exists
        // and is a valid enyo object instance
        if (!data || !(data instanceof enyo.Object)) return;
        // reset our always flag
        this._useData = false;
        // if we had a listener registered on the previous data we
        // need to remove it
        if (this._listener) {
            data.removeObserver("*", this._listener);
        }
        // clear any reference
        this._last = null;
    },

    //*@public
    sync: function () {
        var observers = this.observers;
        var observer;
        var prop;
        var handlers;
        var idx = 0;
        var len;
        var bnd;
        for (prop in observers) {
            handlers = observers[prop];
            if (!handlers || !handlers.length) continue;
            for (idx = 0, len = handlers.length; idx < len; ++idx) {
                observer = handlers[idx];
                if (observer.bindingId) {
                    bnd = enyo.Binding.map[observer.bindingId];
                    if (!bnd) continue;
                    bnd.sync();
                }
            }
        }
    },

    //*@public
    initData: function (data) {
        // if no data was passed in we try and grab the property
        // on our own
        var data = data || this.get("data");
        // we need to go ahead and double check that the data exists
        // and is a valid enyo object instance
        if (!data || !(data instanceof enyo.Object)) return;
        // ok lets go ahead and set our always flag
        this._useData = true;
        // register ourselves as a global listener on the object
        // via the special attribute '*'
        this._listener = data.addObserver("*", this.notifyObservers, this);
        // go ahead and setup our last reference for the future
        this._last = data;
    },

    // ...........................
    // PROTECTED METHODS

    //*@protected
    create: function () {
        this.inherited(arguments);
        this.dataDidChange();
    },

    //*@protected
    dataFindAndInstance: function (ctor, inst) {
        if (inst) {
            if (inst instanceof enyo.Object) {
                this.initData(inst);
            }
        }
    },

    //*@protected
    /**
        This method attempts to find the correct target(s) and
        notify them of any/all the possible properties to force
        them to synchronize to the current values.
    */
    notifyAll: function () {
        // we will try and trick our bindings into firing by simply
        // triggering all of our registered observers since at this
        // moment it is the only way to be sure we get all bindings
        // not just our dispatch targets or owner
        var observers = this.observers;
        var handlers;
        var prop;
        for (prop in observers) {
            if (!observers.hasOwnProperty(prop)) continue;
            if (false === this.isAttribute(prop)) continue;
            handlers = observers[prop];
            enyo.forEach(handlers, function (fn) {
                if ("function" === typeof fn) fn();
            }, this);
        }
    },

    // ...........................
    // OBSERVERS METHODS

    //*@protected
    /**
        This method is intended to fire only when the _data_ property is
        arbitrarily set on the object-controller.
    */
    dataDidChange: enyo.Observer(function () {
        if (this._last) this.releaseData(this._last);
        this.findAndInstance("data");
        this._last = this.get("data");
        this.notifyAll();
    }, "data")

});
<|endoftext|>