<commit_before>/**
 * @fileOverview
 */
define(['stream/stream',
        'ecma/lex/token',
        'ecma/ast/node',
        'ecma/position',
        'atum/compute', 'atum/reference', 'atum/type_conversion', 'atum/value',
        'atum/boolean', 'atum/number', 'atum/string', 'atum/undef'],
function(stream,
        token,
        node,
        position,
        compute, reference, type_conversion, value,
        boolean, number, string, undef){

var evaluate;

/*
 ******************************************************************************/
var curry = function(f) {
    return ((arguments.length === 1) ? f : f.bind.apply(f, arguments));
};

var identity = function(x) {
    return x;
};

var constant = function(x) {
    return function() {
        return x;
    };
};

/* Completions
 ******************************************************************************/
var Completion = function(value, target) {
    this.value = value;
    this.target = target;
};

Completion.prototype.toString = function() {
    return "{" + this.type + " value:" + this.value + " target:" + this.target + "}";
};

/**
 *
 */
var ReturnCompletion = function(value) {
    Completion.call(this, value, null);
};
ReturnCompletion.prototype = new Completion;
ReturnCompletion.prototype.type = "Return";

/**
 *
 */
var ThrowCompletion = function(value) {
    Completion.call(this, value, null);
};
ThrowCompletion.prototype = new Completion;
ThrowCompletion.prototype.type = "Throw";

/**
 *
 */
var ContinueCompletion = function(target) {
    Completion.call(this, null, target);

};
ContinueCompletion.prototype = new Completion;
ContinueCompletion.prototype.type = "Continue";

/**
 *
 */
var BreakCompletion = function(target) {
    Completion.call(this, null, target);
};
BreakCompletion.prototype = new Completion;
BreakCompletion.prototype.type = "Break";

/* Semantics
 ******************************************************************************/
/**
 *
 */
var statementList = function(statements) {
    if (statements.length === 0) {
        return compute.always(new undef.Undefined());
    } else {
        return compute.bind(statements[0], function(x) {
            if (x instanceof Completion || statements.length <= 1) {
                return compute.always(x);
            }
            return statementList(statements.slice(1));
        });
    }
};

var ifStatement = function(test, consequent, alternate) {
    return compute.bind(reference.getValue(test), function(x) {
        return (x.value ?
            consequent :
            alternate);
    });
};

/**
 *
 */
var addOperator = function(left, right) {
    return compute.bind(
        type_conversion.toPrimitive(reference.getValue(left)),
        function(lprim) {
            return compute.bind(
                type_conversion.toPrimitive(reference.getValue(right)),
                function(rprim) {
                    return (value.type(lprim) === "string" || value.type(rprim) === "string" ?
                        string.concat(
                             type_conversion.toString(compute.always(lprim)),
                             type_conversion.toString(compute.always(rprim))) :
                        number.add(
                            type_conversion.toNumber(compute.always(lprim)),
                            type_conversion.toNumber(compute.always(rprim))));
                });
        });
};

/**
 *
 */
var subtractOperator = function(left, right) {
    return number.subtract(
        type_conversion.toNumber(reference.getValue(left)),
        type_conversion.toNumber(reference.getValue(right)));
};

/**
 *
 */
var multiplyOperator = function(left, right) {
    return number.multiply(
        type_conversion.toNumber(reference.getValue(left)),
        type_conversion.toNumber(reference.getValue(right)));
};

/**
 *
 */
var divideOperator = function(left, right) {
    return number.divide(
        type_conversion.toNumber(reference.getValue(left)),
        type_conversion.toNumber(reference.getValue(right)));
};

/**
 *
 */
var remainderOperator = function(left, right) {
    return number.remainder(
        type_conversion.toNumber(reference.getValue(left)),
        type_conversion.toNumber(reference.getValue(right)));
};

/**
 *
 */
var leftShiftOperator = function(left, right) {
    return number.leftShift(
        type_conversion.toInt32(reference.getValue(left)),
        type_conversion.toUint32(reference.getValue(right)));
};

/**
 *
 */
var signedRightShiftOperator = function(left, right) {
    return number.signedRightShift(
        type_conversion.toInt32(reference.getValue(left)),
        type_conversion.toUint32(reference.getValue(right)));
};

/**
 *
 */
var unsignedRightShiftOperator = function(left, right) {
    return number.unsignedRightShift(
        type_conversion.toInt32(reference.getValue(left)),
        type_conversion.toUint32(reference.getValue(right)));
};

/**
 *
 */
var unaryPlusOperator = function(argument) {
    return type_conversion.toNumber(reference.getValue(argument));
};

/**
 *
 */
var unaryMinusOperator = function(argument) {
    return number.negate(type_conversion.toNumber(reference.getValue(argument)));
};

/**
 *
 */
var logicalNotOperator = function(argument) {
    return boolean.logicalNot(type_conversion.toBoolean(reference.getValue(argument)));
};

var bitwiseNotOperator = function(argument) {
    return number.bitwiseNot(type_conversion.toInt32(reference.getValue(argument)));
};

/**
 *
 */
var voidOperator = function(argument) {
    return compute.next(reference.getValue(argument)),
        compute.always(new undef.Undefined());
};

/**
 *
 */
var typeofOperator = function(argument) {
    return compute.bind(argument, function(val) {
        if (val instanceof reference.Reference) {
            return (reference.isUnresolvableReference(val) ?
                compute.always("undefined") :
                typeofOperator(reference.getValue(val)));
        }
        return compute.always(value.type(val));
    });
};

/**
 *
 */
var conditionalOperator = function(test, consequent, alternate) {
    return compute.bind(
        type_conversion.toBoolean(reference.getValue(test)),
        function(x) {
            return reference.getValue(x.value ? consequent : alternate);
        });
};

/**
 *
 */
var commaOperator = function(expressions) {
    if (expressions.length === 1) {
        return reference.getValue(expressions[0]);
    }
    return compute.bind(reference.getValue(expressions[0]), function() {
        return commaOperator(expressions.slice(1));
    });
};

/* Evaluation
 ******************************************************************************/
/**
 * Transforms a AST node to a stream of tokens.
 */
evaluate = function(node) {
    if (!node) {
        return null;
    }

    switch (node.type) {
// clauses
    case 'SwitchCase':
       break;

    case 'CatchClause':
        break;

// Statement
    case 'EmptyStatement':
        return compute.always(null);

    case 'DebuggerStatement':
        break;

    case 'BlockStatement':
        return statementList(node.body.map(evaluate));

    case 'ExpressionStatement':
        return evaluate(node.expression);

    case 'IfStatement':
        return ifStatement(evaluate(node.test),
            evaluate(node.consequent),
            (node.alternate === null ? compute.always(null) : evaluate(node.alternate)))

    case 'LabeledStatement':
        break;

    case 'BreakStatement':
        break;

    case 'ContinueStatement':
        break;

    case 'WithStatement':
        break;

    case 'SwitchStatement':
        break;

    case 'BreakStatement':
        break;

    case'ReturnStatement':
        return (node.argument === null ?
            compute.always(new ReturnCompletion(new undef.Undefined())) :
            compute.bind(evaluate(node.argument), function(x) {
                return compute.always(new ReturnCompletion(x));
            }));

    case 'ThrowStatement':
        break;

    case 'TryStatement':
        break;

    case 'WhileStatement':
        break;

    case 'DoWhileStatement':
        break;

    case 'ForStatement':
        break;

    case 'ForInStatement':
        break;

// Expression
    case 'ThisExpression':
        break;

    case 'SequenceExpression':
        return commaOperator(node.expressions.map(evaluate));

    case 'UnaryExpression':
        var argument = evaluate(node.argument);
        switch (node.operator) {
        case '+': return unaryPlusOperator(argument);
        case '-': return unaryMinusOperator(argument);
        case '!': return logicalNotOperator(argument);
        case '~': return bitwiseNotOperator(argument);
        case 'void': return voidOperator(argument);
        case 'typeof': return typeofOperator(argument);

        case '++':
        case '--':
        default: return compute.never("Unknown op:" + node.operator);
        }

    case 'UpdateExpression':
        var argument = evaluate(node.argument);
        switch (node.operator) {
        case '++':
        case '--':
        default: return compute.never("Unknown op:" + node.operator);
        }

    case 'BinaryExpression':
        var l = evaluate(node.left), r = evaluate(node.right);
        switch (node.operator) {
        case '+': return addOperator(l, r);
        case '-': return subtractOperator(l, r);
        case '*': return multiplyOperator(l, r);
        case '/': return divideOperator(l, r);
        case '%': return remainderOperator(l, r);

        case '<<':  return leftShiftOperator(l, r);
        case '>>':  return signedRightShiftOperator(l, r);
        case '>>>': return unsignedRightShiftOperator(l, r);

        default: return compute.never("Unknown op:" + node.operator);
        }

    case 'LogicalExpression':
        break;

    case 'AssignmentExpression':
        break;

    case 'ConditionalExpression':
        return conditionalOperator(evaluate(node.test), evaluate(node.consequent), evaluate(node.alternate));

    case 'NewExpression':
        break;

    case 'CallExpression':
        break;

    case 'MemberExpression':
        break;

    case 'ArrayExpression': ArrayExpression:
        break;

    case 'ObjectExpression': ArrayExpression:
        break

// Function
    case 'FunctionExpression':
        break;

    case 'FunctionDeclaration':
        break;

// Program
    case 'Program':
        return statementList(node.body.map(evaluate));

// Declarations
    case 'VariableDeclaration':
        break;

    case 'VariableDeclarator':
        break;

// Value
    case 'Identifier':
        return compute.always(node.name);

    case 'Literal':
        switch (node.kind) {
        case 'number':  return compute.always(new number.Number(node.value));
        case 'boolean': return compute.always(new boolean.Boolean(node.value));
        case 'string':  return compute.always(new string.String(node.value));
        default:
            return compute.always(node.value);
        }

    default:
        return compute.never("NEVER!!!");
    }
};

/* Export
 ******************************************************************************/
var exec = function(p, env, ok, err) {
    return p(env, ok, err)();
};

var ret = function(x, env) {
    console.log(x);
    return function() {
        return x;
    };
};

var thr = function(x, env) {
    return function() {
        throw x;
    };
};

/**
 * Transform an AST to a stream of tokens.
 *
 * Returned stream inserts line terminators for pretty printing.
 *
 * @param node Root of AST to unparse.
 *
 * @return Stream of parse-ecma lex tokens.
 */
var interpret = (function(){
   return function(root) {
       return exec(evaluate(root), {}, ret, thr);
   };
}());

/* Export
 ******************************************************************************/
return {
    'interpret': interpret
};

});<commit_msg>Added throw statement semantics.<commit_after>/**
 * @fileOverview
 */
