<commit_before>
var assert = require('assert');
var should = require('should');
var fs = require('fs');
var moment = require('moment');

var RefugeeCountsModel = require('../src/js/model/refugee-counts-model.js');

// for some reason the "total values"
// http://popstats.unhcr.org/en/asylum_seekers_monthly
// displays invalid sums
//
// also the csv export has some more values than the
// web view
//

var lastDayStamp = function(year, month) {
	return moment([year, month]).endOf('month').unix();
};

var firstDayStamp = function(year, month) {
	return moment([year, month]).startOf('month').unix();
};

describe('RefugeeCountsModel', function() {

	var startStamp = firstDayStamp(2012, 0);
	var data = JSON.parse(fs.readFileSync('temp/data-assets/asylum.json'));
	var model = new RefugeeCountsModel(data);

	describe('asylumData', function() {
		it('correct total for germany during jan 2015', function() {
			assert.equal(
				data.filter(function(item) {
					return item.ac == "DEU" && item.year==2012 && item.month==1
				}).reduce(function(prev, val) {
					return prev + val.count;
				}, 0
			), 4667);
		});
	});

	describe('pairCountsByDestination', function() {
		it('correct total for AFG -> DEU @ jan 2015', function() {
			assert.equal(model.pairCountsByDestination['DEU']['AFG'][3][0].count, 1129);
		});
	});

	describe('getTotalDestinationCounts', function() {
		it('correct total for germany for jan 2012', function() {
		 	assert.equal(model.getTotalDestinationCounts(
		 		'DEU', [startStamp, moment([2012, 0, 31]).unix()]).asylumApplications, 4667);
		});
		it('correct total for germany by end of 2014', function() {
			assert.equal(model.getTotalDestinationCounts('DEU', [startStamp, moment([2014, 11, 31]).unix()])
				.asylumApplications, 346633);
		});
		it('correct total for finland by end of 2014', function() {
			assert.equal(model.getTotalDestinationCounts('FIN', [startStamp, moment([2014, 11, 31]).unix()])
				.asylumApplications, 8894);
		});
		it('correct total for finland for 2013', function() {
			assert.equal(model.getTotalDestinationCounts('FIN',
				[firstDayStamp(2013, 0), lastDayStamp(2013, 11)])
				.asylumApplications, 3022);
		});
	});

	describe('getGlobalArrivingFor', function() {
		it('correct total for Jan 2013 (only European destinations)', function() {
			assert.equal(model.getGlobalArrivingFor(moment([2013, 0])).asylumApplications, 31092);
		});
		it('correct total for August 2015 (only European destinations)', function() {
			assert.equal(model.getGlobalArrivingFor(moment([2015, 7])).asylumApplications, 181022);
		});
	});

	describe('getOriginCountsByDestinationCountries()', function() {
		it('correct total for SYR->GER after one months end', function() {
			assert.equal(model.getOriginCountsByDestinationCountries('SYR',
				[startStamp, lastDayStamp(2012, 0)])['DEU'].asylumApplications, 210);
		});
		it('correct total for SYR->GER after two months end', function() {
			assert.equal(model.getOriginCountsByDestinationCountries('SYR',
				[startStamp, lastDayStamp(2012, 1)])['DEU'].asylumApplications, 440);
		});
		it('correct total for SYR->GER during 2013', function() {
			assert.equal(model.getOriginCountsByDestinationCountries('SYR',
				[firstDayStamp(2013, 0), lastDayStamp(2013, 11)])['DEU'].asylumApplications, 11851);
		});
	});

	describe('getDestinationCountsByOriginCountries()', function() {
		it('correct total for SYR->GER after one months end', function() {
			assert.equal(model.getDestinationCountsByOriginCountries('DEU',
				[startStamp, lastDayStamp(2012, 0)])['SYR'].asylumApplications, 210);
		});
		it('correct total for SYR->GER after two months end', function() {
			assert.equal(model.getDestinationCountsByOriginCountries('DEU',
				[startStamp, lastDayStamp(2012, 1)])['SYR'].asylumApplications, 440);
		});
		it('correct total for SYR->GER during 2013', function() {
			assert.equal(model.getDestinationCountsByOriginCountries('DEU',
				[firstDayStamp(2013, 0), lastDayStamp(2013, 11)])['SYR'].asylumApplications, 11851);
		});
	});

});
<commit_msg>Add tests for RefugeeCountsModel.getGlobalArrivingPerMonthForCountry()<commit_after>
var assert = require('assert');
var should = require('should');
var fs = require('fs');
var moment = require('moment');

var RefugeeCountsModel = require('../src/js/model/refugee-counts-model.js');

// for some reason the "total values"
// http://popstats.unhcr.org/en/asylum_seekers_monthly
// displays invalid sums
//
// also the csv export has some more values than the
// web view
//

var lastDayStamp = function(year, month) {
	return moment([year, month]).endOf('month').unix();
};

var firstDayStamp = function(year, month) {
	return moment([year, month]).startOf('month').unix();
};

describe('RefugeeCountsModel', function() {

	var startStamp = firstDayStamp(2012, 0);
	var data = JSON.parse(fs.readFileSync('temp/data-assets/asylum.json'));
	var model = new RefugeeCountsModel(data);

	describe('asylumData', function() {
		it('correct total for germany during jan 2015', function() {
			assert.equal(
				data.filter(function(item) {
					return item.ac == "DEU" && item.year==2012 && item.month==1
				}).reduce(function(prev, val) {
					return prev + val.count;
				}, 0
			), 4667);
		});
	});

	describe('pairCountsByDestination', function() {
		it('correct total for AFG -> DEU @ jan 2015', function() {
			assert.equal(model.pairCountsByDestination['DEU']['AFG'][3][0].count, 1129);
		});
	});

	describe('getTotalDestinationCounts', function() {
		it('correct total for germany for jan 2012', function() {
		 	assert.equal(model.getTotalDestinationCounts(
		 		'DEU', [startStamp, moment([2012, 0, 31]).unix()]).asylumApplications, 4667);
		});
		it('correct total for germany by end of 2014', function() {
			assert.equal(model.getTotalDestinationCounts('DEU', [startStamp, moment([2014, 11, 31]).unix()])
				.asylumApplications, 346633);
		});
		it('correct total for finland by end of 2014', function() {
			assert.equal(model.getTotalDestinationCounts('FIN', [startStamp, moment([2014, 11, 31]).unix()])
				.asylumApplications, 8894);
		});
		it('correct total for finland for 2013', function() {
			assert.equal(model.getTotalDestinationCounts('FIN',
				[firstDayStamp(2013, 0), lastDayStamp(2013, 11)])
				.asylumApplications, 3022);
		});
	});

	describe('getGlobalArrivingFor', function() {
		it('correct total for Jan 2013 (only European destinations)', function() {
			assert.equal(model.getGlobalArrivingFor(moment([2013, 0])).asylumApplications, 31092);
		});
		it('correct total for August 2015 (only European destinations)', function() {
			assert.equal(model.getGlobalArrivingFor(moment([2015, 7])).asylumApplications, 181022);
		});
	});

	describe('getGlobalArrivingPerMonthForCountry', function() {
		var germanyTotals = model.getGlobalArrivingPerMonthForCountry('DEU');

		it('correct total for germany for jan 2012', function() {
		 	assert.equal(germanyTotals[0].asylumApplications, 4667);
		});

		it('correct total for germany for jun 2014', function() {
		 	assert.equal(germanyTotals[12*2 + 5].asylumApplications, 12445);
		});

		it('correct total for germany for oct 2015', function() {
		 	assert.equal(germanyTotals[12*3 + 9].asylumApplications, 52686);
		});
	});

	describe('getOriginCountsByDestinationCountries()', function() {
		it('correct total for SYR->GER after one months end', function() {
			assert.equal(model.getOriginCountsByDestinationCountries('SYR',
				[startStamp, lastDayStamp(2012, 0)])['DEU'].asylumApplications, 210);
		});
		it('correct total for SYR->GER after two months end', function() {
			assert.equal(model.getOriginCountsByDestinationCountries('SYR',
				[startStamp, lastDayStamp(2012, 1)])['DEU'].asylumApplications, 440);
		});
		it('correct total for SYR->GER during 2013', function() {
			assert.equal(model.getOriginCountsByDestinationCountries('SYR',
				[firstDayStamp(2013, 0), lastDayStamp(2013, 11)])['DEU'].asylumApplications, 11851);
		});
	});

	describe('getDestinationCountsByOriginCountries()', function() {
		it('correct total for SYR->GER after one months end', function() {
			assert.equal(model.getDestinationCountsByOriginCountries('DEU',
				[startStamp, lastDayStamp(2012, 0)])['SYR'].asylumApplications, 210);
		});
		it('correct total for SYR->GER after two months end', function() {
			assert.equal(model.getDestinationCountsByOriginCountries('DEU',
				[startStamp, lastDayStamp(2012, 1)])['SYR'].asylumApplications, 440);
		});
		it('correct total for SYR->GER during 2013', function() {
			assert.equal(model.getDestinationCountsByOriginCountries('DEU',
				[firstDayStamp(2013, 0), lastDayStamp(2013, 11)])['SYR'].asylumApplications, 11851);
		});
	});

});
<|endoftext|>


<commit_before>var net = require('net'),
    dns = require('dns'),
    inherits = require('util').inherits,
    EventEmitter = require('events').EventEmitter;

var Parser = require('./server.parser'),
    ipbytes = require('./utils').ipbytes;

var ATYP = require('./constants').ATYP,
    REP = require('./constants').REP;

var BUF_AUTH_NO_ACCEPT = new Buffer([0x05, 0xFF]),
    BUF_REP_INTR_SUCCESS = new Buffer([0x05,
                                       REP.SUCCESS,
                                       0x00,
                                       0x01,
                                       0x00, 0x00, 0x00, 0x00,
                                       0x00, 0x00]),
    BUF_REP_DISALLOW = new Buffer([0x05, REP.DISALLOW]),
    BUF_REP_CMDUNSUPP = new Buffer([0x05, REP.CMDUNSUPP]);

function Server(options, listener) {
  if (!(this instanceof Server))
    return new Server(options, listener);

  var self = this;

  if (typeof options === 'function') {
    self.on('connection', options);
    options = undefined;
  } else if (typeof listener === 'function')
    self.on('connection', listener);

  EventEmitter.call(this);

  this._srv = new net.Server(function(socket) {
    self._onConnection(socket);
  }).on('error', function(err) {
    self.emit('error', err);
  }).on('listening', function() {
    self.emit('listening');
  }).on('close', function() {
    self.emit('close');
  });
  this._auths = [];

  this._connections = 0;
  this.maxConnections = Infinity;
}
inherits(Server, EventEmitter);

Server.prototype._onConnection = function(socket) {
  var self = this,
      parser = new Parser(socket);
  parser.on('methods', function(methods) {
    var auths = self._auths;
    for (var a = 0, alen = auths.length; a < alen; ++a) {
      for (var m = 0, mlen = methods.length; m < mlen; ++m) {
        if (methods[m] === auths[a].METHOD) {
          auths[a](socket, function(success) {
            if (success) {
              parser.authed = true;
              parser.start();
            } else
              socket.end();
          });
          socket.write(new Buffer([0x05, auths[a].METHOD]));
          return;
        }
      }
    }
    socket.end(BUF_AUTH_NO_ACCEPT);
  }).on('request', function(reqInfo) {
    if (reqInfo.cmd !== 'connect')
      return socket.end(BUF_REP_CMDUNSUPP);

    reqInfo.srcAddr = socket.remoteAddress;
    reqInfo.srcPort = socket.remotePort;

    function accept(intercept) {
      if (socket.writable) {
        if (intercept) {
          socket.write(BUF_REP_INTR_SUCCESS);
          return socket;
        } else
          proxySocket(socket, reqInfo);
      }
    }
    function deny() {
      if (socket.writable)
        socket.end(BUF_REP_DISALLOW);
    }

    if (self._events.connection) {
      self.emit('connection', reqInfo, accept, deny);
      return;
    }

    proxySocket(socket, reqInfo);
  });

  function onClose() {
    if (socket.dstSock && socket.dstSock.writable)
      socket.dstSock.destroy();
    socket.dstSock = undefined;
  }

  socket.on('error', onErrorNoop)
        .on('end', onClose)
        .on('close', onClose);
};

