<commit_before>'use strict';

var Download = require('download');
var exec = require('child_process').exec;
var extfs = require('extfs');
var fs = require('fs');
var chalk = require('chalk');
var runCommand = require('./utils/run-command');
var tarsUtils = require('../utils');
var configPromt = require('../promt/config-promt');
var fsPromt = require('../promt/fs-promt');
var saveAnswers = require('../promt/save-answers');
var tarsZipUrl = 'https://github.com/tars/tars/archive/master.zip';
var commandOptions = {};

/**
 * Main init funciton, download all additional tasks.
 * @param  {Object} answers         Object with answers from promt
 */
function mainInit(answers) {
    var cwd = process.cwd();
    var downloadTars = new Download({
        mode: '755',
        extract: true,
        strip: 1
    }).get(tarsZipUrl).dest(cwd);

    tarsUtils.tarsSay('Please, wait for a moment, while magic is happening...');

    downloadTars.run(function (downloadErr) {

        if (downloadErr) {
            tarsUtils.spinner.stop(true);
            throw downloadErr;
        }

        if (answers) {
            saveAnswers(answers);
        }

        var userPackage = require(cwd + '/user-package.json');

        process.env.tarsVersion = require(cwd + '/package.json').version;
        tarsUtils.tarsSay('Tars version is: ' + process.env.tarsVersion);

        extfs.remove(['package.json', 'user-package.json', '_package.json'], function () {
            var packageJson = require(process.env.cliRoot + 'templates/package.json');

            packageJson.dependencies = userPackage.dependencies || {};
            packageJson.devDependencies = userPackage.devDependencies || {};
            packageJson.optionalDependencies = userPackage.optionalDependencies || {};
            fs.writeFileSync('package.json', JSON.stringify(packageJson, null, 2) + '\n');
            tarsUtils.tarsSay('Local package.json has been created');

            exec('npm i && npm i gulp --save', function (error, stdout, stderr) {
                if (error) {
                    console.log(stderr);
                } else {
                    tarsUtils.tarsSay('Local gulp and other dependencies has been installed');
                    runCommand('gulp', ['init', '--silent']);
                }
            });
        });
    });
}

/**
 * Start initialization
 */
function startInit() {
    var cwd = process.cwd();

    tarsUtils.tarsSay(chalk.underline('Initialization has been started!') + '\n');
    tarsUtils.tarsSay('I\'ll be inited in ' + chalk.cyan('"' + cwd + '"'));
    tarsUtils.tarsSay('TARS source will be downloaded from ' + chalk.cyan('"' + tarsZipUrl + '"'));

    if (!commandOptions.source) {
        tarsUtils.tarsSay('You can specify source url by using flag ' + chalk.cyan('"--source"') + ' or ' + chalk.cyan('"-s"'));
        tarsUtils.tarsSay('Example: ' + chalk.cyan('"tars init -s http://url.to.zip.with.tars"'));
        tarsUtils.tarsSay('Run command ' + chalk.cyan('"tars init --help"') + ' for more info.\n');
    }

    tarsUtils.tarsSay('I\'m going to install "gulp" localy and create local package.json');
    tarsUtils.tarsSay('You can modify package.json by using command ' + chalk.cyan('"npm init"') + ' or manually.');

    if (commandOptions.silent) {
        mainInit();
    } else {
        configPromt(mainInit);
    }
}

/**
 * Init TARS
 * @param  {Object} options Options of init
 */
module.exports = function init(options) {
    var cwd = process.cwd();

    commandOptions = options;
    tarsUtils.spinner.start();

    if (options.source) {
        tarsZipUrl = options.source;
    }

    if (tarsUtils.isTarsInited()) {
        tarsUtils.tarsSay('Tars has been inited already!');
        tarsUtils.tarsSay('You can\'t init Tars in current directory again.');
        tarsUtils.tarsSay('If you need to change preproc or templater, run tars ' + chalk.cyan('"tars re-init"') + '.', true);
        return;
    }

    console.log('\n');
    extfs.isEmpty(cwd, function (empty) {
        if (!empty) {
            tarsUtils.tarsSay(chalk.red('Directory "' + cwd + '" is not empty.'), true);
            fsPromt(startInit);
        } else {
            startInit();
        }
    });
};
<commit_msg>fix init command<commit_after>'use strict';

var Download = require('download');
var exec = require('child_process').exec;
var extfs = require('extfs');
var fs = require('fs');
var chalk = require('chalk');
var runCommand = require('./utils/run-command');
var tarsUtils = require('../utils');
var configPromt = require('../promt/config-promt');
var fsPromt = require('../promt/fs-promt');
var saveAnswers = require('../promt/save-answers');
var tarsZipUrl = 'https://github.com/tars/tars/archive/master.zip';
var commandOptions = {};

/**
 * Main init funciton, download all additional tasks.
 * @param  {Object} answers         Object with answers from promt
 */
function mainInit(answers) {
    var cwd = process.cwd();
    var downloadTars = new Download({
        mode: '755',
        extract: true,
        strip: 1
    }).get(tarsZipUrl).dest(cwd);

    tarsUtils.tarsSay('Please, wait for a moment, while magic is happening...');

    downloadTars.run(function (downloadErr) {

        if (downloadErr) {
            tarsUtils.spinner.stop(true);
            throw downloadErr;
        }

        if (answers) {
            saveAnswers(answers);
        }

        var userPackage = require(cwd + '/user-package.json');

        process.env.tarsVersion = require(cwd + '/package.json').version;
        tarsUtils.tarsSay('Tars version is: ' + process.env.tarsVersion);

        extfs.remove(['package.json', 'user-package.json', '_package.json'], function () {
            var packageJson = require(process.env.cliRoot + 'templates/package.json');

            packageJson.dependencies = userPackage.dependencies || {};
            packageJson.devDependencies = userPackage.devDependencies || {};
            packageJson.optionalDependencies = userPackage.optionalDependencies || {};
            fs.writeFileSync('package.json', JSON.stringify(packageJson, null, 2) + '\n');
            tarsUtils.tarsSay('Local package.json has been created');

            exec('npm i && npm i gulp --save', function (error, stdout, stderr) {
                if (error) {
                    console.log(stderr);
                } else {
                    tarsUtils.tarsSay('Local gulp and other dependencies has been installed');
                    runCommand('gulp', ['init', '--silent']);
                }
            });
        });
    });
}

/**
 * Start initialization
 */
function startInit() {
    var cwd = process.cwd();

    tarsUtils.tarsSay(chalk.underline('Initialization has been started!') + '\n');
    tarsUtils.tarsSay('I\'ll be inited in ' + chalk.cyan('"' + cwd + '"'));
    tarsUtils.tarsSay('TARS source will be downloaded from ' + chalk.cyan('"' + tarsZipUrl + '"'));

    if (!commandOptions.source) {
        tarsUtils.tarsSay('You can specify source url by using flag ' + chalk.cyan('"--source"') + ' or ' + chalk.cyan('"-s"'));
        tarsUtils.tarsSay('Example: ' + chalk.cyan('"tars init -s http://url.to.zip.with.tars"'));
        tarsUtils.tarsSay('Run command ' + chalk.cyan('"tars init --help"') + ' for more info.\n');
    }

    tarsUtils.tarsSay('I\'m going to install "gulp" localy and create local package.json');
    tarsUtils.tarsSay('You can modify package.json by using command ' + chalk.cyan('"npm init"') + ' or manually.');

    if (commandOptions.silent || commandOptions.source) {
        mainInit();
    } else {
        configPromt(mainInit);
    }
}

/**
 * Init TARS
 * @param  {Object} options Options of init
 */
module.exports = function init(options) {
    var cwd = process.cwd();

    commandOptions = options;
    tarsUtils.spinner.start();

    if (options.source) {
        tarsZipUrl = options.source;
    }

    if (tarsUtils.isTarsInited()) {
        tarsUtils.tarsSay('Tars has been inited already!');
        tarsUtils.tarsSay('You can\'t init Tars in current directory again.');
        tarsUtils.tarsSay('If you need to change preproc or templater, run tars ' + chalk.cyan('"tars re-init"') + '.', true);
        return;
    }

    console.log('\n');
    extfs.isEmpty(cwd, function (empty) {
        if (!empty) {
            tarsUtils.tarsSay(chalk.red('Directory "' + cwd + '" is not empty.'), true);
            fsPromt(startInit);
        } else {
            startInit();
        }
    });
};
<|endoftext|>


<commit_before>/* ================================================================
 * macaca-client by ziczhu(zic.zhu@gmail.com)
 *
 * first created at : Tue Oct 25 2015 12:30:10 GMT+0800 (CST)
 *
 * ================================================================
 * Copyright ziczhu
 *
 * Licensed under the MIT License
 * You may not use this file except in compliance with the License.
 *
 * ================================================================ */

'use strict';

var childProcess = require('child_process');

module.exports = function(/* command, args, options */) {
  var args = Array.prototype.slice.call(arguments);
  return new Promise(function(resolve, reject) {
    var stdout = '';
    var stderr = '';
    var child = childProcess.spawn.apply(childProcess, args);

    child.on('error', function(error) {
      reject(error);
    });

    child.stdout.on('data', function(data) {
      stdout += data;
    });

    child.stderr.on('data', function(data) {
      stderr += data;
    });

    child.on('close', function(code) {
      var error;
      if (code) {
        error = new Error(stderr);
        error.code = code;
        return reject(error);
      }
      resolve([stdout, stderr]);
    });
  });
};
<commit_msg>clean spawn promise<commit_after><|endoftext|>


