<commit_before>if (process.env.NODE_ENV != 'production') {
  require('dotenv').config();
}

var body_parser = require('body-parser');
var express = require('express');
var flip = require('flip');
var qs = require('qs');
var request = require('request');

if (!process.env.APP_CLIENT_ID || !process.env.APP_CLIENT_SECRET || !process.env.PORT) {
  console.log('Error: Specify APP_CLIENT_ID, APP_CLIENT_SECRET, and PORT in environment');
  process.exit(1);
}

var app = express();
app.use(body_parser.json());
app.use(body_parser.urlencoded({ extended: true }));

// Handle oauth flow
app.get('/oauth', function(req, res) {
  var url = 'https://slack.com/api/oauth.access?' + qs.stringify({
    client_id: process.env.APP_CLIENT_ID,
    client_secret: process.env.APP_CLIENT_SECRET,
    code: req.query.code,
  });

  request.post(url, function(err, http, body) {
    var info = JSON.parse(body);

    if (info.ok && info.access_token) {
      res.redirect(process.env.OAUTH_SUCCESS_URL);
    } else {
      console.log('error:', err, body);
      res.redirect(process.env.OAUTH_ERROR_URL);
    }
  });
});

// Respond to webhooks
app.post('/webhook', function(req, res) {
  console.log(req.body);
  if (req.body.token === process.env.APP_COMMAND_TOKEN) return res.sendStatus(403);
  console.log('token is good');
  console.log(req.body.command);
  if (req.body.command !== '/tableflip') return res.sendStatus(200);
  console.log('command is tableflip');

  var flipped_text = flip(req.body.text) || '┻━┻';
  console.log('flipped_text', flipped_text);
  var response_text =  '(╯°□°）╯︵ ' + flipped_text;
  console.log('resp', response_text);

  res.json({
    response_type: 'in_channel',
    text: response_text,
  });
});

// Leave a route for us to use as a monitor
app.get('/ping', function(req, res) {
  res.sendStatus(200);
});

app.listen(process.env.PORT, function() {
  console.log('Server listening on', process.env.PORT);
});
<commit_msg>Remove all blocks<commit_after>if (process.env.NODE_ENV != 'production') {
  require('dotenv').config();
}

var body_parser = require('body-parser');
var express = require('express');
var flip = require('flip');
var qs = require('qs');
var request = require('request');

if (!process.env.APP_CLIENT_ID || !process.env.APP_CLIENT_SECRET || !process.env.PORT) {
  console.log('Error: Specify APP_CLIENT_ID, APP_CLIENT_SECRET, and PORT in environment');
  process.exit(1);
}

var app = express();
app.use(body_parser.json());
app.use(body_parser.urlencoded({ extended: true }));

// Handle oauth flow
app.get('/oauth', function(req, res) {
  var url = 'https://slack.com/api/oauth.access?' + qs.stringify({
    client_id: process.env.APP_CLIENT_ID,
    client_secret: process.env.APP_CLIENT_SECRET,
    code: req.query.code,
  });

  request.post(url, function(err, http, body) {
    var info = JSON.parse(body);

    if (info.ok && info.access_token) {
      res.redirect(process.env.OAUTH_SUCCESS_URL);
    } else {
      console.log('error:', err, body);
      res.redirect(process.env.OAUTH_ERROR_URL);
    }
  });
});

// Respond to webhooks
app.post('/webhook', function(req, res) {
  console.log(req.body);
  // if (req.body.token === process.env.APP_COMMAND_TOKEN) return res.sendStatus(403);
  console.log('token is good');
  console.log(req.body.command);
  // if (!req.body.command.startsWith('/tableflip')) return res.sendStatus(200);
  console.log('command is tableflip');

  var flipped_text = flip(req.body.text) || '┻━┻';
  console.log('flipped_text', flipped_text);
  var response_text =  '(╯°□°）╯︵ ' + flipped_text;
  console.log('resp', response_text);

  res.json({
    response_type: 'in_channel',
    text: response_text,
  });
});

// Leave a route for us to use as a monitor
app.get('/ping', function(req, res) {
  res.sendStatus(200);
});

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


<commit_before>const Board = require('../lib/board');
const Player = require('../lib/player');
const Game = require('../lib/game');
const Piece = require('../lib/piece');
const Square = require('../lib/square');
const assert = require('assert');

describe('Game', function() {
    it('should instantiate with players, turn, over and board properties', function() {
        var board = new Board();
        var game = new Game(board);

        assert.equal(game.players[0].name, 'player1');
        assert.equal(game.players[0].color, 'black');
        assert.equal(game.players[1].name, 'player2');
        assert.equal(game.players[1].color, 'white');
        assert.equal(game.turn, 0);
        assert.equal(game.over, false);
        assert(game.board);
    });

    it('sets the current player according to the turn number', function() {
        var board = new Board();
        var game = new Game(board);
        assert.equal(game.currentPlayer.color, 'black');
        game.turn = 1;
        game.findCurrentPlayer();
        assert.equal(game.currentPlayer.color, 'white');
        game.turn = 2;
        game.findCurrentPlayer();
        assert.equal(game.currentPlayer.color, 'black');

    });

    it('should place a piece on the square that is clicked on', function() {
        var board = new Board();
        var game = new Game(board);
        board.game = game;
        board.createSquares();
        board.placeFirstFourPieces();
        var square = board.squares[27];

        assert(square.piece);
    });

});
<commit_msg>Fix test<commit_after>const Board = require('../lib/board');
const Player = require('../lib/player');
const Game = require('../lib/game');
const Piece = require('../lib/piece');
const Square = require('../lib/square');
const assert = require('assert');

describe('Game', function() {
    it('should instantiate with players, turn, over and board properties', function() {
        var board = new Board();
        var game = new Game();

        assert.equal(game.players[0].name, 'player1');
        assert.equal(game.players[0].color, 'black');
        assert.equal(game.players[1].name, 'player2');
        assert.equal(game.players[1].color, 'white');
        assert.equal(game.turn, 0);
        assert.equal(game.over, false);
    });

    it('sets the current player according to the turn number', function() {
        var board = new Board();
        var game = new Game(board);
        assert.equal(game.currentPlayer.color, 'black');
        game.turn = 1;
        game.findCurrentPlayer();
        assert.equal(game.currentPlayer.color, 'white');
        game.turn = 2;
        game.findCurrentPlayer();
        assert.equal(game.currentPlayer.color, 'black');

    });

    it('should place a piece on the square that is clicked on', function() {
        var board = new Board();
        var game = new Game(board);
        board.game = game;
        board.createSquares();
        board.placeFirstFourPieces();
        var square = board.squares[27];

        assert(square.piece);
    });

});
<|endoftext|>


<commit_before>// #####################################################################################################################
// ################################################# I M P O R T S #####################################################
// #####################################################################################################################

var assert = require('assert');

// #####################################################################################################################
// ################################################### C L A S S #######################################################
// #####################################################################################################################

function PubSub () {
  this._fns = {};
  this._regexpFns = {};
}

// #####################################################################################################################
// ################################################# M E T H O D S #####################################################
// #####################################################################################################################

PubSub.prototype.subscribe = function (channel, fn) {
  assert.equal(typeof(channel), 'string', "Channel must be a String");
  assert.equal(typeof(fn), 'function', "Callback associated must be a Function");

  if(this._fns[channel] === undefined) {
    this._fns[channel] = [fn];
  } else {
    this._fns[channel].push(fn);
  }
};

// #####################################################################################################################

PubSub.prototype.unsubscribe = function (channel, fn) {
  assert.equal(typeof(channel), 'string', "Channel must be a String");
  if(fn) {
    assert.equal(typeof(fn), 'function', "Callback to remove must be a Function");
  }

  if(this._fns[channel] !== undefined) {
    if(fn) {
      var index = this._fns[channel].indexOf(fn);
      this._fns[channel].splice(index, 1);
    } else {
      delete this._fns[channel];
    }
  }
};

// #####################################################################################################################

PubSub.prototype.regexpSubscribe = function (regexp, fn) {
  assert.ok(regexp instanceof RegExp, "Channel must be a RegExp object");
  assert.equal(typeof(fn), 'function', "Callback associated must be a Function");

  var channel = regexp.toString();
  if(this._regexpFns[channel] === undefined) {
    this._regexpFns[channel] = {
      'regexp': regexp,
      'functions': [fn]
    };
  } else {
    this._regexpFns[channel].functions.push(fn);
  }
};

// #####################################################################################################################

PubSub.prototype.regexpUnsubscribe = function (regexp, fn) {
  assert.ok(regexp instanceof RegExp, "Channel must be a RegExp object");
  if(fn) {
    assert.equal(typeof(fn), 'function', "Callback to remove must be a Function");
  }

  var channel = regexp.toString();
  if(this._regexpFns[channel] !== undefined) {
    if(fn) {
      var index = this._regexpFns[channel].functions.indexOf(fn);
      this._regexpFns[channel].functions.splice(index, 1);
    } else {
      delete this._regexpFns[channel];
    }
  }
};

// #####################################################################################################################

PubSub.prototype.publish = function (channel, msg) {
  assert(typeof(channel), 'string', "Channel must be a String");

  // Check if channel is present in classic subscribings
  if(this._fns[channel] !== undefined) {
    this._fns[channel].forEach(function (elt) {
      elt(channel, msg);
    });
  }

  // Check if channel match regexp objects
  for(var r in this._regexpFns) {
    if(this._regexpFns[r].regexp.test(channel)) {
      this._regexpFns[r].functions.forEach(function (elt) {
        elt(channel, msg);
      });
    }
    this._regexpFns[r].regexp.lastIndex = 0;
  }
};

// #####################################################################################################################
// ################################################# E X P O R T S #####################################################
// #####################################################################################################################

module.exports = PubSub;
<commit_msg>[Doc] Add JSDoc tags<commit_after>// #####################################################################################################################
// ################################################# I M P O R T S #####################################################
// #####################################################################################################################

var assert = require('assert');

// #####################################################################################################################
// ################################################### C L A S S #######################################################
// #####################################################################################################################

function PubSub () {
  /* @member {Object} _fns */
  this._fns = {};
  /* @member {Object} _regexpFns */
  this._regexpFns = {};
}

// #####################################################################################################################
// ################################################# M E T H O D S #####################################################
// #####################################################################################################################

/**
 * @method subscribe
 *
 * @desc Method to subscribe to a specific channel
 *
 * @param  {String}       channel
 * @param  {subscribeCb}  fn
 *
 */
PubSub.prototype.subscribe = function (channel, fn) {
  assert.equal(typeof(channel), 'string', "Channel must be a String");
  assert.equal(typeof(fn), 'function', "Callback associated must be a Function");

  if(this._fns[channel] === undefined) {
    this._fns[channel] = [fn];
  } else {
    this._fns[channel].push(fn);
  }
};
/**
 * @callback subscribeCb
 *
 * @desc Callback assigned to the Channel
 *
 * @param {String}  channel
 * @param {Mixed}   msg
 *
 */

// #####################################################################################################################

/**
 * @method unsubscribe
 *
 * @desc Method to unsubscribe either a specific function either all functions
 *
 * @param  {String}       channel
 * @param  {subscribeCb}  fn
 *
 */
