<commit_before>"use strict";

var Emitter = require( 'vjs/lib/emitter' )
var Event = require( 'vjs/lib/event' )
var Base = require( 'vjs/lib/base' )
  // var cases = require( '../cases' )
var ua = require( '../ua' )
var stamp = 0

console.log( '----------' )
console.log( 'platform?', ua.platform )
console.log( 'device?', ua.device )
console.log( 'browser?', ua.browser )
console.log( 'ua?', ua )
console.log( '----------' )

// function returnFn( val ) {
//   if( typeof val !== 'function' ) {
//     for( var field in val ) {
//       val = val[ field ]
//     }
//   }
//   return val
// }

// function(){}
// {'anykey':function(){}}
var map
  // if(cases.touch){
  //   map = {
  //     $down: 'touchstart',
  //     $move: 'touchmove',
  //     $up: 'touchend'
  //   }
  // }else{
map = {
    $down: 'mousedown',
    $move: 'mousemove',
    $up: 'mouseup'//,
    // $dragstart: 'dragstart',
    // $dragend: 'dragend'
  }
  // }

var basicDomEmitter = new Emitter( {
  $define: {
    $generateConstructor: function() {
      return function DerivedEmitter( val, event, parent, key ) {
        parent.$addEvent( map[ key ], function( event, e ) {
          console.log( '     ', key )
          this.$emit( key, event, e )
        } )
        return Emitter.apply( this, arguments )
      }
    }
  }
} )

exports.$on = {
  $flags: {
    $down: basicDomEmitter,
    $move: basicDomEmitter,
    $up: basicDomEmitter,
    $click: new Emitter( {
      $define: {
        $generateConstructor: function() {
          return function DerivedEmitter( val, event, parent, key ) {
            parent.$addEvent( '$down', function( event, e ) {
              var id = ++stamp
              this.$addEvent( '$up', function(event, e) {
                this.$emit(key,event,e)
                this.$removeEvent( false, id )
              }, id )
            })
            return Emitter.apply(this,arguments)
          }
        }
      }
    } ),
    $drag: new Emitter( {
      $define: {
        $generateConstructor: function() {
          return function DerivedEmitter( val, event, parent, key ) {
            parent.$addEvent( '$down', function( event, e ) {
              var id = ++stamp
              this.$addEvent( '$move', function( event, e ) {
                this.$emit( key, event, e )
              }, id )
              this.$addEvent( '$up', function() {
                this.$removeEvent( false, id )
              }, id )
            } )
            return Emitter.apply( this, arguments )
          }
        }
      }
    } ),
    $grab: new Emitter( {
      $define: {
        $generateConstructor: function() {
          return function DerivedEmitter( val, event, parent, key ) {
            parent.$addEvent( '$down', function(event,e) {
              var id = ++stamp
              this.$emit('$grabstart',event,e)
              this.$addEvent( '$move', function(event,e){
                this.$emit(key,event,e)
              }, id )
              this.$addEvent( '$up', function(event,e){
                this.$emit('$grabend',event,e)
                this.$removeEvent(false,id)
              }, id )
            } )
            return Emitter.apply(this,arguments)
          }
        }
      }
    } )
  },

  $define: {
    $addEvent: function( type, fn, id ) {
      if( !id ) {
        id = ++stamp
      }
      var set = {}
      set[ id ] = fn

      this.setKey( type, set )
      return id
    },
    $ChildConstructor: new Emitter( {
      $define: {
        $noInstances: true,
        _$key: {
          set: function( val ) {

            //this is just for the nice!
            //TODO: make this pointer thing flexible
            // var element = this._$parent._$parent
            // var node = element.$node
            // node.style.cursor = 'pointer'
            // if( val.indexOf( 'drag' ) === 0 ) {
            //   node.setAttribute( 'draggable', true )
            // }
            this.$newDomEvent( val ) //val === 'mnousedonw'
            this.__$key = val
          },
          get: function( val ) {
            return this.__$key
          }
        },
        $domEventCache: {
          value: {}
        },
        $newDomEvent: function( val ) {
          var domEventCache = this.$domEventCache
          if( !domEventCache[ val ] ) {

            document.body.addEventListener( val, function( e ) {

              var node = e.target
              var target = node.$base
              var firstTarget
              var event
              var path
              var i

              if( !target ) {
                path = []
                i = -1
                while( !target ) {
                  i++
                  path.push( node.getAttribute( 'key' ) || node.className )
                  node = node.parentNode
                  target = node.$base
                }

                for( ; i >= 0; i-- ) {
                  target = target[ path[ i ] ]
                }
              }

              while( target ) {
                if( target.$on[ val ] ) {

                  if( !firstTarget ) {
                    firstTarget = target
                    event = new Event( target )
                  } else {
                    target.$emit( val, event, e )
                  }

                }
                target = target._$parent
              }

              if( firstTarget ) {
                firstTarget.$emit( val, event, e )
                  // firstTarget.$text.$val = Math.random()
              }
            } )

            domEventCache[ val ] = true
          }
        }
      }
    } ).$Constructor
  }
}

// exports.$flags = {
//   $domEvents: new Base()
// }

exports.$define = {
  $addEvent: function( type, fn, id ) {
    return this.$on.$addEvent( type, fn, id )
  },
  $removeEvent: function( type, id ) {
    var _this = this
    var on = this.$on
    var domevent
    var key
    if( type ) {
      if( domevent = on[ type ] ) {
        if( id ) {
          if( domevent = domevent[ id ] ) {
            domevent.remove()
          }
        } else {
          domevent.remove()
        }
      }
    } else {
      for( type in on ) {
        if( domevent = on[ type ] ) {
          if( key = domevent._$key ) {
            this.$removeEvent( key, id )
          }
        }
      }
    }
  }
}
<commit_msg>fixed event remove (again)<commit_after>"use strict";

var Emitter = require( 'vjs/lib/emitter' )
var Event = require( 'vjs/lib/event' )
var Base = require( 'vjs/lib/base' )
  // var cases = require( '../cases' )
var ua = require( '../ua' )
var stamp = 0

console.log( '----------' )
console.log( 'platform?', ua.platform )
console.log( 'device?', ua.device )
console.log( 'browser?', ua.browser )
console.log( 'ua?', ua )
console.log( '----------' )

// function returnFn( val ) {
//   if( typeof val !== 'function' ) {
//     for( var field in val ) {
//       val = val[ field ]
//     }
//   }
//   return val
// }

// function(){}
// {'anykey':function(){}}
var map
  // if(cases.touch){
  //   map = {
  //     $down: 'touchstart',
  //     $move: 'touchmove',
  //     $up: 'touchend'
  //   }
  // }else{
map = {
    $down: 'mousedown',
    $move: 'mousemove',
    $up: 'mouseup' //,
      // $dragstart: 'dragstart',
      // $dragend: 'dragend'
  }
  // }

var basicDomEmitter = new Emitter( {
  $define: {
    $generateConstructor: function() {
      return function DerivedEmitter( val, ev, parent, key ) {
        parent.$addEvent( map[ key ], function( event, e ) {
          this.$emit( key, event, e )
        })
        return Emitter.apply( this, arguments )
      }
    }
  }
} )

exports.$on = {
  $flags: {
    $down: basicDomEmitter,
    $move: basicDomEmitter,
    $up: basicDomEmitter,
    $click: new Emitter( {
      $define: {
        $generateConstructor: function() {
          return function DerivedEmitter( val, ev, parent, key ) {
            parent.$addEvent( '$down', function( event, e ) {
              var id = ++stamp
              this.$addEvent( '$up', function( event, e ) {
                this.$emit( key, event, e )
                this.$removeEvent( false, id )
              }, id )
            } )
            return Emitter.apply( this, arguments )
          }
        }
      }
    } ),
    $drag: new Emitter( {
      $define: {
        $generateConstructor: function() {
          return function DerivedEmitter( val, ev, parent, key ) {
            parent.$addEvent( '$down', function( event, e ) {
              var id = ++stamp
              this.$addEvent( '$move', function( event, e ) {
                this.$emit( key, event, e )
              }, id )
              this.$addEvent( '$up', function() {
                this.$removeEvent( false, id )
              }, id )
            } )
            return Emitter.apply( this, arguments )
          }
        }
      }
    } ),
    $grab: new Emitter( {
      $define: {
        $generateConstructor: function() {
          return function DerivedEmitter( val, ev, parent, key ) {
            parent.$addEvent( '$down', function( event, e ) {
              var id = ++stamp
              this.$emit( '$grabstart', event, e )
              this.$addEvent( '$move', function( event, e ) {
                this.$emit( key, event, e )
              }, id )
              this.$addEvent( '$up', function( event, e ) {
                this.$emit( '$grabend', event, e )
                this.$removeEvent( false, id )
              }, id )
            } )
            return Emitter.apply( this, arguments )
          }
        }
      }
    } )
  },

  $define: {
    $addEvent: function( type, fn, id ) {
      if( !id ) {
        id = ++stamp
      }
      var set = {}
      set[ id ] = fn

      this.setKey( type, set )
      return id
    },
    $ChildConstructor: new Emitter( {
      $define: {
        $noInstances: true,
        _$key: {
          set: function( val ) {

            //this is just for the nice!
            //TODO: make this pointer thing flexible
            // var element = this._$parent._$parent
            // var node = element.$node
            // node.style.cursor = 'pointer'
            // if( val.indexOf( 'drag' ) === 0 ) {
            //   node.setAttribute( 'draggable', true )
            // }
            this.$newDomEvent( val ) //val === 'mnousedonw'
            this.__$key = val
          },
          get: function( val ) {
            return this.__$key
          }
        },
        $domEventCache: {
          value: {}
        },
        $newDomEvent: function( val ) {
          var domEventCache = this.$domEventCache
          if( !domEventCache[ val ] ) {

            document.body.addEventListener( val, function( e ) {

              var node = e.target
              var target = node.$base
              var firstTarget
              var event
              var path
              var i

              if( !target ) {
                path = []
                i = -1
                while( !target ) {
                  i++
                  path.push( node.getAttribute( 'key' ) || node.className )
                  node = node.parentNode
                  target = node.$base
                }

                for( ; i >= 0; i-- ) {
                  target = target[ path[ i ] ]
                }
              }

              while( target ) {
                if( target.$on[ val ] ) {

                  if( !firstTarget ) {
                    firstTarget = target
                    event = new Event( target )
                  } else {
                    target.$emit( val, event, e )
                  }

                }
                target = target._$parent
              }

              if( firstTarget ) {
                firstTarget.$emit( val, event, e )
                  // firstTarget.$text.$val = Math.random()
              }
            } )

            domEventCache[ val ] = true
          }
        }
      }
    } ).$Constructor
  }
}