Server.prototype.useAuth = function(fn) {
  if (typeof fn !== 'function' || fn.length !== 2)
    throw new Error('Invalid authentication handler');

  this._auths.push(fn);

  return this;
};

Server.prototype.listen = function() {
  return this._srv.listen.apply(this._srv, arguments);
};

Server.prototype.address = function() {
  return this._srv.address();
};

Server.prototype.getConnections = function(cb) {
  return this._srv.getConnections(cb);
};

Server.prototype.close = function(cb) {
  return this._srv.close(cb);
};

Server.prototype.ref = function() {
  return this._srv.ref();
};

Server.prototype.unref = function() {
  return this._srv.unref();
};

exports.Server = Server;
exports.createServer = function(opts, listener) {
  return new Server(opts, listener);
};

function onErrorNoop(err) {}

function proxySocket(socket, req) {
  dns.lookup(req.dstAddr, function(err, dstIP) {
    if (err) {
      handleProxyError(socket, err);
      return;
    }

    function onError(err) {
      if (!connected)
        handleProxyError(socket, err);
    }

    var dstSock = new net.Socket(),
        connected = false;

    dstSock.setKeepAlive(false);
    dstSock.on('error', onError)
           .on('connect', function() {
             connected = true;
             if (socket.writable) {
              var localbytes = ipbytes(dstSock.localAddress),
                  len = localbytes.length,
                  bufrep = new Buffer(6 + len),
                  p = 4;
              bufrep[0] = 0x05;
              bufrep[1] = REP.SUCCESS;
              bufrep[2] = 0x00;
              bufrep[3] = (len === 4 ? ATYP.IPv4 : ATYP.IPv6);
              for (var i = 0; i < len; ++i, ++p)
                bufrep[p] = localbytes[i];
              bufrep.writeUInt16BE(dstSock.localPort, p, true);

              socket.write(bufrep);
              socket.pipe(dstSock).pipe(socket);
             } else if (dstSock.writable)
              dstSock.destroy();
           })
           .connect(req.dstPort, dstIP);
    socket.dstSock = dstSock;
  });
}

function handleProxyError(socket, err) {
  if (socket.writable) {
    var errbuf = new Buffer([0x05, REP.GENFAIL]);
    if (err.code) {
      switch (err.code) {
        case 'ENOENT':
        case 'ENOTFOUND':
        case 'ETIMEDOUT':
        case 'EHOSTUNREACH':
          errbuf[1] = REP.HOSTUNREACH;
        break;
        case 'ENETUNREACH':
          errbuf[1] = REP.NETUNREACH;
        break;
        case 'ECONNREFUSED':
          errbuf[1] = REP.CONNREFUSED;
        break;
      }
    }
    socket.end(errbuf);
  }
}<commit_msg>server: remove noop error listener on intercept<commit_after>var net = require('net'),
    dns = require('dns'),
    inherits = require('util').inherits,
    EventEmitter = require('events').EventEmitter;

var Parser = require('./server.parser'),
    ipbytes = require('./utils').ipbytes;

var ATYP = require('./constants').ATYP,
    REP = require('./constants').REP;

var BUF_AUTH_NO_ACCEPT = new Buffer([0x05, 0xFF]),
    BUF_REP_INTR_SUCCESS = new Buffer([0x05,
                                       REP.SUCCESS,
                                       0x00,
                                       0x01,
                                       0x00, 0x00, 0x00, 0x00,
                                       0x00, 0x00]),
    BUF_REP_DISALLOW = new Buffer([0x05, REP.DISALLOW]),
    BUF_REP_CMDUNSUPP = new Buffer([0x05, REP.CMDUNSUPP]);

function Server(options, listener) {
  if (!(this instanceof Server))
    return new Server(options, listener);

  var self = this;

  if (typeof options === 'function') {
    self.on('connection', options);
    options = undefined;
  } else if (typeof listener === 'function')
    self.on('connection', listener);

  EventEmitter.call(this);

  this._srv = new net.Server(function(socket) {
    self._onConnection(socket);
  }).on('error', function(err) {
    self.emit('error', err);
  }).on('listening', function() {
    self.emit('listening');
  }).on('close', function() {
    self.emit('close');
  });
  this._auths = [];

  this._connections = 0;
  this.maxConnections = Infinity;
}
inherits(Server, EventEmitter);

Server.prototype._onConnection = function(socket) {
  var self = this,
      parser = new Parser(socket);
  parser.on('methods', function(methods) {
    var auths = self._auths;
    for (var a = 0, alen = auths.length; a < alen; ++a) {
      for (var m = 0, mlen = methods.length; m < mlen; ++m) {
        if (methods[m] === auths[a].METHOD) {
          auths[a](socket, function(success) {
            if (success) {
              parser.authed = true;
              parser.start();
            } else
              socket.end();
          });
          socket.write(new Buffer([0x05, auths[a].METHOD]));
          return;
        }
      }
    }
    socket.end(BUF_AUTH_NO_ACCEPT);
  }).on('request', function(reqInfo) {
    if (reqInfo.cmd !== 'connect')
      return socket.end(BUF_REP_CMDUNSUPP);

    reqInfo.srcAddr = socket.remoteAddress;
    reqInfo.srcPort = socket.remotePort;

    function accept(intercept) {
      if (socket.writable) {
        if (intercept) {
          socket.write(BUF_REP_INTR_SUCCESS);
          socket.removeListener('error', onErrorNoop);
          return socket;
        } else
          proxySocket(socket, reqInfo);
      }
    }
    function deny() {
      if (socket.writable)
        socket.end(BUF_REP_DISALLOW);
    }

    if (self._events.connection) {
      self.emit('connection', reqInfo, accept, deny);
      return;
    }

    proxySocket(socket, reqInfo);
  });

  function onClose() {
    if (socket.dstSock && socket.dstSock.writable)
      socket.dstSock.destroy();
    socket.dstSock = undefined;
  }

  socket.on('error', onErrorNoop)
        .on('end', onClose)
        .on('close', onClose);
};

Server.prototype.useAuth = function(fn) {
  if (typeof fn !== 'function' || fn.length !== 2)
    throw new Error('Invalid authentication handler');

  this._auths.push(fn);

  return this;
};

Server.prototype.listen = function() {
  return this._srv.listen.apply(this._srv, arguments);
};

Server.prototype.address = function() {
  return this._srv.address();
};

Server.prototype.getConnections = function(cb) {
  return this._srv.getConnections(cb);
};

Server.prototype.close = function(cb) {
  return this._srv.close(cb);
};

Server.prototype.ref = function() {
  return this._srv.ref();
};

Server.prototype.unref = function() {
  return this._srv.unref();
};

exports.Server = Server;
exports.createServer = function(opts, listener) {
  return new Server(opts, listener);
};

function onErrorNoop(err) {}

function proxySocket(socket, req) {
  dns.lookup(req.dstAddr, function(err, dstIP) {
    if (err) {
      handleProxyError(socket, err);
      return;
    }

    function onError(err) {
      if (!connected)
        handleProxyError(socket, err);
    }

    var dstSock = new net.Socket(),
        connected = false;

    dstSock.setKeepAlive(false);
    dstSock.on('error', onError)
           .on('connect', function() {
             connected = true;
             if (socket.writable) {
              var localbytes = ipbytes(dstSock.localAddress),
                  len = localbytes.length,
                  bufrep = new Buffer(6 + len),
                  p = 4;
              bufrep[0] = 0x05;
              bufrep[1] = REP.SUCCESS;
              bufrep[2] = 0x00;
              bufrep[3] = (len === 4 ? ATYP.IPv4 : ATYP.IPv6);
              for (var i = 0; i < len; ++i, ++p)
                bufrep[p] = localbytes[i];
              bufrep.writeUInt16BE(dstSock.localPort, p, true);

              socket.write(bufrep);
              socket.pipe(dstSock).pipe(socket);
             } else if (dstSock.writable)
              dstSock.destroy();
           })
           .connect(req.dstPort, dstIP);
    socket.dstSock = dstSock;
  });
}

function handleProxyError(socket, err) {
  if (socket.writable) {
    var errbuf = new Buffer([0x05, REP.GENFAIL]);
    if (err.code) {
      switch (err.code) {
        case 'ENOENT':
        case 'ENOTFOUND':
        case 'ETIMEDOUT':
        case 'EHOSTUNREACH':
          errbuf[1] = REP.HOSTUNREACH;
        break;
        case 'ENETUNREACH':
          errbuf[1] = REP.NETUNREACH;
        break;
        case 'ECONNREFUSED':
          errbuf[1] = REP.CONNREFUSED;
        break;
      }
    }
    socket.end(errbuf);
  }
}<|endoftext|>


<commit_before>var m = require("moment");

/*
 * Change default behaviour of console.log to print timestamp before any message
 * TODO: si se llama mas de una vez los timestamp de van superponiendo a la izquierda del mensaje.
 */
function change_console_log_printing_format(){

	var f = console.log;
	console.log = function(){

		var args = Array.prototype.slice.call(arguments);
		args[0] = "[%s] " + args[0];
		args.splice(1,0,m().format("DD/MM/YYYY H:mm:ss"));
		f.apply(this, args);

	}

}

/*
 * Required built-in and custom modules
 */

var http = require("http");
var url = require("url");
var qs = require("querystring");
var router = require("./router");
var db = require("./db");
var eq = require("./evqueue");
var api = require("./api");

/*
 * Make api functions public to front module, so can call them from front module server.js:
 *
 * var jsa = require("jsa");
 * jsa.api.docs.create({uid:32342,doc:{test:1}},function(err,ret_val){ ... })
 */
exports.api = { docs:{}, init:{}, events:{}, eq:{} };
exports.eq = {};
exports.db = db;

for( key in api.remote ){

	(function(){
		var scopekey = key;
		exports.api.docs[scopekey] = function(params, ret_handler){

			api.remote[scopekey](params, ret_handler);
		}
	})();
};

for( key in api.init ){

	(function(){
		var scopekey = key;
		exports.api.init[scopekey] = function(params, ret_handler){

			api.init[scopekey](params, ret_handler);
		}
	})();
};

exports.api.init.rcpts = function( rcpt_handler ){

	api.rcpts = rcpt_handler;
}

/*
 * Add external handler for api module events. evqueue.js module adds a default handler for
 * all primitive events related to primitive operations: ev_create, ev_join, ev_unjoin,
 * ev_add, ev_rem, ev_set, ev_incr, ev_decr, ev_push, ev_pop, ev_pull.
 */
exports.api.events.on = function( ev_type, ret_handler ){

	api.on(ev_type, ret_handler);
	return exports.api.events;
}

var cancel_default_event = false;
exports.api.events.cancel_default_event = function(){

	cancel_default_event = true;
}

/*
 * Emit external event as an api event.
 */
exports.api.events.emit = function( ev_type, params, rcpts ){

	api.emit( ev_type, params, rcpts );
}

/*
 * add a new operation to the service.
 */
exports.api.newop = function( fname, f_handler ){ //function(params)

	var ev_name = "ev_" + fname;

	api.remote[fname] = function( params, ret_handler){

		exports.api.events[ev_name] = {params:params, rcpts:undefined};

		f_handler(params, function(err,val){

				if(ret_handler)
					ret_handler(err,val);

				if( !cancel_default_event )
					api.emit( ev_name, exports.api.events[ev_name].params , exports.api.events[ev_name].rcpts  );

				cancel_default_event = false;
		});
	}

	exports.api.docs[fname] = function( params, ret_handler ){

		api.remote[fname](params, ret_handler);
	}

	eq.api.listen( ev_name );
}

/*
 * make evqueue listen for custom event types.
 */