define(['stream/stream',
        'ecma/lex/token',
        'ecma/ast/node',
        'ecma/position',
        'atum/compute', 'atum/reference', 'atum/type_conversion', 'atum/value',
        'atum/boolean', 'atum/number', 'atum/string', 'atum/undef'],
function(stream,
        token,
        node,
        position,
        compute, reference, type_conversion, value,
        boolean, number, string, undef){

var evaluate;

/*
 ******************************************************************************/
var curry = function(f) {
    return ((arguments.length === 1) ? f : f.bind.apply(f, arguments));
};

var identity = function(x) {
    return x;
};

var constant = function(x) {
    return function() {
        return x;
    };
};

/* Completions
 ******************************************************************************/
var Completion = function(value, target) {
    this.value = value;
    this.target = target;
};

Completion.prototype.toString = function() {
    return "{" + this.type + " value:" + this.value + " target:" + this.target + "}";
};

/**
 *
 */
var ReturnCompletion = function(value) {
    Completion.call(this, value, null);
};
ReturnCompletion.prototype = new Completion;
ReturnCompletion.prototype.type = "Return";

/**
 *
 */
var ThrowCompletion = function(value) {
    Completion.call(this, value, null);
};
ThrowCompletion.prototype = new Completion;
ThrowCompletion.prototype.type = "Throw";

/**
 *
 */
var ContinueCompletion = function(target) {
    Completion.call(this, null, target);

};
ContinueCompletion.prototype = new Completion;
ContinueCompletion.prototype.type = "Continue";

/**
 *
 */
var BreakCompletion = function(target) {
    Completion.call(this, null, target);
};
BreakCompletion.prototype = new Completion;
BreakCompletion.prototype.type = "Break";

/* Semantics
 ******************************************************************************/
/**
 *
 */
var statementList = function(statements) {
    if (statements.length === 0) {
        return compute.always(new undef.Undefined());
    } else {
        return compute.bind(statements[0], function(x) {
            if (x instanceof Completion || statements.length <= 1) {
                return compute.always(x);
            }
            return statementList(statements.slice(1));
        });
    }
};

/**
 *
 */
var ifStatement = function(test, consequent, alternate) {
    return compute.bind(reference.getValue(test), function(x) {
        return (x.value ?
            consequent :
            alternate);
    });
};

/**
 *
 */
var returnStatement = function(argument) {
    return compute.bind(argument, function(x) {
        return compute.always(new ReturnCompletion(x));
    });
};

/**
 *
 */
var throwStatement = function(argument) {
    return compute.bind(argument, function(x) {
        return compute.always(new ThrowCompletion(x));
    });
};

/**
 *
 */
var addOperator = function(left, right) {
    return compute.bind(
        type_conversion.toPrimitive(reference.getValue(left)),
        function(lprim) {
            return compute.bind(
                type_conversion.toPrimitive(reference.getValue(right)),
                function(rprim) {
                    return (value.type(lprim) === "string" || value.type(rprim) === "string" ?
                        string.concat(
                             type_conversion.toString(compute.always(lprim)),
                             type_conversion.toString(compute.always(rprim))) :
                        number.add(
                            type_conversion.toNumber(compute.always(lprim)),
                            type_conversion.toNumber(compute.always(rprim))));
                });
        });
};

/**
 *
 */
var subtractOperator = function(left, right) {
    return number.subtract(
        type_conversion.toNumber(reference.getValue(left)),
        type_conversion.toNumber(reference.getValue(right)));
};

/**
 *
 */
var multiplyOperator = function(left, right) {
    return number.multiply(
        type_conversion.toNumber(reference.getValue(left)),
        type_conversion.toNumber(reference.getValue(right)));
};

/**
 *
 */
var divideOperator = function(left, right) {
    return number.divide(
        type_conversion.toNumber(reference.getValue(left)),
        type_conversion.toNumber(reference.getValue(right)));
};

/**
 *
 */
var remainderOperator = function(left, right) {
    return number.remainder(
        type_conversion.toNumber(reference.getValue(left)),
        type_conversion.toNumber(reference.getValue(right)));
};

/**
 *
 */
var leftShiftOperator = function(left, right) {
    return number.leftShift(
        type_conversion.toInt32(reference.getValue(left)),
        type_conversion.toUint32(reference.getValue(right)));
};

/**
 *
 */
var signedRightShiftOperator = function(left, right) {
    return number.signedRightShift(
        type_conversion.toInt32(reference.getValue(left)),
        type_conversion.toUint32(reference.getValue(right)));
};

/**
 *
 */
var unsignedRightShiftOperator = function(left, right) {
    return number.unsignedRightShift(
        type_conversion.toInt32(reference.getValue(left)),
        type_conversion.toUint32(reference.getValue(right)));
};

/**
 *
 */
var unaryPlusOperator = function(argument) {
    return type_conversion.toNumber(reference.getValue(argument));
};

/**
 *
 */
var unaryMinusOperator = function(argument) {
    return number.negate(type_conversion.toNumber(reference.getValue(argument)));
};

/**
 *
 */
var logicalNotOperator = function(argument) {
    return boolean.logicalNot(type_conversion.toBoolean(reference.getValue(argument)));
};

var bitwiseNotOperator = function(argument) {
    return number.bitwiseNot(type_conversion.toInt32(reference.getValue(argument)));
};

/**
 *
 */
var voidOperator = function(argument) {
    return compute.next(reference.getValue(argument)),
        compute.always(new undef.Undefined());
};

/**
 *
 */
var typeofOperator = function(argument) {
    return compute.bind(argument, function(val) {
        if (val instanceof reference.Reference) {
            return (reference.isUnresolvableReference(val) ?
                compute.always("undefined") :
                typeofOperator(reference.getValue(val)));
        }
        return compute.always(value.type(val));
    });
};

/**
 *
 */
var conditionalOperator = function(test, consequent, alternate) {
    return compute.bind(
        type_conversion.toBoolean(reference.getValue(test)),
        function(x) {
            return reference.getValue(x.value ? consequent : alternate);
        });
};

/**
 *
 */
var commaOperator = function(expressions) {
    if (expressions.length === 1) {
        return reference.getValue(expressions[0]);
    }
    return compute.bind(reference.getValue(expressions[0]), function() {
        return commaOperator(expressions.slice(1));
    });
};

/* Evaluation
 ******************************************************************************/
/**
 * Transforms a AST node to a stream of tokens.
 */
evaluate = function(node) {
    if (!node) {
        return null;
    }

    switch (node.type) {
// clauses
    case 'SwitchCase':
       break;

    case 'CatchClause':
        break;

// Statement
    case 'EmptyStatement':
        return compute.always(null);

    case 'DebuggerStatement':
        break;

    case 'BlockStatement':
        return statementList(node.body.map(evaluate));

    case 'ExpressionStatement':
        return evaluate(node.expression);

    case 'IfStatement':
        return ifStatement(evaluate(node.test),
            evaluate(node.consequent),
            (node.alternate === null ? compute.always(null) : evaluate(node.alternate)))

    case 'LabeledStatement':
        break;

    case 'BreakStatement':
        break;

    case 'ContinueStatement':
        break;

    case 'WithStatement':
        break;

    case 'SwitchStatement':
        break;

    case 'BreakStatement':
        break;

    case'ReturnStatement':
        return returnStatement(node.argument === null ?
            compute.always(new undef.Undefined()) :
            evaluate(node.argument));

    case 'ThrowStatement':
        return throwStatement(evaluate(node.argument));

    case 'TryStatement':
        break;

    case 'WhileStatement':
        break;

    case 'DoWhileStatement':
        break;

    case 'ForStatement':
        break;

    case 'ForInStatement':
        break;

// Expression
    case 'ThisExpression':
        break;

    case 'SequenceExpression':
        return commaOperator(node.expressions.map(evaluate));

    case 'UnaryExpression':
        var argument = evaluate(node.argument);
        switch (node.operator) {
        case '+': return unaryPlusOperator(argument);
        case '-': return unaryMinusOperator(argument);
        case '!': return logicalNotOperator(argument);
        case '~': return bitwiseNotOperator(argument);
        case 'void': return voidOperator(argument);
        case 'typeof': return typeofOperator(argument);

        case '++':
        case '--':
        default: return compute.never("Unknown op:" + node.operator);
        }

    case 'UpdateExpression':
        var argument = evaluate(node.argument);
        switch (node.operator) {
        case '++':
        case '--':
        default: return compute.never("Unknown op:" + node.operator);
        }

    case 'BinaryExpression':
        var l = evaluate(node.left), r = evaluate(node.right);
        switch (node.operator) {
        case '+': return addOperator(l, r);
        case '-': return subtractOperator(l, r);
        case '*': return multiplyOperator(l, r);
        case '/': return divideOperator(l, r);
        case '%': return remainderOperator(l, r);

        case '<<':  return leftShiftOperator(l, r);
        case '>>':  return signedRightShiftOperator(l, r);
        case '>>>': return unsignedRightShiftOperator(l, r);

        default: return compute.never("Unknown op:" + node.operator);
        }

    case 'LogicalExpression':
        break;

    case 'AssignmentExpression':
        break;

    case 'ConditionalExpression':
        return conditionalOperator(evaluate(node.test),
            evaluate(node.consequent),
            evaluate(node.alternate));

    case 'NewExpression':
        break;

    case 'CallExpression':
        break;

    case 'MemberExpression':
        break;

    case 'ArrayExpression': ArrayExpression:
        break;

    case 'ObjectExpression': ArrayExpression:
        break

// Function
    case 'FunctionExpression':
        break;

    case 'FunctionDeclaration':
        break;

// Program
    case 'Program':
        return statementList(node.body.map(evaluate));

// Declarations
    case 'VariableDeclaration':
        break;

    case 'VariableDeclarator':
        break;

// Value
    case 'Identifier':
        return compute.always(node.name);

    case 'Literal':
        switch (node.kind) {
        case 'number':  return compute.always(new number.Number(node.value));
        case 'boolean': return compute.always(new boolean.Boolean(node.value));
        case 'string':  return compute.always(new string.String(node.value));
        default:
            return compute.always(node.value);
        }

    default:
        return compute.never("NEVER!!!");
    }
};

/* Export
 ******************************************************************************/
var exec = function(p, env, ok, err) {
    return p(env, ok, err)();
};

var ret = function(x, env) {
    console.log(x);
    return function() {
        return x;
    };
};

var thr = function(x, env) {
    return function() {
        throw x;
    };
};

/**
 * Transform an AST to a stream of tokens.
 *
 * Returned stream inserts line terminators for pretty printing.
 *
 * @param node Root of AST to unparse.
 *
 * @return Stream of parse-ecma lex tokens.
 */
var interpret = (function(){
   return function(root) {
       return exec(evaluate(root), {}, ret, thr);
   };
}());

/* Export
 ******************************************************************************/
return {
    'interpret': interpret
};

});<|endoftext|>