// exports.$flags = {
//   $domEvents: new Base()
// }

exports.$define = {
  $addEvent: function( type, fn, id ) {
    return this.$on.$addEvent( type, fn, id )
  },
  $removeEvent: function( type, id ) {
    var _this = this
    var on = this.$on
    var domevent
    var key
    if( type ) {
      if( domevent = on[ type ] ) {
        //TODO: support $base and $passon
        if( domevent = domevent.$fn ){
          if( id ) {
            if( domevent[ id ] ) {
              this.off( type, id )
              // delete domevent[ id ]
            }
          } else {
            this.off( type )
            // domevent.remove()
          }
        }
      }
    } else {
      for( type in on ) {
        if( domevent = on[ type ] ) {
          if( key = domevent._$key ) {
            this.$removeEvent( key, id )
          }
        }
      }
    }
  }
}
<|endoftext|>


<commit_before>// express-path.js
// by @hyubs

"use strict";

var expressPath = function (app, routeParam, opt) {
	// Dependencies
	var fs = require('fs');
	var path = require('path');
	var RouteMap = require('./RouteMap');
	var RouteInfo = require('./RouteInfo');
	var ModuleHandler = require('./ModuleHandler');
	var MethodHandler = require('./MethodHandler');
	var ErrorHandler = require('./ErrorHandler');

	var MODULE_PATH = path.dirname(module.parent.parent.filename) + path.sep;

	opt = opt || {};

	var options = {
		controllersPath: ((typeof opt.controllersPath === 'undefined') ? ("controllers" + path.sep) : opt.controllersPath),
		middlewaresPath: ((typeof opt.middlewaresPath === 'undefined') ? ("middlewares" + path.sep) : opt.controllersPath),
		strict:          ((typeof opt.strict === 'undefined')          ? true : opt.strict),
		verbose:         ((typeof opt.verbose === 'undefined')         ? true : opt.verbose)
	};

	var errorHandler = new ErrorHandler({
		strict:  options.strict,
		verbose: options.verbose
	});

	var routeMap = new RouteMap();
	var routeInfo = new RouteInfo();
	var moduleHandler = new ModuleHandler();
	var methodHandler = new MethodHandler();

	var getModuleMethod = function (parentPath, mmPath) {
		if(routeInfo.isValidMMPath(mmPath)) {
			// If it is valid, then

			// Get the path of the controller and prefix it with the path to the controllers directory.
			var modulePath = parentPath + routeInfo.getModulePath(mmPath);

			// Check if the controller file exists.
			if(moduleHandler.fileExists(modulePath)) {
				// If it does, then

				// Retrieve the file creating an "instance".
				var moduleInstance = moduleHandler.getFile(modulePath);

				// Get the name of the method from the route info.
				var methodName = routeInfo.getMethod(mmPath);

				// Check if the method exists.
				if(methodHandler.exists(methodName, moduleInstance)) {
					// If it does, then

					// Retrieve the method.
					var method = methodHandler.get(methodName, moduleInstance);
					return method;
				}
				else {
					errorHandler.log("Could not load method for " + mmPath);
					return null;
				}
			}
			else {
				errorHandler.log("Module not found: " + controllerPath);
				return null;
			}
		}
		else {
			errorHandler.log("Invalid module-method path: " + mmPath);
			return null;
		}
	}

	// Get the map data. If routeParam was a path, then read that file.
	if(typeof routeParam === 'string') {
		routeParam = MODULE_PATH + routeParam;
	}

	var map = routeMap.eval(routeParam);

	// Check if the map is valid.
	if(routeMap.isValid(map)) {
		// If it is valid, then:

		// Loop through the map.
		for(var i in map) {
			// For each route info:
			var mapRow = map[i];

			// Get the URL path and sanitize it.
			var urlPath = mapRow[0];
			urlPath = routeInfo.sanitizeUrlPath(urlPath);

			// Get the controller-method path.
			var cmPath = mapRow[1];

			// Get the middleware-method paths.
			var mmPaths = [];
			for(var j = 2; j < mapRow.length - 1; j++) {
				mmPaths.push(mapRow[j]);
			}

			// Get the HTTP request method. If there's none, default to all.
			var httpRequestMethod;
			if(mapRow.length > 1) {
				httpRequestMethod = mapRow[mapRow.length - 1];
			}
			else {
				httpRequestMethod = 'all';
			}

			// Get the method of the controller
			var method = getModuleMethod(MODULE_PATH + options.controllersPath, cmPath);

			// Loop through all the middleware-method paths and retrieve each of the methods
			var middlewares = [];
			mmPaths.forEach(function(mmPath) {
				var middlewareMethod = getModuleMethod(MODULE_PATH + options.middlewaresPath, mmPath);
				middlewares.push(middlewareMethod);
			});

			// Finally, bind the route in the Express app
			app[httpRequestMethod](urlPath, middlewares, method);

			// If verbose is true, print out some info about the mapped routes
			if(options.verbose) {
				console.log('[' + httpRequestMethod.toUpperCase() + '] ' + urlPath + '  ->  ' + cmPath);
				if(mmPaths.length > 0) {
					console.log('    - middlewares (' + mmPaths.length + ')');
					mmPaths.forEach(function(mmPath) {
						console.log('        - ' + mmPath);
					})
				}
			}
		}
	}
	else {
		errorHandler.log("Route map should be an array. No route map defined.");
	}
};

module.exports = expressPath;

<commit_msg>Fixed "controllerPath is not defined" error. Changed controllerPath variable to modulePath.<commit_after>// express-path.js
// by @hyubs

"use strict";

var expressPath = function (app, routeParam, opt) {
	// Dependencies
	var fs = require('fs');
	var path = require('path');
	var RouteMap = require('./RouteMap');
	var RouteInfo = require('./RouteInfo');
	var ModuleHandler = require('./ModuleHandler');
	var MethodHandler = require('./MethodHandler');
	var ErrorHandler = require('./ErrorHandler');

	var MODULE_PATH = path.dirname(module.parent.parent.filename) + path.sep;

	opt = opt || {};

	var options = {
		controllersPath: ((typeof opt.controllersPath === 'undefined') ? ("controllers" + path.sep) : opt.controllersPath),
		middlewaresPath: ((typeof opt.middlewaresPath === 'undefined') ? ("middlewares" + path.sep) : opt.controllersPath),
		strict:          ((typeof opt.strict === 'undefined')          ? true : opt.strict),
		verbose:         ((typeof opt.verbose === 'undefined')         ? true : opt.verbose)
	};

	var errorHandler = new ErrorHandler({
		strict:  options.strict,
		verbose: options.verbose
	});

	var routeMap = new RouteMap();
	var routeInfo = new RouteInfo();
	var moduleHandler = new ModuleHandler();
	var methodHandler = new MethodHandler();

	var getModuleMethod = function (parentPath, mmPath) {
		if(routeInfo.isValidMMPath(mmPath)) {
			// If it is valid, then

			// Get the path of the controller and prefix it with the path to the controllers directory.
			var modulePath = parentPath + routeInfo.getModulePath(mmPath);

			// Check if the controller file exists.
			if(moduleHandler.fileExists(modulePath)) {
				// If it does, then

				// Retrieve the file creating an "instance".
				var moduleInstance = moduleHandler.getFile(modulePath);

				// Get the name of the method from the route info.
				var methodName = routeInfo.getMethod(mmPath);

				// Check if the method exists.
				if(methodHandler.exists(methodName, moduleInstance)) {
					// If it does, then

					// Retrieve the method.
					var method = methodHandler.get(methodName, moduleInstance);
					return method;
				}
				else {
					errorHandler.log("Could not load method for " + mmPath);
					return null;
				}
			}
			else {
				errorHandler.log("Module not found: " + modulePath);
				return null;
			}
		}
		else {
			errorHandler.log("Invalid module-method path: " + mmPath);
			return null;
		}
	}

	// Get the map data. If routeParam was a path, then read that file.
	if(typeof routeParam === 'string') {
		routeParam = MODULE_PATH + routeParam;
	}

	var map = routeMap.eval(routeParam);

	// Check if the map is valid.
	if(routeMap.isValid(map)) {
		// If it is valid, then:

		// Loop through the map.
		for(var i in map) {
			// For each route info:
			var mapRow = map[i];

			// Get the URL path and sanitize it.
			var urlPath = mapRow[0];
			urlPath = routeInfo.sanitizeUrlPath(urlPath);

			// Get the controller-method path.
			var cmPath = mapRow[1];

			// Get the middleware-method paths.
			var mmPaths = [];
			for(var j = 2; j < mapRow.length - 1; j++) {
				mmPaths.push(mapRow[j]);
			}

			// Get the HTTP request method. If there's none, default to all.
			var httpRequestMethod;
			if(mapRow.length > 1) {
				httpRequestMethod = mapRow[mapRow.length - 1];
			}
			else {
				httpRequestMethod = 'all';
			}

			// Get the method of the controller
			var method = getModuleMethod(MODULE_PATH + options.controllersPath, cmPath);

			// Loop through all the middleware-method paths and retrieve each of the methods
			var middlewares = [];
			mmPaths.forEach(function(mmPath) {
				var middlewareMethod = getModuleMethod(MODULE_PATH + options.middlewaresPath, mmPath);
				middlewares.push(middlewareMethod);
			});

			// Finally, bind the route in the Express app
			app[httpRequestMethod](urlPath, middlewares, method);

			// If verbose is true, print out some info about the mapped routes
			if(options.verbose) {
				console.log('[' + httpRequestMethod.toUpperCase() + '] ' + urlPath + '  ->  ' + cmPath);
				if(mmPaths.length > 0) {
					console.log('    - middlewares (' + mmPaths.length + ')');
					mmPaths.forEach(function(mmPath) {
						console.log('        - ' + mmPath);
					})
				}
			}
		}
	}
	else {
		errorHandler.log("Route map should be an array. No route map defined.");
	}
};

module.exports = expressPath;

<|endoftext|>


<commit_before>
// Express - Core - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)

/**
 * Module dependencies.
 */

var multipart = require('multipart'),
    utils = require('express/utils'),
    events = require('events'),
    fs = require('fs')

global.merge(require('sys'))
global.merge(require('express/event'))
global.merge(require('express/request'))
global.merge(require('express/plugin'))
global.merge(require('express/dsl'))

// --- Route