exports.eq.listen = function( ev_type, ev_handler ){

	eq.api.listen( ev_type, ev_handler );
}

/*
 *  termination handler.
 */
function termination_handler(sig) {

   if (typeof sig === "string") {
      console.log("Received %s - terminating Node process.", sig);
      process.exit(1);
   }
   console.log("Node process stopped.");
}

if( process.listeners("exit").length  == 1 ){

	//Process on exit and signals.
	process.on('exit', function() { termination_handler(); });

	//Uncaught exceptions handler to make the server stay running forever.
	process.on("uncaughtException", function(err){

		console.warn( err );

	});

	[
	 'SIGHUP', 'SIGINT', 'SIGQUIT', 'SIGILL', 'SIGTRAP', 'SIGABRT', 'SIGBUS',
	 'SIGFPE', 'SIGUSR1', 'SIGSEGV', 'SIGUSR2', 'SIGPIPE', 'SIGTERM'
	].forEach(function(element, index, array) {

	    process.on(element, function() { termination_handler(element); });
	});

}

/*
 * If sconfig is undefined then settings are loaded from file settings.json, otherwise the function
 * prints and/or overwrites these default settings (sconfig) with program settings (econfig).
 *
 */
var sconfig;
var settings = function( econfig ){

	if( sconfig == undefined ){

		try{
			console.log("Reading config file 'settings.json'");
			sconfig = require("../settings.json");
			sconfig.version = require("../package.json").version;
		}catch(err){
			//sconfig = {};
			console.log("Cannot read config file 'settings.json'");
		}
	}else if( typeof econfig == "object" && econfig.reload == 1){

		try{
			console.log("Reloading config file 'settings.json'");
			delete require.cache[ process.cwd() + "/settings.json" ];
			delete require.cache[ process.cwd() + "/package.json" ];
			sconfig = require("../settings.json");
			sconfig.version = require("../package.json").version;
			delete econfig.reload;
		}catch(err){
			console.log("Cannot read config file 'settings.json' or 'package.json'");
		}

	}

	if( typeof econfig == "object" ){
		for(key in econfig)
			sconfig[key] = econfig[key];
	}

	console.log("Settings: " + JSON.stringify(sconfig) );
}
exports.settings = settings;

/*
 * private function to process incoming request: parse url and route request  based on pathname.
 */
function request_handler(request, response){

	if( request.method == "POST" ){

	    if( request.headers["content-type"] == "application/json" ){

			var data = "";
			var pathname = url.parse(request.url).pathname;
			request.setEncoding("utf8");
			request.on("data", function(chunk){
			    data += chunk;
			});
			request.on("end", function(){

			    console.log('Received data: [%s]', data);
			    router.route( pathname, response, data );
			});

	    }else{

			response.writeHead(400, {"Content-Type":"text/html"});
			response.write("400 Bad request.");
			response.end();
	    }
	}else{
	    response.writeHead(405,{"Content-Type":"text/html"});
	    response.write("405 Method not allowed");
	    response.end();
	}
}

/*
 * connect to db
 */
exports.connectdb = function( econfig ){

	if(typeof econfig == "object")
		settings( econfig );

	db.connect( sconfig, function(err,val){

	});
}

var system = {pid:process.pid, uid:process.getuid(), cwd:process.cwd(), version:process.version };

/*
 * start http server and connect driver to db.
 *
 * sconfig properties:
 *
 * sconfig.port : http port to listen
 * sconfig.ipaddr : addr to bind the service
 * sconfig.dbhost: db host ip address
 * sconfig.dbport : db  port
 * sconfig.dbname: database name
 * sconfig.dbuser : db connection user.
 * sconfig.dbpass : db connection password
 */
var httpsrv = http.createServer(request_handler);
exports.start = function( econfig, ret_handler ){

	//set parameter conveniently if only one parameter is passed.
	if( typeof econfig == "function"){

		ret_handler = econfig;
		econfig = undefined;

	}

	change_console_log_printing_format();
	console.log("System settings: %s", JSON.stringify(system) );

	if(httpsrv._handle)
		stop();

	settings(econfig);

	db.connect( sconfig, function(err,val){

		if(!err){

			httpsrv.listen( sconfig.httpport, sconfig.httpaddr, function(){

					if(ret_handler)
						ret_handler();

			} );
			console.log("Httpd listening on %s:%d", sconfig.httpaddr, sconfig.httpport);
		}else
			console.log(err);
	});

}

/*
 * Stop http server.
 */
var stop = function(){

	if( httpsrv )
		httpsrv.close();
	console.log("Httpd stopped.");

}
exports.stop = stop;

<commit_msg>newop loads the document into params.doc when wid parameter is supplied to the remote call of the new operation<commit_after>var m = require("moment");

/*
 * Change default behaviour of console.log to print timestamp before any message
 * TODO: si se llama mas de una vez los timestamp de van superponiendo a la izquierda del mensaje.
 */
function change_console_log_printing_format(){

	var f = console.log;
	console.log = function(){

		var args = Array.prototype.slice.call(arguments);
		args[0] = "[%s] " + args[0];
		args.splice(1,0,m().format("DD/MM/YYYY H:mm:ss"));
		f.apply(this, args);

	}

}

/*
 * Required built-in and custom modules
 */

var http = require("http");
var url = require("url");
var qs = require("querystring");
var router = require("./router");
var db = require("./db");
var eq = require("./evqueue");
var api = require("./api");

/*
 * Make api functions public to front module, so can call them from front module server.js:
 *
 * var jsa = require("jsa");
 * jsa.api.docs.create({uid:32342,doc:{test:1}},function(err,ret_val){ ... })
 */
exports.api = { docs:{}, init:{}, events:{}, eq:{} };
exports.eq = {};
exports.db = db;

for( key in api.remote ){

	(function(){
		var scopekey = key;
		exports.api.docs[scopekey] = function(params, ret_handler){

			api.remote[scopekey](params, ret_handler);
		}
	})();
};

for( key in api.init ){

	(function(){
		var scopekey = key;
		exports.api.init[scopekey] = function(params, ret_handler){

			api.init[scopekey](params, ret_handler);
		}
	})();
};

exports.api.init.rcpts = function( rcpt_handler ){

	api.rcpts = rcpt_handler;
}

/*
 * Add external handler for api module events. evqueue.js module adds a default handler for
 * all primitive events related to primitive operations: ev_create, ev_join, ev_unjoin,
 * ev_add, ev_rem, ev_set, ev_incr, ev_decr, ev_push, ev_pop, ev_pull.
 */
exports.api.events.on = function( ev_type, ret_handler ){

	api.on(ev_type, ret_handler);
	return exports.api.events;
}

var cancel_default_event = false;
exports.api.events.cancel_default_event = function(){

	cancel_default_event = true;
}

/*
 * Emit external event as an api event.
 */
exports.api.events.emit = function( ev_type, params, rcpts ){

	api.emit( ev_type, params, rcpts );
}

/*
 * add a new operation to the service.
 */
exports.api.newop = function( fname, f_handler ){ //function(params)

	var ev_name = "ev_" + fname;

	api.remote[fname] = function( params, ret_handler){

		exports.api.events[ev_name] = {params:params, rcpts:undefined};

		if( params && params.catalog && params.wid ){ //load the document if parameter wid is supplied

			db.select(params.catalog, params.wid, function(err,doc){

				if(!err && doc){

					params.doc = doc;

					f_handler(params, function(err,val){

							db.save(params.catalog, doc, function(err, val){

								if(!err){

									ret_handler(err,val);

									if( !cancel_default_event )
										api.emit( ev_name, exports.api.events[ev_name].params , exports.api.events[ev_name].rcpts  );

									cancel_default_event = false;
								}else{

									ret_handler(err,null);
								}
							});
					});
				}else if(!err && !doc){

					ret_handler({code:-7, message:"Document not found: @" + params.catalog + ":" + params.wid }, null);
				}else{

					ret_handler(err,null);
				}
			});
		}else{

			f_handler(params, function(err,val){

				ret_handler(err,val);

				if( !cancel_default_event )
					api.emit( ev_name, exports.api.events[ev_name].params , exports.api.events[ev_name].rcpts  );

				cancel_default_event = false;
			});
		}
	}

	exports.api.docs[fname] = function( params, ret_handler ){

		api.remote[fname](params, ret_handler);
	}

	eq.api.listen( ev_name );
}

/*
 * make evqueue listen for custom event types.
 */
exports.eq.listen = function( ev_type, ev_handler ){

	eq.api.listen( ev_type, ev_handler );
}

/*
 *  termination handler.
 */
function termination_handler(sig) {

   if (typeof sig === "string") {
      console.log("Received %s - terminating Node process.", sig);
      process.exit(1);
   }
   console.log("Node process stopped.");
}

if( process.listeners("exit").length  == 1 ){

	//Process on exit and signals.
	process.on('exit', function() { termination_handler(); });

	//Uncaught exceptions handler to make the server stay running forever.
	process.on("uncaughtException", function(err){

		console.warn( err );

	});

	[
	 'SIGHUP', 'SIGINT', 'SIGQUIT', 'SIGILL', 'SIGTRAP', 'SIGABRT', 'SIGBUS',
	 'SIGFPE', 'SIGUSR1', 'SIGSEGV', 'SIGUSR2', 'SIGPIPE', 'SIGTERM'
	].forEach(function(element, index, array) {

	    process.on(element, function() { termination_handler(element); });
	});

}

/*
 * If sconfig is undefined then settings are loaded from file settings.json, otherwise the function
 * prints and/or overwrites these default settings (sconfig) with program settings (econfig).
 *
 */
var sconfig;
var settings = function( econfig ){

	if( sconfig == undefined ){

		try{
			console.log("Reading config file 'settings.json'");
			sconfig = require("../settings.json");
			sconfig.version = require("../package.json").version;
		}catch(err){
			//sconfig = {};
			console.log("Cannot read config file 'settings.json'");
		}
	}else if( typeof econfig == "object" && econfig.reload == 1){

		try{
			console.log("Reloading config file 'settings.json'");
			delete require.cache[ process.cwd() + "/settings.json" ];
			delete require.cache[ process.cwd() + "/package.json" ];
			sconfig = require("../settings.json");
			sconfig.version = require("../package.json").version;
			delete econfig.reload;
		}catch(err){
			console.log("Cannot read config file 'settings.json' or 'package.json'");
		}

	}

	if( typeof econfig == "object" ){
		for(key in econfig)
			sconfig[key] = econfig[key];
	}

	console.log("Settings: " + JSON.stringify(sconfig) );
}
exports.settings = settings;

/*
 * private function to process incoming request: parse url and route request  based on pathname.
 */
function request_handler(request, response){

	if( request.method == "POST" ){

	    if( request.headers["content-type"] == "application/json" ){

			var data = "";
			var pathname = url.parse(request.url).pathname;
			request.setEncoding("utf8");
			request.on("data", function(chunk){
			    data += chunk;
			});
			request.on("end", function(){

			    console.log('Received data: [%s]', data);
			    router.route( pathname, response, data );
			});

	    }else{

			response.writeHead(400, {"Content-Type":"text/html"});
			response.write("400 Bad request.");
			response.end();
	    }
	}else{
	    response.writeHead(405,{"Content-Type":"text/html"});
	    response.write("405 Method not allowed");
	    response.end();
	}
}

/*
 * connect to db
 */
exports.connectdb = function( econfig ){

	if(typeof econfig == "object")
		settings( econfig );

	db.connect( sconfig, function(err,val){

	});
}

var system = {pid:process.pid, uid:process.getuid(), cwd:process.cwd(), version:process.version };

/*
 * start http server and connect driver to db.
 *
 * sconfig properties:
 *
 * sconfig.port : http port to listen
 * sconfig.ipaddr : addr to bind the service
 * sconfig.dbhost: db host ip address
 * sconfig.dbport : db  port
 * sconfig.dbname: database name
 * sconfig.dbuser : db connection user.
 * sconfig.dbpass : db connection password
 */