<commit_before>var log = require('debug')('democracyos:jwt');
var config = require('lib/config');
var jwt = require('jwt-simple');
var moment = require('moment');
var utils = require('lib/utils');
var expose = utils.expose;

exports.encodeToken = function encodeToken(user, secret) {
  var expires = moment().add(7, 'days').valueOf();
  var token = jwt.encode({
    iss: user.email,
    exp: expires
  }, secret);

  return {
    token : token,
    expires: expires,
    user: user
  };
};

exports.decodeToken = function decodeToken(token, secret, cb) {
  try {
    log('Attempting to decode token...');
    var decoded = jwt.decode(token, secret);
    if (decoded.exp <= Date.now()) {
      log('Access token has expired');
      return cb(new Error('Access token has expired'));
    }

    var User = require('lib/models').User;
    User.findByEmail(decoded.iss, function(err, user) {
      if (err) log('Token has been decoded, but user fetching failed with the following error: %s', err);
      if (!user) return log('Token has been decoded, but user was not found'), cb(new Error('No user for token %s', token));

      log('Token decoded successfully');
      return cb(err, user);
    });
  } catch (err) {
    log('Cannot decode token: %s', err);
    return cb(err);
  }
};

exports.signin = function signin(user, req, res) {
  req.user = user;
  var token = exports.encodeToken(confidential(user), config.jwtSecret);
  res.cookie('token', token.token, { domain: resolveDomain(), expires: new Date(token.expires) });
  return res.status(200).send();

  function resolveDomain() {
    return !config.deploymentDomain || 'localhost' === config.deploymentDomain.substring(0, 9)
      ? null
      : '.' + config.deploymentDomain;
  }
};

exports.middlewares = {
  user: function user(secret) {
    return function(req, res, next) {
      var qs = req.query ? req.query.access_token : '';
      var token = req.cookies.token || qs || req.headers['x-access-token'];

      if (token) {
        exports.decodeToken(token, secret, function(err, user) {
          if (err || !user) res.clearCookie('token');
          if (err) return log('Error decoding token: %s', err), next();
          if (!user) return log('No user found for jwt %s', token), next();

          if (config.facebookSignin ) {
            if (user.get('profiles.facebook.deauthorized')) {
              res.clearCookie('token');
              log('Restricting deauthorized facebook user with id "%s"', user.id);
              return next();
            }
          }

          log('Logging in user %s', user.id);
          req.login(user, function(err) {
            if (err) return res.json(500, { error: err.message });
            res.locals.access_token = token;
            next();
          });
        });
      } else {
        log('HTTP header x-access-token has no token. Moving on...');
        return next();
      }
    }
  }
};

function confidential(user) {
  return expose('id firstName lastName email avatar staff notifications')(user);
}
<commit_msg>Fix token cookie clearing on error<commit_after>var log = require('debug')('democracyos:jwt');
var config = require('lib/config');
var jwt = require('jwt-simple');
var moment = require('moment');
var utils = require('lib/utils');
var expose = utils.expose;

exports.encodeToken = function encodeToken(user, secret) {
  var expires = moment().add(7, 'days').valueOf();
  var token = jwt.encode({
    iss: user.email,
    exp: expires
  }, secret);

  return {
    token : token,
    expires: expires,
    user: user
  };
};

exports.decodeToken = function decodeToken(token, secret, cb) {
  try {
    log('Attempting to decode token...');
    var decoded = jwt.decode(token, secret);
    if (decoded.exp <= Date.now()) {
      log('Access token has expired');
      return cb(new Error('Access token has expired'));
    }

    var User = require('lib/models').User;
    User.findByEmail(decoded.iss, function(err, user) {
      if (err) log('Token has been decoded, but user fetching failed with the following error: %s', err);
      if (!user) return log('Token has been decoded, but user was not found'), cb(new Error('No user for token %s', token));

      log('Token decoded successfully');
      return cb(err, user);
    });
  } catch (err) {
    log('Cannot decode token: %s', err);
    return cb(err);
  }
};

exports.signin = function signin(user, req, res) {
  req.user = user;
  var token = exports.encodeToken(confidential(user), config.jwtSecret);
  res.cookie('token', token.token, { domain: resolveDomain(), expires: new Date(token.expires) });
  return res.status(200).send();
};

function resolveDomain() {
  return !config.deploymentDomain || 'localhost' === config.deploymentDomain.substring(0, 9)
    ? null
    : '.' + config.deploymentDomain;
}

exports.middlewares = {
  user: function user(secret) {
    return function(req, res, next) {
      var qs = req.query ? req.query.access_token : '';
      var token = req.cookies.token || qs || req.headers['x-access-token'];

      if (token) {
        exports.decodeToken(token, secret, function(err, user) {
          if (err || !user) res.clearCookie('token', { domain: resolveDomain() });
          if (err) return log('Error decoding token: %s', err), next();
          if (!user) return log('No user found for jwt %s', token), next();

          if (config.facebookSignin ) {
            if (user.get('profiles.facebook.deauthorized')) {
              res.clearCookie('token');
              log('Restricting deauthorized facebook user with id "%s"', user.id);
              return next();
            }
          }

          log('Logging in user %s', user.id);
          req.login(user, function(err) {
            if (err) return res.json(500, { error: err.message });
            res.locals.access_token = token;
            next();
          });
        });
      } else {
        log('HTTP header x-access-token has no token. Moving on...');
        return next();
      }
    }
  }
};

function confidential(user) {
  return expose('id firstName lastName email avatar staff notifications')(user);
}
<|endoftext|>


<commit_before>import { createFromBrowser, restore, hashChange } from './actions'
import {
  browserHistory, getKeyIndex, historyMatch, isNewHistory, selectActiveKey, selectHistoryState,
} from './select'
import { locationSerialize } from './utils'