<commit_before>(function () {

  Accounts.oauth.registerService('google', 2, function(query) {

    var response = getTokens(query);
    var accessToken = response.access_token;
    var refreshToken = response.refresh_token;
    var identity = getIdentity(accessToken);

    return {
      serviceData: {
        id: identity.id,
        accessToken: accessToken,
        refreshToken: refreshToken,
        email: identity.email
      },
      options: {profile: {name: identity.name}}
    };
  });

  // returns an object containing access_token, and if this is the first
  // authorization request also refresh_token
  var getTokens = function (query) {
    var config = Accounts.loginServiceConfiguration.findOne({service: 'google'});
    if (!config)
      throw new Accounts.ConfigError("Service not configured");

    var result = Meteor.http.post(
      "https://accounts.google.com/o/oauth2/token", {params: {
        code: query.code,
        client_id: config.clientId,
        client_secret: config.secret,
        redirect_uri: Meteor.absoluteUrl("_oauth/google?close"),
        grant_type: 'authorization_code'
      }});

    if (result.error) // if the http response was an error
      throw result.error;
    if (result.data.error) // if the http response was a json object with an error attribute
      throw result.data;
    return result.data;
  };

  var getIdentity = function (accessToken) {
    var result = Meteor.http.get(
      "https://www.googleapis.com/oauth2/v1/userinfo",
      {params: {access_token: accessToken}});

    if (result.error)
      throw result.error;
    return result.data;
  };
})();
<commit_msg>Don't lose Google refresh tokens<commit_after>(function () {

  Accounts.oauth.registerService('google', 2, function(query) {

    var response = getTokens(query);
    var accessToken = response.access_token;
    var identity = getIdentity(accessToken);

    var serviceData = {
      id: identity.id,
      accessToken: accessToken,
      email: identity.email
    };

    // only set the token in serviceData if it's there. this ensures
    // that we don't lose old ones (since we only get this on the first
    // log in attempt)
    if (response.refresh_token)
      serviceData.refreshToken = response.refresh_token;

    return {
      serviceData: serviceData,
      options: {profile: {name: identity.name}}
    };
  });

  // returns an object containing access_token, and if this is the first
  // authorization request also refresh_token
  var getTokens = function (query) {
    var config = Accounts.loginServiceConfiguration.findOne({service: 'google'});
    if (!config)
      throw new Accounts.ConfigError("Service not configured");

    var result = Meteor.http.post(
      "https://accounts.google.com/o/oauth2/token", {params: {
        code: query.code,
        client_id: config.clientId,
        client_secret: config.secret,
        redirect_uri: Meteor.absoluteUrl("_oauth/google?close"),
        grant_type: 'authorization_code'
      }});

    if (result.error) // if the http response was an error
      throw result.error;
    if (result.data.error) // if the http response was a json object with an error attribute
      throw result.data;
    return result.data;
  };

  var getIdentity = function (accessToken) {
    var result = Meteor.http.get(
      "https://www.googleapis.com/oauth2/v1/userinfo",
      {params: {access_token: accessToken}});

    if (result.error)
      throw result.error;
    return result.data;
  };
})();
<|endoftext|>


<commit_before>(function(root, factory) {
    'use strict';
    // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js, Rhino, and browsers.

    /* istanbul ignore next */
    if (typeof define === 'function' && define.amd) {
        define('stackframe', [], factory);
    } else if (typeof exports === 'object') {
        module.exports = factory();
    } else {
        root.StackFrame = factory();
    }
}(this, function() {
    'use strict';
    function _isNumber(n) {
        return !isNaN(parseFloat(n)) && isFinite(n);
    }

    function _capitalize(str) {
        return str[0].toUpperCase() + str.substring(1);
    }

    function _getter(p) {
        return function() {
            return this[p];
        };
    }

    var booleanProps = ['isConstructor', 'isEval', 'isNative', 'isToplevel'];
    var numericProps = ['columnNumber', 'lineNumber'];
    var stringProps = ['fileName', 'functionName', 'source'];
    var arrayProps = ['args'];

    var props = booleanProps.concat(numericProps, stringProps, arrayProps);

    function StackFrame(obj) {
        if (obj instanceof Object) {
            for (var i = 0; i < props.length; i++) {
                if (obj.hasOwnProperty(props[i]) && obj[props[i]] !== undefined) {
                    this['set' + _capitalize(props[i])](obj[props[i]]);
                }
            }
        }
    }

    StackFrame.prototype = {
        getArgs: function() {
            return this.args;
        },
        setArgs: function(v) {
            if (Object.prototype.toString.call(v) !== '[object Array]') {
                throw new TypeError('Args must be an Array');
            }
            this.args = v;
        },

        getEvalOrigin: function() {
            return this.evalOrigin;
        },
        setEvalOrigin: function(v) {
            if (v instanceof StackFrame) {
                this.evalOrigin = v;
            } else if (v instanceof Object) {
                this.evalOrigin = new StackFrame(v);
            } else {
                throw new TypeError('Eval Origin must be an Object or StackFrame');
            }
        },

        toString: function() {
            var functionName = this.getFunctionName() || '{anonymous}';
            var args = '(' + (this.getArgs() || []).join(',') + ')';
            var fileName = this.getFileName() ? ('@' + this.getFileName()) : '';
            var lineNumber = _isNumber(this.getLineNumber()) ? (':' + this.getLineNumber()) : '';
            var columnNumber = _isNumber(this.getColumnNumber()) ? (':' + this.getColumnNumber()) : '';
            return functionName + args + fileName + lineNumber + columnNumber;
        }
    };

    for (var i = 0; i < booleanProps.length; i++) {
        StackFrame.prototype['get' + _capitalize(booleanProps[i])] = _getter(booleanProps[i]);
        StackFrame.prototype['set' + _capitalize(booleanProps[i])] = (function(p) {
            return function(v) {
                this[p] = Boolean(v);
            };
        })(booleanProps[i]);
    }

    for (var j = 0; j < numericProps.length; j++) {
        StackFrame.prototype['get' + _capitalize(numericProps[j])] = _getter(numericProps[j]);
        StackFrame.prototype['set' + _capitalize(numericProps[j])] = (function(p) {
            return function(v) {
                if (!_isNumber(v)) {
                    throw new TypeError(p + ' must be a Number');
                }
                this[p] = Number(v);
            };
        })(numericProps[j]);
    }

    for (var k = 0; k < stringProps.length; k++) {
        StackFrame.prototype['get' + _capitalize(stringProps[k])] = _getter(stringProps[k]);
        StackFrame.prototype['set' + _capitalize(stringProps[k])] = (function(p) {
            return function(v) {
                this[p] = String(v);
            };
        })(stringProps[k]);
    }

    return StackFrame;
}));
<commit_msg>Use charAt instead of string array notation for ES3 browser support (IE6/7)<commit_after>(function(root, factory) {
    'use strict';
    // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js, Rhino, and browsers.

    /* istanbul ignore next */
    if (typeof define === 'function' && define.amd) {
        define('stackframe', [], factory);
    } else if (typeof exports === 'object') {
        module.exports = factory();
    } else {
        root.StackFrame = factory();
    }
}(this, function() {
    'use strict';
    function _isNumber(n) {
        return !isNaN(parseFloat(n)) && isFinite(n);
    }

    function _capitalize(str) {
        return str.charAt(0).toUpperCase() + str.substring(1);
    }

    function _getter(p) {
        return function() {
            return this[p];
        };
    }

    var booleanProps = ['isConstructor', 'isEval', 'isNative', 'isToplevel'];
    var numericProps = ['columnNumber', 'lineNumber'];
    var stringProps = ['fileName', 'functionName', 'source'];
    var arrayProps = ['args'];

    var props = booleanProps.concat(numericProps, stringProps, arrayProps);

    function StackFrame(obj) {
        if (obj instanceof Object) {
            for (var i = 0; i < props.length; i++) {
                if (obj.hasOwnProperty(props[i]) && obj[props[i]] !== undefined) {
                    this['set' + _capitalize(props[i])](obj[props[i]]);
                }
            }
        }
    }

    StackFrame.prototype = {
        getArgs: function() {
            return this.args;
        },
        setArgs: function(v) {
            if (Object.prototype.toString.call(v) !== '[object Array]') {
                throw new TypeError('Args must be an Array');
            }
            this.args = v;
        },

        getEvalOrigin: function() {
            return this.evalOrigin;
        },
        setEvalOrigin: function(v) {
            if (v instanceof StackFrame) {
                this.evalOrigin = v;
            } else if (v instanceof Object) {
                this.evalOrigin = new StackFrame(v);
            } else {
                throw new TypeError('Eval Origin must be an Object or StackFrame');
            }
        },

        toString: function() {
            var functionName = this.getFunctionName() || '{anonymous}';
            var args = '(' + (this.getArgs() || []).join(',') + ')';
            var fileName = this.getFileName() ? ('@' + this.getFileName()) : '';
            var lineNumber = _isNumber(this.getLineNumber()) ? (':' + this.getLineNumber()) : '';
            var columnNumber = _isNumber(this.getColumnNumber()) ? (':' + this.getColumnNumber()) : '';
            return functionName + args + fileName + lineNumber + columnNumber;
        }
    };

    for (var i = 0; i < booleanProps.length; i++) {
        StackFrame.prototype['get' + _capitalize(booleanProps[i])] = _getter(booleanProps[i]);
        StackFrame.prototype['set' + _capitalize(booleanProps[i])] = (function(p) {
            return function(v) {
                this[p] = Boolean(v);
            };
        })(booleanProps[i]);
    }

    for (var j = 0; j < numericProps.length; j++) {
        StackFrame.prototype['get' + _capitalize(numericProps[j])] = _getter(numericProps[j]);
        StackFrame.prototype['set' + _capitalize(numericProps[j])] = (function(p) {
            return function(v) {
                if (!_isNumber(v)) {
                    throw new TypeError(p + ' must be a Number');
                }
                this[p] = Number(v);
            };
        })(numericProps[j]);
    }

    for (var k = 0; k < stringProps.length; k++) {
        StackFrame.prototype['get' + _capitalize(stringProps[k])] = _getter(stringProps[k]);
        StackFrame.prototype['set' + _capitalize(stringProps[k])] = (function(p) {
            return function(v) {
                this[p] = String(v);
            };
        })(stringProps[k]);
    }

    return StackFrame;
}));
<|endoftext|>


<commit_before>var logger = require('pomelo-logger').getLogger('pomelo', __filename);
var taskManager = require('../common/manager/taskManager');
var pomelo = require('../pomelo');
var rsa = require("node-bignumber");
var events = require('../util/events');
var utils = require('../util/utils');

module.exports = function(app, opts) {
  return new Component(app, opts);
};

/**
 * Connector component. Receive client requests and attach session with socket.
 *
 * @param {Object} app  current application context
 * @param {Object} opts attach parameters
 *                      opts.connector {Object} provides low level network and protocol details implementation between server and clients.
 */
var Component = function(app, opts) {
  opts = opts || {};
  this.app = app;
  this.connector = getConnector(app, opts);
  this.encode = opts.encode;
  this.decode = opts.decode;
  this.useCrypto = opts.useCrypto;
  this.blacklistFun = opts.blacklistFun;
  this.keys = {};
  this.blacklist = [];

  if(opts.useDict) {
    app.load(pomelo.dictionary, app.get('dictionaryConfig'));
  }

  if(opts.useProtobuf) {
    app.load(pomelo.protobuf, app.get('protobufConfig'));
  }

  if(opts.useCrypt) {
    if (opts.verify != null) {
        this.verify = opts.verify;
    } else {
        this.verify = verifyMessage;
    }
  }

  // component dependencies
  this.server = null;
  this.session = null;
  this.connection = null;
};

var pro = Component.prototype;

pro.name = '__connector__';

pro.start = function(cb) {
  this.server = this.app.components.__server__;
  this.session = this.app.components.__session__;
  this.connection = this.app.components.__connection__;

  // check component dependencies
  if(!this.server) {
    process.nextTick(function() {
      utils.invokeCallback(cb, new Error('fail to start connector component for no server component loaded'));
    });
    return;
  }

  if(!this.session) {
    process.nextTick(function() {
      utils.invokeCallback(cb, new Error('fail to start connector component for no session component loaded'));
    });
    return;
  }

  process.nextTick(cb);
};