var httpsrv = http.createServer(request_handler);
exports.start = function( econfig, ret_handler ){

	//set parameter conveniently if only one parameter is passed.
	if( typeof econfig == "function"){

		ret_handler = econfig;
		econfig = undefined;

	}

	change_console_log_printing_format();
	console.log("System settings: %s", JSON.stringify(system) );

	if(httpsrv._handle)
		stop();

	settings(econfig);

	db.connect( sconfig, function(err,val){

		if(!err){

			httpsrv.listen( sconfig.httpport, sconfig.httpaddr, function(){

					if(ret_handler)
						ret_handler();

			} );
			console.log("Httpd listening on %s:%d", sconfig.httpaddr, sconfig.httpport);
		}else
			console.log(err);
	});

}

/*
 * Stop http server.
 */
var stop = function(){

	if( httpsrv )
		httpsrv.close();
	console.log("Httpd stopped.");

}
exports.stop = stop;

<|endoftext|>


<commit_before>const arrayRemove = require('unordered-array-remove')
const http = require('http')
const mime = require('mime')
const pump = require('pump')
const rangeParser = require('range-parser')
const URL = require('url').URL || window.URL // TODO: remove when we drop Node 8 support

function Server (torrent, opts = {}) {
  const server = http.createServer()
  if (!opts.origin) opts.origin = '*' // allow all origins by default

  const sockets = []
  const pendingReady = []
  let closed = false
  const _listen = server.listen
  const _close = server.close

  server.listen = (...args) => {
    closed = false
    server.on('connection', onConnection)
    server.on('request', onRequest)
    _listen.apply(server, args)
    return server
  }

  server.close = cb => {
    closed = true
    server.removeListener('connection', onConnection)
    server.removeListener('request', onRequest)
    while (pendingReady.length) {
      const onReady = pendingReady.pop()
      torrent.removeListener('ready', onReady)
    }
    _close.call(server, cb)
  }

  server.destroy = cb => {
    sockets.forEach(socket => {
      socket.destroy()
    })

    // Only call `server.close` if user has not called it already
    if (!cb) cb = () => {}
    if (closed) process.nextTick(cb)
    else server.close(cb)
    torrent = null
  }

  function isOriginAllowed (req) {
    // When `origin` option is `false`, deny all cross-origin requests
    if (opts.origin === false) return false

    // Requests without an 'Origin' header are not actually cross-origin, so just
    // deny them
    if (req.headers.origin == null) return false

    // The user allowed all origins
    if (opts.origin === '*') return true

    // Allow requests where the 'Origin' header matches the `opts.origin` setting
    return req.headers.origin === opts.origin
  }

  function onConnection (socket) {
    socket.setTimeout(36000000)
    sockets.push(socket)
    socket.once('close', () => {
      arrayRemove(sockets, sockets.indexOf(socket))
    })
  }

  function onRequest (req, res) {
    // If a 'hostname' string is specified, deny requests with a 'Host'
    // header that does not match the origin of the torrent server to prevent
    // DNS rebinding attacks.
    if (opts.hostname && req.headers.host !== `${opts.hostname}:${server.address().port}`) {
      return req.destroy()
    }

    const pathname = new URL(req.url, 'http://example.com').pathname

    if (pathname === '/favicon.ico') {
      return serve404Page()
    }

    // Allow cross-origin requests (CORS)
    if (isOriginAllowed(req)) {
      res.setHeader('Access-Control-Allow-Origin', req.headers.origin)
    }

    // Prevent browser mime-type sniffing
    res.setHeader('X-Content-Type-Options', 'nosniff')

    // Allow CORS requests to specify arbitrary headers, e.g. 'Range',
    // by responding to the OPTIONS preflight request with the specified
    // origin and requested headers.
    if (req.method === 'OPTIONS') {
      if (isOriginAllowed(req)) return serveOptionsRequest()
      else return serveMethodNotAllowed()
    }

    if (req.method === 'GET' || req.method === 'HEAD') {
      if (torrent.ready) {
        handleRequest()
      } else {
        pendingReady.push(onReady)
        torrent.once('ready', onReady)
      }
      return
    }

    return serveMethodNotAllowed()

    function serveOptionsRequest () {
      res.statusCode = 204 // no content
      res.setHeader('Access-Control-Max-Age', '600')
      res.setHeader('Access-Control-Allow-Methods', 'GET,HEAD')

      if (req.headers['access-control-request-headers']) {
        res.setHeader(
          'Access-Control-Allow-Headers',
          req.headers['access-control-request-headers']
        )
      }
      res.end()
    }

    function onReady () {
      arrayRemove(pendingReady, pendingReady.indexOf(onReady))
      handleRequest()
    }

    function handleRequest () {
      if (pathname === '/') {
        return serveIndexPage()
      }

      const index = Number(pathname.split('/')[1])
      if (Number.isNaN(index) || index >= torrent.files.length) {
        return serve404Page()
      }

      const file = torrent.files[index]
      serveFile(file)
    }

    function serveIndexPage () {
      res.statusCode = 200
      res.setHeader('Content-Type', 'text/html')

      const listHtml = torrent.files.map((file, i) => `<li><a download="${file.name}" href="/${i}/${file.name}">${file.path}</a> (${file.length} bytes)</li>`).join('
')

      const html = getPageHTML(
        `${torrent.name} - WebTorrent`,
        `<h1>${torrent.name}</h1><ol>${listHtml}</ol>`
      )
      res.end(html)
    }

    function serve404Page () {
      res.statusCode = 404
      res.setHeader('Content-Type', 'text/html')

      const html = getPageHTML('404 - Not Found', '
404 - Not Found
')
      res.end(html)
    }

    function serveFile (file) {
      res.statusCode = 200
      res.setHeader('Content-Type', mime.getType(file.name) || 'application/octet-stream')

      // Support range-requests
      res.setHeader('Accept-Ranges', 'bytes')

      // Set name of file (for "Save Page As..." dialog)
      res.setHeader(
        'Content-Disposition',
        `inline; filename*=UTF-8''${encodeRFC5987(file.name)}`
      )

      // Support DLNA streaming
      res.setHeader('transferMode.dlna.org', 'Streaming')
      res.setHeader(
        'contentFeatures.dlna.org',
        'DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01700000000000000000000000000000'
      )

      // `rangeParser` returns an array of ranges, or an error code (number) if
      // there was an error parsing the range.
      let range = rangeParser(file.length, req.headers.range || '')

      if (Array.isArray(range)) {
        res.statusCode = 206 // indicates that range-request was understood

        // no support for multi-range request, just use the first range
        range = range[0]

        res.setHeader(
          'Content-Range',
          `bytes ${range.start}-${range.end}/${file.length}`
        )
        res.setHeader('Content-Length', range.end - range.start + 1)
      } else {
        range = null
        res.setHeader('Content-Length', file.length)
      }

      if (req.method === 'HEAD') {
        return res.end()
      }

      pump(file.createReadStream(range), res)
    }

    function serveMethodNotAllowed () {
      res.statusCode = 405
      res.setHeader('Content-Type', 'text/html')
      const html = getPageHTML('405 - Method Not Allowed', '
405 - Method Not Allowed
')
      res.end(html)
    }
  }

  return server
}

function getPageHTML (title, pageHtml) {
  return `<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>${title}</title></head><body>${pageHtml}</body></html>`
}

// From https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
function encodeRFC5987 (str) {
  return encodeURIComponent(str)
    // Note that although RFC3986 reserves "!", RFC5987 does not,
    // so we do not need to escape it
    .replace(/['()]/g, escape) // i.e., %27 %28 %29
    .replace(/\*/g, '%2A')
    // The following are not required for percent-encoding per RFC5987,
    // so we can allow for a little better readability over the wire: |`^
    .replace(/%(?:7C|60|5E)/g, unescape)
}

module.exports = Server
<commit_msg>remove require('url')<commit_after>const arrayRemove = require('unordered-array-remove')
const http = require('http')
const mime = require('mime')
const pump = require('pump')
const rangeParser = require('range-parser')

function Server (torrent, opts = {}) {
  const server = http.createServer()
  if (!opts.origin) opts.origin = '*' // allow all origins by default

  const sockets = []
  const pendingReady = []
  let closed = false
  const _listen = server.listen
  const _close = server.close

  server.listen = (...args) => {
    closed = false
    server.on('connection', onConnection)
    server.on('request', onRequest)
    _listen.apply(server, args)
    return server
  }

  server.close = cb => {
    closed = true
    server.removeListener('connection', onConnection)
    server.removeListener('request', onRequest)
    while (pendingReady.length) {
      const onReady = pendingReady.pop()
      torrent.removeListener('ready', onReady)
    }
    _close.call(server, cb)
  }

  server.destroy = cb => {
    sockets.forEach(socket => {
      socket.destroy()
    })

    // Only call `server.close` if user has not called it already
    if (!cb) cb = () => {}
    if (closed) process.nextTick(cb)
    else server.close(cb)
    torrent = null
  }

  function isOriginAllowed (req) {
    // When `origin` option is `false`, deny all cross-origin requests
    if (opts.origin === false) return false

    // Requests without an 'Origin' header are not actually cross-origin, so just
    // deny them
    if (req.headers.origin == null) return false

    // The user allowed all origins
    if (opts.origin === '*') return true

    // Allow requests where the 'Origin' header matches the `opts.origin` setting
    return req.headers.origin === opts.origin
  }

  function onConnection (socket) {
    socket.setTimeout(36000000)
    sockets.push(socket)
    socket.once('close', () => {
      arrayRemove(sockets, sockets.indexOf(socket))
    })
  }

  function onRequest (req, res) {
    // If a 'hostname' string is specified, deny requests with a 'Host'
    // header that does not match the origin of the torrent server to prevent
    // DNS rebinding attacks.
    if (opts.hostname && req.headers.host !== `${opts.hostname}:${server.address().port}`) {
      return req.destroy()
    }

    const pathname = new URL(req.url, 'http://example.com').pathname

    if (pathname === '/favicon.ico') {
      return serve404Page()
    }

    // Allow cross-origin requests (CORS)
    if (isOriginAllowed(req)) {
      res.setHeader('Access-Control-Allow-Origin', req.headers.origin)
    }

    // Prevent browser mime-type sniffing
    res.setHeader('X-Content-Type-Options', 'nosniff')

    // Allow CORS requests to specify arbitrary headers, e.g. 'Range',
    // by responding to the OPTIONS preflight request with the specified
    // origin and requested headers.
    if (req.method === 'OPTIONS') {
      if (isOriginAllowed(req)) return serveOptionsRequest()
      else return serveMethodNotAllowed()
    }

    if (req.method === 'GET' || req.method === 'HEAD') {
      if (torrent.ready) {
        handleRequest()
      } else {
        pendingReady.push(onReady)
        torrent.once('ready', onReady)
      }
      return
    }

    return serveMethodNotAllowed()

    function serveOptionsRequest () {
      res.statusCode = 204 // no content
      res.setHeader('Access-Control-Max-Age', '600')
      res.setHeader('Access-Control-Allow-Methods', 'GET,HEAD')

      if (req.headers['access-control-request-headers']) {
        res.setHeader(
          'Access-Control-Allow-Headers',
          req.headers['access-control-request-headers']
        )
      }
      res.end()
    }

    function onReady () {
      arrayRemove(pendingReady, pendingReady.indexOf(onReady))
      handleRequest()
    }

    function handleRequest () {
      if (pathname === '/') {
        return serveIndexPage()
      }

      const index = Number(pathname.split('/')[1])
      if (Number.isNaN(index) || index >= torrent.files.length) {
        return serve404Page()
      }

      const file = torrent.files[index]
      serveFile(file)
    }

    function serveIndexPage () {
      res.statusCode = 200
      res.setHeader('Content-Type', 'text/html')

      const listHtml = torrent.files.map((file, i) => `<li><a download="${file.name}" href="/${i}/${file.name}">${file.path}</a> (${file.length} bytes)</li>`).join('
')

      const html = getPageHTML(
        `${torrent.name} - WebTorrent`,
        `<h1>${torrent.name}</h1><ol>${listHtml}</ol>`
      )
      res.end(html)
    }

    function serve404Page () {
      res.statusCode = 404
      res.setHeader('Content-Type', 'text/html')

      const html = getPageHTML('404 - Not Found', '
404 - Not Found
')
      res.end(html)
    }

    function serveFile (file) {
      res.statusCode = 200
      res.setHeader('Content-Type', mime.getType(file.name) || 'application/octet-stream')

      // Support range-requests
      res.setHeader('Accept-Ranges', 'bytes')

      // Set name of file (for "Save Page As..." dialog)
      res.setHeader(
        'Content-Disposition',
        `inline; filename*=UTF-8''${encodeRFC5987(file.name)}`
      )

      // Support DLNA streaming
      res.setHeader('transferMode.dlna.org', 'Streaming')
      res.setHeader(
        'contentFeatures.dlna.org',
        'DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01700000000000000000000000000000'
      )

      // `rangeParser` returns an array of ranges, or an error code (number) if
      // there was an error parsing the range.
      let range = rangeParser(file.length, req.headers.range || '')

      if (Array.isArray(range)) {
        res.statusCode = 206 // indicates that range-request was understood

        // no support for multi-range request, just use the first range
        range = range[0]

        res.setHeader(
          'Content-Range',
          `bytes ${range.start}-${range.end}/${file.length}`
        )
        res.setHeader('Content-Length', range.end - range.start + 1)
      } else {
        range = null
        res.setHeader('Content-Length', file.length)
      }

      if (req.method === 'HEAD') {
        return res.end()
      }

      pump(file.createReadStream(range), res)
    }

    function serveMethodNotAllowed () {
      res.statusCode = 405
      res.setHeader('Content-Type', 'text/html')
      const html = getPageHTML('405 - Method Not Allowed', '
405 - Method Not Allowed
')
      res.end(html)
    }
  }

  return server
}

function getPageHTML (title, pageHtml) {
  return `<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>${title}</title></head><body>${pageHtml}</body></html>`
}

// From https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
function encodeRFC5987 (str) {
  return encodeURIComponent(str)
    // Note that although RFC3986 reserves "!", RFC5987 does not,
    // so we do not need to escape it
    .replace(/['()]/g, escape) // i.e., %27 %28 %29
    .replace(/\*/g, '%2A')
    // The following are not required for percent-encoding per RFC5987,
    // so we can allow for a little better readability over the wire: |`^
    .replace(/%(?:7C|60|5E)/g, unescape)
}

module.exports = Server
<|endoftext|>


<commit_before>var log     = require('./utils/log');
var url     = require('url');
var express = require('express');

var server = function(lingon, ip, port) {
  var app = express();

  lingon.server = app;

  var requestHandler = function(request, response, next) {
    var path = url.parse(request.url);

    // Use index.html if no path was supplied
    // TODO: Scan for other common index filenames
    if(path.href === '/') {
      path = url.parse('/index.html');
    }

    // Remove the slash
    path = path.pathname.substring(1);

    var requestPaths = [path];

    // Run lingon for the requested file
    lingon.build(function(requestFiles) {
      if(!requestFiles || !requestFiles[0]) { return next(); }

      var file = requestFiles[0];

      response.type( express.mime.lookup(file.path) );
      response.body = file.contents;

      next();
    }, requestPaths);
  };

  var responseHandler = function(request, response, next) {
    if(response.body) {
      return response.send(response.body);
    }

    response.send(404, 'File not found');
  };

  app.use(requestHandler);
  lingon.trigger('serverConfigure');
  app.use(responseHandler);

  app.listen(port, ip, function() {
    log('http server listening on: ' + ip + ':' + port);
    lingon.trigger('serverStarted');
  });
};

module.exports = server;
<commit_msg>add support for adding a catch all response instead of 404<commit_after>var log     = require('./utils/log');
var url     = require('url');
var express = require('express');

var server = function(lingon, ip, port) {
  var app = express();

  lingon.server = app;

  var buildCallback = function(request, response, next) {
    return function(requestFiles) {
      if(!requestFiles || !requestFiles[0]) { return next(); }

      var file = requestFiles[0];

      response.type( express.mime.lookup(file.path) );
      response.body = file.contents;

      next();
    };
  };

  var requestHandler = function(request, response, next) {
    var path = url.parse(request.url);

    // Use index.html if no path was supplied
    // TODO: Scan for other common index filenames
    if(path.href === '/') {
      path = url.parse('/index.html');
    }

    // Remove the slash
    path = path.pathname.substring(1);

    var requestPaths = [path];

    // Run lingon for the requested file
    lingon.build(buildCallback(request, response, next), requestPaths);
  };

  var catchAllHandler = function(request, response, next) {
    if(!response.body && lingon.catchAll) {
      lingon.build(buildCallback(request, response, next), [lingon.catchAll]);
    } else {
      next();
    }
  };

  var responseHandler = function(request, response, next) {
    if(response.body) {
      return response.send(response.body);
    }

    response.send(404, 'File not found');
  };

  app.use(requestHandler);
  app.use(catchAllHandler);
  lingon.trigger('serverConfigure');
  app.use(responseHandler);

  app.listen(port, ip, function() {
    log('http server listening on: ' + ip + ':' + port);
    lingon.trigger('serverStarted');
  });
};

module.exports = server;
<|endoftext|>


<commit_before>(function(){
    'use strict';

    if(!global.cloudcmd)
        return console.log(
             '# server.js'                                      + '\n'  +
             '# -----------'                                    + '\n'  +
             '# Module is part of Cloud Commander,'             + '\n'  +
             '# easy to use web server.'                        + '\n'  +
             '# http://coderaiser.github.com/cloudcmd'          + '\n');

    var main                = global.cloudcmd.main,

        DIR                 = main.Dir,
        LIBDIR              = main.LIBDIR,
        SRVDIR              = main.SRVDIR,

        URL                 = main.url,
        Path                = main.path,
        Querystring         = main.querystring,

        Minify              = main.minify,
        AppCache            = main.appcache,
        Socket              = main.socket,

        http                = main.http,
        https               = main.https,
        Util                = main.util,

        Server, Rest, Route, Minimize;

    /* базовая инициализация  */
    function init(pAppCachProcessing){
        var lConfig         = main.config,
            lMinifyAllowed  = lConfig.minification;

        /* Change default parameters of
         * js/css/html minification
         */
        Minify.setAllowed(lMinifyAllowed);

        /* Если нужно минимизируем скрипты */
        Util.exec(Minimize, lMinifyAllowed);

        /* создаём файл app cache */
        if( lConfig.appcache  && AppCache && lConfig.server )
            Util.exec( pAppCachProcessing );
    }

    /**
     * start server function
     * @param pConfig
     * @param pProcessing {index, appcache, rest}
     */
    function start(pProcessing) {
        var lConfig         = main.config;

        if(!pProcessing)
            pProcessing = {};

        Rest            =   pProcessing.rest;
        Route           =   pProcessing.route;
        Minimize        =   pProcessing.minimize;

        init(pProcessing.appcache);

        var lPort       =   process.env.PORT            ||  /* c9           */
                            process.env.app_port        ||  /* nodester     */
                            process.env.VCAP_APP_PORT   ||  /* cloudfoundry */
                            lConfig.port,

            lIP         =   process.env.IP              ||  /* c9           */
                            lConfig.ip                  ||
                            (main.WIN32 ? '127.0.0.1' : '0.0.0.0'),

            lSSL        =   pProcessing.ssl,
            lSSLPort    = lConfig.sslPort,

            lSockets    = function(pServer){
                var lListen;
                if(lConfig.socket && Socket)
                    lListen = Socket.listen(pServer);

                Util.log('* Sockets ' + (lListen ? 'running' : 'disabled'));
            },

            lHTTPServer = function(){
                Server  =  http.createServer( controller );
                Server.listen(lPort, lIP);
                lServerLog('http', lPort);
                lSockets(Server);
            },

            lServerLog  = function(pHTTP, pPort){
                Util.log('* Server running at ' + pHTTP + '://' + lIP + ':' + pPort);
            };
        /* server mode or testing mode */
        if (lConfig.server) {
            if(lSSL){
                var lRedirectServer  =  http.createServer( function(pReq, pRes){
                    main.redirect({
                        response: pRes,
                        url: pReq.url
                    });
                });

                lRedirectServer.listen(lPort, lIP);

                Server  =  https.createServer( lSSL, controller );
                Server.on('error', function (pError) {
                    Util.log('Could not use https port: ' + lSSLPort);
                    Util.log(pError);
                    lRedirectServer.close();

                    lHTTPServer();
                });

                lSockets();

                Server.listen(lSSLPort, lIP);
                lServerLog('https', lSSLPort);
            }
            else
                lHTTPServer();
        }else
            Util.log('Cloud Commander testing mode');
    }

    /**
     * Главная функция, через которую проихсодит
     * взаимодействие, обмен данными с клиентом
     * @param req - запрос клиента (Request)
     * @param res - ответ сервера (Response)
     */
    function controller(pReq, pRes)
    {
        /* Читаем содержимое папки, переданное в url */
        var lRet,
            lConfig     = main.config,
            lParsedUrl  = URL.parse(pReq.url),
            lPath       = lParsedUrl.pathname;

        /* added supporting of Russian language in directory names */
        lPath = Querystring.unescape(lPath);
        Util.log('pathname: ' + lPath);

        Util.log("request for " + lPath + " received...");

        if( lConfig.rest )
            lRet = Util.exec(Rest, {
                name        : lPath,
                request     : pReq,
                response    : pRes
            });

        if( !lRet && Route)
            lRet = Util.exec(Route, {
                    name        : lPath,
                    request     : pReq,
                    response    : pRes
                });

        if(!lRet){
            /* добавляем текующий каталог к пути */
            var lName = '.' + lPath;
            Util.log('reading ' + lName);

            /* watching is file changed */
            if(lConfig.appcache)
                AppCache.watch(lName);

            Util.log(Path.basename(lName));

            var lMin    = Minify.allowed,
                lExt    = Util.getExtension(lName),
                lResult =   lExt === '.js'      && lMin.js  ||
                            lExt === '.css'     && lMin.css ||
                            lExt === '.html'    && lMin.html;

            Util.ifExec(!lResult,
                function(pParams){
                    var lSendName = pParams && pParams.name || lName;

                    main.sendFile({
                        name        : lSendName,
                        request     : pReq,
                        response    : pRes
                    });
            }, function(pCallBack){
                Minify.optimize(lName, {
                    request     : pReq,
                    response    : pRes,
                    callback    : pCallBack,
                    returnName  : true
                });
            });
        }
    }

    exports.start           = start;

})();
<commit_msg>minor changes<commit_after>(function(){
    'use strict';

    if(!global.cloudcmd)
        return console.log(
             '# server.js'                                      + '\n'  +
             '# -----------'                                    + '\n'  +
             '# Module is part of Cloud Commander,'             + '\n'  +
             '# easy to use web server.'                        + '\n'  +
             '# http://coderaiser.github.com/cloudcmd'          + '\n');

    var main                = global.cloudcmd.main,

        DIR                 = main.Dir,
        LIBDIR              = main.LIBDIR,
        SRVDIR              = main.SRVDIR,

        URL                 = main.url,
        Path                = main.path,
        Querystring         = main.querystring,

        Minify              = main.minify,
        AppCache            = main.appcache,
        Socket              = main.socket,

        http                = main.http,
        https               = main.https,
        Util                = main.util,

        Server, Rest, Route, Minimize;

    /* базовая инициализация  */
    function init(pAppCachProcessing){
        var lConfig         = main.config,
            lMinifyAllowed  = lConfig.minification;

        /* Change default parameters of
         * js/css/html minification
         */
        Minify.setAllowed(lMinifyAllowed);

        /* Если нужно минимизируем скрипты */
        Util.exec(Minimize, lMinifyAllowed);

        /* создаём файл app cache */
        if( lConfig.appcache  && AppCache && lConfig.server )
            Util.exec( pAppCachProcessing );
    }

    /**
     * start server function
     * @param pConfig
     * @param pProcessing {index, appcache, rest}
     */
    function start(pProcessing) {
        var lConfig         = main.config;

        if(!pProcessing)
            pProcessing = {};

        Rest            =   pProcessing.rest;
        Route           =   pProcessing.route;
        Minimize        =   pProcessing.minimize;

        init(pProcessing.appcache);

        var lPort       =   process.env.PORT            ||  /* c9           */
                            process.env.app_port        ||  /* nodester     */
                            process.env.VCAP_APP_PORT   ||  /* cloudfoundry */
                            lConfig.port,

            lIP         =   process.env.IP              ||  /* c9           */
                            lConfig.ip                  ||
                            (main.WIN32 ? '127.0.0.1' : '0.0.0.0'),

            lSSL        =   pProcessing.ssl,
            lSSLPort    = lConfig.sslPort,

            lSockets    = function(pServer){
                var lListen;
                if(lConfig.socket && Socket)
                    lListen = Socket.listen(pServer);

                Util.log('* Sockets ' + (lListen ? 'running' : 'disabled'));
            },

            lHTTPServer = function(){
                Server  =  http.createServer( controller );
                Server.listen(lPort, lIP);
                lServerLog('http', lPort);
                lSockets(Server);
            },

            lServerLog  = function(pHTTP, pPort){
                Util.log('* Server running at ' + pHTTP + '://' + lIP + ':' + pPort);
            };
        /* server mode or testing mode */
        if (lConfig.server) {
            if(lSSL){
                var lRedirectServer  =  http.createServer( function(pReq, pRes){
                    main.redirect({
                        response: pRes,
                        url: 'https://' + pReq.url
                    });
                });

                lRedirectServer.listen(lPort, lIP);

                Server  =  https.createServer( lSSL, controller );
                Server.on('error', function (pError) {
                    Util.log('Could not use https port: ' + lSSLPort);
                    Util.log(pError);
                    lRedirectServer.close();

                    lHTTPServer();
                });

                lSockets();

                Server.listen(lSSLPort, lIP);
                lServerLog('https', lSSLPort);
            }
            else
                lHTTPServer();
        }else
            Util.log('Cloud Commander testing mode');
    }

    /**
     * Главная функция, через которую проихсодит
     * взаимодействие, обмен данными с клиентом
     * @param req - запрос клиента (Request)
     * @param res - ответ сервера (Response)
     */
    function controller(pReq, pRes)
    {
        /* Читаем содержимое папки, переданное в url */
        var lRet,
            lConfig     = main.config,
            lParsedUrl  = URL.parse(pReq.url),
            lPath       = lParsedUrl.pathname;

        /* added supporting of Russian language in directory names */
        lPath = Querystring.unescape(lPath);
        Util.log('pathname: ' + lPath);

        Util.log("request for " + lPath + " received...");

        if( lConfig.rest )
            lRet = Util.exec(Rest, {
                name        : lPath,
                request     : pReq,
                response    : pRes
            });

        if( !lRet && Route)
            lRet = Util.exec(Route, {
                    name        : lPath,
                    request     : pReq,
                    response    : pRes
                });

        if(!lRet){
            /* добавляем текующий каталог к пути */
            var lName = '.' + lPath;
            Util.log('reading ' + lName);

            /* watching is file changed */
            if(lConfig.appcache)
                AppCache.watch(lName);

            Util.log(Path.basename(lName));

            var lMin    = Minify.allowed,
                lExt    = Util.getExtension(lName),
                lResult =   lExt === '.js'      && lMin.js  ||
                            lExt === '.css'     && lMin.css ||
                            lExt === '.html'    && lMin.html;

            Util.ifExec(!lResult,
                function(pParams){
                    var lSendName = pParams && pParams.name || lName;

                    main.sendFile({
                        name        : lSendName,
                        request     : pReq,
                        response    : pRes
                    });
            }, function(pCallBack){
                Minify.optimize(lName, {
                    request     : pReq,
                    response    : pRes,
                    callback    : pCallBack,
                    returnName  : true
                });
            });
        }
    }

    exports.start           = start;

})();
<|endoftext|>


<commit_before>/*

server.js
=========

Testem's server. Serves up the HTML, JS, and CSS required for
running the tests in a browser.

*/

var Express = require('express')
  , SocketIO = require('socket.io')
  , BrowserRunner = require('./runners').BrowserRunner
  , Mustache = require('./mustache.exp')
  , fs = require('fs')
  , path = require('path')
  , util = require('util')
  , async = require('async')
  , glob = require('glob')
  , isa = require('./isa')
  , log = require('winston')
  , EventEmitter = require('events').EventEmitter
  , Backbone = require('backbone')
  , path = require('path')
  //, log = new (require('log'))('info', fs.createWriteStream('testem.log2'))

require('./socket.io.patch')

function Server(app){
    this.app = app

    // Build the server
    this.exp = Express.createServer()
    this.initServer()
    this.ieCompatMode = null
}
Server.prototype = {
    __proto__: EventEmitter.prototype
    , start: function(){
        // Start the server!
        this.exp.listen(this.app.config.get('port'))
        process.nextTick(function(){
            this.emit('server-start')
        }.bind(this))
    }
    , initServer: function(){
        var self = this
          , config = this.app.config
          , exp = this.exp
        exp.configure(function(){
            exp.register(".html", Mustache)
            exp.set("view options", {layout: false})
            exp.use(function(req, res, next){
                if (self.ieCompatMode)
                    res.setHeader('X-UA-Compatible', 'IE=' + self.ieCompatMode)
                next()
            })
            exp.use(Express.static(__dirname + '/../public'))
        })
        exp.get('/', function(req, res){
            var framework = config.get('framework') || 'jasmine'
              , test_page = config.get('test_page')
              , serve_files = config.get('serve_files') || config.get('src_files')
              , css_files = config.get('css_files')

            res.header('Cache-Control', 'No-cache')
            res.header('Pragma', 'No-cache')
            function render(err, files){
                var runnerPage = {
                    jasmine: __dirname + '/../views/jasminerunner.html'
                    , qunit: __dirname + '/../views/qunitrunner.html'
                    , mocha: __dirname + '/../views/mocharunner.html'
                    , custom: __dirname + '/../views/customrunner.html'
                }[framework]
                res.render(runnerPage, {
                    locals: {
                      scripts: files,
                      styles: css_files
                    }
                })
            }

            if (test_page){
                var url = '/' + test_page
                res.redirect(url + '#testem')
            }else if (isa(serve_files, Array)){
                // TODO: dedup
                async.reduce(serve_files, [], function(curr, pattern, next){
                    glob(pattern, function(err, files){
                        if (err) next(null, curr)
                        else next(null, curr.concat(files))
                    })
                }, render)
            }else{
                fs.readdir(process.cwd(), function(err, files){
                    files = files.filter(function(file){
                        return !!file.match(/\.js$/)
                    })
                    render(err, files)
                })
            }
        })

        exp.get('/testem.js', function(req, res){

            res.setHeader('Content-Type', 'text/javascript')

            res.write(';(function(){')
            var files = [
                __dirname + '/../public/testem/socket.io.js'
                , __dirname + '/../public/testem/jasmine_adapter.js'
                , __dirname + '/../public/testem/qunit_adapter.js'
                , __dirname + '/../public/testem/mocha_adapter.js'
                , __dirname + '/../public/testem/testem_client.js'
            ]
            async.forEachSeries(files, function(file, done){
                fs.readFile(file, function(err, data){
                    if (err){
                        res.write('// Error reading ' + file + ': ' + err)
                    }else{
                        res.write('\n//============== ' + path.basename(file) + ' ==================\n\n')
                        res.write(data)
                    }
                    done()
                })
            }, function(){
                res.write('}());')
                res.end()
            })

        })

        function killTheCache(req, res){
            res.setHeader('Cache-Control', 'No-cache')
            res.setHeader('Pragma', 'No-cache')
            delete req.headers['if-modified-since']
            delete req.headers['if-none-match']
        }

        function route(uri){
            var routes = config.get('routes') || config.get('route') || {}
            var bestMatchLength = 0
            var bestMatch = null
            for (var prefix in routes){
                if (uri.substring(0, prefix.length) === prefix){
                    if (bestMatchLength < prefix.length){
                        bestMatch = routes[prefix] + '/' + uri.substring(prefix.length)
                        bestMatchLength = prefix.length
                    }
                }
            }
            return bestMatch || uri.substring(1) // get rid of leading slash
        }

        // serve a static file from FS
        function serveStaticFile(req, res){
            killTheCache(req, res)
            var allowUnsafeDirs = config.get('unsafe_file_serving')
            var uri = req.params[0]
            var uri = route(uri)
            var filePath = path.resolve(process.cwd(), uri)
            var isPathPermitted = filePath.indexOf(process.cwd()) !== -1
            if (!allowUnsafeDirs && !isPathPermitted) {
                res.status(403)
                res.write('403 Forbidden')
                res.end()
            } else {
                self.emit('file-requested', filePath)
                res.sendfile(filePath)
            }
        }
        // Everything falls back to serving a static file from the FS
        exp.get(/^(.+)$/, serveStaticFile)

        // Create socket.io sockets
        this.io = SocketIO.listen(this.exp)

        this.io.sockets.on('connection', this.onClientConnected.bind(this))

    }
    , onClientConnected: function(client){
        var app = this.app
        client.once('browser-login', function(browserName){
            log.info('New client connected: ' + browserName)
            app.connectBrowser(browserName, client)
        })
    }
    , removeBrowser: function(browser){
        this.app.removeBrowser(browser)
    }
}

module.exports = Server
<commit_msg>Small cleaner.<commit_after>/*

server.js
=========

Testem's server. Serves up the HTML, JS, and CSS required for
running the tests in a browser.

*/

var Express = require('express')
  , SocketIO = require('socket.io')
  , BrowserRunner = require('./runners').BrowserRunner
  , Mustache = require('./mustache.exp')
  , fs = require('fs')
  , path = require('path')
  , util = require('util')
  , async = require('async')
  , glob = require('glob')
  , isa = require('./isa')
  , log = require('winston')
  , EventEmitter = require('events').EventEmitter
  , Backbone = require('backbone')
  , path = require('path')
  //, log = new (require('log'))('info', fs.createWriteStream('testem.log2'))

require('./socket.io.patch')

function Server(app){
    this.app = app

    // Build the server
    this.exp = Express.createServer()
    this.initServer()
    this.ieCompatMode = null
}
Server.prototype = {
    __proto__: EventEmitter.prototype
    , start: function(){
        // Start the server!
        this.exp.listen(this.app.config.get('port'))
        process.nextTick(function(){
            this.emit('server-start')
        }.bind(this))
    }
    , initServer: function(){
        var self = this
          , config = this.app.config
          , exp = this.exp
        exp.configure(function(){
            exp.register(".html", Mustache)
            exp.set("view options", {layout: false})
            exp.use(function(req, res, next){
                if (self.ieCompatMode)
                    res.setHeader('X-UA-Compatible', 'IE=' + self.ieCompatMode)
                next()
            })
            exp.use(Express.static(__dirname + '/../public'))
        })
        exp.get('/', function(req, res){
            var framework = config.get('framework') || 'jasmine'
              , test_page = config.get('test_page')
              , serve_files = config.get('serve_files') || config.get('src_files')
              , css_files = config.get('css_files')

            res.header('Cache-Control', 'No-cache')
            res.header('Pragma', 'No-cache')
            function render(err, files){
                var runnerPage = {
                    jasmine: __dirname + '/../views/jasminerunner.html'
                    , qunit: __dirname + '/../views/qunitrunner.html'
                    , mocha: __dirname + '/../views/mocharunner.html'
                    , custom: __dirname + '/../views/customrunner.html'
                }[framework]
                res.render(runnerPage, {
                    locals: {
                      scripts: files,
                      styles: css_files
                    }
                })
            }

            if (test_page){
                var url = '/' + test_page
                res.redirect(url + '#testem')
            }else if (isa(serve_files, Array)){
                // TODO: dedup
                async.reduce(serve_files, [], function(curr, pattern, next){
                    glob(pattern, function(err, files){
                        if (err) next(null, curr)
                        else next(null, curr.concat(files))
                    })
                }, render)
            }else{
                fs.readdir(process.cwd(), function(err, files){
                    files = files.filter(function(file){
                        return !!file.match(/\.js$/)
                    })
                    render(err, files)
                })
            }
        })

        exp.get('/testem.js', function(req, res){

            res.setHeader('Content-Type', 'text/javascript')

            res.write(';(function(){')
            var files = [
                __dirname + '/../public/testem/socket.io.js'
                , __dirname + '/../public/testem/jasmine_adapter.js'
                , __dirname + '/../public/testem/qunit_adapter.js'
                , __dirname + '/../public/testem/mocha_adapter.js'
                , __dirname + '/../public/testem/testem_client.js'
            ]
            async.forEachSeries(files, function(file, done){
                fs.readFile(file, function(err, data){
                    if (err){
                        res.write('// Error reading ' + file + ': ' + err)
                    }else{
                        res.write('\n//============== ' + path.basename(file) + ' ==================\n\n')
                        res.write(data)
                    }
                    done()
                })
            }, function(){
                res.write('}());')
                res.end()
            })

        })

        function killTheCache(req, res){
            res.setHeader('Cache-Control', 'No-cache')
            res.setHeader('Pragma', 'No-cache')
            delete req.headers['if-modified-since']
            delete req.headers['if-none-match']
        }

        function route(uri){
            var routes = config.get('routes') || config.get('route') || {}
            var bestMatchLength = 0
            var bestMatch = null
            for (var prefix in routes){
                if (uri.substring(0, prefix.length) === prefix){
                    if (bestMatchLength < prefix.length){
                        bestMatch = routes[prefix] + '/' + uri.substring(prefix.length)
                        bestMatchLength = prefix.length
                    }
                }
            }
            return bestMatch || uri.substring(1) // get rid of leading slash
        }

        // serve a static file from FS
        function serveStaticFile(req, res){
            killTheCache(req, res)
            var allowUnsafeDirs = config.get('unsafe_file_serving')
            var uri = route(req.params[0])
            var filePath = path.resolve(process.cwd(), uri)
            var isPathPermitted = filePath.indexOf(process.cwd()) !== -1
            if (!allowUnsafeDirs && !isPathPermitted) {
                res.status(403)
                res.write('403 Forbidden')
                res.end()
            } else {
                self.emit('file-requested', filePath)
                res.sendfile(filePath)
            }
        }
        // Everything falls back to serving a static file from the FS
        exp.get(/^(.+)$/, serveStaticFile)

        // Create socket.io sockets
        this.io = SocketIO.listen(this.exp)

        this.io.sockets.on('connection', this.onClientConnected.bind(this))

    }
    , onClientConnected: function(client){
        var app = this.app
        client.once('browser-login', function(browserName){
            log.info('New client connected: ' + browserName)
            app.connectBrowser(browserName, client)
        })
    }
    , removeBrowser: function(browser){
        this.app.removeBrowser(browser)
    }
}

module.exports = Server
<|endoftext|>


<commit_before>'use strict';

var utils = require('./utils');

function Slices(width, height, lineXArray, lineYArray, options) {
  options = options || {};

  this.width = width;
  this.height = height;

  this.lineXArray = utils.sortAndUnique(lineXArray) || [];
  this.lineYArray = utils.sortAndUnique(lineYArray) || [];

  // 生成参考线的 X Y 方向的边界
  this.lineXArray.push(height);
  this.lineYArray.push(width);

  // instance properties
  this.options = {};

  // extend instance properties with global defaults and initial properties
  utils.extend(this.options, this.defaults, options);

  // there are no reference lines
  if (!this.lineXArray.length && !this.lineYArray.length) {
    throw new Error('At least one reference line');
  }

  return this;
}

Slices.prototype.defaults = {
  middleBoundaryMode: false
};

Slices.prototype.slice = function() {

  var options = this.options;

  var lineXArray = this.lineXArray;
  var lineYArray = this.lineYArray;
  var width = this.width;
  // var height = this.height;
  var middleBoundaryMode = options.middleBoundaryMode;

  var lineXLength = lineXArray.length;
  var lineYLength = lineYArray.length;

  // 中间边界模式
  // 这时会把 X 轴之间的空间作为一个父区块
  // 并把第一和最后的 Y 轴之间的区块作为父区块的子级，同时生成边界信息
  // Y 轴必须大于 2 根
  if (middleBoundaryMode) {
    if (lineYLength < 3) {
      middleBoundaryMode = false;
    }
  }

  var previousX = 0;
  var previousY = 0;
  var curX, curY, x, y, xEnd, yEnd;

  // full row when is at middleBoundaryMode
  // 整行区块信息
  var fullRow;

  // assign to child block info between the first Y line and the last Y line
  // when is at middleBoundaryMode
  // 中间边界模式时，第 1 根 与最后一根 Y 轴之间的子区块
  var middleChildBlock;
  var parentBlockIndex = 0;

  var blocks = [];

  // 只有 X 轴或 Y 轴的情况
  if (!lineYLength || !lineXLength) {
    return this.getChildBlocks();
  }

  // 非中间边界模式
  if (!middleBoundaryMode) {
    return this.getChildBlocks();
  }

  // 循环 X 和 Y 轴上的参考线，得到区块信息
  for (x = 0, xEnd = lineXLength; x < xEnd; x++) {
    curX = lineXArray[x];

    previousY = lineYArray[0];

    fullRow = {
      width: width,
      height: curX - previousX,
      x: 0,
      y: x === 0 ? 0 : previousX,
      children: [],
      // 需要抹擦去的图片区域
      boundary: {
        leftTop: {
          x: lineYArray[0],
          y: x === 0 ? 0 : previousX
        },
        rightBottom: {
          x: lineYArray[lineYLength - 2],
          y: curX
        }
      }
    };

    blocks.push(fullRow);

    for (y = 1, yEnd = lineYLength - 1; y < yEnd; y++) {
      curY = lineYArray[y];

      middleChildBlock = {
        width: curY - previousY,
        height: curX - previousX,
        x: previousY,
        y: x === 0 ? 0 : previousX
      };

      // 子区块附加额外信息
      // left: 距中间主区块左边界的绝对距离
      // top: 距中间主区块上边界的绝对距离，由于一直靠着最顶部的位置，所以一直是 0
      // parentBlockIndex: 子区块所属父区块的索引值
      // index: 子区块在父区块范围之内的索引值
      utils.extend(middleChildBlock, {
        left: y === 1 ? 0 : previousY - lineYArray[0],
        top: 0,
        parentBlockIndex: parentBlockIndex,
        index: y - 1
      });

      // 将子区块插入到父区块信息中
      fullRow.children.push(middleChildBlock);

      previousY = curY;
    }

    parentBlockIndex++;

    previousX = curX;
  }

  return blocks;
};

Slices.prototype.getChildBlocks = function() {

  var lineXArray = this.lineXArray;
  var lineYArray = this.lineYArray;
  var width = this.width;
  var height = this.height;

  var lineXLength = lineXArray.length;
  var lineYLength = lineYArray.length;

  var previousX = 0;
  var previousY = 0;
  var curX, curY, x, y, xEnd, yEnd;

  var blocks = [];

  // X 轴上有参考线
  if (lineXLength) {
    for (x = 0, xEnd = lineXLength; x < xEnd; x++) {
      curX = lineXArray[x];

      previousY = 0;

      // Y 轴上也有参考线
      if (lineYLength) {
        for (y = 0, yEnd = lineYLength; y < yEnd; y++) {
          curY = lineYArray[y];

          blocks.push({
            width: curY - previousY,
            height: curX - previousX,
            x: previousY,
            y: x === 0 ? 0 : previousX
          });

          previousY = curY;
        }
      }
      // 只有 X 轴的情况
      else {
        blocks.push({
          width: width,
          height: curX - previousX,
          x: 0,
          y: x === 0 ? 0 : previousX
        });
      }

      previousX = curX;
    }
  }
  // 只有 Y 轴的情况
  else {
    previousY = 0;

    for (y = 0, yEnd = lineYLength; y < yEnd; y++) {
      curY = lineYArray[y];

      blocks.push({
        width: curY - previousY,
        height: height,
        x: x === 0 ? 0 : previousY,
        y: 0
      });

      previousY = curY;
    }
  }

  return blocks;
};

module.exports = Slices;<commit_msg>sort and unique lineXArray and lineYArray<commit_after>'use strict';

var utils = require('./utils');

function Slices(width, height, lineXArray, lineYArray, options) {
  options = options || {};

  this.width = width;
  this.height = height;

  lineXArray = lineXArray || [];
  lineYArray = lineYArray || [];

  // 生成参考线的 X Y 方向的边界
  lineXArray.push(height);
  lineYArray.push(width);

  this.lineXArray = utils.sortAndUnique(lineXArray);
  this.lineYArray = utils.sortAndUnique(lineYArray);

  // instance properties
  this.options = {};

  // extend instance properties with global defaults and initial properties
  utils.extend(this.options, this.defaults, options);

  // there are no reference lines
  if (!this.lineXArray.length && !this.lineYArray.length) {
    throw new Error('At least one reference line');
  }

  return this;
}

Slices.prototype.defaults = {
  middleBoundaryMode: false
};

Slices.prototype.slice = function() {

  var options = this.options;

  var lineXArray = this.lineXArray;
  var lineYArray = this.lineYArray;
  var width = this.width;
  // var height = this.height;
  var middleBoundaryMode = options.middleBoundaryMode;

  var lineXLength = lineXArray.length;
  var lineYLength = lineYArray.length;

  // 中间边界模式
  // 这时会把 X 轴之间的空间作为一个父区块
  // 并把第一和最后的 Y 轴之间的区块作为父区块的子级，同时生成边界信息
  // Y 轴必须大于 2 根
  if (middleBoundaryMode) {
    if (lineYLength < 3) {
      middleBoundaryMode = false;
    }
  }

  var previousX = 0;
  var previousY = 0;
  var curX, curY, x, y, xEnd, yEnd;

  // full row when is at middleBoundaryMode
  // 整行区块信息
  var fullRow;

  // assign to child block info between the first Y line and the last Y line
  // when is at middleBoundaryMode
  // 中间边界模式时，第 1 根 与最后一根 Y 轴之间的子区块
  var middleChildBlock;
  var parentBlockIndex = 0;

  var blocks = [];

  // 只有 X 轴或 Y 轴的情况
  if (!lineYLength || !lineXLength) {
    return this.getChildBlocks();
  }

  // 非中间边界模式
  if (!middleBoundaryMode) {
    return this.getChildBlocks();
  }

  // 循环 X 和 Y 轴上的参考线，得到区块信息
  for (x = 0, xEnd = lineXLength; x < xEnd; x++) {
    curX = lineXArray[x];

    previousY = lineYArray[0];

    fullRow = {
      width: width,
      height: curX - previousX,
      x: 0,
      y: x === 0 ? 0 : previousX,
      children: [],
      // 需要抹擦去的图片区域
      boundary: {
        leftTop: {
          x: lineYArray[0],
          y: x === 0 ? 0 : previousX
        },
        rightBottom: {
          x: lineYArray[lineYLength - 2],
          y: curX
        }
      }
    };

    blocks.push(fullRow);

    for (y = 1, yEnd = lineYLength - 1; y < yEnd; y++) {
      curY = lineYArray[y];

      middleChildBlock = {
        width: curY - previousY,
        height: curX - previousX,
        x: previousY,
        y: x === 0 ? 0 : previousX
      };

      // 子区块附加额外信息
      // left: 距中间主区块左边界的绝对距离
      // top: 距中间主区块上边界的绝对距离，由于一直靠着最顶部的位置，所以一直是 0
      // parentBlockIndex: 子区块所属父区块的索引值
      // index: 子区块在父区块范围之内的索引值
      utils.extend(middleChildBlock, {
        left: y === 1 ? 0 : previousY - lineYArray[0],
        top: 0,
        parentBlockIndex: parentBlockIndex,
        index: y - 1
      });

      // 将子区块插入到父区块信息中
      fullRow.children.push(middleChildBlock);

      previousY = curY;
    }

    parentBlockIndex++;

    previousX = curX;
  }

  return blocks;
};

Slices.prototype.getChildBlocks = function() {

  var lineXArray = this.lineXArray;
  var lineYArray = this.lineYArray;
  var width = this.width;
  var height = this.height;

  var lineXLength = lineXArray.length;
  var lineYLength = lineYArray.length;

  var previousX = 0;
  var previousY = 0;
  var curX, curY, x, y, xEnd, yEnd;

  var blocks = [];

  // X 轴上有参考线
  if (lineXLength) {
    for (x = 0, xEnd = lineXLength; x < xEnd; x++) {
      curX = lineXArray[x];

      previousY = 0;

      // Y 轴上也有参考线
      if (lineYLength) {
        for (y = 0, yEnd = lineYLength; y < yEnd; y++) {
          curY = lineYArray[y];

          blocks.push({
            width: curY - previousY,
            height: curX - previousX,
            x: previousY,
            y: x === 0 ? 0 : previousX
          });

          previousY = curY;
        }
      }
      // 只有 X 轴的情况
      else {
        blocks.push({
          width: width,
          height: curX - previousX,
          x: 0,
          y: x === 0 ? 0 : previousX
        });
      }

      previousX = curX;
    }
  }
  // 只有 Y 轴的情况
  else {
    previousY = 0;

    for (y = 0, yEnd = lineYLength; y < yEnd; y++) {
      curY = lineYArray[y];

      blocks.push({
        width: curY - previousY,
        height: height,
        x: x === 0 ? 0 : previousY,
        y: 0
      });

      previousY = curY;
    }
  }

  return blocks;
};

module.exports = Slices;<|endoftext|>


<commit_before>var coreEvents = require('events')
  , iterators = require('./iterators')
  , inverter = require('./inverter');

function namespace(ns, state) {
  return [ ns, state ].join(':');
}

function handle(event, transitions, context) {
  return function () {
    var state = context.state()
      , next = transitions[state];

    if (state !== next) {
      context._emitter.emit(namespace('exit', state));
      context._state = next;
      context._emitter.emit(namespace('enter', next));
    }
  };
}

function Machine(definition) {
  var events = definition.events || inverter.invert(definition.states);

  this._state = definition.initial;
  this._emitter = new coreEvents.EventEmitter();

  iterators.iterate(events, function (transitions, event) {
    this._emitter.on(event, handle(event, transitions, this));
  }, this);
}

Machine.prototype.state = function () {
  return this._state;
};

Machine.prototype.send = function (event) {
  this._emitter.emit(event);
};

Machine.prototype.onEnter = function (state, observer) {
  this._emitter.on(namespace('enter', state), observer);
};

Machine.prototype.onExit = function (state, observer) {
  this._emitter.on(namespace('exit', state), observer);
};

module.exports = {

  machine: function (definition) {
    return new Machine(definition);
  }

};

module.exports = {

  machine: function (definition) {
    return new Machine(definition);
  }

};
<commit_msg>Removes duplicate module.exports<commit_after>var coreEvents = require('events')
  , iterators = require('./iterators')
  , inverter = require('./inverter');

function namespace(ns, state) {
  return [ ns, state ].join(':');
}

function handle(event, transitions, context) {
  return function () {
    var state = context.state()
      , next = transitions[state];

    if (state !== next) {
      context._emitter.emit(namespace('exit', state));
      context._state = next;
      context._emitter.emit(namespace('enter', next));
    }
  };
}

function Machine(definition) {
  var events = definition.events || inverter.invert(definition.states);

  this._state = definition.initial;
  this._emitter = new coreEvents.EventEmitter();

  iterators.iterate(events, function (transitions, event) {
    this._emitter.on(event, handle(event, transitions, this));
  }, this);
}

Machine.prototype.state = function () {
  return this._state;
};

Machine.prototype.send = function (event) {
  this._emitter.emit(event);
};

Machine.prototype.onEnter = function (state, observer) {
  this._emitter.on(namespace('enter', state), observer);
};

Machine.prototype.onExit = function (state, observer) {
  this._emitter.on(namespace('exit', state), observer);
};

module.exports = {

  machine: function (definition) {
    return new Machine(definition);
  }

};
<|endoftext|>


<commit_before>Ink.requireModules(['Ink.UI.Drawer_1', 'Ink.Dom.Css_1', 'Ink.Dom.Event_1', 'Ink.Dom.Element_1'], function (Drawer, Css, InkEvent, InkElement) {

    var leftTrigger = document.body.appendChild(InkElement.create('div', {
        className: 'left-drawer-trigger'
    }))
    var rightTrigger = document.body.appendChild(InkElement.create('div', {
        className: 'right-drawer-trigger'
    }))

    var contentDrawer = document.body.appendChild(InkElement.create('div', {
        className: 'content-drawer'
    }))

    var leftDrawer = contentDrawer.appendChild(InkElement.create('div', {
        className: 'left-drawer'
    }))
    var rightDrawer = contentDrawer.appendChild(InkElement.create('div', {
        className: 'right-drawer'
    }))

    // Make our setTimeout calls which cause reflows or do animations instantaneous
    sinon.stub(window, 'setTimeout').callsArg(0);

    var drawer = new Drawer(document.body)

    test('Clicking the drawer buttons results in a call to Drawer\'s _onTriggerClicked(ev, side)', function () {
        var triggerClicked = sinon.stub(drawer, '_onTriggerClicked')
        stop(2)
        Syn.click(leftTrigger, function () {
            start()
            ok(triggerClicked.calledOnce)
            strictEqual(triggerClicked.lastCall.args[1], 'left')

            Syn.click(rightTrigger, function () {
                start()
                ok(triggerClicked.calledTwice)
                triggerClicked.restore()
                strictEqual(triggerClicked.lastCall.args[1], 'right')
            })
        })
    })

    test('Clicking the content drawer when the drawer is open leads to a close() call', function () {
        drawer.open('left')
        sinon.spy(drawer, 'close')
        stop()
        Syn.click(contentDrawer, function () {
            ok(drawer.close.calledOnce)
            drawer.close.restore()
            start()
        })
    })

    test('_onTriggerClicked', sinon.test(function () {
        this.stub(drawer, 'open')
        this.stub(drawer, 'close')
        var fakeEvent = {
            preventDefault: this.spy()
        }

        drawer._onTriggerClicked(fakeEvent, 'the side')

        ok(drawer.open.calledOnce, 'open() was called')
        ok(drawer.open.calledWith('the side'))

        drawer._isOpen = true;

        drawer._onTriggerClicked(fakeEvent)

        ok(drawer.close.calledOnce, 'close() was called')

        ok(fakeEvent.preventDefault.calledTwice, 'preventDefault was called once each call')

        drawer._isOpen = false;
    }))

    test('open() and close()', function () {
        drawer.open('left')

        // the open() function is actually async because it needs
        // to trigger a reflow.
        ok(Css.hasClassName(document.body, ['push', 'left']));
        ok(Css.hasClassName(leftDrawer, 'show'));

        drawer.close()
        ok(!Css.hasClassName(document.body, 'push'));
        ok(!Css.hasClassName(document.body, 'left'));

        if (!Drawer.transitionSupport) {
            return ok(!Css.hasClassName(leftDrawer, 'show'));
        }

        ok(Css.hasClassName(leftDrawer, 'show'));
        InkEvent.fire(leftDrawer, Drawer.transitionSupport.eventName);
        ok(!Css.hasClassName(leftDrawer, 'show'));
    });

    test('focusing outside the drawer causes it to close', sinon.test(function () {
        this.stub(drawer, 'close');

        drawer.open();

        InkEvent.fire(contentDrawer, 'focus');

        ok(drawer.close.calledOnce, 'close() was called because of focus');
    }));
})
<commit_msg>Skip Drawer test in incompatible IE<commit_after>Ink.requireModules(['Ink.UI.Drawer_1', 'Ink.Dom.Css_1', 'Ink.Dom.Event_1', 'Ink.Dom.Element_1', 'Ink.Dom.Browser_1'], function (Drawer, Css, InkEvent, InkElement, Browser) {

    if (Browser.IE && Browser.version <= 8) {
        test('(skipping test because of incompatible IE version)', function () {
            ok(true, 'Skippin\'');
        })
        return;
    }

    var leftTrigger = document.body.appendChild(InkElement.create('div', {
        className: 'left-drawer-trigger'
    }))
    var rightTrigger = document.body.appendChild(InkElement.create('div', {
        className: 'right-drawer-trigger'
    }))

    var contentDrawer = document.body.appendChild(InkElement.create('div', {
        className: 'content-drawer'
    }))

    var leftDrawer = contentDrawer.appendChild(InkElement.create('div', {
        className: 'left-drawer'
    }))
    var rightDrawer = contentDrawer.appendChild(InkElement.create('div', {
        className: 'right-drawer'
    }))

    // Make our setTimeout calls which cause reflows or do animations instantaneous
    sinon.stub(window, 'setTimeout').callsArg(0);

    var drawer = new Drawer(document.body)

    test('Clicking the drawer buttons results in a call to Drawer\'s _onTriggerClicked(ev, side)', function () {
        var triggerClicked = sinon.stub(drawer, '_onTriggerClicked')
        stop(2)
        Syn.click(leftTrigger, function () {
            start()
            ok(triggerClicked.calledOnce)
            strictEqual(triggerClicked.lastCall.args[1], 'left')

            Syn.click(rightTrigger, function () {
                start()
                ok(triggerClicked.calledTwice)
                triggerClicked.restore()
                strictEqual(triggerClicked.lastCall.args[1], 'right')
            })
        })
    })

    test('Clicking the content drawer when the drawer is open leads to a close() call', function () {
        drawer.open('left')
        sinon.spy(drawer, 'close')
        stop()
        Syn.click(contentDrawer, function () {
            ok(drawer.close.calledOnce)
            drawer.close.restore()
            start()
        })
    })

    test('_onTriggerClicked', sinon.test(function () {
        this.stub(drawer, 'open')
        this.stub(drawer, 'close')
        var fakeEvent = {
            preventDefault: this.spy()
        }

        drawer._onTriggerClicked(fakeEvent, 'the side')

        ok(drawer.open.calledOnce, 'open() was called')
        ok(drawer.open.calledWith('the side'))

        drawer._isOpen = true;

        drawer._onTriggerClicked(fakeEvent)

        ok(drawer.close.calledOnce, 'close() was called')

        ok(fakeEvent.preventDefault.calledTwice, 'preventDefault was called once each call')

        drawer._isOpen = false;
    }))

    test('open() and close()', function () {
        drawer.open('left')

        // the open() function is actually async because it needs
        // to trigger a reflow.
        ok(Css.hasClassName(document.body, ['push', 'left']));
        ok(Css.hasClassName(leftDrawer, 'show'));

        drawer.close()
        ok(!Css.hasClassName(document.body, 'push'));
        ok(!Css.hasClassName(document.body, 'left'));

        if (!Drawer.transitionSupport) {
            return ok(!Css.hasClassName(leftDrawer, 'show'));
        }

        ok(Css.hasClassName(leftDrawer, 'show'));
        InkEvent.fire(leftDrawer, Drawer.transitionSupport.eventName);
        ok(!Css.hasClassName(leftDrawer, 'show'));
    });

    test('focusing outside the drawer causes it to close', sinon.test(function () {
        this.stub(drawer, 'close');

        drawer.open();

        InkEvent.fire(contentDrawer, 'focus');

        ok(drawer.close.calledOnce, 'close() was called because of focus');
    }));
})
<|endoftext|>