export function createPopListener(listener, reset) {
  return event => {
    console.log(event)
    if (event.state && event.state.key) {
      listener(event.state, event.type)
    } else if (reset) {
      reset()
    }
  }
}
// New history address bar URL. Browser won't attempt to load this.
// https://developer.mozilla.org/en-US/docs/Web/API/History_API
// The state object can be anything that can be serialized under 640k.
// reduxHistory is the full state.history object.
export function changeBrowserHistory(reduxHistory, changeState) {
  const activeHistory = browserHistory(reduxHistory)
  const locationStr = locationSerialize(activeHistory.location)
  return changeState(activeHistory, activeHistory.title, locationStr)
}

export function createHistoryListener(store, selectHistory, replaceState) {
  return windowHistory => {
    const historyState = selectHistory(store.getState())
    const storeHasKey = historyState.key[windowHistory.key]
    // Back/Forward after a page refresh.
    if (!storeHasKey) return store.dispatch(createFromBrowser(windowHistory))
    // Change came from here.
    if (historyState.activeKey === windowHistory.key) {
      return changeBrowserHistory(historyState, replaceState)
    }
    // Back/Forward
    return store.dispatch(restore(windowHistory.key, false))
  }
}

export function syncHistoryToStore(store, selectHistory, _window) {
  const replaceState = _window.history.replaceState.bind(_window.history)
  const handleHistoryChange = createHistoryListener(store, selectHistory, replaceState)

  function hashChanges() {
    const keyState = selectActiveKey(selectHistory(store.getState()))
    const stateHash = keyState.location.hash
    if (_window.location.hash !== stateHash) {
      store.dispatch(hashChange(_window.location.hash))
    }
  }
  // Listen for browser history forward/back changes.
  const handlePopState = createPopListener(handleHistoryChange, hashChanges)
  _window.addEventListener('popstate', handlePopState)
}

// Subscribe to store. Save new state to history key index.
export function syncStoreHistory(store, history, selectHistory) {
  function handleStoreChange() {
    const reduxHistory = selectHistory(store.getState())
    // Look for redux change.
    if (historyMatch(reduxHistory, history.state)) return undefined
    // Save new state to history key index.
    if (isNewHistory(reduxHistory, history.state)) {
      console.log('new history')
      return changeBrowserHistory(reduxHistory, history.pushState.bind(history))
    }
    // What is the index of the history key in the Redux store?
    const storeIndex = getKeyIndex(reduxHistory)
    // What is the index of the browser?
    const browserIndex = history.state.index
    // Tell browser to move forward or backward based on activeKey changes.
    // How far back or forward do we need to move the browser?
    const goBy = storeIndex - browserIndex
    console.log('Move browser history', goBy)
    history.go(goBy)
    return undefined
  }
  store.subscribe(handleStoreChange)
}

/**
 * This function saves and restores state with history navigation changes.
 * Externally available.
 */
export default function syncHistoryWithStore(store, _window, {
  selectHistory = selectHistoryState,
} = {}) {
  const historyState = selectHistory(store.getState())
  // Ensure that the reducer is mounted on the store and functioning properly.
  if (!historyState.activeKey) {
    // console.error(selectHistory(store.getState()))
    throw new Error(
      'Expected the history state activeKey to be available as `state.history` ' +
      'or as the custom expression you can specify as `selectHistoryState` ' +
      'in the `syncHistoryWithStore()` options. ' +
      'Ensure you have added the `historyReducer` to your store\'s ' +
      'reducers via `combineReducers` or whatever method you use to isolate ' +
      'your reducers.'
    )
  }
  // We want to make sure the window history state matches Redux initially.
  changeBrowserHistory(historyState, _window.history.replaceState.bind(history))
  // Sync Redux -> Browser History
  syncStoreHistory(store, _window.history, selectHistory)
  // Sync Browser History -> Redux
  syncHistoryToStore(store, selectHistory, _window)
}
<commit_msg>createHashListener<commit_after>import { createFromBrowser, restore, hashChange } from './actions'
import {
  browserHistory, getKeyIndex, historyMatch, isNewHistory, selectActiveKey, selectHistoryState,
} from './select'
import { locationSerialize } from './utils'

export function createPopListener(listener, reset) {
  return event => {
    console.log(event)
    if (event.state && event.state.key) {
      listener(event.state, event.type)
    } else if (reset) {
      reset()
    }
  }
}
// New history address bar URL. Browser won't attempt to load this.
// https://developer.mozilla.org/en-US/docs/Web/API/History_API
// The state object can be anything that can be serialized under 640k.
// reduxHistory is the full state.history object.
export function changeBrowserHistory(reduxHistory, changeState) {
  const activeHistory = browserHistory(reduxHistory)
  const locationStr = locationSerialize(activeHistory.location)
  return changeState(activeHistory, activeHistory.title, locationStr)
}

export function createHistoryListener(store, selectHistory, replaceState) {
  return windowHistory => {
    const historyState = selectHistory(store.getState())
    const storeHasKey = historyState.key[windowHistory.key]
    // Back/Forward after a page refresh.
    if (!storeHasKey) return store.dispatch(createFromBrowser(windowHistory))
    // Change came from here.
    if (historyState.activeKey === windowHistory.key) {
      return changeBrowserHistory(historyState, replaceState)
    }
    // Back/Forward
    return store.dispatch(restore(windowHistory.key, false))
  }
}
export function createHashListener(store, selectHistory, getHash) {
  return () => {
    const keyState = selectActiveKey(selectHistory(store.getState()))
    const stateHash = keyState.location.hash
    const browserHash = getHash()
    if (browserHash !== stateHash) {
      store.dispatch(hashChange(browserHash))
    }
  }
}
export function syncHistoryToStore(store, selectHistory, _window) {
  const replaceState = _window.history.replaceState.bind(_window.history)
  const handleHistoryChange = createHistoryListener(store, selectHistory, replaceState)
  function getHash() { return _window.location.hash }
  const hashChanges = createHashListener(store, selectHistory, getHash)
  // Listen for browser history forward/back changes.
  const handlePopState = createPopListener(handleHistoryChange, hashChanges)
  _window.addEventListener('popstate', handlePopState)
}

// Subscribe to store. Save new state to history key index.
export function syncStoreHistory(store, history, selectHistory) {
  function handleStoreChange() {
    const reduxHistory = selectHistory(store.getState())
    // Look for redux change.
    if (historyMatch(reduxHistory, history.state)) return undefined
    // Save new state to history key index.
    if (isNewHistory(reduxHistory, history.state)) {
      console.log('new history')
      return changeBrowserHistory(reduxHistory, history.pushState.bind(history))
    }
    // What is the index of the history key in the Redux store?
    const storeIndex = getKeyIndex(reduxHistory)
    // What is the index of the browser?
    const browserIndex = history.state.index
    // Tell browser to move forward or backward based on activeKey changes.
    // How far back or forward do we need to move the browser?
    const goBy = storeIndex - browserIndex
    console.log('Move browser history', goBy)
    history.go(goBy)
    return undefined
  }
  store.subscribe(handleStoreChange)
}

/**
 * This function saves and restores state with history navigation changes.
 * Externally available.
 */
export default function syncHistoryWithStore(store, _window, {
  selectHistory = selectHistoryState,
} = {}) {
  const historyState = selectHistory(store.getState())
  // Ensure that the reducer is mounted on the store and functioning properly.
  if (!historyState.activeKey) {
    // console.error(selectHistory(store.getState()))
    throw new Error(
      'Expected the history state activeKey to be available as `state.history` ' +
      'or as the custom expression you can specify as `selectHistoryState` ' +
      'in the `syncHistoryWithStore()` options. ' +
      'Ensure you have added the `historyReducer` to your store\'s ' +
      'reducers via `combineReducers` or whatever method you use to isolate ' +
      'your reducers.'
    )
  }
  // We want to make sure the window history state matches Redux initially.
  changeBrowserHistory(historyState, _window.history.replaceState.bind(history))
  // Sync Redux -> Browser History
  syncStoreHistory(store, _window.history, selectHistory)
  // Sync Browser History -> Redux
  syncHistoryToStore(store, selectHistory, _window)
}
<|endoftext|>


<commit_before>/**
 * Module dependencies.
 */

var express = require('express');
var api = require('lib/db-api');
var utils = require('lib/utils');
var accepts = require('lib/accepts');
var restrict = utils.restrict;
var pluck = utils.pluck;
var expose = utils.expose;
var log = require('debug')('democracyos:law');

var app = module.exports = express();

/**
 * Limit request to json format only
 */

app.use(accepts('application/json'));

app.get('/law/all', function (req, res) {
  log('Request /law/all');

  api.law.all(function(err, lawDocs) {
    if (err) return _handleError(err, req, res);

    log('Serving laws %j', pluck(lawDocs, "id"));

    var keys = [
      'id lawId mediaTitle status open closed',
      'tag participants voted',
      'createdAt updatedAt closingAt'
    ].join(' ');

    lawDocs.forEach(function(law) {
      if (!req.user) return law.voted = false;
      law.voted = law.votedBy(req.user);
    });

    res.json(lawDocs.map(expose(keys)));
  });
});

app.get('/law/:id', function (req, res) {
  log('Request /law/%s', req.params.id);

  api.law.get(req.params.id, function (err, lawDoc) {
    if (err) return _handleError(err, req, res);

    log('Serving law %s', lawDoc.id);
    var keys = [
      'id lawId title mediaTitle summary clauses source status open closed',
      'satus state tag participants',
      'upvotes downvotes abstentions',
      'createdAt updatedAt closingAt'
    ].join(' ');

    res.json(expose(keys)(lawDoc.toJSON()));
  });
});