PubSub.prototype.unsubscribe = function (channel, fn) {
  assert.equal(typeof(channel), 'string', "Channel must be a String");
  if(fn) {
    assert.equal(typeof(fn), 'function', "Callback to remove must be a Function");
  }

  if(this._fns[channel] !== undefined) {
    if(fn) {
      var index = this._fns[channel].indexOf(fn);
      this._fns[channel].splice(index, 1);
    } else {
      delete this._fns[channel];
    }
  }
};

// #####################################################################################################################

/**
 * @method regexpSubscribe
 *
 * @desc Method to subscribe to a regexp object reprensenting the channel
 *
 * @param {RegExp}      regexp
 * @param {subscribeCb} fn
 *
 */
PubSub.prototype.regexpSubscribe = function (regexp, fn) {
  assert.ok(regexp instanceof RegExp, "Channel must be a RegExp object");
  assert.equal(typeof(fn), 'function', "Callback associated must be a Function");

  var channel = regexp.toString();
  if(this._regexpFns[channel] === undefined) {
    this._regexpFns[channel] = {
      'regexp': regexp,
      'functions': [fn]
    };
  } else {
    this._regexpFns[channel].functions.push(fn);
  }
};

// #####################################################################################################################

/**
 * @method regexpUnsubscribe
 *
 * @desc Method to unsubscribe either a specific eiter all functions from a regexp object
 *
 * @param {RegExp}      regexp
 * @param {subscribeCb} fn
 *
 */
PubSub.prototype.regexpUnsubscribe = function (regexp, fn) {
  assert.ok(regexp instanceof RegExp, "Channel must be a RegExp object");
  if(fn) {
    assert.equal(typeof(fn), 'function', "Callback to remove must be a Function");
  }

  var channel = regexp.toString();
  if(this._regexpFns[channel] !== undefined) {
    if(fn) {
      var index = this._regexpFns[channel].functions.indexOf(fn);
      this._regexpFns[channel].functions.splice(index, 1);
    } else {
      delete this._regexpFns[channel];
    }
  }
};

// #####################################################################################################################

/**
 * @method publish
 *
 * @desc Method to publish a message on a channel
 *
 * @param  {String} channel
 * @param  {Mixed}  msg
 *
 */
PubSub.prototype.publish = function (channel, msg) {
  assert(typeof(channel), 'string', "Channel must be a String");

  // Check if channel is present in classic subscribings
  if(this._fns[channel] !== undefined) {
    this._fns[channel].forEach(function (elt) {
      elt(channel, msg);
    });
  }

  // Check if channel match regexp objects
  for(var r in this._regexpFns) {
    if(this._regexpFns[r].regexp.test(channel)) {
      for(var i = 0; i < this._regexpFns[r].functions.length; i++) {
        this._regexpFns[r].functions[i](channel, msg);
      }
    }
    this._regexpFns[r].regexp.lastIndex = 0;
  }
};

// #####################################################################################################################
// ################################################# E X P O R T S #####################################################
// #####################################################################################################################

module.exports = PubSub;
<|endoftext|>


<commit_before>'use strict'
const MarkdownIt = require('markdown-it')
const ssbref = require('ssb-ref')

const replaceNewlines = (text) => text.replace(/\n+(?!$)/g, ' ')

exports.block = function (text, opts) {
  const defaults = {
    imageLink: function (ref) {
      return '#' + ref
    },
    toUrl: function (ref, isImage) {
      // standard ssb-refs
      if (ssbref.isFeedId(ref))
        return '#/profile/'+encodeURIComponent(ref)
      else if (ssbref.isMsgId(ref))
        return '#/msg/'+encodeURIComponent(ref)
      else if (ssbref.isBlobId(ref))
        return '/'+encodeURIComponent(ref)
      else if (ref && ref[0] === '#')
        return '#/channel/'+encodeURIComponent(ref.substr(1))
      return ''
    },
    protocols: ['http', 'https', 'dat']
  }

  opts = Object.assign({}, defaults, opts)

  // init
  const md = new MarkdownIt({ linkify: true })
    .use(require('markdown-it-hashtag'), { hashtagRegExp: '[\\w+-]+' })
    .use(require('markdown-it-emoji'))

  // protocols
  const defaultProtocols = ['http', 'https', 'ftp']
  Object.values(opts.protocols).forEach(protocol => {
    if (!defaultProtocols.includes(protocol))
      md.linkify.add(protocol + ':', 'http:')
  })

  // hashtag
  md.renderer.rules.hashtag_open = function (tokens, idx) {
    var tagName = tokens[idx].content.toLowerCase()
    const url = opts.toUrl('#' + tagName)
    return ''
  }

  // emoji
  md.renderer.rules.emoji = function (token, idx) {
    const emoji = token[idx].markup
    return `<img src="./img/emoji/${emoji}.png" alt=":${emoji}:" title=":${emoji}:" class="emoji" align="absmiddle" height="16" width="16">`
  }

  // links
  var oldRender = md.renderer.rules.link_open || function (tokens, idx, options, env, self) {
    return self.renderToken(tokens, idx, options)
  }
  md.renderer.rules.link_open = (tokens, idx, options, env, self) => {
    const href = tokens[idx].attrs[0][1]
    if (href.indexOf('not-allowed-as-link') >= 0) {
      tokens[idx].attrs[0] = ['class', 'bad']
    } else if (!href.startsWith('#')) {
      tokens[idx].attrPush(['target', '_blank'])
    }

    return oldRender(tokens, idx, options, env, self)
  }

  // sigils
  ['@', '%', '&'].forEach(sigil => {
    md.linkify.add(sigil, {
      validate: function (text, pos, self) {
        var tail = text.slice(pos);

        if (!self.re.sigil) {
          self.re.sigil = new RegExp(
            '^([a-zA-Z0-9\/=]{44}\.[a-z0-9]+)'
          );
        }
        if (self.re.sigil.test(tail)) {
          return tail.match(self.re.sigil)[0].length;
        }
        return 0;
      },
      normalize: function (match) {
        match.url = opts.toUrl(match.raw)
      }
    });
  })

  // image
  md.renderer.rules.image = (tokens, idx, options, env, self) => {
    const token = tokens[idx]

    const text = token.attrs[0][1]
    const alt = token.content
    const url = opts.imageLink(text)
    const src = opts.toUrl(text, true)

    // XXX no support for `titleAttr`
    if (alt.startsWith('audio:')) {
      return `<video controls src="${src}" alt="${alt}" />`
    }  else if (alt.startsWith('video:')) {
      return `<video controls src="${src}" alt="${alt}" />`
    } else {
      return `<a href="${url}"><img src="${src}" alt="${alt}"></a>`
    }
  }

  return md.render('' + (text || ''))
}

exports.inline = function (text, opts) {
  // init
  const md = new MarkdownIt()
    .use(require('markdown-it-emoji'))

  // emoji
  md.renderer.rules.emoji = function (token, idx) {
    const emoji = token[idx].markup
    return `<img src="./img/emoji/${emoji}.png" alt=":${emoji}:" title=":${emoji}:" class="emoji" align="absmiddle" height="12" width="12">`
  }

  // links
  md.renderer.rules.link_open = () => ''
  md.renderer.rules.link_close = () => ''

  // code
  md.renderer.rules.code_inline = (tokens, idx) => {
    // XXX: had to change test to pass because of fenced code blocks with an
    // embedded syntax name (like ```js and ``` js)
    return tokens[idx].content
      .replace(/&/g, '&')
      .replace(/</g, '<')
      .replace(/>/g, '>')
      .replace(/"/g, '"')
  }

  // image
  md.renderer.rules.image = (tokens, idx, options, env, self) => {
    return tokens[idx].content
  }

  return replaceNewlines(md.renderInline('' + (text || '')))
}

<commit_msg>Fix emoji and refactor for simplicity<commit_after>'use strict'
const MarkdownIt = require('markdown-it')
const ssbref = require('ssb-ref')

const replaceNewlines = (text) => text.replace(/\n+(?!$)/g, ' ')

const defaults = {
  imageLink: function (ref) {
    return '#' + ref
  },
  toUrl: function (ref) {
    // standard ssb-refs
    if (ssbref.isFeedId(ref)) {
      return '#/profile/' + encodeURIComponent(ref)
    } else if (ssbref.isMsgId(ref)) {
      return '#/msg/' + encodeURIComponent(ref)
    } else if (ssbref.isBlobId(ref)) {
      return '/' + encodeURIComponent(ref)
    } else if (ref && ref[0] === '#') {
      return '#/channel/' + encodeURIComponent(ref.substr(1))
    }
    return ''
  },
  protocols: ['http', 'https', 'ftp'],
  emoji: (emoji, size) => {
    size = size || 16
    const image = {
      src: `./img/emoji/${emoji}.png`,
      alt: `:${emoji}:`,
      title: `:${emoji}:`,
      class: 'emoji',
      align: 'absmiddle',
      height: size,
      width: size
    }

    let string = ' {
      const value = image[key]
      string += ` ${key}="${value}"`
    })

    string += '>'

    return string
  }
}

exports.block = function (text, opts) {
  const config = Object.assign({}, defaults, opts)

  // init
  const md = new MarkdownIt({ linkify: true })
    .use(require('markdown-it-hashtag'), { hashtagRegExp: '[\\w+-]+' })
    .use(require('markdown-it-emoji'))

  // protocols
  const defaultProtocols = ['http', 'https', 'ftp']
  const additionalProtocols = ['data', 'dat', 'magnet']
  Object.values(config.protocols).forEach(protocol => {
    if (!defaultProtocols.includes(protocol)) { md.linkify.add(protocol + ':', 'http:') }
  })
  Object.values(additionalProtocols).forEach(protocol => {
    if (!config.protocols.includes(protocol)) { md.linkify.add(protocol + ':', 'http:') }
  })

  // hashtag
  md.renderer.rules.hashtag_open = function (tokens, idx) {
    var tagName = tokens[idx].content.toLowerCase()
    const url = config.toUrl('#' + tagName)
    return ''
  }

  // emoji
  md.renderer.rules.emoji = function (token, idx) {
    const emoji = token[idx].markup
    return config.emoji(emoji, 16)
  }

  // links
  var oldRender = md.renderer.rules.link_open || function (tokens, idx, options, env, self) {
    return self.renderToken(tokens, idx, options)
  }
  md.renderer.rules.link_open = (tokens, idx, options, env, self) => {
    const href = tokens[idx].attrs[0][1]
    if (href.indexOf('not-allowed-as-link') >= 0) {
      tokens[idx].attrs[0] = ['class', 'bad']
    } else if (!href.startsWith('#')) {
      tokens[idx].attrPush(['target', '_blank'])
    }

    return oldRender(tokens, idx, options, env, self)
  }

  // sigils
  ['@', '%', '&'].forEach(sigil => {
    md.linkify.add(sigil, {
      validate: function (text, pos, self) {
        var tail = text.slice(pos)

        if (!self.re.sigil) {
          self.re.sigil = new RegExp(
            '^([a-zA-Z0-9+/=]{44}.[a-z0-9]+)'
          )
        }
        if (self.re.sigil.test(tail)) {
          return tail.match(self.re.sigil)[0].length
        }
        return 0
      },
      normalize: function (match) {
        // shorten the link to 7 characters plus the sigil
        match.text = match.text.slice(0, 8)
        // linkify is percent-decoding, so we percent-encode the percent symbol
        match.text = match.text.replace('%', '%25')
        match.url = config.toUrl(match.raw)
      }
    })
  })

  // image
  md.renderer.rules.image = (tokens, idx) => {
    const token = tokens[idx]

    const text = token.attrs[0][1]
    const alt = token.content
    const url = config.imageLink(text)
    const src = config.toUrl(text, true)

    // XXX no support for `titleAttr`
    if (alt.startsWith('audio:')) {
      return `<video controls src="${src}" alt="${alt}" />`
    } else if (alt.startsWith('video:')) {
      return `<video controls src="${src}" alt="${alt}" />`
    } else {
      return `<a href="${url}"><img src="${src}" alt="${alt}"></a>`
    }
  }

  return md.render('' + (text || ''))
}