Route = new Class({

  /**
   * Initialize a route with the given _method_,
   * _path_, and _callback_.
   *
   * The given _path_ becomes #originalPath,
   * #path is then a normalized version converted
   * to a regular expression for routing.
   *
   * @param  {string} method
   * @param  {string} path
   * @param  {function} callback
   * @param  {hash} options
   * @api private
   */

  constructor: function(method, path, callback, options){
    this.method = method
    this.originalPath = path
    this.path = this.normalize(path)
    this.callback = callback
  },

  /**
   * Normalize _path_. When a RegExp it is simply returned,
   * otherwise a string is converted to a regular expression
   * surrounded by ^$. So /user/:id/edit would become:
   *
   * /^\/user\/([^\/]+)\/edit$/i
   *
   * Each param key (:id) will be captured and placed in the
   * params array, so param('id') would give the string captured.
   *
   * The following are valid routes:
   *
   *  - /user/:id         Ex: '/user/12'
   *  - /user/:id?        Ex: '/user', '/user/12'
   *  - /report.:format   Ex: '/report.pdf', 'report.csv'
   *  - /public/*         Ex: '/public/app.js', '/public/javascripts/app.js'
   *  - /public/*.*       Ex: '/public/app.js', '/public/javascripts/app.js'
   *
   * @param  {string} path
   * @return {regexp}
   * @api private
   */

  normalize: function(path) {
    var self = this
    this.keys = []
    if (path instanceof RegExp) return path
    return new RegExp('^' + RegExp.escape(normalizePath(path), '.')
      .replace(/\*/g, '(.+)')
      .replace(/(\/|\\\.):(\w+)\?/g, function(_, c, key){
        self.keys.push(key)
        return '(?:' + c + '([^\/]+))?'
      })
      .replace(/:(\w+)/g, function(_, key){
        self.keys.push(key)
        return '([^\/]+)'
      }) + '$', 'i')
  }
})

// --- Router

Router = new Class({

  /**
   * Initialize with _request_ and parse url.
   *
   * @param  {Request} request
   * @api private
   */

  constructor: function(request) {
    this.request = request
  },

  /**
   * Evaluate the matched route against #request.
   *
   * @return {mixed}
   * @api private
   */

  route: function() {
    var body,
        route = this.matchingRoute()
    if (route) {
      body = route.callback.apply(this.request, this.request.captures.slice(1));
      if (this.request.passed) {
        if (typeof this.request.passed === 'string')
          this.request.url.pathname = this.request.passed
        this.request.passed = false
        return this.route()
      }
      return body
    }
    else
      this.request.notFound()
  },

  /**
   * Attempt to match a route from Express.routes.
   *
   * @return {Route}
   * @api private
   */

  matchingRoute: function() {
    this.lastMatchingRoute = this.lastMatchingRoute || 0
    var routes = Express.routes, route
    while (route = routes[this.lastMatchingRoute++])
      if (this.match(route))
        break
    return route
  },

  /**
   * Check if _route_ matches the current request.
   * If so populate #captures and #params.
   *
   * @param  {object} route
   * @return {bool}
   * @api private
   */

  match: function(route) {
    if (this.request.method.toLowerCase() == route.method)
      if (this.request.captures = this.request.url.pathname.match(route.path)) {
        this.mapParams(route)
        return true
      }
  },

  /**
   * Map #request.captures to #request.params.path based on the
   * given _route_ params.
   *
   * @param  {Route} route
   * @api private
   */

  mapParams: function(route) {
    route.keys.each(function(key, i){
      var val = this.request.captures[++i]
      if (key in Express.params)
        if ((val = Express.params[key].call(this.request, val)) === false)
          this.request.passed = true
      this.request.params.path[key] = this.request.captures[i] = val
    }, this)
  }
})

// --- Server

Server = new Class({

  /**
   * Default port number.
   */

  port: 3000,

  /**
   * Default host ip, when null node will accept requests on
   * all network addresses.
   */

  host: 'localhost',

  /**
   * Maximum number of queued connections.
   */

  backlog: 128,

  /**
   * Run Express.
   *
   *  - Buffers request bodies
   *  - Calls #route() once the request is complete
   *
   * @param  {int} port
   * @param  {string} host
   * @param  {int} backlog
   * @see run()
   * @api private
   */

  run: function(port, host, backlog){
    var self = this
    if (host !== undefined) this.host = host
    if (port !== undefined) this.port = port
    if (backlog !== undefined) this.backlog = backlog
    require('http')
      .createServer(function(req, response){
        var request, pendingFiles = 0
        req.setBodyEncoding('binary')
        request = new Request(req, response)
        request.body = ''
        function callback(err) {
          if (err)
            request.error(err)
          else if (!pendingFiles)
            self.route(request)
        }
        if (request.header('content-type') &&
            request.header('content-type').includes('multipart/form-data')) {
          var stream,
              contentLength = parseInt(request.header('content-length')),
              maxBodyLength = set('max upload size')
          if (maxBodyLength && contentLength > maxBodyLength)
            return callback(new Error('upload size limit exceeded'))
          stream = multipart.parse(req)
          stream
            .addListener('partBegin', function(part) {
              if (part.filename)
                ++pendingFiles,
                part.tempfile = '/tmp/express-' + Number(new Date) + utils.uid(),
                part.fileStream = fs.createWriteStream(part.tempfile),
                part.fileStream.addListener('error', callback)
              else
                part.buf = ''
            })
            .addListener('body', function(chunk) {
              if (stream.part.fileStream)
                stream.part.fileStream.write(chunk)
              else
                stream.part.buf += chunk
            })
            .addListener('partEnd', function(part) {
              if (!part.name) return
              if (part.fileStream)
                part.fileStream.close(function(){
                  --pendingFiles
                  callback()
                }),
                utils.mergeParam(part.name, { filename: part.filename, tempfile: part.tempfile }, request.params.post)
              else
                utils.mergeParam(part.name, part.buf, request.params.post)
            })
            .addListener('error', callback)
            .addListener('complete', callback)
        }
        else
          req
            .addListener('data', function(chunk){ request.body += chunk })
            .addListener('end', callback)
      })
      .listen(this.port, this.host, this.backlog)
    puts('Express started at http://' + this.host + ':' + this.port + '/ in ' + Express.environment + ' mode')
  },

  /**
   * Route the given _request_.
   *
   * @param  {Request} request
   * @api private
   */

  route: function(request){
    var self = this
    request.trigger('request', function(err) {
      try {
        if (err) throw err
        if (request.response.finished) return
        if (typeof (body = (new Router(request)).route()) === 'string')
          request.halt(200, body, function(err){
            if (err) request.error(err)
          })
      } catch (err) {
        request.error(err)
      }
    })
  }
})

// --- Express

Express = {
  version: '0.8.0',
  config: [],
  routes: [],
  plugins: [],
  settings: {},
  params: {},
  server: new Server
}

// --- Defaults

configure(function(){
  use(require('express/plugins/view').View)
  use(require('express/plugins/cache').Cache)
  use(require('express/plugins/redirect').Redirect)
  use(require('express/plugins/body-decoder').BodyDecoder)
})

configure('development', function(){
  enable('helpful 404')
  enable('show exceptions')
})

configure('test', function(){
  enable('throw exceptions')
})

configure('production', function(){
  enable('cache view contents')
  enable('cache view partials')
  enable('cache static files')
})
<commit_msg>Removed unused require<commit_after>
// Express - Core - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)

/**
 * Module dependencies.
 */

var multipart = require('multipart'),
    utils = require('express/utils'),
    fs = require('fs')

global.merge(require('sys'))
global.merge(require('express/event'))
global.merge(require('express/request'))
global.merge(require('express/plugin'))
global.merge(require('express/dsl'))

// --- Route

Route = new Class({

  /**
   * Initialize a route with the given _method_,
   * _path_, and _callback_.
   *
   * The given _path_ becomes #originalPath,
   * #path is then a normalized version converted
   * to a regular expression for routing.
   *
   * @param  {string} method
   * @param  {string} path
   * @param  {function} callback
   * @param  {hash} options
   * @api private
   */

  constructor: function(method, path, callback, options){
    this.method = method
    this.originalPath = path
    this.path = this.normalize(path)
    this.callback = callback
  },

  /**
   * Normalize _path_. When a RegExp it is simply returned,
   * otherwise a string is converted to a regular expression
   * surrounded by ^$. So /user/:id/edit would become:
   *
   * /^\/user\/([^\/]+)\/edit$/i
   *
   * Each param key (:id) will be captured and placed in the
   * params array, so param('id') would give the string captured.
   *
   * The following are valid routes:
   *
   *  - /user/:id         Ex: '/user/12'
   *  - /user/:id?        Ex: '/user', '/user/12'
   *  - /report.:format   Ex: '/report.pdf', 'report.csv'
   *  - /public/*         Ex: '/public/app.js', '/public/javascripts/app.js'
   *  - /public/*.*       Ex: '/public/app.js', '/public/javascripts/app.js'
   *
   * @param  {string} path
   * @return {regexp}
   * @api private
   */

  normalize: function(path) {
    var self = this
    this.keys = []
    if (path instanceof RegExp) return path
    return new RegExp('^' + RegExp.escape(normalizePath(path), '.')
      .replace(/\*/g, '(.+)')
      .replace(/(\/|\\\.):(\w+)\?/g, function(_, c, key){
        self.keys.push(key)
        return '(?:' + c + '([^\/]+))?'
      })
      .replace(/:(\w+)/g, function(_, key){
        self.keys.push(key)
        return '([^\/]+)'
      }) + '$', 'i')
  }
})

// --- Router

Router = new Class({

  /**
   * Initialize with _request_ and parse url.
   *
   * @param  {Request} request
   * @api private
   */

  constructor: function(request) {
    this.request = request
  },

  /**
   * Evaluate the matched route against #request.
   *
   * @return {mixed}
   * @api private
   */

  route: function() {
    var body,
        route = this.matchingRoute()
    if (route) {
      body = route.callback.apply(this.request, this.request.captures.slice(1));
      if (this.request.passed) {
        if (typeof this.request.passed === 'string')
          this.request.url.pathname = this.request.passed
        this.request.passed = false
        return this.route()
      }
      return body
    }
    else
      this.request.notFound()
  },

  /**
   * Attempt to match a route from Express.routes.
   *
   * @return {Route}
   * @api private
   */

  matchingRoute: function() {
    this.lastMatchingRoute = this.lastMatchingRoute || 0
    var routes = Express.routes, route
    while (route = routes[this.lastMatchingRoute++])
      if (this.match(route))
        break
    return route
  },

  /**
   * Check if _route_ matches the current request.
   * If so populate #captures and #params.
   *
   * @param  {object} route
   * @return {bool}
   * @api private
   */

  match: function(route) {
    if (this.request.method.toLowerCase() == route.method)
      if (this.request.captures = this.request.url.pathname.match(route.path)) {
        this.mapParams(route)
        return true
      }
  },

  /**
   * Map #request.captures to #request.params.path based on the
   * given _route_ params.
   *
   * @param  {Route} route
   * @api private
   */

  mapParams: function(route) {
    route.keys.each(function(key, i){
      var val = this.request.captures[++i]
      if (key in Express.params)
        if ((val = Express.params[key].call(this.request, val)) === false)
          this.request.passed = true
      this.request.params.path[key] = this.request.captures[i] = val
    }, this)
  }
})