pro.afterStart = function(cb) {
  this.connector.start(cb);
  this.connector.on('connection', hostFilter.bind(this, bindEvents));
};

pro.stop = function(force, cb) {
  if(this.connector) {
    this.connector.stop(force, cb);
    this.connector = null;
    return;
  }

  process.nextTick(cb);
};

pro.send = function(reqId, route, msg, recvs, opts, cb) {
  logger.debug('[%s] send message reqId: %s, route: %s, msg: %j, receivers: %j, opts: %j', this.app.serverId, reqId, route, msg, recvs, opts);
  var emsg = msg;
  if(this.encode) {
    // use costumized encode
    emsg = this.encode.call(this, reqId, route, msg);
  } else if(this.connector.encode) {
    // use connector default encode
    emsg = this.connector.encode(reqId, route, msg);
  }

  if(!emsg) {
    process.nextTick(function() {
      utils.invokeCallback(cb, new Error('fail to send message for encode result is empty.'));
      return;
    });
  }

  this.app.components.__pushScheduler__.schedule(reqId, route, emsg,
      recvs, opts, cb);
};

pro.setPubKey = function(id, key) {
  var pubKey = new rsa.Key();
  pubKey.n = new rsa.BigInteger(key.rsa_n, 16);
  pubKey.e = key.rsa_e;
  this.keys[id] = pubKey;
};

pro.getPubKey = function(id) {
  return this.keys[id];
};

var getConnector = function(app, opts) {
  var connector = opts.connector;
  if(!connector) {
    return getDefaultConnector(app, opts);
  }

  if(typeof connector !== 'function') {
    return connector;
  }

  var curServer = app.getCurServer();
  return connector(curServer.clientPort, curServer.host, opts);
};

var getDefaultConnector = function(app, opts) {
  var DefaultConnector = require('../connectors/sioconnector');
  var curServer = app.getCurServer();
  return new DefaultConnector(curServer.clientPort, curServer.host, opts);
};

var hostFilter = function(cb, socket) {
  var ip = socket.remoteAddress.ip;
  var check = function(list) {
    for(var address in list) {
      var exp = new RegExp(list[address]);
      if(exp.test(ip)) {
        socket.disconnect();
        return true;
      }
    }
    return false;
  };
  // dynamical check
  if(this.blacklist.length !== 0 && !!check(this.blacklist)) {
    return;
  }
  // static check
  if(!!this.blacklistFun && typeof this.blacklistFun === 'function') {
    var self = this;
    self.blacklistFun(function(err, list) {
      if(!!err) {
        logger.error('connector blacklist error: %j', err.stack);
        utils.invokeCallback(cb, self, socket);
        return;
      }
      if(!Array.isArray(list)) {
        logger.error('connector blacklist is not array: %j', list);
        utils.invokeCallback(cb, self, socket);
        return;
      }
      if(!!check(list)) {
        return;
      } else {
        utils.invokeCallback(cb, self, socket);
        return;
      }
    });
  } else {
    utils.invokeCallback(cb, this, socket);
  }
};

var bindEvents = function(self, socket) {
  if(self.connection) {
    self.connection.increaseConnectionCount();
    var statisticInfo = self.connection.getStatisticsInfo();
    var curServer = self.app.getCurServer();
    if(statisticInfo.totalConnCount > curServer['max-connections']) {
      logger.warn('the server %s has reached the max connections %s', curServer.id, curServer['max-connections']);
      socket.disconnect();
      return;
    }
  }

  //create session for connection
  var session = getSession(self, socket);
  var closed = false;

  socket.on('disconnect', function() {
    if(closed) {
      return;
    }
    closed = true;
    if(self.connection) {
      self.connection.decreaseConnectionCount(session.uid);
    }
  });

  socket.on('error', function() {
    if(closed) {
      return;
    }
    closed = true;
    if(self.connection) {
      self.connection.decreaseConnectionCount(session.uid);
    }
  });

  // new message
  socket.on('message', function(msg) {
    var dmsg = msg;
    if(self.decode) {
      dmsg = self.decode(msg);
    } else if(self.connector.decode) {
      dmsg = self.connector.decode(msg);
    }
    if(!dmsg) {
      // discard invalid message
      return;
    }

    // use rsa crypto
    if(self.useCrypto) {
      var verified = self.verify(self, session, dmsg);
      if(!verified) {
        logger.error('fail to verify the data received from client.');
        return;
      }
    }

    handleMessage(self, session, dmsg);
  }); //on message end
};

/**
 * get session for current connection
 */
var getSession = function(self, socket) {
  var app = self.app, sid = socket.id;
  var session = self.session.get(sid);
  if(session) {
    return session;
  }

  session = self.session.create(sid, app.getServerId(), socket);
  logger.debug('[%s] getSession session is created with session id: %s', app.getServerId(), sid);

  // bind events for session
  socket.on('disconnect', session.closed.bind(session));
  socket.on('error', session.closed.bind(session));
  session.on('closed', onSessionClose.bind(null, app));
  session.on('bind', function(uid) {
    logger.debug('session on [%s] bind with uid: %s', self.app.serverId, uid);
    // update connection statistics if necessary
    if(self.connection) {
      self.connection.addLoginedUser(uid, {
        loginTime: Date.now(),
        uid: uid,
        address: socket.remoteAddress.ip + ':' + socket.remoteAddress.port
      });
    }
    self.app.event.emit(events.BIND_SESSION, session);
  });

  session.on('unbind', function(uid) {
    if(self.connection) {
      self.connection.removeLoginedUser(uid);
    }
    self.app.event.emit(events.UNBIND_SESSION, session);
  });

  return session;
};

var onSessionClose = function(app, session, reason) {
  taskManager.closeQueue(session.id, true);
  app.event.emit(events.CLOSE_SESSION, session);
};

var handleMessage = function(self, session, msg) {
  logger.debug('[%s] handleMessage session id: %s, msg: %j', self.app.serverId, session.id, msg);
  var type = checkServerType(msg.route);
  if(!type) {
    logger.error('invalid route string. route : %j', msg.route);
    return;
  }
  self.server.globalHandle(msg, session.toFrontendSession(), function(err, resp, opts) {
    if(resp && !msg.id) {
      logger.warn('try to response to a notify: %j', msg.route);
      return;
    }
    if (!msg.id && !resp) return;
    if (!resp) resp = {};
    if (!!err){
      resp.code = 500;
    }
    opts = {type: 'response', userOptions: opts || {}};
    // for compatiablity
    opts.isResponse = true;

    self.send(msg.id, msg.route, resp, [session.id], opts,
      function() {});
  });
};

/**
 * Get server type form request message.
 */
var checkServerType = function (route) {
  if(!route) {
    return null;
  }
  var idx = route.indexOf('.');
  if(idx < 0) {
    return null;
  }
  return route.substring(0, idx);
};

var verifyMessage = function (self, session, msg) {
  var sig = msg.body.__crypto__;
  if(!sig) {
    logger.error('receive data from client has no signature [%s]', self.app.serverId);
    return false;
  }

  var pubKey;

  if(!session) {
    logger.error('could not find session.');
    return false;
  }

  if(!session.get('pubKey')) {
    pubKey = self.getPubKey(session.id);
    if(!!pubKey) {
      delete self.keys[session.id];
      session.set('pubKey', pubKey);
    }
    else {
      logger.error('could not get public key, session id is %s', session.id);
      return false;
    }
  }
  else {
    pubKey = session.get('pubKey');
  }

  if(!pubKey.n || !pubKey.e) {
    logger.error('could not verify message without public key [%s]', self.app.serverId);
    return false;
  }

  delete  msg.body.__crypto__;

  var message = JSON.stringify(msg.body);
  if(utils.hasChineseChar(message))
    message = utils.unicodeToUtf8(message);

  return pubKey.verifyString(message, sig);
};
<commit_msg>fixed typo for crypt(o)<commit_after>var logger = require('pomelo-logger').getLogger('pomelo', __filename);
var taskManager = require('../common/manager/taskManager');
var pomelo = require('../pomelo');
var rsa = require("node-bignumber");
var events = require('../util/events');
var utils = require('../util/utils');

module.exports = function(app, opts) {
  return new Component(app, opts);
};

/**
 * Connector component. Receive client requests and attach session with socket.
 *
 * @param {Object} app  current application context
 * @param {Object} opts attach parameters
 *                      opts.connector {Object} provides low level network and protocol details implementation between server and clients.
 */
var Component = function(app, opts) {
  opts = opts || {};
  this.app = app;
  this.connector = getConnector(app, opts);
  this.encode = opts.encode;
  this.decode = opts.decode;
  this.useCrypto = opts.useCrypto;
  this.blacklistFun = opts.blacklistFun;
  this.keys = {};
  this.blacklist = [];

  if(opts.useDict) {
    app.load(pomelo.dictionary, app.get('dictionaryConfig'));
  }

  if(opts.useProtobuf) {
    app.load(pomelo.protobuf, app.get('protobufConfig'));
  }

  if(opts.useCrypto) {
    if (opts.verify != null) {
        this.verify = opts.verify;
    } else {
        this.verify = verifyMessage;
    }
  }

  // component dependencies
  this.server = null;
  this.session = null;
  this.connection = null;
};

var pro = Component.prototype;

pro.name = '__connector__';

pro.start = function(cb) {
  this.server = this.app.components.__server__;
  this.session = this.app.components.__session__;
  this.connection = this.app.components.__connection__;

  // check component dependencies
  if(!this.server) {
    process.nextTick(function() {
      utils.invokeCallback(cb, new Error('fail to start connector component for no server component loaded'));
    });
    return;
  }

  if(!this.session) {
    process.nextTick(function() {
      utils.invokeCallback(cb, new Error('fail to start connector component for no session component loaded'));
    });
    return;
  }

  process.nextTick(cb);
};

pro.afterStart = function(cb) {
  this.connector.start(cb);
  this.connector.on('connection', hostFilter.bind(this, bindEvents));
};

pro.stop = function(force, cb) {
  if(this.connector) {
    this.connector.stop(force, cb);
    this.connector = null;
    return;
  }

  process.nextTick(cb);
};

pro.send = function(reqId, route, msg, recvs, opts, cb) {
  logger.debug('[%s] send message reqId: %s, route: %s, msg: %j, receivers: %j, opts: %j', this.app.serverId, reqId, route, msg, recvs, opts);
  var emsg = msg;
  if(this.encode) {
    // use costumized encode
    emsg = this.encode.call(this, reqId, route, msg);
  } else if(this.connector.encode) {
    // use connector default encode
    emsg = this.connector.encode(reqId, route, msg);
  }

  if(!emsg) {
    process.nextTick(function() {
      utils.invokeCallback(cb, new Error('fail to send message for encode result is empty.'));
      return;
    });
  }

  this.app.components.__pushScheduler__.schedule(reqId, route, emsg,
      recvs, opts, cb);
};

pro.setPubKey = function(id, key) {
  var pubKey = new rsa.Key();
  pubKey.n = new rsa.BigInteger(key.rsa_n, 16);
  pubKey.e = key.rsa_e;
  this.keys[id] = pubKey;
};