exports.inline = function (text, opts) {
  const config = Object.assign({}, defaults, opts)

  // init
  const md = new MarkdownIt()
    .use(require('markdown-it-emoji'))

  // emoji
  md.renderer.rules.emoji = function (token, idx) {
    const emoji = token[idx].markup
    return config.emoji(emoji, 12)
  }

  // links
  md.renderer.rules.link_open = () => ''
  md.renderer.rules.link_close = () => ''

  // code
  md.renderer.rules.code_inline = (tokens, idx) => {
    // XXX: had to change test to pass because of fenced code blocks with an
    // embedded syntax name (like ```js and ``` js)
    return tokens[idx].content
      .replace(/&/g, '&')
      .replace(/</g, '<')
      .replace(/>/g, '>')
      .replace(/"/g, '"')
  }

  // image
  md.renderer.rules.image = (tokens, idx) => {
    return tokens[idx].content
  }

  return replaceNewlines(md.renderInline('' + (text || '')))
}
<|endoftext|>


<commit_before>module('Generator tests')

test('Non allocating generator',function() {
	var Simple = Generator(function(v) { return v; }, { alloc: false });

	var s8 = Simple(8);
	equal(s8, 8);

})

test('Generator inherit from constructor defined on main constructor',function(){

	var _Shape = sinon.spy();
	var _Rectangle = sinon.spy();
	_Rectangle.bases = [_Shape];

	var Shape = Generator(_Shape);
	var Rectangle = Generator(_Rectangle);

	ok(!! Shape.__generator__)
	ok(!! _Shape.__generator__)
	ok(!! Rectangle.__generator__)
	ok(!! _Rectangle.__generator__)
	equal(Rectangle.bases.length, 1)
	equal(Rectangle.bases[0], _Shape)

	Rectangle(1,2,3,4);
	equal(_Shape.callCount,1)
	equal(_Rectangle.callCount,1)
	ok(_Shape.calledWith(1,2,3,4))
	ok(_Rectangle.calledWith(1,2,3,4))
})

test('Generator inherit from generator defined on main constructor',function(){

	var _Shape = sinon.spy();
	var Shape = Generator(_Shape);

	var _Rectangle = sinon.spy();
	_Rectangle.bases = [Shape];

	var Rectangle = Generator(_Rectangle);

	ok(!! Shape.__generator__)
	ok(!! _Shape.__generator__)
	ok(!! Rectangle.__generator__)
	ok(!! _Rectangle.__generator__)
	equal(Rectangle.bases.length, 1)
	equal(Rectangle.bases[0], _Shape)

	Rectangle(1,2,3,4);
	equal(_Shape.callCount,1)
	equal(_Rectangle.callCount,1)
	ok(_Shape.calledWith(1,2,3,4))
	ok(_Rectangle.calledWith(1,2,3,4))
})

test("Simple Generator and descendants",function() {

	function _Simple(){
		return { v:"v" };
	}
	var Simple = Generator(_Simple,{ alloc:false });

	// simple gen
	var s = Simple();
	ok(s);
	equal(s.v,"v");
	equal(typeof s,"object")

	// derived simple
	var _Derived = sinon.spy();
	var Derived = Generator(_Derived,Simple);
	var d = Derived();
	ok(d);
	equal(d.v,"v");
	equal(typeof d,"object");
	equal(_Derived.callCount,1);

	// derived simple
	var _Doubly = sinon.spy();
	var Doubly = Generator(_Doubly,Derived);
	var d = Doubly();
	ok(d);
	equal(d.v,"v");
	equal(typeof d,"object");
	equal(_Doubly.callCount,1);

	// derived simple, explicit
	var _Derived = sinon.spy();
	var Derived = Generator(_Derived,Simple, { alloc:false });
	var d = Derived();
	ok(d);
	equal(d.v,"v");
	equal(typeof d,"object");
	equal(_Derived.callCount,1);

	function _Simple2(){
		return { v:"v" };
	}

	// derived simple constructor, explicit
	var _Derived = sinon.spy();
	var Derived = Generator(_Derived,_Simple2, { alloc:false });
	var d = Derived();
	ok(d);
	equal(d.v,"v");
	equal(typeof d,"object");
	equal(_Derived.callCount,1);

})

/*
test("Generator inherit from Resolver generator",function() {

	var _WithResolver = sinon.spy();
	var WithResolver = Generator(_WithResolver,Resolver, { alloc:false });

	var r = WithResolver({});
	ok(r.namespace);
	equal(typeof r,"function");
})
*/

test('Generator with passed prototype',function(){
	function constr() {

	}
	function method() {

	}

	var gen = Generator(constr, { prototype: { method:method }});

	equal(gen.info.constructors[-1],constr);
	equal(gen.prototype.method,method);
	equal(constr.prototype.method,method);
})

test('Generator with passed prototype of other',function(){
	function base() {

	}
	function method() {

	}
	base.prototype.method = method;

	function constr() {

	}

	var gen = Generator(constr, { prototype: base.prototype });

	equal(gen.info.constructors[-1],constr);
	equal(gen.prototype.method,method);
	equal(constr.prototype.method,method);
})

test('Shape generator with parameters',function() {

	var NumberType = Resolver("essential")("Type").variant("Number");

	function _Shape() {}
	_Shape.args = [ ];

	function _Rectangle(width,height) {

	}
	_Rectangle.bases = [_Shape];
	_Rectangle.args = [ NumberType({name:"width",preset:true}), NumberType({name:"height",preset:true}) ]; //TODO NumberType({name:"width"}) optional: , default:  seed:
	_Rectangle.prototype.earlyFunc = function() {};

	var Shape = Generator(_Shape);
	var Rectangle = Generator(_Rectangle);

	Rectangle.prototype.getWidth = function() {
		return this.width;
	};

	equal(typeof Shape.info.options,'object');
	equal(Rectangle.bases, _Rectangle.bases);
	equal(Rectangle.args, _Rectangle.args);

	var s = Shape();
	var r55 = Rectangle(5,5);
	ok(r55 instanceof _Rectangle);
	ok(r55 instanceof Rectangle);
	ok(r55 instanceof _Shape);
	ok(r55 instanceof Shape);
	equal(Rectangle.prototype.getWidth, _Rectangle.prototype.getWidth);
	equal(typeof Rectangle.prototype.earlyFunc, 'function');
	equal(r55.width, 5);
	equal(r55.getWidth(), 5);

	/*
	Shape.mixin({
		sides: 0
	});

	Rectangle.mixin({
		sides: 2,
		getRatio: function() { return this.width / this.height; }
	});

	equal(_Shape.prototype.sides, 0);
	equal(s.sides, 0);
	equal(Rectangle.prototype.sides, 2);
	equal(r55.sides, 2);
	*/

	function _Circle(diameter) {

	}
	_Circle.prototype.earlyFunc = function() {};

	var Circle = Generator(_Circle,Shape,{
		args : [ NumberType({name:"diameter",preset:true}) ] //TODO NumberType({name:"width"}) optional: , default:  seed:
	});

	Circle.prototype.getWidth = function() {
		return this.diameter;
	};

	equal(Circle.bases.length, 1);
	equal(Circle.bases[0], _Shape);
	equal(Circle.info.options.args.length, 1);
	equal(typeof Circle.info.options.args[0], 'object');
	//TODO equal(_Circle.prototype.sides, 0);

	var c9 = Circle(9);
	ok(c9 instanceof _Circle);
	ok(c9 instanceof Circle);
	ok(c9 instanceof _Shape);
	ok(c9 instanceof Shape);
	equal(c9.diameter, 9);
	equal(c9.getWidth(), 9);

})

// inherit from generator not constructor

// Generator.set get declare on the prototype

test("Inherit from Builtin",function(){
	function _Test() {
		this.a = "a";

	}
	var Test = Generator(_Test,Array);
	_Test.prototype.b = function(){
		return "b";
	}

	var test = Test();
	// debugger;
	ok(test.a);
	ok(test.b);
	ok(test.b());
	equal(test.length,0);
	test.push("d");
	equal(test.length,1);
});

//TODO singleton construction and discard/teardown
//TODO singleton page lifecycle

test("Generator variant for one or more options",function(){
	var Base = Generator(function(){});
	var V1 = Base.variant("1",Generator(function(){}));
	var V2 = Base.variant("2",Generator(function(){}));

	equal(Base.variant("1"),V1);
	equal(Base.variant(["1"]),V1);
	equal(Base.variant(["1","2"]),V1);
	equal(Base.variant(["0","1","2"]),V1);

	equal(Base.variant("2"),V2);
	equal(Base.variant(["2"]),V2);
	equal(Base.variant(["2","1"]),V2);
	equal(Base.variant(["0","2","1"]),V2);

});

//TODO only call constructor once for multiple inheritance

test("Generator presets",function(){
	ok(1,"presets.declare")
	ok(1,"presets.declare assign/copy/resolve")
});

//TODO test restrict called multiple times

<commit_msg>very basic singleton test<commit_after>module('Generator tests')

test('Non allocating generator',function() {
	var Simple = Generator(function(v) { return v; }, { alloc: false });

	var s8 = Simple(8);
	equal(s8, 8);

})

test('Generator inherit from constructor defined on main constructor',function(){

	var _Shape = sinon.spy();
	var _Rectangle = sinon.spy();
	_Rectangle.bases = [_Shape];

	var Shape = Generator(_Shape);
	var Rectangle = Generator(_Rectangle);

	ok(!! Shape.__generator__)
	ok(!! _Shape.__generator__)
	ok(!! Rectangle.__generator__)
	ok(!! _Rectangle.__generator__)
	equal(Rectangle.bases.length, 1)
	equal(Rectangle.bases[0], _Shape)

	Rectangle(1,2,3,4);
	equal(_Shape.callCount,1)
	equal(_Rectangle.callCount,1)
	ok(_Shape.calledWith(1,2,3,4))
	ok(_Rectangle.calledWith(1,2,3,4))
})

test('Generator inherit from generator defined on main constructor',function(){

	var _Shape = sinon.spy();
	var Shape = Generator(_Shape);

	var _Rectangle = sinon.spy();
	_Rectangle.bases = [Shape];

	var Rectangle = Generator(_Rectangle);

	ok(!! Shape.__generator__)
	ok(!! _Shape.__generator__)
	ok(!! Rectangle.__generator__)
	ok(!! _Rectangle.__generator__)
	equal(Rectangle.bases.length, 1)
	equal(Rectangle.bases[0], _Shape)

	Rectangle(1,2,3,4);
	equal(_Shape.callCount,1)
	equal(_Rectangle.callCount,1)
	ok(_Shape.calledWith(1,2,3,4))
	ok(_Rectangle.calledWith(1,2,3,4))
})

test("Simple Generator and descendants",function() {

	function _Simple(){
		return { v:"v" };
	}
	var Simple = Generator(_Simple,{ alloc:false });

	// simple gen
	var s = Simple();
	ok(s);
	equal(s.v,"v");
	equal(typeof s,"object")

	// derived simple
	var _Derived = sinon.spy();
	var Derived = Generator(_Derived,Simple);
	var d = Derived();
	ok(d);
	equal(d.v,"v");
	equal(typeof d,"object");
	equal(_Derived.callCount,1);

	// derived simple
	var _Doubly = sinon.spy();
	var Doubly = Generator(_Doubly,Derived);
	var d = Doubly();
	ok(d);
	equal(d.v,"v");
	equal(typeof d,"object");
	equal(_Doubly.callCount,1);

	// derived simple, explicit
	var _Derived = sinon.spy();
	var Derived = Generator(_Derived,Simple, { alloc:false });
	var d = Derived();
	ok(d);
	equal(d.v,"v");
	equal(typeof d,"object");
	equal(_Derived.callCount,1);

	function _Simple2(){
		return { v:"v" };
	}

	// derived simple constructor, explicit
	var _Derived = sinon.spy();
	var Derived = Generator(_Derived,_Simple2, { alloc:false });
	var d = Derived();
	ok(d);
	equal(d.v,"v");
	equal(typeof d,"object");
	equal(_Derived.callCount,1);

})

/*
test("Generator inherit from Resolver generator",function() {

	var _WithResolver = sinon.spy();
	var WithResolver = Generator(_WithResolver,Resolver, { alloc:false });

	var r = WithResolver({});
	ok(r.namespace);
	equal(typeof r,"function");
})
*/

test('Generator with passed prototype',function(){
	function constr() {

	}
	function method() {

	}

	var gen = Generator(constr, { prototype: { method:method }});

	equal(gen.info.constructors[-1],constr);
	equal(gen.prototype.method,method);
	equal(constr.prototype.method,method);
})

test('Generator with passed prototype of other',function(){
	function base() {

	}
	function method() {

	}
	base.prototype.method = method;

	function constr() {

	}

	var gen = Generator(constr, { prototype: base.prototype });

	equal(gen.info.constructors[-1],constr);
	equal(gen.prototype.method,method);
	equal(constr.prototype.method,method);
})

test('Shape generator with parameters',function() {

	var NumberType = Resolver("essential")("Type").variant("Number");

	function _Shape() {}
	_Shape.args = [ ];

	function _Rectangle(width,height) {

	}
	_Rectangle.bases = [_Shape];
	_Rectangle.args = [ NumberType({name:"width",preset:true}), NumberType({name:"height",preset:true}) ]; //TODO NumberType({name:"width"}) optional: , default:  seed:
	_Rectangle.prototype.earlyFunc = function() {};

	var Shape = Generator(_Shape);
	var Rectangle = Generator(_Rectangle);

	Rectangle.prototype.getWidth = function() {
		return this.width;
	};

	equal(typeof Shape.info.options,'object');
	equal(Rectangle.bases, _Rectangle.bases);
	equal(Rectangle.args, _Rectangle.args);

	var s = Shape();
	var r55 = Rectangle(5,5);
	ok(r55 instanceof _Rectangle);
	ok(r55 instanceof Rectangle);
	ok(r55 instanceof _Shape);
	ok(r55 instanceof Shape);
	equal(Rectangle.prototype.getWidth, _Rectangle.prototype.getWidth);
	equal(typeof Rectangle.prototype.earlyFunc, 'function');
	equal(r55.width, 5);
	equal(r55.getWidth(), 5);

	/*
	Shape.mixin({
		sides: 0
	});

	Rectangle.mixin({
		sides: 2,
		getRatio: function() { return this.width / this.height; }
	});

	equal(_Shape.prototype.sides, 0);
	equal(s.sides, 0);
	equal(Rectangle.prototype.sides, 2);
	equal(r55.sides, 2);
	*/

	function _Circle(diameter) {

	}
	_Circle.prototype.earlyFunc = function() {};

	var Circle = Generator(_Circle,Shape,{
		args : [ NumberType({name:"diameter",preset:true}) ] //TODO NumberType({name:"width"}) optional: , default:  seed:
	});

	Circle.prototype.getWidth = function() {
		return this.diameter;
	};

	equal(Circle.bases.length, 1);
	equal(Circle.bases[0], _Shape);
	equal(Circle.info.options.args.length, 1);
	equal(typeof Circle.info.options.args[0], 'object');
	//TODO equal(_Circle.prototype.sides, 0);

	var c9 = Circle(9);
	ok(c9 instanceof _Circle);
	ok(c9 instanceof Circle);
	ok(c9 instanceof _Shape);
	ok(c9 instanceof Shape);
	equal(c9.diameter, 9);
	equal(c9.getWidth(), 9);

})

// inherit from generator not constructor

// Generator.set get declare on the prototype

test("Inherit from Builtin",function(){
	function _Test() {
		this.a = "a";

	}
	var Test = Generator(_Test,Array);
	_Test.prototype.b = function(){
		return "b";
	}

	var test = Test();
	// debugger;
	ok(test.a);
	ok(test.b);
	ok(test.b());
	equal(test.length,0);
	test.push("d");
	equal(test.length,1);
});

test("Singleton generator is created and destroyed correctly",function(){

	function _Test() {
		this.a = "a";

	}
	var Test = Generator(_Test);
	_Test.prototype.b = function(){
		return "b";
	}
	Test.restrict({ "singleton":true });

	var test = Test();
	ok(test);
	equal(typeof Test.info.existing[0],"object");
});

//TODO singleton construction and discard/teardown
//TODO singleton page lifecycle

test("Generator variant for one or more options",function(){
	var Base = Generator(function(){});
	var V1 = Base.variant("1",Generator(function(){}));
	var V2 = Base.variant("2",Generator(function(){}));

	equal(Base.variant("1"),V1);
	equal(Base.variant(["1"]),V1);
	equal(Base.variant(["1","2"]),V1);
	equal(Base.variant(["0","1","2"]),V1);

	equal(Base.variant("2"),V2);
	equal(Base.variant(["2"]),V2);
	equal(Base.variant(["2","1"]),V2);
	equal(Base.variant(["0","2","1"]),V2);

});

//TODO only call constructor once for multiple inheritance

test("Generator presets",function(){
	ok(1,"presets.declare")
	ok(1,"presets.declare assign/copy/resolve")
});

//TODO test restrict called multiple times

<|endoftext|>


<commit_before>var extend = require('xtend');
var Route = require('./Route');
var Request = require('./Request');
var Response = require('./Response');
var Unhandled = require('./errors/Unhandled');
var delayed = require('./utils/delayed');
var getDefaultHistory = require('./history/getHistory');
var series = require('run-series');
var inherits = require('inherits');
var EventEmitter = require('wolfy87-eventemitter');
var attach = require('./attach');
var LinkHijacker = require('./LinkHijacker');
var withoutResults = require('./utils/withoutResults');
var thunkifyAll = require('./utils/thunkifyAll');

function Router(opts) {
  if (opts) {
    this.state = extend(opts.initialState);
    this.history = opts.history;
  }
  this.url = this.constructor.url.bind(this.constructor);
  // TODO: Should we add the properties from the class (i.e. rootURL) to the instance?
}

inherits(Router, EventEmitter);

/**
 * Expose the `Unhandled` error so it can be used in handlers more easily.
 */
Router.Unhandled = Unhandled;

function throwNoEngine() {
  throw new Error('You are attempting to render but your router has no rendering engine.');
}

Router.engine = {
  renderInto: throwNoEngine,
  renderToString: throwNoEngine
};

/**
 * Add a route to the router class. This API can be used in a manner similar
 * to many server-side JS routing libraries—by chainging calls to `route`—
 * however, it's more likely that you'll want to pass your routes to the
 * router class constructor using JSX.
 *
 * This function accepts either `(path, handlers...)`, or
 * `(name, path, handlers...)`.
 */
Router.route = function() {
  var RouterClass = this;
  var args = Array.prototype.slice.call(arguments, 0);
  var name, path, handlers;

  if (typeof args[1] !== 'function') name = args.shift();
  path = args.shift();
  handlers = args;

  var route = new Route(path);

  if (name) {
    if (RouterClass.namedRoutes[name])
      throw new Error('Route with name "' + name + '" already exists.');
    RouterClass.namedRoutes[name] = route;
  }

  // Create and register a middleware to represent this route.
  RouterClass.use(function(req, next) {
    // If this route doesn't match, skip it.
    var match = route.match(req.path);
    if (!match) return next();

    req.params = match;

    series(thunkifyAll(handlers, this, [req]), withoutResults(next));
  });

  // For chaining!
  return this;
};

Router.prototype.setState = function(state) {
  this.state = extend(this.state, state);
  this.emit('stateChange');
};

Router.prototype.setView = function(view) {
  this.view = view;
  this.emit('viewChange');
};

Router.prototype.render = function() {
  if (!this.view) {
    throw new Error('You must set a view before rendering');
  }
  return this.view(extend(this.state));
};

Router.finalMiddleware = [
  // If we've exhausted the middleware without handling the request, call the
  // `unhandled()` method. Going through `unhandled` instead of just creating an
  // error in the dispatch callback means we're always going through the same
  // process, getting the same events, etc.
  function(req, next) {
    if (!this.error && !this.ended) return this.unhandled();
    next();
  }
];

/**
 *
 * @param {string} url
 * @param {function?} callback
 */
Router.prototype.dispatch = function(url, opts, callback) {
  var RouterClass = this.constructor;

  if (typeof arguments[1] === 'function') {
    callback = arguments[1];
    opts = null;
  }

  // Wrap the callback, imposing a delay to force asynchronousness in
  // case the user calls it synchronously.
  var cb = function(err) {
    // Clean up listeners
    res.off('error', cb).off('end', cb);

    this._currentResponse = null;
    if (callback) callback(err, err ? null : res);
  }.bind(this);

  // Resolve the URL to our root.
  var outsideRoot = true;
  if (url.slice(0, RouterClass.rootURL.length) === RouterClass.rootURL) {
    url = url.slice(RouterClass.rootURL.length);
    outsideRoot = false;
  }

  var req = new Request(url, opts);
  var res = new Response(req, this)
    .on('error', cb)
    .on('end', cb);

  if (outsideRoot) {
    delayed(function() {
      res.unhandled('URL not within router root: ' + url);
    })();
    return res;
  }

  if (this._currentResponse) {
    this._currentResponse.request.cancel();
  }
  this._currentResponse = res;

  // Force async behavior so you have a chance to add listeners to the
  // request object.
  var middleware = RouterClass.middleware.concat(RouterClass.finalMiddleware);
  delayed(function() {
    series(thunkifyAll(middleware, res, [req]), function(err) {
      if (err) res['throw'](err);
    });
  })();

  return res;
};

Router.prototype.captureClicks = function(el, opts) {
  return new LinkHijacker(this, el, opts);
};

Router.use = function(middleware) {
  var RouterClass = this;
  RouterClass.middleware.push(middleware);
  return this;
};

Router.extend = function(opts) {
  var SuperClass = this;
  var NewRouter = function(opts) {
    if (!(this instanceof NewRouter)) {
      return new NewRouter(opts);
    }
    SuperClass.call(this, opts);
  };
  inherits(NewRouter, SuperClass);

  // Add "static" props to the new router.
  for (var k in SuperClass) {
    if (SuperClass.hasOwnProperty(k)) {
      NewRouter[k] = SuperClass[k];
    }
  }

  NewRouter.engine = opts && opts.engine;
  NewRouter.rootURL = opts && opts.rootURL || '';
  NewRouter.middleware = [];
  NewRouter.namedRoutes = {};

  return NewRouter;
};

Router.attach = function(element, opts) {
  return attach(this, element, opts);
};

/**
 * Extensions are just functions that mutate the router in any way they want
 * and return it. This function is just a prettier way to use them than calling
 * them directly.
 */
Router.setup = function(extension) {
  var router = extension(this);
  if (!router)
    throw new Error('Invalid extension: extension did not return router.');
  return router;
};

Router.url = function(name, params) {
  var route = this.namedRoutes[name];
  if (!route)
    throw new Error('There is no route named "' + name + '".');
  return route.url(params);
};

module.exports = Router;
<commit_msg>Process errors in error middleware<commit_after>var extend = require('xtend');
var Route = require('./Route');
var Request = require('./Request');
var Response = require('./Response');
var Unhandled = require('./errors/Unhandled');
var delayed = require('./utils/delayed');
var getDefaultHistory = require('./history/getHistory');
var series = require('run-series');
var inherits = require('inherits');
var EventEmitter = require('wolfy87-eventemitter');
var attach = require('./attach');
var LinkHijacker = require('./LinkHijacker');
var withoutResults = require('./utils/withoutResults');
var thunkifyAll = require('./utils/thunkifyAll');

function Router(opts) {
  if (opts) {
    this.state = extend(opts.initialState);
    this.history = opts.history;
  }
  this.url = this.constructor.url.bind(this.constructor);
  // TODO: Should we add the properties from the class (i.e. rootURL) to the instance?
}

inherits(Router, EventEmitter);

/**
 * Expose the `Unhandled` error so it can be used in handlers more easily.
 */
Router.Unhandled = Unhandled;

function throwNoEngine() {
  throw new Error('You are attempting to render but your router has no rendering engine.');
}

Router.engine = {
  renderInto: throwNoEngine,
  renderToString: throwNoEngine
};

/**
 * Add a route to the router class. This API can be used in a manner similar
 * to many server-side JS routing libraries—by chainging calls to `route`—
 * however, it's more likely that you'll want to pass your routes to the
 * router class constructor using JSX.
 *
 * This function accepts either `(path, handlers...)`, or
 * `(name, path, handlers...)`.
 */
Router.route = function() {
  var RouterClass = this;
  var args = Array.prototype.slice.call(arguments, 0);
  var name, path, handlers;

  if (typeof args[1] !== 'function') name = args.shift();
  path = args.shift();
  handlers = args;

  var route = new Route(path);

  if (name) {
    if (RouterClass.namedRoutes[name])
      throw new Error('Route with name "' + name + '" already exists.');
    RouterClass.namedRoutes[name] = route;
  }

  // Create and register a middleware to represent this route.
  RouterClass.use(function(req, next) {
    // If this route doesn't match, skip it.
    var match = route.match(req.path);
    if (!match) return next();

    req.params = match;

    series(thunkifyAll(handlers, this, [req]), withoutResults(next));
  });

  // For chaining!
  return this;
};

Router.prototype.setState = function(state) {
  this.state = extend(this.state, state);
  this.emit('stateChange');
};

Router.prototype.setView = function(view) {
  this.view = view;
  this.emit('viewChange');
};

Router.prototype.render = function() {
  if (!this.view) {
    throw new Error('You must set a view before rendering');
  }
  return this.view(extend(this.state));
};

Router.finalMiddleware = [
  // If we've exhausted the middleware without handling the request, call the
  // `unhandled()` method. Going through `unhandled` instead of just creating an
  // error in the dispatch callback means we're always going through the same
  // process, getting the same events, etc.
  function(req, next) {
    if (!this.error && !this.ended) return this.unhandled();
    next();
  }
];

/**
 *
 * @param {string} url
 * @param {function?} callback
 */
Router.prototype.dispatch = function(url, opts, callback) {
  var RouterClass = this.constructor;

  if (typeof arguments[1] === 'function') {
    callback = arguments[1];
    opts = null;
  }

  // Wrap the callback, imposing a delay to force asynchronousness in
  // case the user calls it synchronously.
  var cb = function(err) {
    // Clean up listeners
    res.off('error', cb).off('end', cb);

    this._currentResponse = null;
    if (callback) callback(err, err ? null : res);
  }.bind(this);

  // Resolve the URL to our root.
  var outsideRoot = true;
  if (url.slice(0, RouterClass.rootURL.length) === RouterClass.rootURL) {
    url = url.slice(RouterClass.rootURL.length);
    outsideRoot = false;
  }

  var req = new Request(url, opts);
  var res = new Response(req, this)
    .on('error', cb)
    .on('end', cb);

  if (outsideRoot) {
    delayed(function() {
      res.unhandled('URL not within router root: ' + url);
    })();
    return res;
  }

  if (this._currentResponse) {
    this._currentResponse.request.cancel();
  }
  this._currentResponse = res;

  // Force async behavior so you have a chance to add listeners to the
  // request object.
  var middleware = RouterClass.middleware.concat(RouterClass.finalMiddleware);
  delayed(function() {
    series(thunkifyAll(middleware, res, [req]), function(err) {
      if (err) {
        series(thunkifyAll(RouterClass.errorMiddleware, res, [err, req]), function(err) {
          if (err) res['throw'](err);
        })
      }
    });
  })();

  return res;
};

Router.prototype.captureClicks = function(el, opts) {
  return new LinkHijacker(this, el, opts);
};

Router.use = function(middleware) {
  var RouterClass = this,
      middlewares = middleware.length === 3 ? RouterClass.errorMiddleware : RouterClass.middleware;
  middlewares.push(middleware);
  return this;
};

Router.extend = function(opts) {
  var SuperClass = this;
  var NewRouter = function(opts) {
    if (!(this instanceof NewRouter)) {
      return new NewRouter(opts);
    }
    SuperClass.call(this, opts);
  };
  inherits(NewRouter, SuperClass);

  // Add "static" props to the new router.
  for (var k in SuperClass) {
    if (SuperClass.hasOwnProperty(k)) {
      NewRouter[k] = SuperClass[k];
    }
  }

  NewRouter.engine = opts && opts.engine;
  NewRouter.rootURL = opts && opts.rootURL || '';
  NewRouter.middleware = [];
  NewRouter.errorMiddleware = [];
  NewRouter.namedRoutes = {};

  return NewRouter;
};

Router.attach = function(element, opts) {
  return attach(this, element, opts);
};

/**
 * Extensions are just functions that mutate the router in any way they want
 * and return it. This function is just a prettier way to use them than calling
 * them directly.
 */
Router.setup = function(extension) {
  var router = extension(this);
  if (!router)
    throw new Error('Invalid extension: extension did not return router.');
  return router;
};

Router.url = function(name, params) {
  var route = this.namedRoutes[name];
  if (!route)
    throw new Error('There is no route named "' + name + '".');
  return route.url(params);
};

module.exports = Router;
<|endoftext|>


<commit_before>// oplog by jbk
var fs        = require('fs')
var argv      = require('minimist')(process.argv.slice(2))
var dias      = require('dias')
var microtime = require('microtime-nodejs')
var opUA      = ''
var platform  = {}
var options   = {}
var opStream  = process.stdout

// opOn = timestamp
// opUA = useragent
// opAt = reserve for: geo
// opBy = reserve for: md564(opUA) alias of opUA

function init(opts, cb) {
	if (typeof opts == 'function') {
		cb   = opts
		opts = undefined
	}

	var streamOptions = {
	  flags:     (opts && opts.flags)                           ? opts.flags     : 'w',
	  encoding:  (opts && opts.encoding)                        ? opts.encoding  : 'utf8',
	  mode:      (opts && opts.mode)                            ? opts.mode      : 0666,
	  autoClose: (opts && typeof opts.autoClose != 'undefined') ? opts.autoClose : true
	}

	var filename           = (argv.log) ? argv.log : (opts && opts.file) ? opts.file : undefined
	if (filename) opStream = fs.createWriteStream(filename, options)

	if (typeof opts == 'object') { options = opts }
	dias({seperator:'; '}, function(data) {
		platform = data
		opUA     = platform.useragent
		if (opts && opts.ua) {
			opUA               = opts.ua + ' (' + opUA + ')'
			platform.useragent = opUA
		}
		cb(platform)
	})
}

// log operation
function log(msg) {
	var log            = (options.log) ? options.log : {}
	if (msg instanceof Error) {
		log.op         = msg.name
		log.error      = msg.message
		log.errorStack = msg.stack
	} else if (typeof msg == 'object') {
		log            = msg
	} else {
		log.op         = msg
	}
	log.opUA = platform.useragent
	log.opOn = new Date().toISOString().replace('T', ' ').split('.')[0]
	if (options.microtime) {
		log.opOn += '.' + microtime.nowStruct()[1]
	}
	opStream.write(JSON.stringify(log) + "\n")
	if (log.error) process.exit(1)
}

function useragent() {
	return platform.useragent
}

module.exports = {
	ua:   useragent,
	init: init,
	log:  log
}<commit_msg>add useragent<commit_after>// oplog by jbk
var fs        = require('fs')
var argv      = require('minimist')(process.argv.slice(2))
var dias      = require('dias')
var microtime = require('microtime-nodejs')
var opUA      = ''
var platform  = {}
var options   = {}
var opStream  = process.stdout

// opOn = timestamp
// opUA = useragent
// opAt = reserve for: geo
// opBy = reserve for: md564(opUA) alias of opUA

function init(opts, cb) {
	if (typeof opts == 'function') {
		cb   = opts
		opts = undefined
	}

	var streamOptions = {
	  flags:     (opts && opts.flags)                           ? opts.flags     : 'w',
	  encoding:  (opts && opts.encoding)                        ? opts.encoding  : 'utf8',
	  mode:      (opts && opts.mode)                            ? opts.mode      : 0666,
	  autoClose: (opts && typeof opts.autoClose != 'undefined') ? opts.autoClose : true
	}

	var filename           = (argv.log) ? argv.log : (opts && opts.file) ? opts.file : undefined
	if (filename) opStream = fs.createWriteStream(filename, options)

	if (typeof opts == 'object') { options = opts }
	dias({seperator:'; '}, function(data) {
		platform = data
		opUA     = platform.useragent
		if (opts && opts.ua) {
			opUA               = opts.ua + ' (' + opUA + ')'
			platform.useragent = opUA
		}
		cb(platform)
	})
}

// log operation
function log(msg) {
	var log            = (options.log) ? options.log : {}
	if (msg instanceof Error) {
		log.op         = msg.name
		log.error      = msg.message
		log.errorStack = msg.stack
	} else if (typeof msg == 'object') {
		log            = msg
	} else {
		log.op         = msg
	}
	log.opUA = platform.useragent
	log.opOn = new Date().toISOString().replace('T', ' ').split('.')[0]
	if (options.microtime) {
		log.opOn += '.' + microtime.nowStruct()[1]
	}
	opStream.write(JSON.stringify(log) + "\n")
	if (log.error) process.exit(1)
}

function useragent() {
	return platform.useragent
}

module.exports = {
	useragent: useragent,
	init:      init,
	log:       log
}<|endoftext|>


<commit_before>dojo.provide("dojox.grid.enhanced._Events");

dojo.declare("dojox.grid.enhanced._Events", null, {
	// summary:
	//		Overwrite some default events of DataGrid
	//
	// description:
	//		Methods are copied or replaced for overwriting, this might be refined once
	//		an overall plugin architecture is set up for DataGrid.

	//_events: Object
	//		Method map cached from dojox.grid._Events().
	_events: null,

	// headerCellActiveClass: String
	//		css class to apply to grid header cells when activated(mouse down)
	headerCellActiveClass: 'dojoxGridHeaderActive',

	// cellActiveClass: String
	//		css class to apply to grid content cells when activated(mouse down)
	cellActiveClass: 'dojoxGridCellActive',

	// rowActiveClass: String
	//		css class to apply to grid rows when activated(mouse down)
	rowActiveClass: 'dojoxGridRowActive',

	constructor: function(inGrid){
		//get the default Grid events
		this._events = new dojox.grid._Events();
		//for methods that won't be overwritten, copy them to "this" scope
		for(var p in this._events){
			if(!this[p]){
				this.p = this._events.p;
			}
		}
		//mixin "this" to Grid
		inGrid.mixin(inGrid, this);
	},
	dokeyup: function(e){
		// summary:
		//		Grid key up event handler.
		// e: Event
		//		Un-decorated event object
		this.focus.currentArea().keyup(e);
	},
	onKeyDown: function(e){
		// summary:
		//		Overwritten, see dojox.grid._Events.onKeyDown();
		if(e.altKey || e.metaKey){ return; }
		var dk = dojo.keys;
		var focus = this.focus;
		switch(e.keyCode){
			case dk.TAB:
				if(e.ctrlKey){ return; }
				focus.tab(e.shiftKey ? -1:1,e);
				break;
			case dk.UP_ARROW:
			case dk.DOWN_ARROW:
				focus.currentArea().move(e.keyCode == dk.UP_ARROW ? -1 : 1, 0, e);
				break;
			case dk.LEFT_ARROW:
			case dk.RIGHT_ARROW:
				var offset = (e.keyCode == dk.LEFT_ARROW) ? 1 : -1;
				if(dojo._isBodyLtr()){ offset *= -1; }
				focus.currentArea().move(0, offset, e);
				break;
			case dk.F10:
				if(this.menus && e.shiftKey){
					this.onRowContextMenu(e);
				}
				break;
			default:
				focus.currentArea().keydown(e);
				break;
		}
	},
	//TODO - make the following events more reasonalble - e.g. more accurate conditions
	//events for row selectors
	domouseup: function(e){
		if(e.cellNode){
			this.onMouseUp(e);
		}else{
			this.onRowSelectorMouseUp(e);
		}
	},
	domousedown: function(e){
		if(!e.cellNode){
			this.onRowSelectorMouseDown(e);
		}
	},
	onMouseUp: function(e){
		// summary:
		//		New - Event fired when mouse is up inside grid.
		// e: Event
		//		Decorated event object that contains reference to grid, cell, and rowIndex
		this[e.rowIndex == -1 ? "onHeaderCellMouseUp" : "onCellMouseUp"](e);
	},
	onCellMouseDown: function(e){
		// summary:
		//		Overwritten, see dojox.grid._Events.onCellMouseDown()
		dojo.addClass(e.cellNode, this.cellActiveClass);
		dojo.addClass(e.rowNode, this.rowActiveClass);
	},
	onCellMouseUp: function(e){
		// summary:
		//		New - Event fired when mouse is up inside content cell.
		// e: Event
		//		Decorated event object that contains reference to grid, cell, and rowIndex
		dojo.removeClass(e.cellNode, this.cellActiveClass);
		dojo.removeClass(e.rowNode, this.rowActiveClass);
	},
	onCellClick: function(e){
		// summary:
		//		Overwritten, see dojox.grid._Events.onCellClick()

		//invoke dojox.grid._Events.onCellClick()
		this._events.onCellClick.call(this, e);
		//move mouse events to the focus manager.
		this.focus.contentMouseEvent(e);//TODO
	},
	onCellDblClick: function(e){
		// summary:
		//		Overwritten, see dojox.grid._Events.onCellDblClick()
		if(this.pluginMgr.isFixedCell(e.cell)){ return; }
		if(this._click.length > 1 && (!this._click[0] || !this._click[1])){
			this._click[0] = this._click[1] = e;
		}
		//invoke dojox.grid._Events.onCellDblClick()
		this._events.onCellDblClick.call(this, e);
		//now focus.setFocusCell need isEditing info, so call it after that is set.
		this.focus.setFocusCell(e.cell, e.rowIndex);
	},
	onRowClick: function(e){
		// summary:
		//		Overwritten, see dojox.grid._Events.onRowClick()
		this.edit.rowClick(e);
		if(e.cell && !e.cell.isRowSelector && (!this.rowSelectCell || !this.rowSelectCell.disabled(e.rowIndex))){
			this.selection.clickSelectEvent(e);
		}
	},
	onRowContextMenu: function(e){
		// summary:
		//		Overwritten, see dojox.grid._Events.onRowContextMenu()
		if(!this.edit.isEditing() && this.menus){
			this.showMenu(e);
		}
	},
	onSelectedRegionContextMenu: function(e){
		// summary:
		//		New - Event fired when a selected region context menu is accessed via mouse right click.
		// e: Event
		//		Decorated event object which contains reference to grid and info of selected
		//		regions(selection type - row|column, selected index - [...])
		if(this.selectedRegionMenu){
			this.selectedRegionMenu._openMyself({
				target: e.target,
				coords: e.keyCode !== dojo.keys.F10 && "pageX" in e ? {
					x: e.pageX,
					y: e.pageY
				} : null
			});
			dojo.stopEvent(e);
		}
	},
	onHeaderCellMouseOut: function(e){
		// summary:
		//		Overwritten, see dojox.grid._Events.onHeaderCellMouseOut()
		if(e.cellNode){
			dojo.removeClass(e.cellNode, this.cellOverClass);
			dojo.removeClass(e.cellNode, this.headerCellActiveClass);
		}
	},
	onHeaderCellMouseDown: function(e){
		// summary:
		//		Overwritten, see dojox.grid._Events.onHeaderCellMouseDown()
		if(e.cellNode){//TBD - apply to selection region for nested sorting?
			dojo.addClass(e.cellNode, this.headerCellActiveClass);
		}
	},
	onHeaderCellMouseUp: function(e){
		// summary:
		//		New event
		if(e.cellNode){
			dojo.removeClass(e.cellNode, this.headerCellActiveClass);
		}
	},
	onHeaderCellClick: function(e){
		// summary:
		//		Overwritten, see dojox.grid._Events.onHeaderCellClick()
		//move focus to header.
		this.focus.currentArea("header");
		//invoke dojox.grid._Events.onHeaderCellClick()
		if(!e.cell.isRowSelector){
			this._events.onHeaderCellClick.call(this, e);
		}
		//move mouse events to the focus manager.
		this.focus.headerMouseEvent(e);
	},
	onRowSelectorMouseDown: function(e){
		this.focus.focusArea("rowHeader", e);
	},

	onRowSelectorMouseUp: function(e){},

	//triggered in _View, see Selector plugin
	onMouseUpRow: function(e){
		if(e.rowIndex != -1){
			this.onRowMouseUp(e);
		}
	},
	onRowMouseUp: function(e){}
});<commit_msg>Fixes #12546, avoid setFocusCell() in the end of double cell click<commit_after>dojo.provide("dojox.grid.enhanced._Events");

dojo.declare("dojox.grid.enhanced._Events", null, {
	// summary:
	//		Overwrite some default events of DataGrid
	//
	// description:
	//		Methods are copied or replaced for overwriting, this might be refined once
	//		an overall plugin architecture is set up for DataGrid.

	//_events: Object
	//		Method map cached from dojox.grid._Events().
	_events: null,

	// headerCellActiveClass: String
	//		css class to apply to grid header cells when activated(mouse down)
	headerCellActiveClass: 'dojoxGridHeaderActive',

	// cellActiveClass: String
	//		css class to apply to grid content cells when activated(mouse down)
	cellActiveClass: 'dojoxGridCellActive',

	// rowActiveClass: String
	//		css class to apply to grid rows when activated(mouse down)
	rowActiveClass: 'dojoxGridRowActive',

	constructor: function(inGrid){
		//get the default Grid events
		this._events = new dojox.grid._Events();
		//for methods that won't be overwritten, copy them to "this" scope
		for(var p in this._events){
			if(!this[p]){
				this.p = this._events.p;
			}
		}
		//mixin "this" to Grid
		inGrid.mixin(inGrid, this);
	},
	dokeyup: function(e){
		// summary:
		//		Grid key up event handler.
		// e: Event
		//		Un-decorated event object
		this.focus.currentArea().keyup(e);
	},
	onKeyDown: function(e){
		// summary:
		//		Overwritten, see dojox.grid._Events.onKeyDown();
		if(e.altKey || e.metaKey){ return; }
		var dk = dojo.keys;
		var focus = this.focus;
		switch(e.keyCode){
			case dk.TAB:
				if(e.ctrlKey){ return; }
				focus.tab(e.shiftKey ? -1:1,e);
				break;
			case dk.UP_ARROW:
			case dk.DOWN_ARROW:
				focus.currentArea().move(e.keyCode == dk.UP_ARROW ? -1 : 1, 0, e);
				break;
			case dk.LEFT_ARROW:
			case dk.RIGHT_ARROW:
				var offset = (e.keyCode == dk.LEFT_ARROW) ? 1 : -1;
				if(dojo._isBodyLtr()){ offset *= -1; }
				focus.currentArea().move(0, offset, e);
				break;
			case dk.F10:
				if(this.menus && e.shiftKey){
					this.onRowContextMenu(e);
				}
				break;
			default:
				focus.currentArea().keydown(e);
				break;
		}
	},
	//TODO - make the following events more reasonalble - e.g. more accurate conditions
	//events for row selectors
	domouseup: function(e){
		if(e.cellNode){
			this.onMouseUp(e);
		}else{
			this.onRowSelectorMouseUp(e);
		}
	},
	domousedown: function(e){
		if(!e.cellNode){
			this.onRowSelectorMouseDown(e);
		}
	},
	onMouseUp: function(e){
		// summary:
		//		New - Event fired when mouse is up inside grid.
		// e: Event
		//		Decorated event object that contains reference to grid, cell, and rowIndex
		this[e.rowIndex == -1 ? "onHeaderCellMouseUp" : "onCellMouseUp"](e);
	},
	onCellMouseDown: function(e){
		// summary:
		//		Overwritten, see dojox.grid._Events.onCellMouseDown()
		dojo.addClass(e.cellNode, this.cellActiveClass);
		dojo.addClass(e.rowNode, this.rowActiveClass);
	},
	onCellMouseUp: function(e){
		// summary:
		//		New - Event fired when mouse is up inside content cell.
		// e: Event
		//		Decorated event object that contains reference to grid, cell, and rowIndex
		dojo.removeClass(e.cellNode, this.cellActiveClass);
		dojo.removeClass(e.rowNode, this.rowActiveClass);
	},
	onCellClick: function(e){
		// summary:
		//		Overwritten, see dojox.grid._Events.onCellClick()

		//invoke dojox.grid._Events.onCellClick()
		this._events.onCellClick.call(this, e);
		//move mouse events to the focus manager.
		this.focus.contentMouseEvent(e);//TODO
	},
	onCellDblClick: function(e){
		// summary:
		//		Overwritten, see dojox.grid._Events.onCellDblClick()
		if(this.pluginMgr.isFixedCell(e.cell)){ return; }
		if(this._click.length > 1 && (!this._click[0] || !this._click[1])){
			this._click[0] = this._click[1] = e;
		}
		//invoke dojox.grid._Events.onCellDblClick()
		this._events.onCellDblClick.call(this, e);
		//now focus.setFocusCell need isEditing info, so call it after that is set.
		//this.focus.setFocusCell(e.cell, e.rowIndex);
	},
	onRowClick: function(e){
		// summary:
		//		Overwritten, see dojox.grid._Events.onRowClick()
		this.edit.rowClick(e);
		if(e.cell && !e.cell.isRowSelector && (!this.rowSelectCell || !this.rowSelectCell.disabled(e.rowIndex))){
			this.selection.clickSelectEvent(e);
		}
	},
	onRowContextMenu: function(e){
		// summary:
		//		Overwritten, see dojox.grid._Events.onRowContextMenu()
		if(!this.edit.isEditing() && this.menus){
			this.showMenu(e);
		}
	},
	onSelectedRegionContextMenu: function(e){
		// summary:
		//		New - Event fired when a selected region context menu is accessed via mouse right click.
		// e: Event
		//		Decorated event object which contains reference to grid and info of selected
		//		regions(selection type - row|column, selected index - [...])
		if(this.selectedRegionMenu){
			this.selectedRegionMenu._openMyself({
				target: e.target,
				coords: e.keyCode !== dojo.keys.F10 && "pageX" in e ? {
					x: e.pageX,
					y: e.pageY
				} : null
			});
			dojo.stopEvent(e);
		}
	},
	onHeaderCellMouseOut: function(e){
		// summary:
		//		Overwritten, see dojox.grid._Events.onHeaderCellMouseOut()
		if(e.cellNode){
			dojo.removeClass(e.cellNode, this.cellOverClass);
			dojo.removeClass(e.cellNode, this.headerCellActiveClass);
		}
	},
	onHeaderCellMouseDown: function(e){
		// summary:
		//		Overwritten, see dojox.grid._Events.onHeaderCellMouseDown()
		if(e.cellNode){//TBD - apply to selection region for nested sorting?
			dojo.addClass(e.cellNode, this.headerCellActiveClass);
		}
	},
	onHeaderCellMouseUp: function(e){
		// summary:
		//		New event
		if(e.cellNode){
			dojo.removeClass(e.cellNode, this.headerCellActiveClass);
		}
	},
	onHeaderCellClick: function(e){
		// summary:
		//		Overwritten, see dojox.grid._Events.onHeaderCellClick()
		//move focus to header.
		this.focus.currentArea("header");
		//invoke dojox.grid._Events.onHeaderCellClick()
		if(!e.cell.isRowSelector){
			this._events.onHeaderCellClick.call(this, e);
		}
		//move mouse events to the focus manager.
		this.focus.headerMouseEvent(e);
	},
	onRowSelectorMouseDown: function(e){
		this.focus.focusArea("rowHeader", e);
	},

	onRowSelectorMouseUp: function(e){},

	//triggered in _View, see Selector plugin
	onMouseUpRow: function(e){
		if(e.rowIndex != -1){
			this.onRowMouseUp(e);
		}
	},
	onRowMouseUp: function(e){}
});<|endoftext|>


<commit_before>"use strict";

// Modules
let fs = require('fs');
let XRegExp = require('xregexp').XRegExp;
let Context = require('./Context');
let _ = require('lodash');
let co = require('co');
const bind = require('co-bind');
const SkipError = require('./SkipError');
const ExitError = require('./ExitError');
const Q = require('q');

/**
 * Router designed to route
 * to controller/action
 */
class Router {

  static routeMiddleware(req, res) {
    // Lets find the route
    let routes = Router.getRoutes(req.method);
    let context = new Context(req, res);

    // Add the context to the domain
    sand.context = context;

    try {
      if (!routes) {
        // No Routes to check, Probably unsupported method
        context.notFound();
      }

      // Lets loop through the routes and match them
      for (let route of routes) {

        let matches = route.regex.xexec(req.path);
        if (matches) {
          if (Router.callController(context, route, matches)) {
            return;
          }
        }
      }

      // Lets check for a default Controller/Action match
      var regex = XRegExp('^/(?<controller>(?:[^\\WA-Z][\\w]*/)*[A-Z][\\w\\\\]*)/?(?<action>\\w+)?/?(?<params>[^\\?]+)?\\??.*$');
      let matches = regex.xexec(req.path);
      if (matches && matches.controller && matches.action) {
        let route = {
          regex: regex,
          ctrlAction: `${matches.controller}.${matches.action}`
        };

        if (Router.callController(context, route, matches)) {
          return;
        }
      }

      context.notFound();
    } catch(e) {
      context.onError(e);
    }
  }

  /**
   * Find the Controller/Action then call
   * the before, then the action
   *
   * @param {Context} context - the context object
   * @param {object} route - object with regex and ctrlAction
   *
   * @returns {boolean} - TRUE on successful call, FALSE otherwise
   */
  static callController(context, route, matches) {
    // Parse out the real controller action
    let ctrlAction = XRegExp.replace(context.req.path, route.regex, route.ctrlAction);
    let split = ctrlAction.split('.');
    let hasSpace = /\s/.test(ctrlAction);

    // If we have the right number, controller/action
    if (2 === split.length && !hasSpace) {
      let controller = Router.getController(split[0]);
      if (!controller) {
        return false;
      }

      // Make sure the action exists
      if ('function' !== typeof controller[split[1]]) {
        if ('function' === typeof controller.prototype[split[1]]) {
          sand.http.warn(`${split[0] + '.' + split[1]} was found as an instance method, you probably meant to mark it as static`);
        }
        return false;
      }

      context.controllerName = split[0];
      context.actionName = split[1];

      // Lets add Named params
      for (var key in matches) {
        if (!matches.hasOwnProperty(key)) {
          continue;
        }

        if (!_.isNumber(key)) {
          context.req.params[key] = matches[key];
        }
      }

      co(function *() {
        try {
          // Execute the before, and then check if we should continue to the action
          yield controller.before.call(context);
        } catch(e) {
          if (e instanceof ExitError) {
            // Lets Exit Immediately
            return;
          }else if (!(e instanceof SkipError)) {
            return context.throw(e);
          }
        }

        if (route.middleware) {
          try {
            yield Router.callMiddleware(context, route.middleware);
          } catch (e) {
            sand.error('Middleware exception:', e);
            context.serverError('Middleware exception.');
          }
        }

        let action = controller[context.actionName];
        if ('function' === typeof action) {
          yield action.call(context);
        } else {
          context.serverError('Could not find action');
        }
      }).catch(context.onError.bind(context));

      return true;
    } else {
      // Lets check for redirect
      split = route.ctrlAction.split(/\s/);
      if (2 === split.length) {
        let code = parseInt(split[0]);
        let url = XRegExp.replace(context.req.path, route.regex, split[1]);
        if (301 === code || 302 === code) {
          context.redirect(code, url);
          return true;
        }
       }
    }

    return false;
  }

  static callMiddleware(context, middleware) {
    return co(function *() {
      if (!_.isArray(middleware)) {
        return Promise.resolve();
      }

      for (var m of middleware) {
        yield Q.nfcall(m, context.req, context.res);
      }
    });
  }

  /**
   * Load controllers from disk and store
   * in memory for later use
   *
   * @param http
   */
  static loadControllers(http) {
    let config = http.config;
    let controllerPath = require('path').normalize(sand.appPath + config.controllerPath);

    if (!fs.existsSync(controllerPath)) {
      http.log.warn("WARN: Missing controllers directory. Skipping loading of controllers...".yellow);
      return;
    }

    let controllers = require('require-all')({
      dirname: controllerPath,
      filter: /(\w+)\.js/
    });

    Router.controllers = flattenObject(controllers);
  }

  /**
   * Load the routes and split them up into
   * sections separated by method
   *
   * @param http
   */
  static loadRoutes(http) {
    let config = http.config;
    let routesPath = require('path').normalize(sand.appPath + config.routesPath);

    if (!routesPath.endsWith('.js')) {
      routesPath += '.js';
    }

    if (!fs.existsSync(routesPath)) {
      http.log.warn(`WARN: Missing routes file: ${routesPath}. Skipping the loading of routes...`);
      return;
    }

    // Load Routes Config
    var routes = require(routesPath);

    // Loop through routes
    for (var key in routes) {
      if (routes.hasOwnProperty(key)) {
        // Get Method and Route
        let split = Router.getMethodAndRegex(key, http);
        if (!split) {
          http.warn(`Invalid route key: ${key}`);
          continue;
        }

        if (_.isArray(routes[key])) {
          Router.addRoute(split.method, split.regex, getRouteFromArray(routes[key]));
        } else if ('object' === typeof routes[key]) {
          // If they passed and object we need to treat it differently
          // For each method add new route
          for(var meth in routes[key]) {
            if (routes[key].hasOwnProperty(meth)) {
              if (_.indexOf(Router.methods, meth.toUpperCase()) !== -1) {
                if (_.isArray(routes[key][meth])) {
                  Router.addRoute(meth, split.regex, getRouteFromArray(routes[key][meth]));
                } else {
                  // Add supported Routes
                  Router.addRoute(meth, split.regex, routes[key][meth]);
                }
              } else if ('action' === meth.toLowerCase()) {
                let route = {
                  action: routes[key][meth]
                };

                if (routes[key].middleware) {
                  route.middleware = _.isArray(routes[key].middleware) ? routes[key].middleware : [routes[key].middleware];
                }
                Router.addRoute(split.method, split.regex, route);
              }
            }
          }
        } else if ('string' === typeof routes[key]) {
          // Lets add the simple route
          Router.addRoute(split.method, split.regex, routes[key]);
        } else {
          http.warn('Invalid Controller/Action', routes[key]);
        }
      }
    }

    function getRouteFromArray(array) {
      let action = routes[key].pop();
      let middleware = routes[key];
      return {
        action: action,
        middleware: middleware
      };
    }
  }

  /**
   * Add the route to the global object
   *
   * @param {string} method - the HTTP method
   * @param {string} regex - the route regex
   * @param {string} action - the controller/action
   */
  static addRoute(method, regex, action) {
    method = method.toUpperCase();

    // Create the method routes holder if it doesn't exist
    if ('undefined' === typeof Router.routes[method]) {
      Router.routes[method] = [];
    }

    let route = {
      regex: XRegExp(regex),
      ctrlAction: action
    };

    if (_.isObject(action)) {
      route.ctrlAction = action.action;
      route.middleware = action.middleware;
    }

    // Add the route
    Router.routes[method].push(route);
  }

  static getMethodAndRegex(str) {
    let split = str.split(' ');
    var meth = 'GET';
    var regex = '';

    // Lets split out the method and regex
    if (2 === split.length) {
      meth = split[0];
      regex = split[1];
    } else if (1 === split.length) {
      regex = split[0];
    } else {
      return null;
    }

    // If this string is not a regex, add `^` and `$`
    if (!regex.startsWith('^') && !regex.endsWith('$')) {
      regex = `^${regex}$`;
    }

    return {
      method: meth,
      regex: regex
    };
  }

  /**
   * Get the routes attached to a HTTP method
   *
   * @param {string} method - the HTTP method
   *
   * @returns {*}
   */
  static getRoutes(method) {
    return Router.routes[method.toUpperCase()];
  }

  /**
   * Get the controller instance if it exists
   * null otherwise
   *
   * @param {string} name - controller name
   *
   * @returns {*}
   */
  static getController(name) {
    name = name.replace('/', '.');
    if ('function' === typeof Router.controllers[name]) {
      return Router.controllers[name];
    }

    return null;
  }
}

Router.controllers = null;
Router.routes = {};
Router.methods = [
  'OPTIONS',
  'GET',
  'HEAD',
  'POST',
  'PUT',
  'DELETE',
  'TRACE',
  'CONNECT'
];

module.exports = function(http) {
  Router.loadControllers(http);
  Router.loadRoutes(http);

  return Router.routeMiddleware;
};

function flattenObject(obj) {
  var toReturn = {};

  for (var i in obj) {
    if (!obj.hasOwnProperty(i)) {
      continue;
    }

    if ('object' === typeof obj[i] && obj !== null) {
      var flatObject = flattenObject(obj[i]);
      for (var x in flatObject) {
        if (!flatObject.hasOwnProperty(x)) continue;

        toReturn[i + '.' + x] = flatObject[x];
      }
    } else {
      toReturn[i] = obj[i];
    }
  }
  return toReturn;
}<commit_msg>added route info to context<commit_after>"use strict";

// Modules
let fs = require('fs');
let XRegExp = require('xregexp').XRegExp;
let Context = require('./Context');
let _ = require('lodash');
let co = require('co');
const bind = require('co-bind');
const SkipError = require('./SkipError');
const ExitError = require('./ExitError');
const Q = require('q');

/**
 * Router designed to route
 * to controller/action
 */
class Router {

  static routeMiddleware(req, res) {
    // Lets find the route
    let routes = Router.getRoutes(req.method);
    let context = new Context(req, res);

    // Add the context to the domain
    sand.context = context;

    try {
      if (!routes) {
        // No Routes to check, Probably unsupported method
        context.notFound();
      }

      // Lets loop through the routes and match them
      for (let route of routes) {

        let matches = route.regex.xexec(req.path);
        if (matches) {
          if (Router.callController(context, route, matches)) {
            return;
          }
        }
      }

      // Lets check for a default Controller/Action match
      let path = '^/(?<controller>(?:[^\\WA-Z][\\w]*/)*[A-Z][\\w\\\\]*)/?(?<action>\\w+)?/?(?<params>[^\\?]+)?\\??.*$';
      var regex = XRegExp(path);
      let matches = regex.xexec(req.path);
      if (matches && matches.controller && matches.action) {
        let route = {
          regex: regex,
          path: path,
          method: req.method,
          ctrlAction: `${matches.controller}.${matches.action}`
        };

        if (Router.callController(context, route, matches)) {
          return;
        }
      }

      context.notFound();
    } catch(e) {
      context.onError(e);
    }
  }

  /**
   * Find the Controller/Action then call
   * the before, then the action
   *
   * @param {Context} context - the context object
   * @param {object} route - object with regex and ctrlAction
   *
   * @returns {boolean} - TRUE on successful call, FALSE otherwise
   */
  static callController(context, route, matches) {
    // Parse out the real controller action
    let ctrlAction = XRegExp.replace(context.req.path, route.regex, route.ctrlAction);
    let split = ctrlAction.split('.');
    let hasSpace = /\s/.test(ctrlAction);

    // If we have the right number, controller/action
    if (2 === split.length && !hasSpace) {
      let controller = Router.getController(split[0]);
      if (!controller) {
        return false;
      }

      // Make sure the action exists
      if ('function' !== typeof controller[split[1]]) {
        if ('function' === typeof controller.prototype[split[1]]) {
          sand.http.warn(`${split[0] + '.' + split[1]} was found as an instance method, you probably meant to mark it as static`);
        }
        return false;
      }

      context.controllerName = split[0];
      context.actionName = split[1];
      context.route = route;

      // Lets add Named params
      for (var key in matches) {
        if (!matches.hasOwnProperty(key)) {
          continue;
        }

        if (!_.isNumber(key)) {
          context.req.params[key] = matches[key];
        }
      }

      co(function *() {
        try {
          // Execute the before, and then check if we should continue to the action
          yield controller.before.call(context);
        } catch(e) {
          if (e instanceof ExitError) {
            // Lets Exit Immediately
            return;
          }else if (!(e instanceof SkipError)) {
            return context.throw(e);
          }
        }

        if (route.middleware) {
          try {
            yield Router.callMiddleware(context, route.middleware);
          } catch (e) {
            sand.error('Middleware exception:', e);
            context.serverError('Middleware exception.');
          }
        }

        let action = controller[context.actionName];
        if ('function' === typeof action) {
          yield action.call(context);
        } else {
          context.serverError('Could not find action');
        }
      }).catch(context.onError.bind(context));

      return true;
    } else {
      // Lets check for redirect
      split = route.ctrlAction.split(/\s/);
      if (2 === split.length) {
        let code = parseInt(split[0]);
        let url = XRegExp.replace(context.req.path, route.regex, split[1]);
        if (301 === code || 302 === code) {
          context.redirect(code, url);
          return true;
        }
       }
    }

    return false;
  }

  static callMiddleware(context, middleware) {
    return co(function *() {
      if (!_.isArray(middleware)) {
        return Promise.resolve();
      }

      for (var m of middleware) {
        yield Q.nfcall(m, context.req, context.res);
      }
    });
  }

  /**
   * Load controllers from disk and store
   * in memory for later use
   *
   * @param http
   */
  static loadControllers(http) {
    let config = http.config;
    let controllerPath = require('path').normalize(sand.appPath + config.controllerPath);

    if (!fs.existsSync(controllerPath)) {
      http.log.warn("WARN: Missing controllers directory. Skipping loading of controllers...".yellow);
      return;
    }

    let controllers = require('require-all')({
      dirname: controllerPath,
      filter: /(\w+)\.js/
    });

    Router.controllers = flattenObject(controllers);
  }

  /**
   * Load the routes and split them up into
   * sections separated by method
   *
   * @param http
   */
  static loadRoutes(http) {
    let config = http.config;
    let routesPath = require('path').normalize(sand.appPath + config.routesPath);

    if (!routesPath.endsWith('.js')) {
      routesPath += '.js';
    }

    if (!fs.existsSync(routesPath)) {
      http.log.warn(`WARN: Missing routes file: ${routesPath}. Skipping the loading of routes...`);
      return;
    }

    // Load Routes Config
    var routes = require(routesPath);

    // Loop through routes
    for (var key in routes) {
      if (routes.hasOwnProperty(key)) {
        // Get Method and Route
        let split = Router.getMethodAndRegex(key, http);
        if (!split) {
          http.warn(`Invalid route key: ${key}`);
          continue;
        }

        if (_.isArray(routes[key])) {
          Router.addRoute(split.method, split.regex, getRouteFromArray(routes[key]));
        } else if ('object' === typeof routes[key]) {
          // If they passed and object we need to treat it differently
          // For each method add new route
          for(var meth in routes[key]) {
            if (routes[key].hasOwnProperty(meth)) {
              if (_.indexOf(Router.methods, meth.toUpperCase()) !== -1) {
                if (_.isArray(routes[key][meth])) {
                  Router.addRoute(meth, split.regex, getRouteFromArray(routes[key][meth]));
                } else {
                  // Add supported Routes
                  Router.addRoute(meth, split.regex, routes[key][meth]);
                }
              } else if ('action' === meth.toLowerCase()) {
                let route = {
                  action: routes[key][meth]
                };

                if (routes[key].middleware) {
                  route.middleware = _.isArray(routes[key].middleware) ? routes[key].middleware : [routes[key].middleware];
                }
                Router.addRoute(split.method, split.regex, route);
              }
            }
          }
        } else if ('string' === typeof routes[key]) {
          // Lets add the simple route
          Router.addRoute(split.method, split.regex, routes[key]);
        } else {
          http.warn('Invalid Controller/Action', routes[key]);
        }
      }
    }

    function getRouteFromArray(array) {
      let action = routes[key].pop();
      let middleware = routes[key];
      return {
        action: action,
        middleware: middleware
      };
    }
  }

  /**
   * Add the route to the global object
   *
   * @param {string} method - the HTTP method
   * @param {string} regex - the route regex
   * @param {string} action - the controller/action
   */
  static addRoute(method, regex, action) {
    method = method.toUpperCase();

    // Create the method routes holder if it doesn't exist
    if ('undefined' === typeof Router.routes[method]) {
      Router.routes[method] = [];
    }

    let route = {
      regex: XRegExp(regex),
      path: regex,
      method: method,
      ctrlAction: action
    };

    if (_.isObject(action)) {
      route.ctrlAction = action.action;
      route.middleware = action.middleware;
    }

    // Add the route
    Router.routes[method].push(route);
  }

  static getMethodAndRegex(str) {
    let split = str.split(' ');
    var meth = 'GET';
    var regex = '';

    // Lets split out the method and regex
    if (2 === split.length) {
      meth = split[0];
      regex = split[1];
    } else if (1 === split.length) {
      regex = split[0];
    } else {
      return null;
    }

    // If this string is not a regex, add `^` and `$`
    if (!regex.startsWith('^') && !regex.endsWith('$')) {
      regex = `^${regex}$`;
    }

    return {
      method: meth,
      regex: regex
    };
  }

  /**
   * Get the routes attached to a HTTP method
   *
   * @param {string} method - the HTTP method
   *
   * @returns {*}
   */
  static getRoutes(method) {
    return Router.routes[method.toUpperCase()];
  }

  /**
   * Get the controller instance if it exists
   * null otherwise
   *
   * @param {string} name - controller name
   *
   * @returns {*}
   */
  static getController(name) {
    name = name.replace('/', '.');
    if ('function' === typeof Router.controllers[name]) {
      return Router.controllers[name];
    }

    return null;
  }
}

Router.controllers = null;
Router.routes = {};
Router.methods = [
  'OPTIONS',
  'GET',
  'HEAD',
  'POST',
  'PUT',
  'DELETE',
  'TRACE',
  'CONNECT'
];

module.exports = function(http) {
  Router.loadControllers(http);
  Router.loadRoutes(http);

  return Router.routeMiddleware;
};

function flattenObject(obj) {
  var toReturn = {};

  for (var i in obj) {
    if (!obj.hasOwnProperty(i)) {
      continue;
    }

    if ('object' === typeof obj[i] && obj !== null) {
      var flatObject = flattenObject(obj[i]);
      for (var x in flatObject) {
        if (!flatObject.hasOwnProperty(x)) continue;

        toReturn[i + '.' + x] = flatObject[x];
      }
    } else {
      toReturn[i] = obj[i];
    }
  }
  return toReturn;
}<|endoftext|>