// --- Server

Server = new Class({

  /**
   * Default port number.
   */

  port: 3000,

  /**
   * Default host ip, when null node will accept requests on
   * all network addresses.
   */

  host: 'localhost',

  /**
   * Maximum number of queued connections.
   */

  backlog: 128,

  /**
   * Run Express.
   *
   *  - Buffers request bodies
   *  - Calls #route() once the request is complete
   *
   * @param  {int} port
   * @param  {string} host
   * @param  {int} backlog
   * @see run()
   * @api private
   */

  run: function(port, host, backlog){
    var self = this
    if (host !== undefined) this.host = host
    if (port !== undefined) this.port = port
    if (backlog !== undefined) this.backlog = backlog
    require('http')
      .createServer(function(req, response){
        var request, pendingFiles = 0
        req.setBodyEncoding('binary')
        request = new Request(req, response)
        request.body = ''
        function callback(err) {
          if (err)
            request.error(err)
          else if (!pendingFiles)
            self.route(request)
        }
        if (request.header('content-type') &&
            request.header('content-type').includes('multipart/form-data')) {
          var stream,
              contentLength = parseInt(request.header('content-length')),
              maxBodyLength = set('max upload size')
          if (maxBodyLength && contentLength > maxBodyLength)
            return callback(new Error('upload size limit exceeded'))
          stream = multipart.parse(req)
          stream
            .addListener('partBegin', function(part) {
              if (part.filename)
                ++pendingFiles,
                part.tempfile = '/tmp/express-' + Number(new Date) + utils.uid(),
                part.fileStream = fs.createWriteStream(part.tempfile),
                part.fileStream.addListener('error', callback)
              else
                part.buf = ''
            })
            .addListener('body', function(chunk) {
              if (stream.part.fileStream)
                stream.part.fileStream.write(chunk)
              else
                stream.part.buf += chunk
            })
            .addListener('partEnd', function(part) {
              if (!part.name) return
              if (part.fileStream)
                part.fileStream.close(function(){
                  --pendingFiles
                  callback()
                }),
                utils.mergeParam(part.name, { filename: part.filename, tempfile: part.tempfile }, request.params.post)
              else
                utils.mergeParam(part.name, part.buf, request.params.post)
            })
            .addListener('error', callback)
            .addListener('complete', callback)
        }
        else
          req
            .addListener('data', function(chunk){ request.body += chunk })
            .addListener('end', callback)
      })
      .listen(this.port, this.host, this.backlog)
    puts('Express started at http://' + this.host + ':' + this.port + '/ in ' + Express.environment + ' mode')
  },

  /**
   * Route the given _request_.
   *
   * @param  {Request} request
   * @api private
   */

  route: function(request){
    var self = this
    request.trigger('request', function(err) {
      try {
        if (err) throw err
        if (request.response.finished) return
        if (typeof (body = (new Router(request)).route()) === 'string')
          request.halt(200, body, function(err){
            if (err) request.error(err)
          })
      } catch (err) {
        request.error(err)
      }
    })
  }
})

// --- Express

Express = {
  version: '0.8.0',
  config: [],
  routes: [],
  plugins: [],
  settings: {},
  params: {},
  server: new Server
}

// --- Defaults

configure(function(){
  use(require('express/plugins/view').View)
  use(require('express/plugins/cache').Cache)
  use(require('express/plugins/redirect').Redirect)
  use(require('express/plugins/body-decoder').BodyDecoder)
})

configure('development', function(){
  enable('helpful 404')
  enable('show exceptions')
})

configure('test', function(){
  enable('throw exceptions')
})

configure('production', function(){
  enable('cache view contents')
  enable('cache view partials')
  enable('cache static files')
})
<|endoftext|>


<commit_before>
// Express - Core - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)

/**
 * Module dependencies.
 */

var multipart = require('multipart'),
    utils = require('express/utils'),
    events = require('events'),
    fs = require('fs')

global.merge(require('sys'))
global.merge(require('express/event'))
global.merge(require('express/request'))
global.merge(require('express/plugin'))
global.merge(require('express/dsl'))

// --- Route

Route = new Class({

  /**
   * Initialize a route with the given _method_,
   * _path_, and _callback_.
   *
   * The given _path_ becomes #originalPath,
   * #path is then a normalized version converted
   * to a regular expression for routing.
   *
   * @param  {string} method
   * @param  {string} path
   * @param  {function} callback
   * @param  {hash} options
   * @api private
   */

  constructor: function(method, path, callback, options){
    this.method = method
    this.originalPath = path
    this.path = this.normalize(path)
    this.callback = callback
  },

  /**
   * Normalize _path_. When a RegExp it is simply returned,
   * otherwise a string is converted to a regular expression
   * surrounded by ^$. So /user/:id/edit would become:
   *
   * /^\/user\/([^\/]+)\/edit$/i
   *
   * Each param key (:id) will be captured and placed in the
   * params array, so param('id') would give the string captured.
   *
   * The following are valid routes:
   *
   *  - /user/:id         Ex: '/user/12'
   *  - /user/:id?        Ex: '/user', '/user/12'
   *  - /report.:format   Ex: '/report.pdf', 'report.csv'
   *  - /public/*         Ex: '/public/app.js', '/public/javascripts/app.js'
   *  - /public/*.*       Ex: '/public/app.js', '/public/javascripts/app.js'
   *
   * @param  {string} path
   * @return {regexp}
   * @api private
   */

  normalize: function(path) {
    var self = this
    this.keys = []
    if (path instanceof RegExp) return path
    return new RegExp('^' + RegExp.escape(normalizePath(path), '.')
      .replace(/\*/g, '(.+)')
      .replace(/(\/|\\\.):(\w+)\?/g, function(_, c, key){
        self.keys.push(key)
        return '(?:' + c + '([^\/]+))?'
      })
      .replace(/:(\w+)/g, function(_, key){
        self.keys.push(key)
        return '([^\/]+)'
      }) + '$', 'i')
  }
})

// --- Router

Router = new Class({

  /**
   * Initialize with _request_ and parse url.
   *
   * @param  {Request} request
   * @api private
   */

  constructor: function(request) {
    this.request = request
  },

  /**
   * Evaluate the matched route against #request.
   *
   * @return {mixed}
   * @api private
   */

  route: function() {
    var body,
        route = this.matchingRoute()
    if (route) {
      body = route.callback.apply(this.request, this.request.captures.slice(1));
      if (this.request.passed) {
        if (typeof this.request.passed === 'string')
          this.request.url.pathname = this.request.passed
        this.request.passed = false
        return this.route()
      }
      return body
    }
    else
      this.request.notFound()
  },

  /**
   * Attempt to match a route from Express.routes.
   *
   * @return {Route}
   * @api private
   */

  matchingRoute: function() {
    this.lastMatchingRoute = this.lastMatchingRoute || 0
    var routes = Express.routes, route
    while (route = routes[this.lastMatchingRoute++])
      if (this.match(route))
        break
    return route
  },

  /**
   * Check if _route_ matches the current request.
   * If so populate #captures and #params.
   *
   * @param  {object} route
   * @return {bool}
   * @api private
   */

  match: function(route) {
    if (this.request.method.toLowerCase() == route.method)
      if (this.request.captures = this.request.url.pathname.match(route.path)) {
        this.mapParams(route)
        return true
      }
  },

  /**
   * Map #request.captures to #request.params.path based on the
   * given _route_ params.
   *
   * @param  {Route} route
   * @api private
   */

  mapParams: function(route) {
    route.keys.each(function(key, i){
      var val = this.request.captures[++i]
      if (key in Express.params)
        if ((val = Express.params[key].call(this.request, val)) === false)
          this.request.passed = true
      this.request.params.path[key] = this.request.captures[i] = val
    }, this)
  }
})

// --- Server

Server = new Class({

  /**
   * Default port number.
   */

  port: 3000,

  /**
   * Default host ip, when null node will accept requests on
   * all network addresses.
   */

  host: 'localhost',

  /**
   * Maximum number of queued connections.
   */

  backlog: 128,

  /**
   * Run Express.
   *
   *  - Buffers request bodies
   *  - Calls #route() once the request is complete
   *
   * @param  {int} port
   * @param  {string} host
   * @param  {int} backlog
   * @see run()
   * @api private
   */

  run: function(port, host, backlog){
    var self = this
    if (host !== undefined) this.host = host
    if (port !== undefined) this.port = port
    if (backlog !== undefined) this.backlog = backlog
    this.http = require('http')
      .createServer(function(req, response){
        var request, pendingFiles = 0
        req.setBodyEncoding('binary')
        request = new Request(req, response)
        request.body = ''
        function callback(err) {
          if (err)
            request.error(err)
          else if (!pendingFiles)
            self.route(request)
        }
        if (request.header('content-type') &&
            request.header('content-type').includes('multipart/form-data')) {
          var stream,
              contentLength = parseInt(request.header('content-length')),
              maxBodyLength = set('max upload size')
          if (maxBodyLength && contentLength > maxBodyLength)
            return callback(new Error('upload size limit exceeded'))
          stream = multipart.parse(req)
          stream
            .addListener('partBegin', function(part) {
              if (part.filename)
                ++pendingFiles,
                part.tempfile = '/tmp/express-' + Number(new Date) + utils.uid(),
                part.fileStream = fs.createWriteStream(part.tempfile),
                part.fileStream.addListener('error', callback)
              else
                part.buf = ''
            })
            .addListener('body', function(chunk) {
              if (stream.part.fileStream)
                stream.part.fileStream.write(chunk)
              else
                stream.part.buf += chunk
            })
            .addListener('partEnd', function(part) {
              if (!part.name) return
              if (part.fileStream)
                part.fileStream.close(function(){
                  --pendingFiles
                  callback()
                }),
                utils.mergeParam(part.name, { filename: part.filename, tempfile: part.tempfile }, request.params.post)
              else
                utils.mergeParam(part.name, part.buf, request.params.post)
            })
            .addListener('error', callback)
            .addListener('complete', callback)
        }
        else
          req
            .addListener('data', function(chunk){ request.body += chunk })
            .addListener('end', callback)
      })
      .listen(this.port, this.host, this.backlog)
    puts('Express started at http://' + this.host + ':' + this.port + '/ in ' + Express.environment + ' mode')
  },

  /**
   * Route the given _request_.
   *
   * @param  {Request} request
   * @api private
   */

  route: function(request){
    var self = this
    request.trigger('request', function(err) {
      try {
        if (err) throw err
        if (request.response.finished) return
        if (typeof (body = (new Router(request)).route()) === 'string')
          request.halt(200, body, function(err){
            if (err) request.error(err)
          })
      } catch (err) {
        request.error(err)
      }
    })
  }
})