pro.getPubKey = function(id) {
  return this.keys[id];
};

var getConnector = function(app, opts) {
  var connector = opts.connector;
  if(!connector) {
    return getDefaultConnector(app, opts);
  }

  if(typeof connector !== 'function') {
    return connector;
  }

  var curServer = app.getCurServer();
  return connector(curServer.clientPort, curServer.host, opts);
};

var getDefaultConnector = function(app, opts) {
  var DefaultConnector = require('../connectors/sioconnector');
  var curServer = app.getCurServer();
  return new DefaultConnector(curServer.clientPort, curServer.host, opts);
};

var hostFilter = function(cb, socket) {
  var ip = socket.remoteAddress.ip;
  var check = function(list) {
    for(var address in list) {
      var exp = new RegExp(list[address]);
      if(exp.test(ip)) {
        socket.disconnect();
        return true;
      }
    }
    return false;
  };
  // dynamical check
  if(this.blacklist.length !== 0 && !!check(this.blacklist)) {
    return;
  }
  // static check
  if(!!this.blacklistFun && typeof this.blacklistFun === 'function') {
    var self = this;
    self.blacklistFun(function(err, list) {
      if(!!err) {
        logger.error('connector blacklist error: %j', err.stack);
        utils.invokeCallback(cb, self, socket);
        return;
      }
      if(!Array.isArray(list)) {
        logger.error('connector blacklist is not array: %j', list);
        utils.invokeCallback(cb, self, socket);
        return;
      }
      if(!!check(list)) {
        return;
      } else {
        utils.invokeCallback(cb, self, socket);
        return;
      }
    });
  } else {
    utils.invokeCallback(cb, this, socket);
  }
};

var bindEvents = function(self, socket) {
  if(self.connection) {
    self.connection.increaseConnectionCount();
    var statisticInfo = self.connection.getStatisticsInfo();
    var curServer = self.app.getCurServer();
    if(statisticInfo.totalConnCount > curServer['max-connections']) {
      logger.warn('the server %s has reached the max connections %s', curServer.id, curServer['max-connections']);
      socket.disconnect();
      return;
    }
  }

  //create session for connection
  var session = getSession(self, socket);
  var closed = false;

  socket.on('disconnect', function() {
    if(closed) {
      return;
    }
    closed = true;
    if(self.connection) {
      self.connection.decreaseConnectionCount(session.uid);
    }
  });

  socket.on('error', function() {
    if(closed) {
      return;
    }
    closed = true;
    if(self.connection) {
      self.connection.decreaseConnectionCount(session.uid);
    }
  });

  // new message
  socket.on('message', function(msg) {
    var dmsg = msg;
    if(self.decode) {
      dmsg = self.decode(msg);
    } else if(self.connector.decode) {
      dmsg = self.connector.decode(msg);
    }
    if(!dmsg) {
      // discard invalid message
      return;
    }

    // use rsa crypto
    if(self.useCrypto) {
      var verified = self.verify(self, session, dmsg);
      if(!verified) {
        logger.error('fail to verify the data received from client.');
        return;
      }
    }

    handleMessage(self, session, dmsg);
  }); //on message end
};

/**
 * get session for current connection
 */
var getSession = function(self, socket) {
  var app = self.app, sid = socket.id;
  var session = self.session.get(sid);
  if(session) {
    return session;
  }

  session = self.session.create(sid, app.getServerId(), socket);
  logger.debug('[%s] getSession session is created with session id: %s', app.getServerId(), sid);

  // bind events for session
  socket.on('disconnect', session.closed.bind(session));
  socket.on('error', session.closed.bind(session));
  session.on('closed', onSessionClose.bind(null, app));
  session.on('bind', function(uid) {
    logger.debug('session on [%s] bind with uid: %s', self.app.serverId, uid);
    // update connection statistics if necessary
    if(self.connection) {
      self.connection.addLoginedUser(uid, {
        loginTime: Date.now(),
        uid: uid,
        address: socket.remoteAddress.ip + ':' + socket.remoteAddress.port
      });
    }
    self.app.event.emit(events.BIND_SESSION, session);
  });

  session.on('unbind', function(uid) {
    if(self.connection) {
      self.connection.removeLoginedUser(uid);
    }
    self.app.event.emit(events.UNBIND_SESSION, session);
  });

  return session;
};

var onSessionClose = function(app, session, reason) {
  taskManager.closeQueue(session.id, true);
  app.event.emit(events.CLOSE_SESSION, session);
};

var handleMessage = function(self, session, msg) {
  logger.debug('[%s] handleMessage session id: %s, msg: %j', self.app.serverId, session.id, msg);
  var type = checkServerType(msg.route);
  if(!type) {
    logger.error('invalid route string. route : %j', msg.route);
    return;
  }
  self.server.globalHandle(msg, session.toFrontendSession(), function(err, resp, opts) {
    if(resp && !msg.id) {
      logger.warn('try to response to a notify: %j', msg.route);
      return;
    }
    if (!msg.id && !resp) return;
    if (!resp) resp = {};
    if (!!err){
      resp.code = 500;
    }
    opts = {type: 'response', userOptions: opts || {}};
    // for compatiablity
    opts.isResponse = true;

    self.send(msg.id, msg.route, resp, [session.id], opts,
      function() {});
  });
};

/**
 * Get server type form request message.
 */
var checkServerType = function (route) {
  if(!route) {
    return null;
  }
  var idx = route.indexOf('.');
  if(idx < 0) {
    return null;
  }
  return route.substring(0, idx);
};

var verifyMessage = function (self, session, msg) {
  var sig = msg.body.__crypto__;
  if(!sig) {
    logger.error('receive data from client has no signature [%s]', self.app.serverId);
    return false;
  }

  var pubKey;

  if(!session) {
    logger.error('could not find session.');
    return false;
  }

  if(!session.get('pubKey')) {
    pubKey = self.getPubKey(session.id);
    if(!!pubKey) {
      delete self.keys[session.id];
      session.set('pubKey', pubKey);
    }
    else {
      logger.error('could not get public key, session id is %s', session.id);
      return false;
    }
  }
  else {
    pubKey = session.get('pubKey');
  }

  if(!pubKey.n || !pubKey.e) {
    logger.error('could not verify message without public key [%s]', self.app.serverId);
    return false;
  }

  delete  msg.body.__crypto__;

  var message = JSON.stringify(msg.body);
  if(utils.hasChineseChar(message))
    message = utils.unicodeToUtf8(message);

  return pubKey.verifyString(message, sig);
};
<|endoftext|>