app.get('/law/:id/comments', function (req, res) {
  log('Request /law/%s/comments', req.params.id);

  api.law.comments(req.params.id, function (err, comments) {
    if (err) return _handleError(err, req, res);

    log('Serving law %s comments %j', req.params.id, pluck(comments, 'id'));

    var keys = [
      "id text createdAt context reference",
      "author.id author.fullName author.gravatar()",
      "flags upvotes downvotes votes"
    ].join(' ');

    res.json(comments.map(expose(keys)));
  });
});

app.post('/law/:id/comment', restrict, function (req, res) {
  log('Request /law/%s/comment %j', req.params.id, req.body.comment);

  var comment = req.body.comment;
  // This must be uncommented on production!
  comment.author = req.user;

  api.law.comment(comment, function (err, commentDoc) {
    if (err) return _handleError(err, req, res);

    var keys = [
      'id text',
      'author.id author.fullName author.gravatar()',
      'upvotes downvotes flags',
      'createdAt'
    ].join(' ');

    res.json(200, expose(keys)(commentDoc));
  });
});

app.post('/law/:id/vote', function (req, res) {
  log('Request /law/%s/vote', req.param('id'));

  api.law
  .vote(
    req.param('id'),
    req.user,
    req.param('value'),
    function (err, comments) {
      if (err) return _handleError(err, req, res);

      log('Serving 200 OK response');
      res.send(200);
    }
  );
});

// app.post('/law/create', function (req, res) {
//   log('Request /law/create %j', req.body.law);

//   var law = req.body.law;

//   api.law.create(law, function (err, lawDoc) {
//     if (err) return _handleError(err, req, res);

//     res.format({
//       html: function() {
//         log('Redirect to home.')
//         res.redirect('/');
//       },
//       json: function() {
//         log('Serving law %j', lawDoc.id);
//         res.json(lawDoc);
//       }
//     })
//   });
// });

function _handleError (err, req, res) {
  log("Error found: %s", err);
  var error = err;
  if (err.errors && err.errors.text) error = err.errors.text;
  if (error.type) error = error.type;

  res.json(400, { error: error });
}<commit_msg>[law] - Add staff middleware check for `/law/create`. #241<commit_after>/**
 * Module dependencies.
 */

var express = require('express');
var api = require('lib/db-api');
var utils = require('lib/utils');
var accepts = require('lib/accepts');
var restrict = utils.restrict;
var staff = utils.staff;
var pluck = utils.pluck;
var expose = utils.expose;
var log = require('debug')('democracyos:law');

var app = module.exports = express();

/**
 * Limit request to json format only
 */

app.use(accepts('application/json'));

app.get('/law/all', function (req, res) {
  log('Request /law/all');

  api.law.all(function(err, lawDocs) {
    if (err) return _handleError(err, req, res);

    log('Serving laws %j', pluck(lawDocs, "id"));

    var keys = [
      'id lawId mediaTitle status open closed',
      'tag participants voted',
      'createdAt updatedAt closingAt'
    ].join(' ');

    lawDocs.forEach(function(law) {
      if (!req.user) return law.voted = false;
      law.voted = law.votedBy(req.user);
    });

    res.json(lawDocs.map(expose(keys)));
  });
});

app.get('/law/:id', function (req, res) {
  log('Request /law/%s', req.params.id);

  api.law.get(req.params.id, function (err, lawDoc) {
    if (err) return _handleError(err, req, res);

    log('Serving law %s', lawDoc.id);
    var keys = [
      'id lawId title mediaTitle summary clauses source status open closed',
      'satus state tag participants',
      'upvotes downvotes abstentions',
      'createdAt updatedAt closingAt'
    ].join(' ');

    res.json(expose(keys)(lawDoc.toJSON()));
  });
});

app.get('/law/:id/comments', function (req, res) {
  log('Request /law/%s/comments', req.params.id);

  api.law.comments(req.params.id, function (err, comments) {
    if (err) return _handleError(err, req, res);

    log('Serving law %s comments %j', req.params.id, pluck(comments, 'id'));

    var keys = [
      "id text createdAt context reference",
      "author.id author.fullName author.gravatar()",
      "flags upvotes downvotes votes"
    ].join(' ');

    res.json(comments.map(expose(keys)));
  });
});

app.post('/law/:id/comment', restrict, function (req, res) {
  log('Request /law/%s/comment %j', req.params.id, req.body.comment);

  var comment = req.body.comment;
  // This must be uncommented on production!
  comment.author = req.user;

  api.law.comment(comment, function (err, commentDoc) {
    if (err) return _handleError(err, req, res);

    var keys = [
      'id text',
      'author.id author.fullName author.gravatar()',
      'upvotes downvotes flags',
      'createdAt'
    ].join(' ');

    res.json(200, expose(keys)(commentDoc));
  });
});

app.post('/law/:id/vote', function (req, res) {
  log('Request /law/%s/vote', req.param('id'));

  api.law
  .vote(
    req.param('id'),
    req.user,
    req.param('value'),
    function (err, comments) {
      if (err) return _handleError(err, req, res);

      log('Serving 200 OK response');
      res.send(200);
    }
  );
});

app.post('/law/create', restrict, staff, function (req, res, next) {
  log('Request /law/create %j', req.body.law);
  res.send(200);
  // var law = req.body.law;

  // api.law.create(law, function (err, lawDoc) {
  //   if (err) return _handleError(err, req, res);
  //   var keys = [
  //     'id lawId title mediaTitle summary clauses source',
  //     'satus state tag participants',
  //     'upvotes downvotes abstentions',
  //     'createdAt updatedAt closingAt'
  //   ].join(' ');
  //   res.json(expose(keys)(lawDoc));
  // });
});

function _handleError (err, req, res) {
  log("Error found: %s", err);
  var error = err;
  if (err.errors && err.errors.text) error = err.errors.text;
  if (error.type) error = error.type;

  res.json(400, { error: error });
}<|endoftext|>


<commit_before>'use strict;'

import { connect } from 'react-redux'
import List from 'butter-component-list'

import {bindPersistActions} from '../redux/persist'

const ListContainer = connect(
    ({tabs, collections, cache, ...state}, {tab, history}) => {
        console.error('here', tab)

        const cols = tab.providers.map(provider =>
            Object.assign(collections[provider], {id: provider})
        )

        const tabState = cols.reduce((acc, col) => {
            let url = `/list/${tab.id}/${col.id}`

            console.error('url', url)

            return {
                items: acc.items.concat(
                    col.items.map(id => Object.assign({}, cache[id], {
                        actions: {
                            show: () => history.push(`${url}/${id}`),
                            play: () => history.push(`${url}/${id}/play`),
                        }
                    }))
                ),
                isFetching: acc.isFetching ? acc.isFetching : col.isFetching,
                failed: acc.failed ? acc.failed : col.failed
            }
        }, {items: []})

        return {
            ...state,
            ...tabState,
        }
    },
    (dispatch) => ({
        actions: {
            ...bindPersistActions(dispatch)
        }
    })
)(List)

export {ListContainer as default}
<commit_msg>list: better error debuging<commit_after>'use strict;'

import { connect } from 'react-redux'
import List from 'butter-component-list'

import {bindPersistActions} from '../redux/persist'

const ListContainer = connect(
    ({collections}, {tab, history}) => {
        console.error('list view', tab)

        const cols = tab.providers.map(provider =>
            Object.assign(collections[provider], {id: provider})
        )

        const tabState = cols.reduce((acc, col) => {
            let url = `/list/${tab.id}/${col.id}`

            console.error('url', url)

            return {
                items: acc.items.concat(
                    col.items.map(id => Object.assign({}, cache[id], {
                        actions: {
                            show: () => history.push(`${url}/${id}`),
                            play: () => history.push(`${url}/${id}/play`),
                        }
                    }))
                ),
                isFetching: acc.isFetching ? acc.isFetching : col.isFetching,
                failed: acc.failed ? acc.failed : col.failed
            }
        }, {items: []})

        return {
            ...state,
            ...tabState,
        }
    },
    (dispatch) => ({
        actions: {
            ...bindPersistActions(dispatch)
        }
    })
)(List)

export {ListContainer as default}
<|endoftext|>


<commit_before>/**

 @author: Hans van den Akker (mysim1)
 @license NPOSL-3.0
 @copyright Bizboard, 2015

 */

import extend                   from 'lodash/extend.js';
import EventEmitter             from 'eventemitter3';
import AnimationController      from 'famous-flex/AnimationController.js';

import {Dialog}                 from '../components/Dialog.js';
import {DialogManager}          from '../utils/DialogManager.js';
import {inject}                 from '../utils/di/Decorators.js';
import {ObjectHelper}           from '../utils/ObjectHelper.js';
import {Router}                 from './Router.js';

/**
 * The Controller class provides the highest level of control regarding the application features. Within the Controller context
 * each method will registered to receive calls from the Routing engine. With direct access to the Famo.us Context, every method can
 * control the creation of Views and Transitions.
 */
@inject(Router, AnimationController, DialogManager)
export class Controller extends EventEmitter {

