var validator = require("validator"),
    object = require("object-additions").object;
    
/*!
 * Express - Form
 * Copyright(c) 2010 Dan Dean <me@dandean.com>
 * MIT Licensed
 */

/**
 * camelize(str): -> String
 * - str (String): The string to make camel-case.
 *
 * Converts dash-separated words into camelCase words. Cribbed from Prototype.js.
 *
 *    field-name -> fieldName
 *    -field-name -> FieldName
**/
function camelize(str) {
  return (str || "").replace(/-+(.)?/g, function(match, chr) {
    return chr ? chr.toUpperCase() : '';
  });
}

var Filter = require("./filter");
var Validator = require("./validator");

var dataSources = ["body", "query", "params"];
var autoLocals = true;

function form() {
  var routines = Array.prototype.slice.call(arguments);
  
  return function(req, res, next) {
    if (!req.form) {
      req.form = {};
    }
    
    dataSources.forEach(function(source) {
      if (req[source] && !object.isString(req[source])) {
        Object.keys(req[source]).forEach(function(name) {
          if (autoLocals) {
            // Make all params locals, so they're accessible from the view.
            // If they have dashes in their names, camelize them so they don't
            // break ejs: <%= field-name %> becomes <%= fieldName %> in your views.
            res.options[camelize(name)] = req[source][name];
          }

          // Copy data as is from source to the form object.
          req.form[name] = req[source][name];
        });
      }
    });
    
    var map = {};
    
    Object.defineProperties(req.form, {
      "errors": {
        value: [],
        enumerable: false
      },
      "getErrors": {
        value: function(name) {
          return map[name] || [];
        },
        enumerable: false
      },
      "isValid": {
        get: function() {
          return this.errors.length == 0;
        },
        enumerable: false
      }
    });

    routines.forEach(function(routine) {
      var result = routine.run(req.form);
      
      if (Array.isArray(result) && result.length) {
        var errors = req.form.errors = req.form.errors || [],
            name = routine.name;
        
        map[name] = map[name] || [];
        
        result.forEach(function(error) {
          errors.push(error);
          map[name].push(error);
        });
      }
    });
        
    if (next) next();
  };
}

form.filter = function(fieldname) {
  return new Filter(fieldname);
};

form.validator = function(fieldname, label) {
  return new Validator(fieldname, label);
};

form.configure = function(options) {
  if (options) {
    if ("dataSources" in options) {
      dataSources = [];
      var sources = options.dataSources;
      if (object.isString(sources)) {
        dataSources.push(sources);
      } else if (Array.isArray(sources)) {
        sources.forEach(function(source) { dataSources.push(source); });
      }
    }
    
    if ("autoLocals" in options) {
      autoLocals = !!options.autoLocals;
    }
  }
  return this;
};

module.exports = form;

@@ -692,16 +692,40 @@
  = true;
+%0Avar flashErrors = true;
 %0A%0Afuncti
@@ -1067,24 +1067,70 @@
 toLocals) %7B%0A
+            if (!res.locals) res.locals = %7B%7D;%0A
             
@@ -1372,22 +1372,21 @@
     res.
-option
+local
 s%5Bcameli
@@ -2398,24 +2398,272 @@
   %7D%0A    %7D);%0A
+    %0A    var flashed = %7B%7D;%0A    %0A    req.form.flashErrors = function() %7B%0A      req.form.errors.forEach(function(error) %7B%0A        if (!flashed%5Berror%5D) %7B%0A          flashed%5Berror%5D = true;%0A          req.flash(%22error%22, error);%0A        %7D%0A      %7D);%0A    %7D;%0A
         %0A   

Make sure locals object exists before writing to it. New form.flashErrors method.