<commit_before>angular.module('mwUI.Utils')

  .service('mwUrlStorage', function ($rootScope, $location, $route) {
    var storage = {};

    var preventRouteReload = function () {
      //Check whether reloadOnSearch is already disabled
      if ($route.current.$$route.reloadOnSearch === false) {
        return;
      }
      // Remember the state so we can set it to the original state after we have updated the route
      var prevReloadOnSearchVal = $route.current.$$route.reloadOnSearch;
      //Set reloadOnSearch false so angular does not reinitialize the controller
      $route.current.$$route.reloadOnSearch = false;

      var unbindRouteUpdateListener,
        unbindRouteChangeSuccessListener;

      //Route update is triggered when reloadOnSearch is set to true and a search param has changed
      unbindRouteUpdateListener = $rootScope.$on('$routeUpdate', function () {
        $route.current.$$route.reloadOnSearch = prevReloadOnSearchVal;
        unbindRouteUpdateListener();
        unbindRouteChangeSuccessListener();
      });

      // //Route change success is triggered when reloadOnSearch is set to false and a search param has changed
      unbindRouteChangeSuccessListener = $rootScope.$on('$routeChangeSuccess', function () {
        $route.current.$$route.reloadOnSearch = prevReloadOnSearchVal;
        unbindRouteChangeSuccessListener();
        unbindRouteUpdateListener();
      });
    };

    var hasChangedValues = function (params, removeParams) {
      var currentSearchParams = $location.search();
      var changedParams = _.difference(_.values(params), _.values(currentSearchParams));

      removeParams = _.difference(_.values(removeParams), _.values(currentSearchParams));
      return changedParams.length > 0 || removeParams.length > 0;
    };

    var setUrlQueryParams = function (params, preferQueryOverStorage, removeKeys, options) {
      options = options || {};
      if (hasChangedValues(params, removeKeys)) {
        var currentSearchParams = $location.search(),
          newSearchParams;

        if (removeKeys) {
          removeKeys.forEach(function (key) {
            currentSearchParams[key] = null;
          });
        }

        if (preferQueryOverStorage) {
          newSearchParams = _.extend(params, currentSearchParams);
        } else {
          newSearchParams = _.extend(currentSearchParams, params);
        }

        preventRouteReload();
        $location.search(newSearchParams);

        if (!options.keepInHistory) {
          $location.replace();
        }
      }
    };

    $rootScope.$on('$locationChangeSuccess', function (ev, newUrl, oldUrl) {
      if (newUrl !== oldUrl) {
        setUrlQueryParams(storage, true);
      }
    });

    return {
      getItem: function (key) {
        return $location.search()[key];
      },
      setObject: function (obj, options) {
        options = options || {};
        var wasChanged = false;
        if (_.isObject(obj)) {
          for (var key in obj) {
            if (obj.hasOwnProperty(key) && storage[key] !== obj[key]) {
              if (!options.removeOnUrlChange) {
                storage[key] = obj[key];
              }
              wasChanged = true;
            }
          }
        } else {
          throw new Error('[mwUrlStorage] parameter has to be an object otherwise setItem(key, val) should be called ');
        }

        if (wasChanged) {
          setUrlQueryParams(obj);
        }
      },
      setItem: function (key, value, options) {
        options = options || {};
        if (storage[key] !== value) {
          if (!options.removeOnUrlChange) {
            storage[key] = value;
          }
          var obj = {};
          obj[key] = value;
          setUrlQueryParams(obj);
        }
      },
      removeObject: function (obj) {
        var wasChanged = false;
        var removeKeys = [];
        if (_.isObject(obj)) {
          for (var key in obj) {
            if (obj.hasOwnProperty(key)) {
              if (storage[key] || this.getItem(key)) {
                storage[key] = null;
                wasChanged = true;
                removeKeys.push(key);
              }
            }
          }
        } else {
          throw new Error('[mwUrlStorage] parameter has to be an object otherwise deleteItem(key, val) should be called ');
        }

        if (wasChanged) {
          setUrlQueryParams(storage, false, removeKeys);
        }
      },
      removeItem: function (key) {
        if (storage[key]) {
          storage[key] = null;
          setUrlQueryParams(storage, false, [key]);
          return true;
        } else if (this.getItem(key)) {
          setUrlQueryParams(storage, false, [key]);
          return true;
        } else {
          return false;
        }
      },
      clear: function () {
        var removeKeys = [];
        for (var key in storage) {
          if (storage.hasOwnProperty(key) && storage[key]) {
            removeKeys.push(key);
          }
        }
        storage = {};
        setUrlQueryParams(storage, false, removeKeys);
      }
    };
  });<commit_msg>Add documentation<commit_after>angular.module('mwUI.Utils')

  .service('mwUrlStorage', function ($rootScope, $location, $route) {
    var storage = {};

    var preventRouteReload = function () {
      //Check whether reloadOnSearch is already disabled
      if ($route.current.$$route.reloadOnSearch === false) {
        return;
      }
      // Remember the state so we can set it to the original state after we have updated the route
      var prevReloadOnSearchVal = $route.current.$$route.reloadOnSearch;
      //Set reloadOnSearch false so angular does not reinitialize the controller
      $route.current.$$route.reloadOnSearch = false;

      var unbindRouteUpdateListener,
        unbindRouteChangeSuccessListener;

      //Route update is triggered when reloadOnSearch is set to true and a search param has changed
      unbindRouteUpdateListener = $rootScope.$on('$routeUpdate', function () {
        $route.current.$$route.reloadOnSearch = prevReloadOnSearchVal;
        unbindRouteUpdateListener();
        unbindRouteChangeSuccessListener();
      });

      // //Route change success is triggered when reloadOnSearch is set to false and a search param has changed
      unbindRouteChangeSuccessListener = $rootScope.$on('$routeChangeSuccess', function () {
        $route.current.$$route.reloadOnSearch = prevReloadOnSearchVal;
        unbindRouteChangeSuccessListener();
        unbindRouteUpdateListener();
      });
    };

    var hasChangedValues = function (params, removeParams) {
      var currentSearchParams = $location.search();
      var changedParams = _.difference(_.values(params), _.values(currentSearchParams));

      removeParams = _.difference(_.values(removeParams), _.values(currentSearchParams));
      return changedParams.length > 0 || removeParams.length > 0;
    };

    var setUrlQueryParams = function (params, preferQueryOverStorage, removeKeys, options) {
      options = options || {};
      if (hasChangedValues(params, removeKeys)) {
        var currentSearchParams = $location.search(),
          newSearchParams;

        if (removeKeys) {
          removeKeys.forEach(function (key) {
            currentSearchParams[key] = null;
          });
        }

        if (preferQueryOverStorage) {
          newSearchParams = _.extend(params, currentSearchParams);
        } else {
          newSearchParams = _.extend(currentSearchParams, params);
        }

        preventRouteReload();
        $location.search(newSearchParams);

        if (!options.keepInHistory) {
          $location.replace();
        }
      }
    };

    $rootScope.$on('$locationChangeSuccess', function (ev, newUrl, oldUrl) {
      if (newUrl !== oldUrl) {
        setUrlQueryParams(storage, true);
      }
    });

    return {
      /*
       * Return the value that is stored in the url for a key
       *
       * params:
       * key: string
       */
      getItem: function (key) {
        return $location.search()[key];
      },
      /*
       * Calls setItem but allows you to set a whole object instead of a key,value
       *
       * params:
       * obj: object
       * options: {
       *   removeOnUrlChange: boolean
       *   keepInHistory: boolean
       * }
       */
      setObject: function (obj, options) {
        options = options || {};
        var wasChanged = false;
        if (_.isObject(obj)) {
          for (var key in obj) {
            if (obj.hasOwnProperty(key) && storage[key] !== obj[key]) {
              if (!options.removeOnUrlChange) {
                storage[key] = obj[key];
              }
              wasChanged = true;
            }
          }
        } else {
          throw new Error('[mwUrlStorage] parameter has to be an object otherwise setItem(key, val) should be called ');
        }

        if (wasChanged) {
          setUrlQueryParams(obj, false, false, options);
        }
      },
      /*
       * Save a key value pair as query param in the url
       * The query will be in the url until you call removeItem('key') also on url change
       * You don't have to worry about the param it will just stay
       *
       * In cases where you don't wan't to keep the query param in the url forever you can set the options param
       * removeOnUrlChange to true (default is false
       *
       * The query won't be stored in the url history so when using the back button you will not go back to the previous
       * query state but to the previous url
       * In cases where you want to go to store the query in the history you can set the options param
       * `keepInHistory` to true
       *
       * params:
       * key: string
       * value: string
       * options: {
       *   removeOnUrlChange: boolean
       *   keepInHistory: boolean
       * }
       */
      setItem: function (key, value, options) {
        options = options || {};
        if (storage[key] !== value) {
          if (!options.removeOnUrlChange) {
            storage[key] = value;
          }
          var obj = {};
          obj[key] = value;
          setUrlQueryParams(obj, false, false, options);
        }
      },
      /*
       * Calls removeItem but allows you to remove a whole object instead of a key
       *
       * params:
       * obj: object
       */
      removeObject: function (obj) {
        var wasChanged = false;
        var removeKeys = [];
        if (_.isObject(obj)) {
          for (var key in obj) {
            if (obj.hasOwnProperty(key)) {
              if (storage[key] || this.getItem(key)) {
                storage[key] = null;
                wasChanged = true;
                removeKeys.push(key);
              }
            }
          }
        } else {
          throw new Error('[mwUrlStorage] parameter has to be an object otherwise deleteItem(key, val) should be called ');
        }

        if (wasChanged) {
          setUrlQueryParams(storage, false, removeKeys);
        }
      },
      /*
       * Removes a key with its value from the url
       *
       * params:
       * key: string
       */
      removeItem: function (key) {
        if (storage[key]) {
          storage[key] = null;
          setUrlQueryParams(storage, false, [key]);
          return true;
        } else if (this.getItem(key)) {
          setUrlQueryParams(storage, false, [key]);
          return true;
        } else {
          return false;
        }
      },
      /*
       * Removes all items that have been set by setItem or setObject from the url
       * Other url query params that have been set e.g. by calling $location.search() won't be affected
       */
      clear: function () {
        var removeKeys = [];
        for (var key in storage) {
          if (storage.hasOwnProperty(key) && storage[key]) {
            removeKeys.push(key);
          }
        }
        storage = {};
        setUrlQueryParams(storage, false, removeKeys);
      }
    };
  });<|endoftext|>


<commit_before>/**
 * Copyright 2015 Telefonica Investigación y Desarrollo, S.A.U
 *
 * This file is part of fiware-cosmos (FI-WARE project).
 *
 * fiware-cosmos is free software: you can redistribute it and/or modify it under the terms of the GNU Affero
 * General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 * fiware-tidoop is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Affero General Public License along with fiware-cosmos. If not, see
 * http://www.gnu.org/licenses/.
 *
 * For those usages not covered by the GNU Affero General Public License please contact with
 * francisco dot romerobueno at telefonica dot com
 */

/**
 * Http REST server for cosmos auth token generator
 *
 * Author: frb
 */

// Module dependencies
var Hapi = require('hapi');
var boom = require('boom');
var packageJson = require('../package.json');
var config = require('../conf/cosmos-auth.json');
var client = require('./client.js');
var fs = require('fs');
var logger = require('./logger.js');

// Create a Hapi server with a host and port
var server = new Hapi.Server();

server.connection({
    host: config.host,
    port: config.port,
    tls: {
        key: fs.readFileSync(config.private_key_file),
        cert: fs.readFileSync(config.certificate_file)
    }
});

// Add routes
server.route({
    method: 'GET',
    path: '/cosmos-auth/v1/version',
    handler: function (request, reply) {
        logger.info("[user --> cosmos-auth] Request: GET /tidoop/v1/version");
        var response = '{version: ' + packageJson.version + '}';
        logger.info("[user <-- cosmos-auth] Response: " + response);
        reply(response);
    } // handler
});

server.route({
    method: 'POST',
    path: '/cosmos-auth/v1/token',
    config: {
        payload: {
            parse: 'false'
        }
    },
    handler: function(request, reply) {
        logger.info('[user --> cosmos-auth] Request: POST /cosmos-auth/v1/token');
        logger.info('[user --> cosmos-auth] ' + JSON.stringify(request.headers));
        logger.info('[user --> cosmos-auth] ' + request.payload.toString());

        // Check the request parameters
        // TBD

        // POST to the IdM token-related API
        client.doAuthorizedRequest(config.idm.host, config.idm.port, config.idm.path, 'POST',
            config.cosmos_app.client_id, config.cosmos_app.client_secret, request.payload.toString(),
            function(error, result) {
                if (error) {
                    reply(boom.internal('Could not connect to the IdM', error));
                } else {
                    logger.info('[user <-- cosmos-auth] Response: ' + result);
                    reply(result);
                } // if else
            })
    } // handler
});

// Start the Hapi server
server.start(function(error) {
    if(error) {
        return logger.error('Some error occurred during the starting of the Hapi server. Details: ' + error);
    } // if

    logger.info('cosmos-auth running at http://' + config.host + ':' + config.port);
});
<commit_msg>ADD a missing log trace in the server<commit_after>/**
 * Copyright 2015 Telefonica Investigación y Desarrollo, S.A.U
 *
 * This file is part of fiware-cosmos (FI-WARE project).
 *
 * fiware-cosmos is free software: you can redistribute it and/or modify it under the terms of the GNU Affero
 * General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 * fiware-tidoop is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Affero General Public License along with fiware-cosmos. If not, see
 * http://www.gnu.org/licenses/.
 *
 * For those usages not covered by the GNU Affero General Public License please contact with
 * francisco dot romerobueno at telefonica dot com
 */

/**
 * Http REST server for cosmos auth token generator
 *
 * Author: frb
 */

// Module dependencies
var Hapi = require('hapi');
var boom = require('boom');
var packageJson = require('../package.json');
var config = require('../conf/cosmos-auth.json');
var client = require('./client.js');
var fs = require('fs');
var logger = require('./logger.js');

// Create a Hapi server with a host and port
var server = new Hapi.Server();

server.connection({
    host: config.host,
    port: config.port,
    tls: {
        key: fs.readFileSync(config.private_key_file),
        cert: fs.readFileSync(config.certificate_file)
    }
});

// Add routes
server.route({
    method: 'GET',
    path: '/cosmos-auth/v1/version',
    handler: function (request, reply) {
        logger.info("[user --> cosmos-auth] Request: GET /tidoop/v1/version");
        var response = '{version: ' + packageJson.version + '}';
        logger.info("[user <-- cosmos-auth] Response: " + response);
        reply(response);
    } // handler
});

server.route({
    method: 'POST',
    path: '/cosmos-auth/v1/token',
    config: {
        payload: {
            parse: 'false'
        }
    },
    handler: function(request, reply) {
        logger.info('[user --> cosmos-auth] Request: POST /cosmos-auth/v1/token');
        logger.info('[user --> cosmos-auth] ' + JSON.stringify(request.headers));
        logger.info('[user --> cosmos-auth] ' + request.payload.toString());

        // Check the request parameters
        // TBD

        // POST to the IdM token-related API
        client.doAuthorizedRequest(config.idm.host, config.idm.port, config.idm.path, 'POST',
            config.cosmos_app.client_id, config.cosmos_app.client_secret, request.payload.toString(),
            function(error, result) {
                if (error) {
                    logger.error('Could not connect to the IdM', error);
                    reply(boom.internal('Could not connect to the IdM', error));
                } else {
                    logger.info('[user <-- cosmos-auth] Response: ' + result);
                    reply(result);
                } // if else
            })
    } // handler
});