    /**
     * Saves the router and context to this.router and this.context, respectively.
     * @param {Router} router Injected globally used router
     * @param {AnimationController} context Injected animationController used by the app
     * @param {DialogManager} dialogManager The dialog manager that shows and hides dialogs
     */
    constructor(router, context, dialogManager) {
        super();

        /**
         * The router used globally in the app
         * @type {Router}
         */
        this.router = router;
        /**
         * The AnimationController used globally in the app to change screens
         * @type {AnimationController}
         */
        this.context = context;
        /**
         * The dialogmanager used to show and hide dialogs. If a controller returns a Dialog, this will be used
         */
        this.dialogManager = dialogManager;

        /* The this._name property can be set by Arva's babel-plugin-transform-runtime-constructor-name plugin.
         * This allows Arva code to be minified and mangled without losing automated route creation.
         * If the plugin is not set up to run, which is done e.g. when not minifying your code, we default back to the runtime constructor name.*/
        let controllerName = this.constructor._name || Object.getPrototypeOf(this).constructor.name;

        ObjectHelper.bindAllMethods(this, this);

        /* Add the controller route to the router. */
        let routeName = controllerName.replace('Controller', '');
        routeName += '/:method';

        /* handle router url changes and execute the appropiate controller method. */
        this.router.add(routeName, {enter: this.onRouteCalled, leave: this.onLeave}, this);
    }

    /**
     * Called to notify the Controller that the route is changed
     * @param newRoute
     */
    onLeave(newRoute) {
        this.isActive = false;
    }

    /**
     * Called by the Router when this controller instance is being navigated to. Calls the controller's method in the given route,
     * and triggers a famous-flex AnimationController show() with the View instance that the method returns. Is also capable of receiving
     * a Promise from the method, in which case the show() is called after the promise is resolved.
     * @param {Object} route Route object generated by the Router. Contains a method name to call, and a render spec for passing to the AnimationController.
     * @returns {Boolean} success Whether the controller method was fully executed, and the Router should emit a routechange event.
     */
    onRouteCalled(route) {
        this.isActive = true;
        if (typeof this[route.method] === 'function') {
            let result = this[route.method].apply(this, route.values);

            if (result) {
                this.emit('renderstart', route.method);
                if (result instanceof Promise) { /* We can assume the method called was asynchronous from nature, therefore we await the result. */
                    result.then((delegatedresult) => {
                        this._showView(delegatedresult, route);
                    });
                } else {
                    this._showView(result, route);
                }
                return true;
            } else {
                console.log('Method did not return a View or a Promise instance.');
                return false;
            }
        } else {
            console.log('Route does not exist!');
            return false;
        }
    }

    /**
     * Shows a view that was returned from a child controller
     * @param {View} view
     * @param {Object} route
     * @private
     */
    _showView(view, route) {
        if(view instanceof Dialog){
            if(this.dialogManager.getOpenDialog() !== view){
                this.dialogManager.show({dialog: view, canCancel: false, shouldGoToRoute: view.goToRoute || this.router.getPreviousRoute()});
                this.dialogManager.once('dialogShown', () => {
                    this.emit('renderend', route.method);
                });
            } else {
                this.emit('renderend', route.method);
            }
        } else {
            /* Close if there's currently an open dialog */
            this.dialogManager.hasOpenDialog() && this.dialogManager._close();
            /* Assemble a callback based on the execution scope and have that called when rendering is completed. */
            this.context.show(view, route.spec, () => { this.emit('renderend', route.method); });
        }
        this.emit('rendering', route.method);
    }
}
<commit_msg>Removed unused import.<commit_after>/**

 @author: Hans van den Akker (mysim1)
 @license NPOSL-3.0
 @copyright Bizboard, 2015

 */

import EventEmitter             from 'eventemitter3';
import AnimationController      from 'famous-flex/AnimationController.js';

import {Dialog}                 from '../components/Dialog.js';
import {DialogManager}          from '../utils/DialogManager.js';
import {inject}                 from '../utils/di/Decorators.js';
import {ObjectHelper}           from '../utils/ObjectHelper.js';
import {Router}                 from './Router.js';

/**
 * The Controller class provides the highest level of control regarding the application features. Within the Controller context
 * each method will registered to receive calls from the Routing engine. With direct access to the Famo.us Context, every method can
 * control the creation of Views and Transitions.
 */
@inject(Router, AnimationController, DialogManager)
export class Controller extends EventEmitter {

    /**
     * Saves the router and context to this.router and this.context, respectively.
     * @param {Router} router Injected globally used router
     * @param {AnimationController} context Injected animationController used by the app
     * @param {DialogManager} dialogManager The dialog manager that shows and hides dialogs
     */
    constructor(router, context, dialogManager) {
        super();

        /**
         * The router used globally in the app
         * @type {Router}
         */
        this.router = router;
        /**
         * The AnimationController used globally in the app to change screens
         * @type {AnimationController}
         */
        this.context = context;
        /**
         * The dialogmanager used to show and hide dialogs. If a controller returns a Dialog, this will be used
         */
        this.dialogManager = dialogManager;

        /* The this._name property can be set by Arva's babel-plugin-transform-runtime-constructor-name plugin.
         * This allows Arva code to be minified and mangled without losing automated route creation.
         * If the plugin is not set up to run, which is done e.g. when not minifying your code, we default back to the runtime constructor name.*/
        let controllerName = this.constructor._name || Object.getPrototypeOf(this).constructor.name;

        ObjectHelper.bindAllMethods(this, this);

        /* Add the controller route to the router. */
        let routeName = controllerName.replace('Controller', '');
        routeName += '/:method';

        /* handle router url changes and execute the appropiate controller method. */
        this.router.add(routeName, {enter: this.onRouteCalled, leave: this.onLeave}, this);
    }

    /**
     * Called to notify the Controller that the route is changed
     * @param newRoute
     */
    onLeave(newRoute) {
        this.isActive = false;
    }

    /**
     * Called by the Router when this controller instance is being navigated to. Calls the controller's method in the given route,
     * and triggers a famous-flex AnimationController show() with the View instance that the method returns. Is also capable of receiving
     * a Promise from the method, in which case the show() is called after the promise is resolved.
     * @param {Object} route Route object generated by the Router. Contains a method name to call, and a render spec for passing to the AnimationController.
     * @returns {Boolean} success Whether the controller method was fully executed, and the Router should emit a routechange event.
     */
    onRouteCalled(route) {
        this.isActive = true;
        if (typeof this[route.method] === 'function') {
            let result = this[route.method].apply(this, route.values);

            if (result) {
                this.emit('renderstart', route.method);
                if (result instanceof Promise) { /* We can assume the method called was asynchronous from nature, therefore we await the result. */
                    result.then((delegatedresult) => {
                        this._showView(delegatedresult, route);
                    });
                } else {
                    this._showView(result, route);
                }
                return true;
            } else {
                console.log('Method did not return a View or a Promise instance.');
                return false;
            }
        } else {
            console.log('Route does not exist!');
            return false;
        }
    }

    /**
     * Shows a view that was returned from a child controller
     * @param {View} view
     * @param {Object} route
     * @private
     */
    _showView(view, route) {
        if(view instanceof Dialog){
            if(this.dialogManager.getOpenDialog() !== view){
                this.dialogManager.show({dialog: view, canCancel: false, shouldGoToRoute: view.goToRoute || this.router.getPreviousRoute()});
                this.dialogManager.once('dialogShown', () => {
                    this.emit('renderend', route.method);
                });
            } else {
                this.emit('renderend', route.method);
            }
        } else {
            /* Close if there's currently an open dialog */
            this.dialogManager.hasOpenDialog() && this.dialogManager._close();
            /* Assemble a callback based on the execution scope and have that called when rendering is completed. */
            this.context.show(view, route.spec, () => { this.emit('renderend', route.method); });
        }
        this.emit('rendering', route.method);
    }
}
<|endoftext|>


<commit_before>import { concat, get, find } from 'lodash';
import promiseRetry from 'promise-retry';
import { ValidatorError, InvalidAttributeError, StepNotFound } from './exceptions';

function validateAnswer(bot, step, answer) {
  const validators = concat([],
    step.validators || [],
    bot.types[step.type].validators || [],
  );
  validators.forEach(obj => {
    Object.keys(obj).forEach(k => {
      const validator = bot.validators[k];
      if (!validator || k === 'warning') { return; }
      if (!validator.validate(obj[k], answer, step)) {
        const warning = obj.warning || validator.warning;
        const message = typeof warning === 'function' ? warning(obj[k]) : warning;
        throw new ValidatorError(message, step);
      }
    });
  });
}