// --- Express

Express = {
  version: '0.8.0',
  config: [],
  routes: [],
  plugins: [],
  settings: {},
  params: {},
  server: new Server
}

// --- Defaults

configure(function(){
  use(require('express/plugins/view').View)
  use(require('express/plugins/cache').Cache)
  use(require('express/plugins/redirect').Redirect)
  use(require('express/plugins/body-decoder').BodyDecoder)
})

configure('development', function(){
  enable('helpful 404')
  enable('show exceptions')
})

configure('test', function(){
  enable('throw exceptions')
})

configure('production', function(){
  enable('cache view contents')
  enable('cache view partials')
  enable('cache static files')
})
<commit_msg>Correction<commit_after>
// Express - Core - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)

/**
 * Module dependencies.
 */

var multipart = require('multipart'),
    utils = require('express/utils'),
    events = require('events'),
    fs = require('fs')

global.merge(require('sys'))
global.merge(require('express/event'))
global.merge(require('express/request'))
global.merge(require('express/plugin'))
global.merge(require('express/dsl'))

// --- Route

Route = new Class({

  /**
   * Initialize a route with the given _method_,
   * _path_, and _callback_.
   *
   * The given _path_ becomes #originalPath,
   * #path is then a normalized version converted
   * to a regular expression for routing.
   *
   * @param  {string} method
   * @param  {string} path
   * @param  {function} callback
   * @param  {hash} options
   * @api private
   */

  constructor: function(method, path, callback, options){
    this.method = method
    this.originalPath = path
    this.path = this.normalize(path)
    this.callback = callback
  },

  /**
   * Normalize _path_. When a RegExp it is simply returned,
   * otherwise a string is converted to a regular expression
   * surrounded by ^$. So /user/:id/edit would become:
   *
   * /^\/user\/([^\/]+)\/edit$/i
   *
   * Each param key (:id) will be captured and placed in the
   * params array, so param('id') would give the string captured.
   *
   * The following are valid routes:
   *
   *  - /user/:id         Ex: '/user/12'
   *  - /user/:id?        Ex: '/user', '/user/12'
   *  - /report.:format   Ex: '/report.pdf', 'report.csv'
   *  - /public/*         Ex: '/public/app.js', '/public/javascripts/app.js'
   *  - /public/*.*       Ex: '/public/app.js', '/public/javascripts/app.js'
   *
   * @param  {string} path
   * @return {regexp}
   * @api private
   */

  normalize: function(path) {
    var self = this
    this.keys = []
    if (path instanceof RegExp) return path
    return new RegExp('^' + RegExp.escape(normalizePath(path), '.')
      .replace(/\*/g, '(.+)')
      .replace(/(\/|\\\.):(\w+)\?/g, function(_, c, key){
        self.keys.push(key)
        return '(?:' + c + '([^\/]+))?'
      })
      .replace(/:(\w+)/g, function(_, key){
        self.keys.push(key)
        return '([^\/]+)'
      }) + '$', 'i')
  }
})

// --- Router

Router = new Class({

  /**
   * Initialize with _request_ and parse url.
   *
   * @param  {Request} request
   * @api private
   */

  constructor: function(request) {
    this.request = request
  },

  /**
   * Evaluate the matched route against #request.
   *
   * @return {mixed}
   * @api private
   */

  route: function() {
    var body,
        route = this.matchingRoute()
    if (route) {
      body = route.callback.apply(this.request, this.request.captures.slice(1));
      if (this.request.passed) {
        if (typeof this.request.passed === 'string')
          this.request.url.pathname = this.request.passed
        this.request.passed = false
        return this.route()
      }
      return body
    }
    else
      this.request.notFound()
  },

  /**
   * Attempt to match a route from Express.routes.
   *
   * @return {Route}
   * @api private
   */

  matchingRoute: function() {
    this.lastMatchingRoute = this.lastMatchingRoute || 0
    var routes = Express.routes, route
    while (route = routes[this.lastMatchingRoute++])
      if (this.match(route))
        break
    return route
  },

  /**
   * Check if _route_ matches the current request.
   * If so populate #captures and #params.
   *
   * @param  {object} route
   * @return {bool}
   * @api private
   */

  match: function(route) {
    if (this.request.method.toLowerCase() == route.method)
      if (this.request.captures = this.request.url.pathname.match(route.path)) {
        this.mapParams(route)
        return true
      }
  },

  /**
   * Map #request.captures to #request.params.path based on the
   * given _route_ params.
   *
   * @param  {Route} route
   * @api private
   */

  mapParams: function(route) {
    route.keys.each(function(key, i){
      var val = this.request.captures[++i]
      if (key in Express.params)
        if ((val = Express.params[key].call(this.request, val)) === false)
          this.request.passed = true
      this.request.params.path[key] = this.request.captures[i] = val
    }, this)
  }
})

// --- Server

Server = new Class({

  /**
   * Default port number.
   */

  port: 3000,

  /**
   * Default host ip, when null node will accept requests on
   * all network addresses.
   */

  host: 'localhost',

  /**
   * Maximum number of queued connections.
   */

  backlog: 128,

  /**
   * Run Express.
   *
   *  - Buffers request bodies
   *  - Calls #route() once the request is complete
   *
   * @param  {int} port
   * @param  {string} host
   * @param  {int} backlog
   * @see run()
   * @api private
   */

  run: function(port, host, backlog){
    var self = this
    if (host !== undefined) this.host = host
    if (port !== undefined) this.port = port
    if (backlog !== undefined) this.backlog = backlog
    this.http = require('http')
      .createServer(function(req, response){
        var request, pendingFiles = 0
        req.setBodyEncoding('binary')
        request = new Request(req, response)
        request.body = ''
        function callback(err) {
          if (err)
            request.error(err)
          else if (!pendingFiles)
            self.route(request)
        }
        if (request.header('content-type') &&
            request.header('content-type').includes('multipart/form-data')) {
          var stream,
              contentLength = parseInt(request.header('content-length')),
              maxBodyLength = set('max upload size')
          if (maxBodyLength && contentLength > maxBodyLength)
            return callback(new Error('upload size limit exceeded'))
          stream = multipart.parse(req)
          stream
            .addListener('partBegin', function(part) {
              if (part.filename)
                ++pendingFiles,
                part.tempfile = '/tmp/express-' + Number(new Date) + utils.uid(),
                part.fileStream = fs.createWriteStream(part.tempfile),
                part.fileStream.addListener('error', callback)
              else
                part.buf = ''
            })
            .addListener('body', function(chunk) {
              if (stream.part.fileStream)
                stream.part.fileStream.write(chunk)
              else
                stream.part.buf += chunk
            })
            .addListener('partEnd', function(part) {
              if (!part.name) return
              if (part.fileStream)
                part.fileStream.close(function(){
                  --pendingFiles
                  callback()
                }),
                utils.mergeParam(part.name, { filename: part.filename, tempfile: part.tempfile }, request.params.post)
              else
                utils.mergeParam(part.name, part.buf, request.params.post)
            })
            .addListener('error', callback)
            .addListener('complete', callback)
        }
        else
          req
            .addListener('data', function(chunk){ request.body += chunk })
            .addListener('end', callback)
      })
    this.http.listen(this.port, this.host, this.backlog)
    puts('Express started at http://' + this.host + ':' + this.port + '/ in ' + Express.environment + ' mode')
  },

  /**
   * Route the given _request_.
   *
   * @param  {Request} request
   * @api private
   */

  route: function(request){
    var self = this
    request.trigger('request', function(err) {
      try {
        if (err) throw err
        if (request.response.finished) return
        if (typeof (body = (new Router(request)).route()) === 'string')
          request.halt(200, body, function(err){
            if (err) request.error(err)
          })
      } catch (err) {
        request.error(err)
      }
    })
  }
})

// --- Express

Express = {
  version: '0.8.0',
  config: [],
  routes: [],
  plugins: [],
  settings: {},
  params: {},
  server: new Server
}

// --- Defaults

configure(function(){
  use(require('express/plugins/view').View)
  use(require('express/plugins/cache').Cache)
  use(require('express/plugins/redirect').Redirect)
  use(require('express/plugins/body-decoder').BodyDecoder)
})

configure('development', function(){
  enable('helpful 404')
  enable('show exceptions')
})

configure('test', function(){
  enable('throw exceptions')
})

configure('production', function(){
  enable('cache view contents')
  enable('cache view partials')
  enable('cache static files')
})
<|endoftext|>


<commit_before>'use strict';

/**
 * Emerging Citizens
 * Developed by Engagement Lab, 2016
 * ==============
 * Common functionality game controller
 *
 * @class lib/games
 * @static
 * @author Johnny Richardson
 *
 * ==========
 */

class Common {

    constructor() {

        this.Templates,
        this.Session = require('../SessionManager'),
        this.events = require('events'),
        this.eventEmitter = new this.events.EventEmitter(),

        this._current_players = {},
        this._currentPlayerIndex = 0,
        this._game_session,
        this._game_timeout,
        this._game_in_session,
        this._config = {},

        this._current_submissions = {},
        this._players_submitted = [],

        this._countdown,
        this._countdown_duration,
        this._countdown_paused,
        this._event_countdown_done,
        this._current_round,

        // Stores last event sent and its data
        this._objLastTemplate,
        this._strLastEventId,

        this.currentPlayerCap,

        // Identifies targets for socket events
        this.players_id,
        this.group_id,
        this.keystone = require('keystone');

    }