// Start the Hapi server
server.start(function(error) {
    if(error) {
        return logger.error('Some error occurred during the starting of the Hapi server. Details: ' + error);
    } // if

    logger.info('cosmos-auth running at http://' + config.host + ':' + config.port);
});
<|endoftext|>


<commit_before>/*!
 * Ext JS Connect 0.0.1
 * Copyright(c) 2010 Ext JS, Inc.
 * MIT Licensed
 */

var child_process = require('child_process'),
    sys = require('sys');

module.exports = {

    handle: function (err, req, res) {
        var writeHead = res.writeHead,
            write = res.write,
            end = res.end;

        res.writeHead = function (code, headers) {
            var type = headers["Content-Type"],
                accept = req.headers["accept-encoding"];

            if (!(code === 200 && accept && accept.indexOf('gzip') >= 0
                  && type && (/(text|javascript)/).test(type))) {
                res.writeHead = writeHead;
                res.writeHead(code, headers);
                return;
            }

            headers["Content-Encoding"] = "gzip";
            delete headers["Content-Length"];

            var gzip = child_process.spawn("gzip", ["-9"]);

            res.write = function (chunk, encoding) {
                gzip.stdin.write(chunk, encoding);
            };

            res.end = function (chunk, encoding) {
                if (chunk) {
                    res.write(chunk, encoding);
                }
                gzip.stdin.end();
            };

            gzip.stdout.addListener('data', function (chunk) {
                write.call(res, chunk);
            });

            gzip.addListener("exit", function (code) {
                res.write = write;
                res.end = end;
                res.end();
            });

            res.writeHead = writeHead;
            res.writeHead(code, headers);

        };

        next();
    }
};<commit_msg>Add a missing next parameter.<commit_after>/*!
 * Ext JS Connect 0.0.1
 * Copyright(c) 2010 Ext JS, Inc.
 * MIT Licensed
 */

var child_process = require('child_process'),
    sys = require('sys');