export default ctrl => ctrl

  .define('configure', bot => {
    ctrl.indexes = {};

    bot.steps.forEach((step, idx) => {
      ctrl.indexes[step.name] = idx;
    });

    return ctrl;
  })

  .define('step', (bot, idx) => {
    if (!bot.steps[idx]) {
      return { exit: true };
    }
    const step = bot.steps[idx];
    return Object.assign({}, bot.defaults.step, step);
  })

  .define('run', async (bot, idx = 0) => {
    bot.setStore('currentIdx', idx);

    const step = ctrl.step(bot, idx);

    if (step.message) {
      await ctrl.send(bot, step.message, step.delay);
    }

    if (step.sleep) {
      await bot.actions.wait(step.sleep);
    }

    if (step.exit) {
      return bot.end();
    }

    if (!step.type) {
      return ctrl.next(bot);
    } else if (!bot.types[step.type]) {
      throw new InvalidAttributeError('type', step);
    }

    bot.setStore('waitingForAnswer', true);
    bot._dispatch('hear');

    return ctrl;
  })

  .define('send', async (bot, message, delay) => {
    bot._dispatch('typing');

    if (delay) {
      await bot.actions.wait(delay);
    }

    bot.talk(message);
    bot._dispatch('typed');
  })

  .define('receive', async (bot, message) => {
    const idx = bot.store('currentIdx');
    const step = ctrl.step(bot, idx);

    if (!bot.store('waitingForAnswer')) {
      return;
    }

    let answer = message;
    if ('parser' in bot.types[step.type]) {
      answer = bot.types[step.type].parser(answer);
    }

    try {
      validateAnswer(bot, step, message);
    } catch(e) {
      if (e instanceof ValidatorError) {
        await ctrl.send(bot, e.message, step.delay);
        return;
      }
      throw e;
    }

    bot.setStore('waitingForAnswer', false);

    const output = step.output || step.name;
    if (output) {
      bot.setStore(`data.${output}`, answer);
    }

    if (step.replyMessage) {
      await ctrl.send(bot, step.replyMessage, step.delay);
    }

    // TODO:
    // - check next in options
    // - check next in flow

    const nextStep = step.next || (find(get(step, 'options', []), { value: answer }) || {}).next;
    if (nextStep) {
      return ctrl.jump(bot, nextStep);
    }

    if (bot.steps[idx + 1]) {
      return ctrl.next(bot);
    }

    return bot.end();
  })

  .define('jump', (bot, stepName) => {
    const idx = ctrl.indexes[stepName];
    if (!idx) {
      throw new StepNotFound(stepName);
    }
    return ctrl.run(bot, idx);
  })

  .define('next', bot => ctrl.run(bot, bot.store('currentIdx') + 1))

;
<commit_msg>remove unused import<commit_after>import { concat, get, find } from 'lodash';
import { ValidatorError, InvalidAttributeError, StepNotFound } from './exceptions';

function validateAnswer(bot, step, answer) {
  const validators = concat([],
    step.validators || [],
    bot.types[step.type].validators || [],
  );
  validators.forEach(obj => {
    Object.keys(obj).forEach(k => {
      const validator = bot.validators[k];
      if (!validator || k === 'warning') { return; }
      if (!validator.validate(obj[k], answer, step)) {
        const warning = obj.warning || validator.warning;
        const message = typeof warning === 'function' ? warning(obj[k]) : warning;
        throw new ValidatorError(message, step);
      }
    });
  });
}

export default ctrl => ctrl

  .define('configure', bot => {
    ctrl.indexes = {};

    bot.steps.forEach((step, idx) => {
      ctrl.indexes[step.name] = idx;
    });

    return ctrl;
  })

  .define('step', (bot, idx) => {
    if (!bot.steps[idx]) {
      return { exit: true };
    }
    const step = bot.steps[idx];
    return Object.assign({}, bot.defaults.step, step);
  })

  .define('run', async (bot, idx = 0) => {
    bot.setStore('currentIdx', idx);

    const step = ctrl.step(bot, idx);

    if (step.message) {
      await ctrl.send(bot, step.message, step.delay);
    }

    if (step.sleep) {
      await bot.actions.wait(step.sleep);
    }

    if (step.exit) {
      return bot.end();
    }

    if (!step.type) {
      return ctrl.next(bot);
    } else if (!bot.types[step.type]) {
      throw new InvalidAttributeError('type', step);
    }

    bot.setStore('waitingForAnswer', true);
    bot._dispatch('hear');

    return ctrl;
  })

  .define('send', async (bot, message, delay) => {
    bot._dispatch('typing');

    if (delay) {
      await bot.actions.wait(delay);
    }

    bot.talk(message);
    bot._dispatch('typed');
  })

  .define('receive', async (bot, message) => {
    const idx = bot.store('currentIdx');
    const step = ctrl.step(bot, idx);

    if (!bot.store('waitingForAnswer')) {
      return;
    }

    let answer = message;
    if ('parser' in bot.types[step.type]) {
      answer = bot.types[step.type].parser(answer);
    }

    try {
      validateAnswer(bot, step, message);
    } catch(e) {
      if (e instanceof ValidatorError) {
        await ctrl.send(bot, e.message, step.delay);
        return;
      }
      throw e;
    }

    bot.setStore('waitingForAnswer', false);

    const output = step.output || step.name;
    if (output) {
      bot.setStore(`data.${output}`, answer);
    }

    if (step.replyMessage) {
      await ctrl.send(bot, step.replyMessage, step.delay);
    }

    // TODO:
    // - check next in options
    // - check next in flow

    const nextStep = step.next || (find(get(step, 'options', []), { value: answer }) || {}).next;
    if (nextStep) {
      return ctrl.jump(bot, nextStep);
    }

    if (bot.steps[idx + 1]) {
      return ctrl.next(bot);
    }

    return bot.end();
  })

  .define('jump', (bot, stepName) => {
    const idx = ctrl.indexes[stepName];
    if (!idx) {
      throw new StepNotFound(stepName);
    }
    return ctrl.run(bot, idx);
  })

  .define('next', bot => ctrl.run(bot, bot.store('currentIdx') + 1))

;
<|endoftext|>


<commit_before>
// @require ./cash.js

let fragment;

function initFragment () {
  if ( fragment ) return;
  fragment = doc.implementation.createHTMLDocument ( '' );
  const base = fragment.createElement ( 'base' );
  base.href = doc.location.href;
  fragment.head.appendChild ( base );
}

function parseHTML ( html ) {
  initFragment ();
  fragment.body.innerHTML = html;
  return fragment.body.childNodes;
}

cash.parseHTML = parseHTML;
<commit_msg>$.parseHTML: added a todo<commit_after>
// @require ./cash.js

let fragment;

function initFragment () {
  if ( fragment ) return;
  fragment = doc.implementation.createHTMLDocument ( '' );
  const base = fragment.createElement ( 'base' );
  base.href = doc.location.href;
  fragment.head.appendChild ( base );
}

function parseHTML ( html ) { //FIXME: `<tr></tr>` can't be parsed with this
  initFragment ();
  fragment.body.innerHTML = html;
  return fragment.body.childNodes;
}

cash.parseHTML = parseHTML;
<|endoftext|>


<commit_before>/**
* messages
*
* Needs jquery.min.js / zepto.min.js and plugins/transitionend.js
* Launches a messages fixed in the top of the window.
* ----
* To add a message from the html template, paste this code into the template:
*
*
*
* Then, pass the array to alerts.init inside main.scripts.js (or maybe mobile.scripts.js) like this:
*
* alerts.init(push_message);
*/