    Initialize(gameSession, callback) {

        this._game_in_session = false;

        var GameConfig = this.keystone.list('GameConfig').model;

        this._countdown_paused = false;

        // Init session
        this._game_session = gameSession;

        // Init template loader with current game type
        var TemplateLoader = require('../TemplateLoader');
        this.Templates = new TemplateLoader(gameSession.gameType);

        // Identify targets for socket events
        this.players_id = gameSession.accessCode,
        this.group_id = gameSession.accessCode + '-group';

        var HTMLEntities = require("html-entities").XmlEntities;
        this.entity = new HTMLEntities();

        // Get config for game
        var queryConfig = GameConfig.findOne({}, {}, {
            sort: {
                'createdAt': -1
            }
        });
        queryConfig.exec(function(err, resultConfig) {

            this._config = resultConfig;

            // console.log (this._config, "common config");
            callback(this._config);
        });

        console.info('Game "' + gameSession.accessCode + '" started! ');

        this.Session.GameSession

    }

    GetGameType() {

        return this._game_session.gameType;

    }

    GetPlayerById(id) {

        let player = _.findWhere(this._current_players, {socket_id: id});

        if(!player) {
            console.log('Could not find player with socket id %s', id);
            return null;
        }

        return player;

    }

    GetGameType() {

        return this._game_session.gameType;

    }

    // Is username a player entered available?
    UsernameAvailable(name) {

        return (_.where(this._current_players, {username: name}).length === 0);

    }

    // Is user whose UID is provided still active?
    PlayerIsActive(uid) {

        return this._current_players[uid] !== undefined;

    }

    // Is game full of players?
    IsFull() {

        return (Object.keys(this._current_players).length === 8);

    }

    GameInSession() {
        console.log (this._game_in_session);
        return this._game_in_session;
    }

    Reset() {

        // Stop countdown
        clearInterval(this._countdown);
        this._countdown = undefined;
        this._countdown_paused = false;

        this._current_players = {};
        this._current_submissions = {};
        this._players_submitted = [];

        this._currentPlayerIndex = 0;

        console.info('Game "' + this._game_session.accessCode + '" ended! ');

    }

    /**
     * Begin the game's tutorial.
     *
     * @class lib/games/Common
     */
    StartTutorial(socket) {

        this.Templates.Load('partials/group/tutorial', undefined, (html) => {

            socket.to(this.group_id).emit('game:tutorial', html);

        });

    }

    Countdown(socket, data) {

        console.log ("countdown called");

        if(this._countdown !== undefined) {
            console.log ("clearing interval");
            // this.eventEmitter.removeAllListeners("countdownEnded");
            clearInterval(this._countdown);
        }

        // Use provided time limit if provided
        if(data && data.timeLimit)
            this._countdown_duration = data.timeLimit;
        else {
            if (this._game_session.gameType === "wikigeeks")
                this._countdown_duration = this._config.timeLimitWiki;

            else if (this._game_session.gameType === "htyi")
                this._countdown_duration = this._config.timeLimitHashtag;
        }

        // Start countdown
        this._countdown = setInterval(() => {

            if(this._countdown_paused)
                return;

            this._countdown_duration--;
            console.log (this._countdown_duration, "duration remaining");

            if(this._countdown_duration === 0) {

                this.eventEmitter.emit ('countdownEnded', data, socket);
                this.StopCountdown();

            }

            // TODO: This needs to be its own timeout given use of this method for a lot of game events
            else if(this._countdown_duration === 30) {

                // Dispatch countdown when running out of time for player
                this.Templates.Load('partials/player/timerunningout', undefined, (html) => {
                    socket.to(this.players_id).emit('game:countdown_ending', html);

                    this._objLastTemplate = html;
                    this._strLastEventId = 'game:countdown_ending';
                });

            }

        }, 1000);

        // Dispatch time to group view
        let socketInfo = {duration: this._countdown_duration}

        if(data && data.countdownName)
            socketInfo.name = data.countdownName;

        console.log (socketInfo);
        socket.to(this.group_id).emit('game:countdown', socketInfo);

    }

    PauseResumeCooldown(socket) {

        if(process.env.NODE_ENV === 'production')
            return;

        this._countdown_paused = !this._countdown_paused;
        socket.to(this.group_id).emit('debug:pause');

    }

    StopCountdown() {

        console.log ("StopCountdown");

        clearInterval(this._countdown);

        this.eventEmitter.removeAllListeners('countdownEnded');

    }

    ModeratorJoin(socket) {

        clearInterval(this._game_timeout);

        // Inform players of resumed game (if applicable)
        socket.to(this.PLAYERS_ID).emit('game:resumed');

    }

    // Game ended
    End(socket, noTimeout) {

        if(noTimeout === undefined || !noTimeout) {

            var timeoutTime = 30;

            console.info('Game "' + this._game_session.accessCode + '" is timing out! ');

            this._game_timeout = setInterval(() => {

                timeoutTime--;

                if(timeoutTime === 0) {

                    clearInterval(this._game_timeout);

                    this.Reset();
                    Session.Delete(this.players_id);

                    this.Templates.Load('partials/player/gameended', undefined, (html) => {
                        socket.to(this.players_id).emit('game:ended', html);

                        lastTemplate = html;
                        lastEventId = 'game:ended';
                    });

                }
                else {

                    // Dispatch countdown for game timeout
                    this.Templates.Load('partials/player/gameending', {time: timeoutTime}, (html) => {
                        socket.to(this.players_id).emit('game:ending', html);

                        lastTemplate = html;
                        lastEventId = 'game:ending';
                    });

                }

            }, 1000);

            this._game_in_session = false;

        }
        else {

            this.Reset();
            // this.Session.Delete(this.players_id);

            // Dispatch countdown for game timeout
            this.Templates.Load('partials/player/gameended', {time: timeoutTime}, (html) => {
                socket.to(this.players_id).emit('game:ended', html);

                this._objLastTemplate = html;
                this._strLastEventId = 'game:ended';
            });

        }

    }

    PlayerReady(player, socket) {

        // If player is re-connecting, set them back to connected, and re-broadcast the last update
        if(this._current_players[player.uid] !== undefined)
        {
            this._current_players[player.uid].connected = true;
            this._current_players[player.uid].socket_id = player.socket_id;

            console.info(player.username + ' has re-joined the game and has uid ' + player.uid);
            console.log (this._strLastEventId, this._objLastTemplate);
            socket.emit(this._strLastEventId, this._objLastTemplate);
        }
        else {

            this._current_players[player.uid] = {
                                        username: player.username,
                                        socket_id: player.socket_id,
                                        index: this.currentPlayerIndex,
                                        submitted: false,
                                        connected: true
                                    };

            console.info(player.username + ' has joined the game and has uid ' + player.uid);

            this.currentPlayerIndex++;

            socket.to(this.group_id).emit('players:update', this._current_players);

        }

    }

    PlayerLost(playerSocketId, socket) {

        var thisPlayer = GetPlayerById(playerSocketId)

        if(thisPlayer === undefined)
            return;

        thisPlayer.connected = false;

        console.info(thisPlayer.username + ' has left the game. Waiting for re-join.');

        setTimeout(() => {
            if (!thisPlayer.connected) {

                delete this._current_players[thisPlayer.uid];

                socket.to(this.group_id).emit('players:update', this._current_players);

                // Decrease current # of players
                this._currentPlayerCap = Object.keys(this._current_players).length;

                if(Object.keys(this._current_players).length === 0)
                    Reset();

            }
        }, 15000);

    }

    AdvanceRound(socket) {

        // Reset players state for next round
        _.each(this._current_players, (playerId, index) => {

            this._current_players[index] = _.omit(this._current_players[index], 'score');
            this._current_players[index].submitted = false;

        });

        // End of game?

        this.SaveSession();

        // Advance round
        this._current_round++;

    }

    SaveState(strEventId, objTemplate) {

        this._strLastEventId = strEventId;
        this._objLastTemplate = objTemplate;

        // Save debug data?
        if(process.env.NODE_ENV !== 'production') {
            if(!this._game_session.debugData)
                this._game_session.debugData = {};

            this._game_session.debugData[strEventId] = objTemplate;
            this.SaveSession();
        }

    }

    SaveSession() {

        this.Session.Save(this._game_session);

    }

}

module.exports = Common;<commit_msg>game in session<commit_after>'use strict';

/**
 * Emerging Citizens
 * Developed by Engagement Lab, 2016
 * ==============
 * Common functionality game controller
 *
 * @class lib/games
 * @static
 * @author Johnny Richardson
 *
 * ==========
 */

class Common {

    constructor() {

        this.Templates,
        this.Session = require('../SessionManager'),
        this.events = require('events'),
        this.eventEmitter = new this.events.EventEmitter(),

        this._current_players = {},
        this._currentPlayerIndex = 0,
        this._game_session,
        this._game_timeout,
        this._game_in_session,
        this._config = {},

        this._current_submissions = {},
        this._players_submitted = [],

        this._countdown,
        this._countdown_duration,
        this._countdown_paused,
        this._event_countdown_done,
        this._current_round,

        // Stores last event sent and its data
        this._objLastTemplate,
        this._strLastEventId,

        this.currentPlayerCap,

        // Identifies targets for socket events
        this.players_id,
        this.group_id,
        this.keystone = require('keystone');

    }

    Initialize(gameSession, callback) {

        this._game_in_session = false;

        var GameConfig = this.keystone.list('GameConfig').model;

        this._countdown_paused = false;

        // Init session
        this._game_session = gameSession;

        // Init template loader with current game type
        var TemplateLoader = require('../TemplateLoader');
        this.Templates = new TemplateLoader(gameSession.gameType);

        // Identify targets for socket events
        this.players_id = gameSession.accessCode,
        this.group_id = gameSession.accessCode + '-group';

        var HTMLEntities = require("html-entities").XmlEntities;
        this.entity = new HTMLEntities();

        // Get config for game
        var queryConfig = GameConfig.findOne({}, {}, {
            sort: {
                'createdAt': -1
            }
        });
        queryConfig.exec(function(err, resultConfig) {

            this._config = resultConfig;

            // console.log (this._config, "common config");
            callback(this._config);
        });

        console.info('Game "' + gameSession.accessCode + '" started! ');

        this.Session.GameSession

    }

    GetGameType() {

        return this._game_session.gameType;

    }

    GetPlayerById(id) {

        let player = _.findWhere(this._current_players, {socket_id: id});

        if(!player) {
            console.log('Could not find player with socket id %s', id);
            return null;
        }

        return player;

    }