module.exports = {

    handle: function (err, req, res, next) {
        var writeHead = res.writeHead,
            write = res.write,
            end = res.end;

        res.writeHead = function (code, headers) {
            var type = headers["Content-Type"],
                accept = req.headers["accept-encoding"];

            if (!(code === 200 && accept && accept.indexOf('gzip') >= 0
                  && type && (/(text|javascript)/).test(type))) {
                res.writeHead = writeHead;
                res.writeHead(code, headers);
                return;
            }

            headers["Content-Encoding"] = "gzip";
            delete headers["Content-Length"];

            var gzip = child_process.spawn("gzip", ["-9"]);

            res.write = function (chunk, encoding) {
                gzip.stdin.write(chunk, encoding);
            };

            res.end = function (chunk, encoding) {
                if (chunk) {
                    res.write(chunk, encoding);
                }
                gzip.stdin.end();
            };

            gzip.stdout.addListener('data', function (chunk) {
                write.call(res, chunk);
            });

            gzip.addListener("exit", function (code) {
                res.write = write;
                res.end = end;
                res.end();
            });

            res.writeHead = writeHead;
            res.writeHead(code, headers);

        };

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


<commit_before>/* eslint class-methods-use-this: 0 */
// @flow
import React from 'react';
import Dropdown, { DropdownTrigger, DropdownContent } from 'react-simple-dropdown';

import {
  executeCell,
  removeCell,
  toggleStickyCell,
  clearOutputs,
  changeOutputVisibility,
  changeInputVisibility,
  changeCellType,
  toggleOutputExpansion,
} from '../../actions';

type Props = {
  cell: any,
  id: string,
  type: string,
}

export default class Toolbar extends React.PureComponent {
  removeCell: () => void;
  executeCell: () => void;
  clearOutputs: () => void;
  toggleStickyCell: () => void;
  changeInputVisibility: () => void;
  changeOutputVisibility: () => void;
  changeCellType: () => void;
  dropdown: Dropdown;
  toggleOutputExpansion: () => void;

  static contextTypes = {
    store: React.PropTypes.object,
  };

  constructor(props: Props): void {
    super(props);
    this.removeCell = this.removeCell.bind(this);
    this.executeCell = this.executeCell.bind(this);
    this.clearOutputs = this.clearOutputs.bind(this);
    this.toggleStickyCell = this.toggleStickyCell.bind(this);
    this.changeInputVisibility = this.changeInputVisibility.bind(this);
    this.changeOutputVisibility = this.changeOutputVisibility.bind(this);
    this.changeCellType = this.changeCellType.bind(this);
    this.toggleOutputExpansion = this.toggleOutputExpansion.bind(this);
  }

  toggleStickyCell(): void {
    this.context.store.dispatch(toggleStickyCell(this.props.id));
  }

  removeCell(): void {
    this.context.store.dispatch(removeCell(this.props.id));
  }

  executeCell(): void {
    this.context.store.dispatch(executeCell(
                                      this.props.id,
                                      this.props.cell.get('source')));
  }

  clearOutputs(): void {
    this.dropdown.hide();
    this.context.store.dispatch(clearOutputs(this.props.id));
  }

  changeInputVisibility(): void {
    this.dropdown.hide();
    this.context.store.dispatch(changeInputVisibility(this.props.id));
  }

  changeOutputVisibility(): void {
    this.dropdown.hide();
    this.context.store.dispatch(changeOutputVisibility(this.props.id));
  }

  changeCellType(): void {
    this.dropdown.hide();
    const to = this.props.type === 'markdown' ? 'code' : 'markdown';
    this.context.store.dispatch(changeCellType(this.props.id, to));
  }

  toggleOutputExpansion(): void {
    this.context.store.dispatch(toggleOutputExpansion(this.props.id));
  }

  render(): ?React.Element<any> {
    const showPlay = this.props.type !== 'markdown';
    return (

          {showPlay &&

            <button onClick={this.executeCell} className="executeButton" >

            </button>
          }
          <button onClick={this.toggleStickyCell} className="stickyButton" >

          </button>
          <button onClick={this.removeCell} className="deleteButton" >

          </button>
          <Dropdown ref={(dropdown) => { this.dropdown = dropdown; }}>
            <DropdownTrigger>
              <button>

              </button>
            </DropdownTrigger>
            <DropdownContent>
              {
              (this.props.type === 'code') ?

                    Clear Cell Output

                    Toggle Input Visibility

                    Toggle Output Visibility

                    Toggle Expanded Output

 : null
              }

                    Convert to {this.props.type === 'markdown' ? 'Code' : 'Markdown'} Cell

            </DropdownContent>
          </Dropdown>

    );
  }
}
<commit_msg>feature(toolbar): Add text complementing toolbar<commit_after>/* eslint class-methods-use-this: 0 */
// @flow
import React from 'react';
import Dropdown, { DropdownTrigger, DropdownContent } from 'react-simple-dropdown';

import {
  executeCell,
  removeCell,
  toggleStickyCell,
  clearOutputs,
  changeOutputVisibility,
  changeInputVisibility,
  changeCellType,
  toggleOutputExpansion,
} from '../../actions';

type Props = {
  cell: any,
  id: string,
  type: string,
}

export default class Toolbar extends React.PureComponent {
  removeCell: () => void;
  executeCell: () => void;
  clearOutputs: () => void;
  toggleStickyCell: () => void;
  changeInputVisibility: () => void;
  changeOutputVisibility: () => void;
  changeCellType: () => void;
  dropdown: Dropdown;
  toggleOutputExpansion: () => void;

  static contextTypes = {
    store: React.PropTypes.object,
  };

  constructor(props: Props): void {
    super(props);
    this.removeCell = this.removeCell.bind(this);
    this.executeCell = this.executeCell.bind(this);
    this.clearOutputs = this.clearOutputs.bind(this);
    this.toggleStickyCell = this.toggleStickyCell.bind(this);
    this.changeInputVisibility = this.changeInputVisibility.bind(this);
    this.changeOutputVisibility = this.changeOutputVisibility.bind(this);
    this.changeCellType = this.changeCellType.bind(this);
    this.toggleOutputExpansion = this.toggleOutputExpansion.bind(this);
  }

  toggleStickyCell(): void {
    this.context.store.dispatch(toggleStickyCell(this.props.id));
  }

  removeCell(): void {
    this.context.store.dispatch(removeCell(this.props.id));
  }

  executeCell(): void {
    this.context.store.dispatch(executeCell(
                                      this.props.id,
                                      this.props.cell.get('source')));
  }

  clearOutputs(): void {
    this.dropdown.hide();
    this.context.store.dispatch(clearOutputs(this.props.id));
  }

  changeInputVisibility(): void {
    this.dropdown.hide();
    this.context.store.dispatch(changeInputVisibility(this.props.id));
  }

  changeOutputVisibility(): void {
    this.dropdown.hide();
    this.context.store.dispatch(changeOutputVisibility(this.props.id));
  }

  changeCellType(): void {
    this.dropdown.hide();
    const to = this.props.type === 'markdown' ? 'code' : 'markdown';
    this.context.store.dispatch(changeCellType(this.props.id, to));
  }

  toggleOutputExpansion(): void {
    this.context.store.dispatch(toggleOutputExpansion(this.props.id));
  }

  render(): ?React.Element<any> {
    const showPlay = this.props.type !== 'markdown';
    return (

          {showPlay &&

            <button
              onClick={this.executeCell}
              title= "execute cell"
              className="executeButton"
            >

            </button>
          }
          <button
            onClick={this.toggleStickyCell}
            title="pin cell"
            className="stickyButton"
          >

          </button>
          <button
            onClick={this.removeCell}
            title="delete cell"
            className="deleteButton"
          >

          </button>
          <Dropdown ref={(dropdown) => { this.dropdown = dropdown; }}>
            <DropdownTrigger>
              <button title="show additional actions">

              </button>
            </DropdownTrigger>
            <DropdownContent>
              {
              (this.props.type === 'code') ?

                    Clear Cell Output

                    Toggle Input Visibility

                    Toggle Output Visibility

                    Toggle Expanded Output

 : null
              }

                    Convert to {this.props.type === 'markdown' ? 'Code' : 'Markdown'} Cell

            </DropdownContent>
          </Dropdown>

    );
  }
}
<|endoftext|>


<commit_before>
    $(document).ready(function()
    {
        var fetch = false;
        var autoscroll = true;
        var load = undefined;

        var req = undefined;
        var time = undefined;

        function addLogLevelClass(row) {
            row.addClass("error");
            return row;
        }

        $("#autoscroll").click(function() {
            if(autoscroll == false) {
                $(this).removeClass("success");
                $(this).text("Autoscroll (On)");
                autoscroll = true;
            }
            else {
                $(this).addClass("success");
                $(this).text("Autoscroll (Off)");
                autoscroll = false;
            }
        });

        $("#clear").click(function() {
            $(".log").remove();
        });

        $("#fetch").click(function() {
            if(fetch == false) {
                $(this).removeClass("success");
                $(this).text("Fetch (On)");
                $("#clear").click();
                fetch = true;
                load = 0;
                reconnect(0);
            }
            else {
                $(this).addClass("success");
                $(this).text("Fetch (Off)");
                fetch = false;
                disconnect();
            }
        });

        function reconnect(delay) {
            disconnect();
            if(!fetch)
                return;
            time = setTimeout(function() {
                    disconnect();
                    req = new XMLHttpRequest ();
                    req.onreadystatechange = stateChangeHandler;
                    req.open("GET", "/log/stream", true);
                    req.send();
            }, delay);
        }

        function disconnect() {
            if (time)
                clearTimeout(time);
            if (req) {
                req.onreadystatechange = undefined;
                req.abort();
            }
            time = undefined;
            req = undefined;
        }

        function stateChangeHandler() {
            var log = $("#list");
            var buffer = "";

            if(req.status != 200) {
                reconnect(1000);
                return false;
            }
            if(req.readyState == 1) {
                load = 0;
                return;
            }
            if(req.readyState == 4) {
                reconnect(1000);
                return false;
            }

            if(req.responseText.length <= load)
                return;
            var lastLog = req.responseText.slice(load);
            lastLog = lastLog.replace(/\r/g, '');
            lastLog = lastLog.replace(/ /g, " ");
            lastLog = lastLog.replace(/</g, "<");
            lastLog = lastLog.replace(/>/g, ">");
            load = req.responseText.length;

            buffer += lastLog;
            if(buffer.lastIndexOf("\n") >= 0) {
                var lines = buffer.split("\n");
                if (buffer[buffer.length - 1] == "\n") {
                    buffer = "";
                }
                else {
                    buffer = lines.pop();
                }
                $.each(lines, function(index, line) {
                    var row = $("
" + line + "
");
                    row = addLogLevelClass(row);
                    log.append(row);
                });
            }

            if(autoscroll) {
                var logs = document.getElementById("logs");
                logs.scrollTop = logs.scrollHeight;
            }
        }

        setTimeout(function() {
            $("#fetch").click();
        }, 500);
    });

<commit_msg>Fixed log.<commit_after>
    $(document).ready(function()
    {
        var fetch = false;
        var autoscroll = true;
        var load = undefined;

        var req = undefined;
        var time = undefined;

        function addLogLevelClass(row) {
            row.addClass("error");
            return row;
        }

        $("#autoscroll").click(function() {
            if(autoscroll == false) {
                $(this).removeClass("success");
                $(this).text("Autoscroll (On)");
                autoscroll = true;
            }
            else {
                $(this).addClass("success");
                $(this).text("Autoscroll (Off)");
                autoscroll = false;
            }
        });

        $("#clear").click(function() {
            $(".log").remove();
        });

        $("#fetch").click(function() {
            if(fetch == false) {
                $(this).removeClass("success");
                $(this).text("Fetch (On)");
                $("#clear").click();
                fetch = true;
                load = 0;
                reconnect(0);
            }
            else {
                $(this).addClass("success");
                $(this).text("Fetch (Off)");
                fetch = false;
                disconnect();
            }
        });

        function reconnect(delay) {
            disconnect();
            if(!fetch)
                return;
            time = setTimeout(function() {
                    disconnect();
                    req = new XMLHttpRequest ();
                    req.onreadystatechange = stateChangeHandler;
                    req.open("GET", "/log/stream", true);
                    req.send();
            }, delay);
        }

        function disconnect() {
            if (time)
                clearTimeout(time);
            if (req) {
                req.onreadystatechange = undefined;
                req.abort();
            }
            time = undefined;
            req = undefined;
        }

        function stateChangeHandler() {
            var log = $("#list");
            var buffer = "";

            if(req.status != 200) {
                reconnect(1000);
                return false;
            }
            if(req.readyState == 1) {
                load = 0;
                return;
            }
            if(req.readyState == 4) {
                reconnect(1000);
                return false;
            }

            if(req.responseText.length <= load)
                return;
            var lastLog = req.responseText.slice(load);
            lastLog = lastLog.replace(/\r/g, '');
            lastLog = lastLog.replace(/ /g, " ");
            lastLog = lastLog.replace(/</g, "<");
            lastLog = lastLog.replace(/>/g, ">");
            load = req.responseText.length;

            buffer += lastLog;
            if(buffer.lastIndexOf("\n") >= 0) {
                var lines = buffer.split("\n");
                if (buffer[buffer.length - 1] == "\n") {
                    buffer = "";
                    lines.pop();
                }
                else {
                    buffer = lines.pop();
                }
                $.each(lines, function(index, line) {
                    var row = $("
" + line + "
");
                    row = addLogLevelClass(row);
                    log.append(row);
                });
            }

            if(autoscroll) {
                var logs = document.getElementById("logs");
                logs.scrollTop = logs.scrollHeight;
            }
        }

        setTimeout(function() {
            $("#fetch").click();
        }, 500);
    });

<|endoftext|>


<commit_before>/*jshint bitwise:false*/
(function( window, document, undefined ) {
  'use strict';

  var PI2 = 2 * Math.PI;

  function lerp( a, b, t ) {
    return a + t * ( b - a );
  }

  function BezierCurve( x0, y0, x1, y1, cpx0, cpy0, cpx1, cpy1 ) {
    this.x0 = x0 || 0;
    this.y0 = y0 || 0;

    this.x1 = x1 || 0;
    this.y1 = y1 || 0;

    this.cpx0 = cpx0 || 0;
    this.cpy0 = cpy0 || 0;

    this.cpx1 = cpx1 || 0;
    this.cpy1 = cpy1 || 0;
  }

  BezierCurve.prototype.draw = function( ctx ) {
    ctx.moveTo( this.x0, this.y0 );
    ctx.bezierCurveTo(
      this.cpx0, this.cpy0,
      this.cpx1, this.cpy1,
      this.x1, this.y1
    );
  };

  BezierCurve.prototype.createControlPoints = function() {
    return [
      new Endpoint( this, 0 ),
      new ControlPoint( this, 0 ),
      new ControlPoint( this, 1 ),
      new Endpoint( this, 1 )
    ];
  };

  function CurvePoint( curve, index ) {
    this.curve = curve || null;
    this.index = index || 0;
    this.prefix = '';
  }

  CurvePoint.prototype.contains = function( x, y ) {
    var dx = x - this.x,
        dy = y - this.y;

    var distanceSquared = dx * dx + dy * dy;

    var radius = 8;
    var radiusSquared = radius * radius;

    if ( distanceSquared < radiusSquared ) {
      return true;
    }

    return false;
  };

  /**
   * Define getters/setters for the point properties (x, y, nx, ny).
   * (nx, ny) are the coordinates of the other curve point.
   */
  [ 'x', 'y' ].forEach(function( axis ) {
    Object.defineProperty( CurvePoint.prototype, axis, {
      get: function() {
        return this.curve[ this.prefix + axis + this.index ];
      },

      set: function( number ) {
        this.curve[ this.prefix + axis + this.index ] = number;
        return number;
      }
    });

    // Get/set the coordinates of the other curve point.
    Object.defineProperty( CurvePoint.prototype, 'n' + axis, {
      get: function() {
        return this.curve[ this.prefix + axis + this.index ^ 1 ];
      },

      set: function( number ) {
        this.curve[ this.prefix + axis + this.index ^ 1 ] = number;
        return number;
      }
    });
  });

  function Endpoint( curve, index ) {
    CurvePoint.call( this, curve, index );
  }

  Endpoint.prototype = Object.create( CurvePoint.prototype );
  Endpoint.prototype.constructor = Endpoint;

  Endpoint.prototype.draw = function( ctx ) {
    ctx.arc( this.x, this.y, 8, 0, PI2 );
  };

  function ControlPoint( curve, index ) {
    CurvePoint.call( this, curve, index );
    this.prefix = 'cp';
  }

  ControlPoint.prototype = Object.create( CurvePoint.prototype );
  ControlPoint.prototype.constructor = ControlPoint;

  ControlPoint.prototype.draw = function( ctx ) {
    ctx.rect( this.x - 4, this.y - 4, 8, 8 );
  };

  function cubicBezier( curve, x1, y1, x2, y2 ) {
    curve.cpx0 = lerp( curve.x0, curve.x1, x1 );
    curve.cpy0 = lerp( curve.y0, curve.y1, y1 );

    curve.cpx1 = lerp( curve.x0, curve.x1, x2 );
    curve.cpy1 = lerp( curve.y0, curve.y1, y2 );

    return curve;
  }

  function BezierPath() {
    this.curves = [];
  }

  BezierPath.prototype.draw = function( ctx ) {
    if ( !this.curves.length ) {
      return;
    }

    var curve = this.curves[0];
    ctx.moveTo( curve.x0, curve.y0 );
    for ( var i = 0, il = this.curves.length; i < il; i++ ) {
      curve = this.curves[i];
      ctx.bezierCurveTo(
        curve.cpx0, curve.cpy0,
        curve.cpx1, curve.cpy1,
        curve.x1, curve.y1
      );
    }
  };

  BezierPath.prototype.createControlPoints = function() {
    return this.curves.reduce(function( array, curve ) {
      return array.concat( curve.createControlPoints() );
    }, [] );
  };

  var canvas  = document.querySelector( 'canvas' ),
      context = canvas.getContext( '2d' );

  var curve;
  var cp0, cp1;

  var path;
  var controlPoints;

  var mouse = {
    x: 0,
    y: 0,

    down: false
  };

  var selection = [];
  var offsets = [];

  function init() {
    var width  = window.innerWidth,
        height = window.innerHeight;

    canvas.width  = width;
    canvas.height = height;

    curve = new BezierCurve(
      0.2 * width, 0.2 * height,
      0.8 * width, 0.8 * height
    );

    cubicBezier( curve, 0.25, 0.1, 0.25, 1 );
    cp0 = new ControlPoint( curve, 0 );
    cp1 = new ControlPoint( curve, 1 );

    path = new BezierPath();
    path.curves.push( new BezierCurve( 10, 20, 80, 90, 30, 50, 70, 80 ) );
    path.curves.push( new BezierCurve( 80, 90, 80, 300, 90, 120, 110, 80 ) );

    controlPoints = path.createControlPoints();
  }

  function draw( ctx ) {
    ctx.clearRect( 0, 0, ctx.canvas.width, ctx.canvas.height );

    ctx.strokeStyle = '#fff';

    // Draw curve.
    ctx.beginPath();
    curve.draw( ctx );
    ctx.lineCap = 'round';
    ctx.lineWidth = 5;
    ctx.stroke();

    // Draw path.
    ctx.beginPath();
    path.draw( ctx );
    ctx.stroke();

    // Draw control points.
    ctx.beginPath();
    cp0.draw( ctx );
    cp1.draw( ctx );
    ctx.lineWidth = 3;
    ctx.stroke();

    // Draw path control points.
    ctx.lineWidth = 2;
    controlPoints.forEach(function( controlPoint ) {
      ctx.beginPath();
      controlPoint.draw( ctx );

      // Connect to bezier curve endpoints.
      var index = controlPoint.index;
      ctx.moveTo( controlPoint.x, controlPoint.y );
      ctx.lineTo(
        controlPoint.curve[ 'x' + index ],
        controlPoint.curve[ 'y' + index ]
      );

      ctx.stroke();
    });

    // Draw line segments from endpoints to control points.
    ctx.beginPath();
    ctx.moveTo( curve.x0, curve.y0 );
    ctx.lineTo( curve.cpx0, curve.cpy0 );
    ctx.moveTo( curve.x1, curve.y1 );
    ctx.lineTo( curve.cpx1, curve.cpy1 );
    ctx.stroke();
  }

  function mousePosition( event ) {
    mouse.x = event.pageX - canvas.offsetLeft;
    mouse.y = event.pageY - canvas.offsetTop;
  }

  function onMouseDown( event ) {
    mousePosition( event );
    mouse.down = true;

    selection = controlPoints.filter(function( controlPoint ) {
      return controlPoint.contains( mouse.x, mouse.y );
    });

    offsets = selection.map(function( controlPoint ) {
      return {
        x: controlPoint.x - mouse.x,
        y: controlPoint.y - mouse.y
      };
    });
  }

  function onMouseMove( event ) {
    mousePosition( event );
    if ( !mouse.down ) {
      return;
    }

    selection.forEach(function( element, index ) {
      var offset = offsets[ index ];
      element.x = mouse.x + offset.x;
      element.y = mouse.y + offset.y;
    });

    draw( context );
  }

  function onMouseUp() {
    mouse.down = false;
  }

  init();
  draw( context );

  document.addEventListener( 'mousedown', onMouseDown );
  document.addEventListener( 'mousemove', onMouseMove );
  document.addEventListener( 'mouseup', onMouseUp );

}) ( window, document );
<commit_msg>bezier-curve: Position ControlPoints relative to Endpoint.<commit_after>/*jshint bitwise:false*/
/*globals requestAnimationFrame*/
(function( window, document, undefined ) {
  'use strict';

  var PI2 = 2 * Math.PI;

  function lerp( a, b, t ) {
    return a + t * ( b - a );
  }

  function BezierCurve( x0, y0, x1, y1, cpx0, cpy0, cpx1, cpy1 ) {
    this.x0 = x0 || 0;
    this.y0 = y0 || 0;

    this.x1 = x1 || 0;
    this.y1 = y1 || 0;

    this.cpx0 = cpx0 || 0;
    this.cpy0 = cpy0 || 0;

    this.cpx1 = cpx1 || 0;
    this.cpy1 = cpy1 || 0;
  }

  BezierCurve.prototype.draw = function( ctx ) {
    ctx.moveTo( this.x0, this.y0 );
    ctx.bezierCurveTo(
      this.cpx0, this.cpy0,
      this.cpx1, this.cpy1,
      this.x1, this.y1
    );
  };

  BezierCurve.prototype.createControlPoints = function() {
    return [
      new Endpoint( this, 0 ),
      new ControlPoint( this, 0 ),
      new ControlPoint( this, 1 ),
      new Endpoint( this, 1 )
    ];
  };

  function CurvePoint( curve, index ) {
    this.curve = curve || null;
    this.index = index || 0;
    this.prefix = '';
  }

  CurvePoint.prototype.contains = function( x, y ) {
    var dx = x - this.x,
        dy = y - this.y;

    var distanceSquared = dx * dx + dy * dy;

    var radius = 8;
    var radiusSquared = radius * radius;

    if ( distanceSquared < radiusSquared ) {
      return true;
    }

    return false;
  };

  /**
   * Define getters/setters for the point properties (x, y, nx, ny).
   * (nx, ny) are the coordinates of the other curve point.
   */
  [ 'x', 'y' ].forEach(function( axis ) {
    Object.defineProperty( CurvePoint.prototype, axis, {
      get: function() {
        return this.curve[ this.prefix + axis + this.index ];
      },

      set: function( number ) {
        var prop = this.prefix + axis + this.index;
        Object.getNotifier( this ).notify({
          type: 'update',
          name: axis,
          oldValue: this.curve[ prop ]
        });

        this.curve[ this.prefix + axis + this.index ] = number;
        return number;
      }
    });

    // Get/set the coordinates of the other curve point.
    Object.defineProperty( CurvePoint.prototype, 'n' + axis, {
      get: function() {
        return this.curve[ this.prefix + axis + this.index ^ 1 ];
      },

      set: function( number ) {
        this.curve[ this.prefix + axis + this.index ^ 1 ] = number;
        return number;
      }
    });
  });

  function Endpoint( curve, index ) {
    CurvePoint.call( this, curve, index );
  }

  Endpoint.prototype = Object.create( CurvePoint.prototype );
  Endpoint.prototype.constructor = Endpoint;

  Endpoint.prototype.draw = function( ctx ) {
    ctx.arc( this.x, this.y, 8, 0, PI2 );
  };

  function ControlPoint( curve, index ) {
    CurvePoint.call( this, curve, index );
    this.prefix = 'cp';

    Object.observe( curve, function( changes ) {
      changes.forEach(function( change ) {
        var value;
        if ( change.name === 'x' + this.index ) {
          value = change.object[ change.name ];
          this.x += value - change.oldValue;
        }

        if ( change.name === 'y' + this.index ) {
          value = change.object[ change.name ];
          this.y += value - change.oldValue;
        }

      }, this );
    }.bind( this ));
  }

  ControlPoint.prototype = Object.create( CurvePoint.prototype );
  ControlPoint.prototype.constructor = ControlPoint;

  ControlPoint.prototype.draw = function( ctx ) {
    ctx.rect( this.x - 4, this.y - 4, 8, 8 );
  };

  function cubicBezier( curve, x1, y1, x2, y2 ) {
    curve.cpx0 = lerp( curve.x0, curve.x1, x1 );
    curve.cpy0 = lerp( curve.y0, curve.y1, y1 );

    curve.cpx1 = lerp( curve.x0, curve.x1, x2 );
    curve.cpy1 = lerp( curve.y0, curve.y1, y2 );

    return curve;
  }

  function BezierPath() {
    this.curves = [];
  }

  BezierPath.prototype.draw = function( ctx ) {
    if ( !this.curves.length ) {
      return;
    }

    var curve = this.curves[0];
    ctx.moveTo( curve.x0, curve.y0 );
    for ( var i = 0, il = this.curves.length; i < il; i++ ) {
      curve = this.curves[i];
      ctx.bezierCurveTo(
        curve.cpx0, curve.cpy0,
        curve.cpx1, curve.cpy1,
        curve.x1, curve.y1
      );
    }
  };

  BezierPath.prototype.createControlPoints = function() {
    return this.curves.reduce(function( array, curve ) {
      return array.concat( curve.createControlPoints() );
    }, [] );
  };

  var canvas  = document.querySelector( 'canvas' ),
      context = canvas.getContext( '2d' );

  var curve;
  var cp0, cp1;

  var path;
  var controlPoints;

  var mouse = {
    x: 0,
    y: 0,

    down: false
  };

  var selection = [];
  var offsets = [];

  function init() {
    var width  = window.innerWidth,
        height = window.innerHeight;

    canvas.width  = width;
    canvas.height = height;

    curve = new BezierCurve(
      0.2 * width, 0.2 * height,
      0.8 * width, 0.8 * height
    );

    cubicBezier( curve, 0.25, 0.1, 0.25, 1 );
    cp0 = new ControlPoint( curve, 0 );
    cp1 = new ControlPoint( curve, 1 );

    path = new BezierPath();
    path.curves.push( new BezierCurve( 10, 20, 80, 90, 30, 50, 70, 80 ) );
    path.curves.push( new BezierCurve( 80, 90, 80, 300, 90, 120, 110, 80 ) );

    controlPoints = path.createControlPoints();
  }

  function draw( ctx ) {
    ctx.clearRect( 0, 0, ctx.canvas.width, ctx.canvas.height );

    ctx.strokeStyle = '#fff';

    // Draw curve.
    ctx.beginPath();
    curve.draw( ctx );
    ctx.lineCap = 'round';
    ctx.lineWidth = 5;
    ctx.stroke();

    // Draw path.
    ctx.beginPath();
    path.draw( ctx );
    ctx.stroke();

    // Draw control points.
    ctx.beginPath();
    cp0.draw( ctx );
    cp1.draw( ctx );
    ctx.lineWidth = 3;
    ctx.stroke();

    // Draw path control points.
    ctx.lineWidth = 2;
    controlPoints.forEach(function( controlPoint ) {
      ctx.beginPath();
      controlPoint.draw( ctx );

      // Connect to bezier curve endpoints.
      var index = controlPoint.index;
      ctx.moveTo( controlPoint.x, controlPoint.y );
      ctx.lineTo(
        controlPoint.curve[ 'x' + index ],
        controlPoint.curve[ 'y' + index ]
      );

      ctx.stroke();
    });

    // Draw line segments from endpoints to control points.
    ctx.beginPath();
    ctx.moveTo( curve.x0, curve.y0 );
    ctx.lineTo( curve.cpx0, curve.cpy0 );
    ctx.moveTo( curve.x1, curve.y1 );
    ctx.lineTo( curve.cpx1, curve.cpy1 );
    ctx.stroke();
  }

  function mousePosition( event ) {
    mouse.x = event.pageX - canvas.offsetLeft;
    mouse.y = event.pageY - canvas.offsetTop;
  }

  function onMouseDown( event ) {
    mousePosition( event );
    mouse.down = true;

    selection = controlPoints.filter(function( controlPoint ) {
      return controlPoint.contains( mouse.x, mouse.y );
    });

    offsets = selection.map(function( controlPoint ) {
      return {
        x: controlPoint.x - mouse.x,
        y: controlPoint.y - mouse.y
      };
    });
  }

  function onMouseMove( event ) {
    mousePosition( event );
    if ( !mouse.down ) {
      return;
    }

    selection.forEach(function( element, index ) {
      var offset = offsets[ index ];
      element.x = mouse.x + offset.x;
      element.y = mouse.y + offset.y;
    });

    // Wait for Object.observe changes to propagate.
    requestAnimationFrame( draw );
  }

  function onMouseUp() {
    mouse.down = false;
  }

  init();
  draw = draw.bind( this, context );
  draw();

  document.addEventListener( 'mousedown', onMouseDown );
  document.addEventListener( 'mousemove', onMouseMove );
  document.addEventListener( 'mouseup', onMouseUp );

}) ( window, document );
<|endoftext|>