var alerts = {

	/**
	 * Define some basic variable settings for the 'dismiss' button label (and hidden close/cross text)
	 */
	settings: {
		dismiss : 'Dismiss'
	},
	/**
	 * Define the basic options the init of the messages
	 */
	options: {
		type: 'bar'
	},

	/**
	* Initiate the messages function
	*/
	init: function(push_message) {
		// are there language variables set?
		if (typeof LANG_MESSAGE_DISMISS !== 'undefined'){
			this.settings.dismiss = LANG_MESSAGE_DISMISS;
		}

		// are there generated messages from the page?
		if (typeof push_message !== 'undefined'){
			// loop trough the messages array
			for(var i=0; i<push_message.length; i++){
				// show these messages
				alerts.addMessage({status: push_message[i].status, content: push_message[i].text, timeout: push_message[i].timeout, type: push_message[i].type});
			}
		}

		// Close messages when cliked on .bar__dismiss
		var dismissbutton = document.querySelectorAll('[data-dismiss]');
		if(dismissbutton !== null) {
			Array.prototype.forEach.call(dismissbutton, function(el, i){
				el.addEventListener('click', alerts.hideNotification, false);
			});
		}
	},

	/**
	* Method for adding message to DOM body
	* @param {user_options} object with settings for the messages
	* @object user_options {status} a string with the status for the messages (error, info, success, warning)
	* @object user_options {content} a string with the content of the messages
	* @object user_options {timeout} a string with timeout time for the messages (Can be empty)
	* @object user_options {type} a string to determine what type of box/bar we want (bar, modal) (Can be empty)
	*/
	addMessage: function(options) {

		// TODO: check if alerts are accessible without JS, for IE8 and other old fucks.

		var message_element;

		extend.object(alerts.options, options);

		message_element = document.createElement('div');

		if(alerts.options.type === 'bar'){
			// Create message DOM element
			message_element.className = 'Alert Alert--bar Alert--' + alerts.options.status + ' js-alert';
			message_element.setAttribute('data-element-type','bar');
			message_element.innerHTML = '
' + alerts.options.content + '
<button type="button" class="Alert-close" data-dismiss="Alert" aria-hidden="true" role="presentation">×</button>';
			document.body.insertBefore(message_element, document.body.firstChild);
		}
		if(alerts.options.type === 'modal'){
			// Create backdrop DOM element
			backdrop_element = document.createElement('div');
			backdrop_element.className = 'Backdrop js-backdrop';
			// Create message DOM element
			message_element.className = 'Alert Alert--modal Alert--' + alerts.options.status + ' js-alert';
			message_element.setAttribute('data-element-type','modal');
			message_element.innerHTML = '
' + alerts.options.content + '
<button class="Button Button--primary" data-dismiss="Alert">'+ this.settings.dismiss +'</button>';
			document.body.appendChild(backdrop_element);
			document.body.appendChild(message_element);
		}

		this.showNotification(message_element);

		// if there is no timeout set in addMessage, or timeout = 0, don't set timeout
		if((typeof alerts.options.timeout === 'undefined')||alerts.options.timeout === 0){
			return;
		} else {
			setTimeout(function(){
				alerts.onTimeoutNotification(message_element);
			},alerts.options.timeout);
		}
	},

	/**
	* Method for showing the messages
	*/
	showNotification: function(element) {

		var notification = element;

		// Use a short timeout, to make sure the transition runs
		setTimeout(function(){
			notification.classList.add('is-visible');
		},10);

		// notification.classList.add('is-visible');
		if(alerts.options.type === 'modal'){
			var backdrops = document.querySelectorAll('.Backdrop');
			Array.prototype.forEach.call(backdrops, function(el, i){
				el.classList.add('is-visible');
			});
		}
	},

	/**
	* Method for hiding the message
	*/
	hideNotification: function(event) {

		var notification = event.target.parentNode;

		// remove and set classes for css animation
		notification.classList.remove('is-visible');
		notification.classList.remove('is-hidden');
		if(alerts.options.type === 'modal'){
			var backdrop = document.querySelector('.Backdrop');
			backdrop.classList.remove('is-visible');
			backdrop.classList.add('is-hiding');
		}

		var notificationHasTransformSet = null;
		var notificationHasWebkitTransformSet = null;

		// Get the transform of an element via getComputedStyle (if the browser supports this..)
		if (window.getComputedStyle) {
			notificationHasTransformSet = window.getComputedStyle(notification, null).getPropertyValue('transform');
			notificationHasWebkitTransformSet = window.getComputedStyle(notification, null).getPropertyValue('-webkit-transform');
		}

		// If browser doesn't support transitions or there is no transform (or -webkit-transform...) set
		if(!Modernizr.csstransitions || notificationHasTransformSet === 'none' || notificationHasWebkitTransformSet === 'none'){
			alerts.removeNotificationElements(notification);
		}
		else {
			// Wait for transition to end, makes use of transitionend.js plugin!
			notification.addEventListener(transitionEnd, function(){
				alerts.removeNotificationElements(this);
			}, false);
		}

		return false;
	},

	removeNotificationElements: function(element) {
		var elementtype = element.getAttribute('data-element-type');
		element.parentNode.removeChild(element);
		if(elementtype === 'modal'){
			var backdrop = document.querySelector('.Backdrop');
			backdrop.parentNode.removeChild(backdrop);
		}
	},

	onTimeoutNotification: function(element) {
		var dismiss = element.querySelector('[data-dismiss]');
		// Fire off a click on the dismiss button
		clickevent = document.createEvent('HTMLEvents');
		clickevent.initEvent('click', true, false);
		dismiss.dispatchEvent(clickevent);
	}
};
<commit_msg>Fix bug in alert extending object.<commit_after>/**
* messages
*
* Needs jquery.min.js / zepto.min.js and plugins/transitionend.js
* Launches a messages fixed in the top of the window.
* ----
* To add a message from the html template, paste this code into the template:
*
*
*
* Then, pass the array to alerts.init inside main.scripts.js (or maybe mobile.scripts.js) like this:
*
* alerts.init(push_message);
*/

var alerts = {

	/**
	 * Define some basic variable settings for the 'dismiss' button label (and hidden close/cross text)
	 */
	settings: {
		dismiss : 'Dismiss'
	},
	/**
	 * Define the basic options the init of the messages
	 */
	options: {
		type: 'bar'
	},

	/**
	* Initiate the messages function
	*/
	init: function(push_message) {
		// are there language variables set?
		if (typeof LANG_MESSAGE_DISMISS !== 'undefined'){
			this.settings.dismiss = LANG_MESSAGE_DISMISS;
		}

		// are there generated messages from the page?
		if (typeof push_message !== 'undefined'){
			// loop trough the messages array
			for(var i=0; i<push_message.length; i++){
				// show these messages
				alerts.addMessage({status: push_message[i].status, content: push_message[i].text, timeout: push_message[i].timeout, type: push_message[i].type});
			}
		}

		// Close messages when cliked on .bar__dismiss
		var dismissbutton = document.querySelectorAll('[data-dismiss]');
		if(dismissbutton !== null) {
			Array.prototype.forEach.call(dismissbutton, function(el, i){
				el.addEventListener('click', alerts.hideNotification, false);
			});
		}
	},

	/**
	* Method for adding message to DOM body
	* @param {user_options} object with settings for the messages
	* @object user_options {status} a string with the status for the messages (error, info, success, warning)
	* @object user_options {content} a string with the content of the messages
	* @object user_options {timeout} a string with timeout time for the messages (Can be empty)
	* @object user_options {type} a string to determine what type of box/bar we want (bar, modal) (Can be empty)
	*/
	addMessage: function(options) {

		// TODO: check if alerts are accessible without JS, for IE8 and other old fucks.

		var message_element;

		extend.object(alerts.options, options);

		message_element = document.createElement('div');

		if(alerts.options.type === 'bar' || typeof alerts.options.type === 'undefined'){
			// Create message DOM element
			message_element.className = 'Alert Alert--bar Alert--' + alerts.options.status + ' js-alert';
			message_element.setAttribute('data-element-type','bar');
			message_element.innerHTML = '
' + alerts.options.content + '
<button type="button" class="Alert-close" data-dismiss="Alert" aria-hidden="true" role="presentation">×</button>';
			document.body.insertBefore(message_element, document.body.firstChild);
		}
		if(alerts.options.type === 'modal'){
			// Create backdrop DOM element
			backdrop_element = document.createElement('div');
			backdrop_element.className = 'Backdrop js-backdrop';
			// Create message DOM element
			message_element.className = 'Alert Alert--modal Alert--' + alerts.options.status + ' js-alert';
			message_element.setAttribute('data-element-type','modal');
			message_element.innerHTML = '
' + alerts.options.content + '
<button class="Button Button--primary" data-dismiss="Alert">'+ this.settings.dismiss +'</button>';
			document.body.appendChild(backdrop_element);
			document.body.appendChild(message_element);
		}

		this.showNotification(message_element);

		// if there is no timeout set in addMessage, or timeout = 0, don't set timeout
		if((typeof alerts.options.timeout === 'undefined')||alerts.options.timeout === 0){
			return;
		} else {
			setTimeout(function(){
				alerts.onTimeoutNotification(message_element);
			},alerts.options.timeout);
		}
	},

	/**
	* Method for showing the messages
	*/
	showNotification: function(element) {

		var notification = element;

		// Use a short timeout, to make sure the transition runs
		setTimeout(function(){
			notification.classList.add('is-visible');
		},10);

		// notification.classList.add('is-visible');
		if(alerts.options.type === 'modal'){
			var backdrops = document.querySelectorAll('.Backdrop');
			Array.prototype.forEach.call(backdrops, function(el, i){
				el.classList.add('is-visible');
			});
		}
	},

	/**
	* Method for hiding the message
	*/
	hideNotification: function(event) {

		var notification = event.target.parentNode;

		// remove and set classes for css animation
		notification.classList.remove('is-visible');
		notification.classList.remove('is-hidden');
		if(alerts.options.type === 'modal'){
			var backdrop = document.querySelector('.Backdrop');
			backdrop.classList.remove('is-visible');
			backdrop.classList.add('is-hiding');
		}

		var notificationHasTransformSet = null;
		var notificationHasWebkitTransformSet = null;

		// Get the transform of an element via getComputedStyle (if the browser supports this..)
		if (window.getComputedStyle) {
			notificationHasTransformSet = window.getComputedStyle(notification, null).getPropertyValue('transform');
			notificationHasWebkitTransformSet = window.getComputedStyle(notification, null).getPropertyValue('-webkit-transform');
		}

		// If browser doesn't support transitions or there is no transform (or -webkit-transform...) set
		if(!Modernizr.csstransitions || notificationHasTransformSet === 'none' || notificationHasWebkitTransformSet === 'none'){
			alerts.removeNotificationElements(notification);
		}
		else {
			// Wait for transition to end, makes use of transitionend.js plugin!
			notification.addEventListener(transitionEnd, function(){
				alerts.removeNotificationElements(this);
			}, false);
		}

		return false;
	},

	removeNotificationElements: function(element) {
		var elementtype = element.getAttribute('data-element-type');
		element.parentNode.removeChild(element);
		if(elementtype === 'modal'){
			var backdrop = document.querySelector('.Backdrop');
			backdrop.parentNode.removeChild(backdrop);
		}
	},

	onTimeoutNotification: function(element) {
		var dismiss = element.querySelector('[data-dismiss]');
		// Fire off a click on the dismiss button
		clickevent = document.createEvent('HTMLEvents');
		clickevent.initEvent('click', true, false);
		dismiss.dispatchEvent(clickevent);
	}
};
<|endoftext|>