    GetGameType() {

        return this._game_session.gameType;

    }

    // Is username a player entered available?
    UsernameAvailable(name) {

        return (_.where(this._current_players, {username: name}).length === 0);

    }

    // Is user whose UID is provided still active?
    PlayerIsActive(uid) {

        return this._current_players[uid] !== undefined;

    }

    // Is game full of players?
    IsFull() {

        return (Object.keys(this._current_players).length === 8);

    }

    GameInSession() {
        console.log (this._game_in_session);
        return this._game_in_session;
    }

    Reset() {

        // Stop countdown
        clearInterval(this._countdown);
        this._countdown = undefined;
        this._countdown_paused = false;

        this._current_players = {};
        this._current_submissions = {};
        this._players_submitted = [];

        this._currentPlayerIndex = 0;

        this._game_in_session = false;

        console.info('Game "' + this._game_session.accessCode + '" ended! ');

    }

    /**
     * Begin the game's tutorial.
     *
     * @class lib/games/Common
     */
    StartTutorial(socket) {

        this.Templates.Load('partials/group/tutorial', undefined, (html) => {

            socket.to(this.group_id).emit('game:tutorial', html);

        });

    }

    Countdown(socket, data) {

        console.log ("countdown called");

        if(this._countdown !== undefined) {
            console.log ("clearing interval");
            // this.eventEmitter.removeAllListeners("countdownEnded");
            clearInterval(this._countdown);
        }

        // Use provided time limit if provided
        if(data && data.timeLimit)
            this._countdown_duration = data.timeLimit;
        else {
            if (this._game_session.gameType === "wikigeeks")
                this._countdown_duration = this._config.timeLimitWiki;

            else if (this._game_session.gameType === "htyi")
                this._countdown_duration = this._config.timeLimitHashtag;
        }

        // Start countdown
        this._countdown = setInterval(() => {

            if(this._countdown_paused)
                return;

            this._countdown_duration--;
            console.log (this._countdown_duration, "duration remaining");

            if(this._countdown_duration === 0) {

                this.eventEmitter.emit ('countdownEnded', data, socket);
                this.StopCountdown();

            }

            // TODO: This needs to be its own timeout given use of this method for a lot of game events
            else if(this._countdown_duration === 30) {

                // Dispatch countdown when running out of time for player
                this.Templates.Load('partials/player/timerunningout', undefined, (html) => {
                    socket.to(this.players_id).emit('game:countdown_ending', html);

                    this._objLastTemplate = html;
                    this._strLastEventId = 'game:countdown_ending';
                });

            }

        }, 1000);

        // Dispatch time to group view
        let socketInfo = {duration: this._countdown_duration}

        if(data && data.countdownName)
            socketInfo.name = data.countdownName;

        console.log (socketInfo);
        socket.to(this.group_id).emit('game:countdown', socketInfo);

    }

    PauseResumeCooldown(socket) {

        if(process.env.NODE_ENV === 'production')
            return;

        this._countdown_paused = !this._countdown_paused;
        socket.to(this.group_id).emit('debug:pause');

    }

    StopCountdown() {

        console.log ("StopCountdown");

        clearInterval(this._countdown);

        this.eventEmitter.removeAllListeners('countdownEnded');

    }

    ModeratorJoin(socket) {

        clearInterval(this._game_timeout);

        // Inform players of resumed game (if applicable)
        socket.to(this.PLAYERS_ID).emit('game:resumed');

    }

    // Game ended
    End(socket, noTimeout) {

        if(noTimeout === undefined || !noTimeout) {

            var timeoutTime = 30;

            console.info('Game "' + this._game_session.accessCode + '" is timing out! ');

            this._game_timeout = setInterval(() => {

                timeoutTime--;

                if(timeoutTime === 0) {

                    clearInterval(this._game_timeout);

                    this.Reset();
                    Session.Delete(this.players_id);

                    this.Templates.Load('partials/player/gameended', undefined, (html) => {
                        socket.to(this.players_id).emit('game:ended', html);

                        lastTemplate = html;
                        lastEventId = 'game:ended';
                    });

                }
                else {

                    // Dispatch countdown for game timeout
                    this.Templates.Load('partials/player/gameending', {time: timeoutTime}, (html) => {
                        socket.to(this.players_id).emit('game:ending', html);

                        lastTemplate = html;
                        lastEventId = 'game:ending';
                    });

                }

            }, 1000);

            this._game_in_session = false;

        }
        else {

            this.Reset();
            // this.Session.Delete(this.players_id);

            // Dispatch countdown for game timeout
            this.Templates.Load('partials/player/gameended', {time: timeoutTime}, (html) => {
                socket.to(this.players_id).emit('game:ended', html);

                this._objLastTemplate = html;
                this._strLastEventId = 'game:ended';
            });

        }

    }

    PlayerReady(player, socket) {

        // If player is re-connecting, set them back to connected, and re-broadcast the last update
        if(this._current_players[player.uid] !== undefined)
        {
            this._current_players[player.uid].connected = true;
            this._current_players[player.uid].socket_id = player.socket_id;

            console.info(player.username + ' has re-joined the game and has uid ' + player.uid);
            console.log (this._strLastEventId, this._objLastTemplate);
            socket.emit(this._strLastEventId, this._objLastTemplate);
        }
        else {

            this._current_players[player.uid] = {
                                        username: player.username,
                                        socket_id: player.socket_id,
                                        index: this.currentPlayerIndex,
                                        submitted: false,
                                        connected: true
                                    };

            console.info(player.username + ' has joined the game and has uid ' + player.uid);

            this.currentPlayerIndex++;

            socket.to(this.group_id).emit('players:update', this._current_players);

        }

    }

    PlayerLost(playerSocketId, socket) {

        var thisPlayer = GetPlayerById(playerSocketId)

        if(thisPlayer === undefined)
            return;

        thisPlayer.connected = false;

        console.info(thisPlayer.username + ' has left the game. Waiting for re-join.');

        setTimeout(() => {
            if (!thisPlayer.connected) {

                delete this._current_players[thisPlayer.uid];

                socket.to(this.group_id).emit('players:update', this._current_players);

                // Decrease current # of players
                this._currentPlayerCap = Object.keys(this._current_players).length;

                if(Object.keys(this._current_players).length === 0)
                    Reset();

            }
        }, 15000);

    }

    AdvanceRound(socket) {

        // Reset players state for next round
        _.each(this._current_players, (playerId, index) => {

            this._current_players[index] = _.omit(this._current_players[index], 'score');
            this._current_players[index].submitted = false;

        });

        // End of game?

        this.SaveSession();

        // Advance round
        this._current_round++;

    }

    SaveState(strEventId, objTemplate) {

        this._strLastEventId = strEventId;
        this._objLastTemplate = objTemplate;

        // Save debug data?
        if(process.env.NODE_ENV !== 'production') {
            if(!this._game_session.debugData)
                this._game_session.debugData = {};

            this._game_session.debugData[strEventId] = objTemplate;
            this.SaveSession();
        }

    }

    SaveSession() {

        this.Session.Save(this._game_session);

    }

}

module.exports = Common;<|endoftext|>


<commit_before>var utils = require('../utils/httpUtils.js');
var _ = require('underscore');
var constants = require('../utils/constants.js');

exports.create = function(options) {
  var optionsToSend = {
    url: options.apiUrls.groups,
    accessToken : options.accessToken
  };

  var listGroups = function(getOptions, callback) {
    return utils.get(_.extend(optionsToSend, getOptions), callback);
  };

  var createGroup = function(postOptions, callback) {
    return utils.post(_.extend(optionsToSend, postOptions), callback);
  };

  var addGroupMembers = function(deleteOptions, callback) {
    optionsToSend.url = options.apiUrls.groups + getOptions.groupId + '/members/' + getOptions.userId;
    return utils.post(_.extend(optionsToSend, deleteOptions), callback);
  };

  var updateGroup = function(putOptions, callback) {
    return utils.put(_.extend(optionsToSend, putOptions), callback);
  };

  var deleteGroup = function(deleteOptions, callback) {
    return utils.delete(_.extend(optionsToSend, deleteOptions), callback);
  };

  var removeGroupMember = function(deleteOptions, callback) {
    optionsToSend.url = options.apiUrls.groups + getOptions.groupId + '/members/' + getOptions.userId;
    return utils.delete(_.extend(optionsToSend, deleteOptions), callback);
  };

  return {
    listGroups        : listGroups,
    getGroup          : listGroups,
    createGroup       : createGroup,
    addGroupMembers   : addGroupMembers,
    updateGroup       : updateGroup,
    deleteGroup       : deleteGroup,
    removeGroupMember : removeGroupMember
  };
};
<commit_msg>Fixed group member functions<commit_after>var utils = require('../utils/httpUtils.js');
var _ = require('underscore');
var constants = require('../utils/constants.js');

exports.create = function(options) {
  var optionsToSend = {
    url: options.apiUrls.groups,
    accessToken : options.accessToken
  };

  var listGroups = function(getOptions, callback) {
    return utils.get(_.extend(optionsToSend, getOptions), callback);
  };

  var createGroup = function(postOptions, callback) {
    return utils.post(_.extend(optionsToSend, postOptions), callback);
  };

  var addGroupMembers = function(postOptions, callback) {
    optionsToSend.url = options.apiUrls.groups + postOptions.groupId + '/members/';
    return utils.post(_.extend(optionsToSend, postOptions), callback);
  };

  var updateGroup = function(putOptions, callback) {
    return utils.put(_.extend(optionsToSend, putOptions), callback);
  };

  var deleteGroup = function(deleteOptions, callback) {
    return utils.delete(_.extend(optionsToSend, deleteOptions), callback);
  };

  var removeGroupMember = function(deleteOptions, callback) {
    optionsToSend.url = options.apiUrls.groups + deleteOptions.groupId + '/members/' + deleteOptions.userId;
    return utils.delete(_.extend(optionsToSend, deleteOptions), callback);
  };

  return {
    listGroups        : listGroups,
    getGroup          : listGroups,
    createGroup       : createGroup,
    addGroupMembers   : addGroupMembers,
    updateGroup       : updateGroup,
    deleteGroup       : deleteGroup,
    removeGroupMember : removeGroupMember
  };
};
<|endoftext|>


<commit_before>'use strict';

var dejavu =     require('dejavu');
var cp           = require('child_process');
var EventEmitter = require('events').EventEmitter;

var Runner = dejavu.Class.declare({
    $name: 'Runner',

    _queue: [],
    _fork: null,
    _inited: false,

    _keepAliveInterval: 2000,
    _keepAliveIntervalId: null,

    /**
     * Constructor.
     */
    initialize: function () {
        // if this process exits, destroy the worker gracefully
        process.once('SIGTERM', this.kill.$bind(this));
        process.once('SIGINT', this.kill.$bind(this));
        process.once('exit', this.kill.$bind(this));
        process.once('uncaughtException', function (e) {
            this.kill();
            throw e;
        }.$bind(this));
    },

    /**
     * Runs the given grunt task with the given options.
     * When done, calls the callback with the error if any (node style).
     *
     * The task might get queued if there's a task already running.
     * Please note that the task will actually be run in a worker (fork).
     *
     * An EventEmitter is returned.
     * It will emit 'start', 'data', 'error' and 'end' events.
     *
     * @param {String}   name        The task name
     * @param {Object}   [$options]  The task options
     * @param {Object}   [$config]   The grunt config
     * @param {Function} [$callback] The callback
     *
     * @return {EventEmitter} The event emitter
     */
    run: function (name, $options, $config, $callback) {
        var emitter = new EventEmitter();

        this._queue.push({
            name: name,
            opts: $options,
            config: $config,
            emitter: emitter,
            cb: $callback || function () {}
        });
        process.nextTick(this._processQueue.$bind(this));

        return emitter;
    },

    /**
     * Kills the underlying grunt worker (gracefully).
     *
     * @return {Runner} Chainable!
     */
    kill: function () {
        if (this._fork) {
            this._inited = false;

            // stop the keep alive
            this._stopKeepAlive();

            // cleanup listeners
            this._fork.removeAllListeners();

            // send exit message if the worker has not exited yet
            if (!this._fork._exited) {
                this._fork.on('error', function () {});
                this._fork.kill();
            }

            this._fork = null;
        }
    },

    /**
     * Processes the queue, running the next task if the worker is free.
     * If the worker is not yet created, it will make sure to create it before
     * running any task.
     */
    _processQueue: function () {
        // do nothing if not tasks are queued
        if (this._queue.length) {
            // if the worker is not yet ready, then create it
            if (!this._inited) {
                this._createWorker();
            // process the next task on queue if no task is beeing run
            } else if (!this._current) {
                this._current = this._queue.shift();
                this._current.emitter.emit('start');

                // inform the worker to run it
                this._fork.send({ msg: 'run', task: {
                    name: this._current.name,
                    opts: this._current.opts,
                    config: this._current.config
                }});
            }
        }
    },

    /**
     * Creates and setups the grunt worker (if not already created).
     */
    _createWorker: function () {
        var current,
            err;

        // check if the worker is already spawned but not yet inited
        if (this._fork) {
            return;
        }

        // spawn the worker
        this._fork = cp.fork(__dirname + '/worker', [this._keepAliveInterval * 2], {
            cwd: process.cwd()
        });

        // handle worker messages
        this._fork.on('message', this._onWorkerMessage);

        // caught errors and log them
        this._fork.on('error', function (err) {
            if (this._current) {
                this._current.emitter.emit('error', err);
            }
        }.$bind(this));

        // if the worker exited too early
        this._fork.once('exit', function () {
            if (this._inited) {
                this._fork._exited = true;

                // kill the worker
                this.kill();

                // if there was a task running, report error
                if (this._current) {
                    current = this._current;
                    this._current = null;
                    err = new Error('Grunt runner exited unexpectedly');
                    this._current.emitter.emit('error', err);
                    this._current.emitter.emit('end', false);
                    current.cb(err);
                }

                // process next on queue (resume operations)
                this._processQueue();
            }
        }.$bind(this));

        // start keep alive to keep the fork alive
        this._startKeepAlive();
    },

    /**
     * Handles messages coming from the grunt worker.
     *
     * @param {Object} data The message data
     */
    _onWorkerMessage: function (data) {
        var current,
            err;

        // inited message arrived?
        if (data.msg === 'inited') {
            this._inited = true;
            this._processQueue();
        // runned message arrived?
        } else if (data.msg === 'runned') {
            current = this._current;
            this._current = null;
            if (data.code) {
                err = new Error('Failed to run grunt task');
                current.emitter.emit('error', err);
                current.emitter.emit('end', false);
                current.cb(err);
            } else {
                current.emitter.emit('end', true);
                current.cb();
            }
            this._processQueue();
        // write message arrived (log)?
        } else if (data.msg === 'write') {
            if (this._current) {
                this._current.emitter.emit('data', data.data);
            }
        }
    }.$bound(),

    /**
     * Starts the keep alive mechanism to maintain the worker alive.
     * If the worker does not receive any keep alive messages within a certain
     * timeframe, it commits suicide.
     */
    _startKeepAlive: function () {
        this._keepAliveIntervalId = setInterval(function () {
            this._fork.send({ msg: 'keep-alive' });
        }.$bind(this), this._keepAliveInterval);
    },

    /**
     * Stops the keep alive.
     */
    _stopKeepAlive: function () {
        if (this._keepAliveIntervalId) {
            clearInterval(this._keepAliveIntervalId);
            this._keepAliveIntervalId = null;
        }
    }
});

module.exports = Runner;<commit_msg>Use on() instead of once().<commit_after>'use strict';

var dejavu       = require('dejavu');
var cp           = require('child_process');
var EventEmitter = require('events').EventEmitter;

var Runner = dejavu.Class.declare({
    $name: 'Runner',

    _queue: [],
    _fork: null,
    _inited: false,

    _keepAliveInterval: 2000,
    _keepAliveIntervalId: null,

    /**
     * Constructor.
     */
    initialize: function () {
        // if this process exits, destroy the worker gracefully
        process.on('SIGTERM', this.kill.$bind(this));
        process.on('SIGINT', this.kill.$bind(this));
        process.on('exit', this.kill.$bind(this));
        process.on('uncaughtException', function (e) {
            this.kill();
            throw e;
        }.$bind(this));
    },

    /**
     * Runs the given grunt task with the given options.
     * When done, calls the callback with the error if any (node style).
     *
     * The task might get queued if there's a task already running.
     * Please note that the task will actually be run in a worker (fork).
     *
     * An EventEmitter is returned.
     * It will emit 'start', 'data', 'error' and 'end' events.
     *
     * @param {String}   name        The task name
     * @param {Object}   [$options]  The task options
     * @param {Object}   [$config]   The grunt config
     * @param {Function} [$callback] The callback
     *
     * @return {EventEmitter} The event emitter
     */
    run: function (name, $options, $config, $callback) {
        var emitter = new EventEmitter();

        this._queue.push({
            name: name,
            opts: $options,
            config: $config,
            emitter: emitter,
            cb: $callback || function () {}
        });
        process.nextTick(this._processQueue.$bind(this));

        return emitter;
    },

    /**
     * Kills the underlying grunt worker (gracefully).
     *
     * @return {Runner} Chainable!
     */
    kill: function () {
        if (this._fork) {
            this._inited = false;

            // stop the keep alive
            this._stopKeepAlive();

            // cleanup listeners
            this._fork.removeAllListeners();

            // send exit message if the worker has not exited yet
            if (!this._fork._exited) {
                this._fork.on('error', function () {});
                this._fork.kill();
            }

            this._fork = null;
        }
    },

    /**
     * Processes the queue, running the next task if the worker is free.
     * If the worker is not yet created, it will make sure to create it before
     * running any task.
     */
    _processQueue: function () {
        // do nothing if not tasks are queued
        if (this._queue.length) {
            // if the worker is not yet ready, then create it
            if (!this._inited) {
                this._createWorker();
            // process the next task on queue if no task is beeing run
            } else if (!this._current) {
                this._current = this._queue.shift();
                this._current.emitter.emit('start');

                // inform the worker to run it
                this._fork.send({ msg: 'run', task: {
                    name: this._current.name,
                    opts: this._current.opts,
                    config: this._current.config
                }});
            }
        }
    },

    /**
     * Creates and setups the grunt worker (if not already created).
     */
    _createWorker: function () {
        var current,
            err;

        // check if the worker is already spawned but not yet inited
        if (this._fork) {
            return;
        }

        // spawn the worker
        this._fork = cp.fork(__dirname + '/worker', [this._keepAliveInterval * 2], {
            cwd: process.cwd()
        });

        // handle worker messages
        this._fork.on('message', this._onWorkerMessage);

        // caught errors and log them
        this._fork.on('error', function (err) {
            if (this._current) {
                this._current.emitter.emit('error', err);
            }
        }.$bind(this));

        // if the worker exited too early
        this._fork.once('exit', function () {
            if (this._inited) {
                this._fork._exited = true;

                // kill the worker
                this.kill();

                // if there was a task running, report error
                if (this._current) {
                    current = this._current;
                    this._current = null;
                    err = new Error('Grunt runner exited unexpectedly');
                    this._current.emitter.emit('error', err);
                    this._current.emitter.emit('end', false);
                    current.cb(err);
                }

                // process next on queue (resume operations)
                this._processQueue();
            }
        }.$bind(this));

        // start keep alive to keep the fork alive
        this._startKeepAlive();
    },

    /**
     * Handles messages coming from the grunt worker.
     *
     * @param {Object} data The message data
     */
    _onWorkerMessage: function (data) {
        var current,
            err;

        // inited message arrived?
        if (data.msg === 'inited') {
            this._inited = true;
            this._processQueue();
        // runned message arrived?
        } else if (data.msg === 'runned') {
            current = this._current;
            this._current = null;
            if (data.code) {
                err = new Error('Failed to run grunt task');
                current.emitter.emit('error', err);
                current.emitter.emit('end', false);
                current.cb(err);
            } else {
                current.emitter.emit('end', true);
                current.cb();
            }
            this._processQueue();
        // write message arrived (log)?
        } else if (data.msg === 'write') {
            if (this._current) {
                this._current.emitter.emit('data', data.data);
            }
        }
    }.$bound(),

    /**
     * Starts the keep alive mechanism to maintain the worker alive.
     * If the worker does not receive any keep alive messages within a certain
     * timeframe, it commits suicide.
     */
    _startKeepAlive: function () {
        this._keepAliveIntervalId = setInterval(function () {
            this._fork.send({ msg: 'keep-alive' });
        }.$bind(this), this._keepAliveInterval);
    },

    /**
     * Stops the keep alive.
     */
    _stopKeepAlive: function () {
        if (this._keepAliveIntervalId) {
            clearInterval(this._keepAliveIntervalId);
            this._keepAliveIntervalId = null;
        }
    }
});

module.exports = Runner;<|endoftext|>