Demo for query storing

Change-Id: I947bcac841992c3f6cfd01ab337c265b0d01cb70
diff --git a/node_modules/grunt/lib/grunt.js b/node_modules/grunt/lib/grunt.js
new file mode 100644
index 0000000..0253333
--- /dev/null
+++ b/node_modules/grunt/lib/grunt.js
@@ -0,0 +1,174 @@
+'use strict';
+
+// Nodejs libs.
+var path = require('path');
+
+// This allows grunt to require() .coffee files.
+try {
+  // Note: grunt no longer depends on CoffeeScript, it will only use it if it is intentionally
+  // installed in the project.
+  require('coffeescript/register');
+} catch (e) {
+  // This is fine, and will cause no problems so long as the user doesn't load .coffee files.
+  // Print a useful error if we attempt to load a .coffee file.
+  if (require.extensions) {
+    var FILE_EXTENSIONS = ['.coffee', '.litcoffee', '.coffee.md'];
+    for (var i = 0; i < FILE_EXTENSIONS.length; i++) {
+      require.extensions[FILE_EXTENSIONS[i]] = function() {
+        throw new Error(
+          'Grunt attempted to load a .coffee file but CoffeeScript was not installed.\n' +
+          'Please run `npm install --dev coffeescript` to enable loading CoffeeScript.'
+        );
+      };
+    }
+  }
+}
+
+// The module to be exported.
+var grunt = module.exports = {};
+
+// Expose internal grunt libs.
+function gRequire(name) {
+  return grunt[name] = require('./grunt/' + name);
+}
+
+var util = require('grunt-legacy-util');
+grunt.util = util;
+grunt.util.task = require('./util/task');
+
+var Log = require('grunt-legacy-log').Log;
+var log = new Log({grunt: grunt});
+grunt.log = log;
+
+gRequire('template');
+gRequire('event');
+var fail = gRequire('fail');
+gRequire('file');
+var option = gRequire('option');
+var config = gRequire('config');
+var task = gRequire('task');
+var help = gRequire('help');
+gRequire('cli');
+var verbose = grunt.verbose = log.verbose;
+
+// Expose some grunt metadata.
+grunt.package = require('../package.json');
+grunt.version = grunt.package.version;
+
+// Expose specific grunt lib methods on grunt.
+function gExpose(obj, methodName, newMethodName) {
+  grunt[newMethodName || methodName] = obj[methodName].bind(obj);
+}
+gExpose(task, 'registerTask');
+gExpose(task, 'registerMultiTask');
+gExpose(task, 'registerInitTask');
+gExpose(task, 'renameTask');
+gExpose(task, 'loadTasks');
+gExpose(task, 'loadNpmTasks');
+gExpose(config, 'init', 'initConfig');
+gExpose(fail, 'warn');
+gExpose(fail, 'fatal');
+
+// Expose the task interface. I've never called this manually, and have no idea
+// how it will work. But it might.
+grunt.tasks = function(tasks, options, done) {
+  // Update options with passed-in options.
+  option.init(options);
+
+  // Display the grunt version and quit if the user did --version.
+  var _tasks, _options;
+  if (option('version')) {
+    // Not --verbose.
+    log.writeln('grunt v' + grunt.version);
+
+    if (option('verbose')) {
+      // --verbose
+      verbose.writeln('Install path: ' + path.resolve(__dirname, '..'));
+      // Yes, this is a total hack, but we don't want to log all that verbose
+      // task initialization stuff here.
+      grunt.log.muted = true;
+      // Initialize task system so that available tasks can be listed.
+      grunt.task.init([], {help: true});
+      // Re-enable logging.
+      grunt.log.muted = false;
+
+      // Display available tasks (for shell completion, etc).
+      _tasks = Object.keys(grunt.task._tasks).sort();
+      verbose.writeln('Available tasks: ' + _tasks.join(' '));
+
+      // Display available options (for shell completion, etc).
+      _options = [];
+      Object.keys(grunt.cli.optlist).forEach(function(long) {
+        var o = grunt.cli.optlist[long];
+        _options.push('--' + (o.negate ? 'no-' : '') + long);
+        if (o.short) { _options.push('-' + o.short); }
+      });
+      verbose.writeln('Available options: ' + _options.join(' '));
+    }
+
+    return;
+  }
+
+  // Init colors.
+  log.initColors();
+
+  // Display help and quit if the user did --help.
+  if (option('help')) {
+    help.display();
+    return;
+  }
+
+  // A little header stuff.
+  verbose.header('Initializing').writeflags(option.flags(), 'Command-line options');
+
+  // Determine and output which tasks will be run.
+  var tasksSpecified = tasks && tasks.length > 0;
+  tasks = task.parseArgs([tasksSpecified ? tasks : 'default']);
+
+  // Initialize tasks.
+  task.init(tasks, options);
+
+  verbose.writeln();
+  if (!tasksSpecified) {
+    verbose.writeln('No tasks specified, running default tasks.');
+  }
+  verbose.writeflags(tasks, 'Running tasks');
+
+  // Handle otherwise unhandleable (probably asynchronous) exceptions.
+  var uncaughtHandler = function(e) {
+    fail.fatal(e, fail.code.TASK_FAILURE);
+  };
+  process.on('uncaughtException', uncaughtHandler);
+
+  // Report, etc when all tasks have completed.
+  task.options({
+    error: function(e) {
+      fail.warn(e, fail.code.TASK_FAILURE);
+    },
+    done: function() {
+      // Stop handling uncaught exceptions so that we don't leave any
+      // unwanted process-level side effects behind. There is no need to do
+      // this in the error callback, because fail.warn() will either kill
+      // the process, or with --force keep on going all the way here.
+      process.removeListener('uncaughtException', uncaughtHandler);
+
+      // Output a final fail / success report.
+      fail.report();
+
+      if (done) {
+        // Execute "done" function when done (only if passed, of course).
+        done();
+      } else {
+        // Otherwise, explicitly exit.
+        util.exit(0);
+      }
+    }
+  });
+
+  // Execute all tasks, in order. Passing each task individually in a forEach
+  // allows the error callback to execute multiple times.
+  tasks.forEach(function(name) { task.run(name); });
+  // Run tasks async internally to reduce call-stack, per:
+  // https://github.com/gruntjs/grunt/pull/1026
+  task.start({asyncDone: true});
+};
diff --git a/node_modules/grunt/lib/grunt/cli.js b/node_modules/grunt/lib/grunt/cli.js
new file mode 100644
index 0000000..1252f54
--- /dev/null
+++ b/node_modules/grunt/lib/grunt/cli.js
@@ -0,0 +1,55 @@
+'use strict';
+
+var grunt = require('../grunt');
+
+// External libs.
+var nopt = require('nopt');
+var gruntOptions = require('grunt-known-options');
+
+// This is only executed when run via command line.
+var cli = module.exports = function(options, done) {
+  // CLI-parsed options override any passed-in "default" options.
+  if (options) {
+    // For each default option...
+    Object.keys(options).forEach(function(key) {
+      if (!(key in cli.options)) {
+        // If this option doesn't exist in the parsed cli.options, add it in.
+        cli.options[key] = options[key];
+      } else if (cli.optlist[key].type === Array) {
+        // If this option's type is Array, append it to any existing array
+        // (or create a new array).
+        [].push.apply(cli.options[key], options[key]);
+      }
+    });
+  }
+
+  // Run tasks.
+  grunt.tasks(cli.tasks, cli.options, done);
+};
+
+// Default options.
+var optlist = cli.optlist = gruntOptions;
+
+// Parse `optlist` into a form that nopt can handle.
+var aliases = {};
+var known = {};
+
+Object.keys(optlist).forEach(function(key) {
+  var short = optlist[key].short;
+  if (short) {
+    aliases[short] = '--' + key;
+  }
+  known[key] = optlist[key].type;
+});
+
+var parsed = nopt(known, aliases, process.argv, 2);
+cli.tasks = parsed.argv.remain;
+cli.options = parsed;
+delete parsed.argv;
+
+// Initialize any Array options that weren't initialized.
+Object.keys(optlist).forEach(function(key) {
+  if (optlist[key].type === Array && !(key in cli.options)) {
+    cli.options[key] = [];
+  }
+});
diff --git a/node_modules/grunt/lib/grunt/config.js b/node_modules/grunt/lib/grunt/config.js
new file mode 100644
index 0000000..ef2bf80
--- /dev/null
+++ b/node_modules/grunt/lib/grunt/config.js
@@ -0,0 +1,115 @@
+'use strict';
+
+var grunt = require('../grunt');
+
+// Get/set config data. If value was passed, set. Otherwise, get.
+var config = module.exports = function(prop, value) {
+  if (arguments.length === 2) {
+    // Two arguments were passed, set the property's value.
+    return config.set(prop, value);
+  } else {
+    // Get the property's value (or the entire data object).
+    return config.get(prop);
+  }
+};
+
+// The actual config data.
+config.data = {};
+
+// Escape any . in name with \. so dot-based namespacing works properly.
+config.escape = function(str) {
+  return str.replace(/\./g, '\\.');
+};
+
+// Return prop as a string.
+config.getPropString = function(prop) {
+  return Array.isArray(prop) ? prop.map(config.escape).join('.') : prop;
+};
+
+// Get raw, unprocessed config data.
+config.getRaw = function(prop) {
+  if (prop) {
+    // Prop was passed, get that specific property's value.
+    return grunt.util.namespace.get(config.data, config.getPropString(prop));
+  } else {
+    // No prop was passed, return the entire config.data object.
+    return config.data;
+  }
+};
+
+// Match '<%= FOO %>' where FOO is a propString, eg. foo or foo.bar but not
+// a method call like foo() or foo.bar().
+var propStringTmplRe = /^<%=\s*([a-z0-9_$]+(?:\.[a-z0-9_$]+)*)\s*%>$/i;
+
+// Get config data, recursively processing templates.
+config.get = function(prop) {
+  return config.process(config.getRaw(prop));
+};
+
+// Expand a config value recursively. Used for post-processing raw values
+// already retrieved from the config.
+config.process = function(raw) {
+  return grunt.util.recurse(raw, function(value) {
+    // If the value is not a string, return it.
+    if (typeof value !== 'string') { return value; }
+    // If possible, access the specified property via config.get, in case it
+    // doesn't refer to a string, but instead refers to an object or array.
+    var matches = value.match(propStringTmplRe);
+    var result;
+    if (matches) {
+      result = config.get(matches[1]);
+      // If the result retrieved from the config data wasn't null or undefined,
+      // return it.
+      if (result != null) { return result; }
+    }
+    // Process the string as a template.
+    return grunt.template.process(value, {data: config.data});
+  });
+};
+
+// Set config data.
+config.set = function(prop, value) {
+  return grunt.util.namespace.set(config.data, config.getPropString(prop), value);
+};
+
+// Deep merge config data.
+config.merge = function(obj) {
+  grunt.util._.merge(config.data, obj);
+  return config.data;
+};
+
+// Initialize config data.
+config.init = function(obj) {
+  grunt.verbose.write('Initializing config...').ok();
+  // Initialize and return data.
+  return (config.data = obj || {});
+};
+
+// Test to see if required config params have been defined. If not, throw an
+// exception (use this inside of a task).
+config.requires = function() {
+  var p = grunt.util.pluralize;
+  var props = grunt.util.toArray(arguments).map(config.getPropString);
+  var msg = 'Verifying propert' + p(props.length, 'y/ies') +
+    ' ' + grunt.log.wordlist(props) + ' exist' + p(props.length, 's') +
+    ' in config...';
+  grunt.verbose.write(msg);
+  var failProps = config.data && props.filter(function(prop) {
+    return config.get(prop) == null;
+  }).map(function(prop) {
+    return '"' + prop + '"';
+  });
+  if (config.data && failProps.length === 0) {
+    grunt.verbose.ok();
+    return true;
+  } else {
+    grunt.verbose.or.write(msg);
+    grunt.log.error().error('Unable to process task.');
+    if (!config.data) {
+      throw grunt.util.error('Unable to load config.');
+    } else {
+      throw grunt.util.error('Required config propert' +
+        p(failProps.length, 'y/ies') + ' ' + failProps.join(', ') + ' missing.');
+    }
+  }
+};
diff --git a/node_modules/grunt/lib/grunt/event.js b/node_modules/grunt/lib/grunt/event.js
new file mode 100644
index 0000000..7ec1027
--- /dev/null
+++ b/node_modules/grunt/lib/grunt/event.js
@@ -0,0 +1,7 @@
+'use strict';
+
+// External lib.
+var EventEmitter2 = require('eventemitter2').EventEmitter2;
+
+// Awesome.
+module.exports = new EventEmitter2({wildcard: true});
diff --git a/node_modules/grunt/lib/grunt/fail.js b/node_modules/grunt/lib/grunt/fail.js
new file mode 100644
index 0000000..631e249
--- /dev/null
+++ b/node_modules/grunt/lib/grunt/fail.js
@@ -0,0 +1,75 @@
+'use strict';
+
+var grunt = require('../grunt');
+
+// The module to be exported.
+var fail = module.exports = {};
+
+// Error codes.
+fail.code = {
+  FATAL_ERROR: 1,
+  MISSING_GRUNTFILE: 2,
+  TASK_FAILURE: 3,
+  TEMPLATE_ERROR: 4,
+  INVALID_AUTOCOMPLETE: 5,
+  WARNING: 6,
+};
+
+// DRY it up!
+function writeln(e, mode) {
+  grunt.log.muted = false;
+  var msg = String(e.message || e);
+  if (!grunt.option('no-color')) { msg += '\x07'; } // Beep!
+  if (mode === 'warn') {
+    msg = 'Warning: ' + msg + ' ';
+    msg += (grunt.option('force') ? 'Used --force, continuing.'.underline : 'Use --force to continue.');
+    msg = msg.yellow;
+  } else {
+    msg = ('Fatal error: ' + msg).red;
+  }
+  grunt.log.writeln(msg);
+}
+
+// If --stack is enabled, log the appropriate error stack (if it exists).
+function dumpStack(e) {
+  if (grunt.option('stack')) {
+    if (e.origError && e.origError.stack) {
+      console.log(e.origError.stack);
+    } else if (e.stack) {
+      console.log(e.stack);
+    }
+  }
+}
+
+// A fatal error occurred. Abort immediately.
+fail.fatal = function(e, errcode) {
+  writeln(e, 'fatal');
+  dumpStack(e);
+  grunt.util.exit(typeof errcode === 'number' ? errcode : fail.code.FATAL_ERROR);
+};
+
+// Keep track of error and warning counts.
+fail.errorcount = 0;
+fail.warncount = 0;
+
+// A warning occurred. Abort immediately unless -f or --force was used.
+fail.warn = function(e, errcode) {
+  var message = typeof e === 'string' ? e : e.message;
+  fail.warncount++;
+  writeln(message, 'warn');
+  // If -f or --force aren't used, stop script processing.
+  if (!grunt.option('force')) {
+    dumpStack(e);
+    grunt.log.writeln().fail('Aborted due to warnings.');
+    grunt.util.exit(typeof errcode === 'number' ? errcode : fail.code.WARNING);
+  }
+};
+
+// This gets called at the very end.
+fail.report = function() {
+  if (fail.warncount > 0) {
+    grunt.log.writeln().fail('Done, but with warnings.');
+  } else {
+    grunt.log.writeln().success('Done.');
+  }
+};
diff --git a/node_modules/grunt/lib/grunt/file.js b/node_modules/grunt/lib/grunt/file.js
new file mode 100644
index 0000000..7e0e2fb
--- /dev/null
+++ b/node_modules/grunt/lib/grunt/file.js
@@ -0,0 +1,460 @@
+'use strict';
+
+var grunt = require('../grunt');
+
+// Nodejs libs.
+var fs = require('fs');
+var path = require('path');
+
+// The module to be exported.
+var file = module.exports = {};
+
+// External libs.
+file.glob = require('glob');
+file.minimatch = require('minimatch');
+file.findup = require('findup-sync');
+var YAML = require('js-yaml');
+var rimraf = require('rimraf');
+var iconv = require('iconv-lite');
+var mkdirp = require('mkdirp').sync;
+
+// Windows?
+var win32 = process.platform === 'win32';
+
+// Normalize \\ paths to / paths.
+var unixifyPath = function(filepath) {
+  if (win32) {
+    return filepath.replace(/\\/g, '/');
+  } else {
+    return filepath;
+  }
+};
+
+// Change the current base path (ie, CWD) to the specified path.
+file.setBase = function() {
+  var dirpath = path.join.apply(path, arguments);
+  process.chdir(dirpath);
+};
+
+// Process specified wildcard glob patterns or filenames against a
+// callback, excluding and uniquing files in the result set.
+var processPatterns = function(patterns, fn) {
+  // Filepaths to return.
+  var result = [];
+  // Iterate over flattened patterns array.
+  grunt.util._.flattenDeep(patterns).forEach(function(pattern) {
+    // If the first character is ! it should be omitted
+    var exclusion = pattern.indexOf('!') === 0;
+    // If the pattern is an exclusion, remove the !
+    if (exclusion) { pattern = pattern.slice(1); }
+    // Find all matching files for this pattern.
+    var matches = fn(pattern);
+    if (exclusion) {
+      // If an exclusion, remove matching files.
+      result = grunt.util._.difference(result, matches);
+    } else {
+      // Otherwise add matching files.
+      result = grunt.util._.union(result, matches);
+    }
+  });
+  return result;
+};
+
+// Match a filepath or filepaths against one or more wildcard patterns. Returns
+// all matching filepaths.
+file.match = function(options, patterns, filepaths) {
+  if (grunt.util.kindOf(options) !== 'object') {
+    filepaths = patterns;
+    patterns = options;
+    options = {};
+  }
+  // Return empty set if either patterns or filepaths was omitted.
+  if (patterns == null || filepaths == null) { return []; }
+  // Normalize patterns and filepaths to arrays.
+  if (!Array.isArray(patterns)) { patterns = [patterns]; }
+  if (!Array.isArray(filepaths)) { filepaths = [filepaths]; }
+  // Return empty set if there are no patterns or filepaths.
+  if (patterns.length === 0 || filepaths.length === 0) { return []; }
+  // Return all matching filepaths.
+  return processPatterns(patterns, function(pattern) {
+    return file.minimatch.match(filepaths, pattern, options);
+  });
+};
+
+// Match a filepath or filepaths against one or more wildcard patterns. Returns
+// true if any of the patterns match.
+file.isMatch = function() {
+  return file.match.apply(file, arguments).length > 0;
+};
+
+// Return an array of all file paths that match the given wildcard patterns.
+file.expand = function() {
+  var args = grunt.util.toArray(arguments);
+  // If the first argument is an options object, save those options to pass
+  // into the file.glob.sync method.
+  var options = grunt.util.kindOf(args[0]) === 'object' ? args.shift() : {};
+  // Use the first argument if it's an Array, otherwise convert the arguments
+  // object to an array and use that.
+  var patterns = Array.isArray(args[0]) ? args[0] : args;
+  // Return empty set if there are no patterns or filepaths.
+  if (patterns.length === 0) { return []; }
+  // Return all matching filepaths.
+  var matches = processPatterns(patterns, function(pattern) {
+    // Find all matching files for this pattern.
+    return file.glob.sync(pattern, options);
+  });
+  // Filter result set?
+  if (options.filter) {
+    matches = matches.filter(function(filepath) {
+      filepath = path.join(options.cwd || '', filepath);
+      try {
+        if (typeof options.filter === 'function') {
+          return options.filter(filepath);
+        } else {
+          // If the file is of the right type and exists, this should work.
+          return fs.statSync(filepath)[options.filter]();
+        }
+      } catch (e) {
+        // Otherwise, it's probably not the right type.
+        return false;
+      }
+    });
+  }
+  return matches;
+};
+
+var pathSeparatorRe = /[\/\\]/g;
+
+// The "ext" option refers to either everything after the first dot (default)
+// or everything after the last dot.
+var extDotRe = {
+  first: /(\.[^\/]*)?$/,
+  last: /(\.[^\/\.]*)?$/,
+};
+
+// Build a multi task "files" object dynamically.
+file.expandMapping = function(patterns, destBase, options) {
+  options = grunt.util._.defaults({}, options, {
+    extDot: 'first',
+    rename: function(destBase, destPath) {
+      return path.join(destBase || '', destPath);
+    }
+  });
+  var files = [];
+  var fileByDest = {};
+  // Find all files matching pattern, using passed-in options.
+  file.expand(options, patterns).forEach(function(src) {
+    var destPath = src;
+    // Flatten?
+    if (options.flatten) {
+      destPath = path.basename(destPath);
+    }
+    // Change the extension?
+    if ('ext' in options) {
+      destPath = destPath.replace(extDotRe[options.extDot], options.ext);
+    }
+    // Generate destination filename.
+    var dest = options.rename(destBase, destPath, options);
+    // Prepend cwd to src path if necessary.
+    if (options.cwd) { src = path.join(options.cwd, src); }
+    // Normalize filepaths to be unix-style.
+    dest = dest.replace(pathSeparatorRe, '/');
+    src = src.replace(pathSeparatorRe, '/');
+    // Map correct src path to dest path.
+    if (fileByDest[dest]) {
+      // If dest already exists, push this src onto that dest's src array.
+      fileByDest[dest].src.push(src);
+    } else {
+      // Otherwise create a new src-dest file mapping object.
+      files.push({
+        src: [src],
+        dest: dest,
+      });
+      // And store a reference for later use.
+      fileByDest[dest] = files[files.length - 1];
+    }
+  });
+  return files;
+};
+
+// Like mkdir -p. Create a directory and any intermediary directories.
+file.mkdir = function(dirpath, mode) {
+  if (grunt.option('no-write')) { return; }
+  try {
+    mkdirp(dirpath, { mode: mode });
+  } catch (e) {
+    throw grunt.util.error('Unable to create directory "' + dirpath + '" (Error code: ' + e.code + ').', e);
+  }
+};
+
+// Recurse into a directory, executing callback for each file.
+file.recurse = function recurse(rootdir, callback, subdir) {
+  var abspath = subdir ? path.join(rootdir, subdir) : rootdir;
+  fs.readdirSync(abspath).forEach(function(filename) {
+    var filepath = path.join(abspath, filename);
+    if (fs.statSync(filepath).isDirectory()) {
+      recurse(rootdir, callback, unixifyPath(path.join(subdir || '', filename || '')));
+    } else {
+      callback(unixifyPath(filepath), rootdir, subdir, filename);
+    }
+  });
+};
+
+// The default file encoding to use.
+file.defaultEncoding = 'utf8';
+// Whether to preserve the BOM on file.read rather than strip it.
+file.preserveBOM = false;
+
+// Read a file, return its contents.
+file.read = function(filepath, options) {
+  if (!options) { options = {}; }
+  var contents;
+  grunt.verbose.write('Reading ' + filepath + '...');
+  try {
+    contents = fs.readFileSync(String(filepath));
+    // If encoding is not explicitly null, convert from encoded buffer to a
+    // string. If no encoding was specified, use the default.
+    if (options.encoding !== null) {
+      contents = iconv.decode(contents, options.encoding || file.defaultEncoding, {stripBOM: !file.preserveBOM});
+    }
+    grunt.verbose.ok();
+    return contents;
+  } catch (e) {
+    grunt.verbose.error();
+    throw grunt.util.error('Unable to read "' + filepath + '" file (Error code: ' + e.code + ').', e);
+  }
+};
+
+// Read a file, parse its contents, return an object.
+file.readJSON = function(filepath, options) {
+  var src = file.read(filepath, options);
+  var result;
+  grunt.verbose.write('Parsing ' + filepath + '...');
+  try {
+    result = JSON.parse(src);
+    grunt.verbose.ok();
+    return result;
+  } catch (e) {
+    grunt.verbose.error();
+    throw grunt.util.error('Unable to parse "' + filepath + '" file (' + e.message + ').', e);
+  }
+};
+
+// Read a YAML file, parse its contents, return an object.
+file.readYAML = function(filepath, options, yamlOptions) {
+  if (!options) { options = {}; }
+  if (!yamlOptions) { yamlOptions = {}; }
+
+  var src = file.read(filepath, options);
+  var result;
+  grunt.verbose.write('Parsing ' + filepath + '...');
+  try {
+    // use the recommended way of reading YAML files
+    // https://github.com/nodeca/js-yaml#safeload-string---options-
+    if (yamlOptions.unsafeLoad) {
+      result = YAML.load(src);
+    } else {
+      result = YAML.safeLoad(src);
+    }
+    grunt.verbose.ok();
+    return result;
+  } catch (e) {
+    grunt.verbose.error();
+    throw grunt.util.error('Unable to parse "' + filepath + '" file (' + e.message + ').', e);
+  }
+};
+
+// Write a file.
+file.write = function(filepath, contents, options) {
+  if (!options) { options = {}; }
+  var nowrite = grunt.option('no-write');
+  grunt.verbose.write((nowrite ? 'Not actually writing ' : 'Writing ') + filepath + '...');
+  // Create path, if necessary.
+  file.mkdir(path.dirname(filepath));
+  try {
+    // If contents is already a Buffer, don't try to encode it. If no encoding
+    // was specified, use the default.
+    if (!Buffer.isBuffer(contents)) {
+      contents = iconv.encode(contents, options.encoding || file.defaultEncoding);
+    }
+    // Actually write file.
+    if (!nowrite) {
+      fs.writeFileSync(filepath, contents, 'mode' in options ? {mode: options.mode} : {});
+    }
+    grunt.verbose.ok();
+    return true;
+  } catch (e) {
+    grunt.verbose.error();
+    throw grunt.util.error('Unable to write "' + filepath + '" file (Error code: ' + e.code + ').', e);
+  }
+};
+
+// Read a file, optionally processing its content, then write the output.
+// Or read a directory, recursively creating directories, reading files,
+// processing content, writing output.
+file.copy = function copy(srcpath, destpath, options) {
+  if (file.isDir(srcpath)) {
+    // Copy a directory, recursively.
+    // Explicitly create new dest directory.
+    file.mkdir(destpath);
+    // Iterate over all sub-files/dirs, recursing.
+    fs.readdirSync(srcpath).forEach(function(filepath) {
+      copy(path.join(srcpath, filepath), path.join(destpath, filepath), options);
+    });
+  } else {
+    // Copy a single file.
+    file._copy(srcpath, destpath, options);
+  }
+};
+
+// Read a file, optionally processing its content, then write the output.
+file._copy = function(srcpath, destpath, options) {
+  if (!options) { options = {}; }
+  // If a process function was specified, and noProcess isn't true or doesn't
+  // match the srcpath, process the file's source.
+  var process = options.process && options.noProcess !== true &&
+    !(options.noProcess && file.isMatch(options.noProcess, srcpath));
+  // If the file will be processed, use the encoding as-specified. Otherwise,
+  // use an encoding of null to force the file to be read/written as a Buffer.
+  var readWriteOptions = process ? options : {encoding: null};
+  // Actually read the file.
+  var contents = file.read(srcpath, readWriteOptions);
+  if (process) {
+    grunt.verbose.write('Processing source...');
+    try {
+      contents = options.process(contents, srcpath, destpath);
+      grunt.verbose.ok();
+    } catch (e) {
+      grunt.verbose.error();
+      throw grunt.util.error('Error while processing "' + srcpath + '" file.', e);
+    }
+  }
+  // Abort copy if the process function returns false.
+  if (contents === false) {
+    grunt.verbose.writeln('Write aborted.');
+  } else {
+    file.write(destpath, contents, readWriteOptions);
+  }
+};
+
+// Delete folders and files recursively
+file.delete = function(filepath, options) {
+  filepath = String(filepath);
+
+  var nowrite = grunt.option('no-write');
+  if (!options) {
+    options = {force: grunt.option('force') || false};
+  }
+
+  grunt.verbose.write((nowrite ? 'Not actually deleting ' : 'Deleting ') + filepath + '...');
+
+  if (!file.exists(filepath)) {
+    grunt.verbose.error();
+    grunt.log.warn('Cannot delete nonexistent file.');
+    return false;
+  }
+
+  // Only delete cwd or outside cwd if --force enabled. Be careful, people!
+  if (!options.force) {
+    if (file.isPathCwd(filepath)) {
+      grunt.verbose.error();
+      grunt.fail.warn('Cannot delete the current working directory.');
+      return false;
+    } else if (!file.isPathInCwd(filepath)) {
+      grunt.verbose.error();
+      grunt.fail.warn('Cannot delete files outside the current working directory.');
+      return false;
+    }
+  }
+
+  try {
+    // Actually delete. Or not.
+    if (!nowrite) {
+      rimraf.sync(filepath);
+    }
+    grunt.verbose.ok();
+    return true;
+  } catch (e) {
+    grunt.verbose.error();
+    throw grunt.util.error('Unable to delete "' + filepath + '" file (' + e.message + ').', e);
+  }
+};
+
+// True if the file path exists.
+file.exists = function() {
+  var filepath = path.join.apply(path, arguments);
+  return fs.existsSync(filepath);
+};
+
+// True if the file is a symbolic link.
+file.isLink = function() {
+  var filepath = path.join.apply(path, arguments);
+  try {
+    return fs.lstatSync(filepath).isSymbolicLink();
+  } catch (e) {
+    if (e.code === 'ENOENT') {
+      // The file doesn't exist, so it's not a symbolic link.
+      return false;
+    }
+    throw grunt.util.error('Unable to read "' + filepath + '" file (Error code: ' + e.code + ').', e);
+  }
+};
+
+// True if the path is a directory.
+file.isDir = function() {
+  var filepath = path.join.apply(path, arguments);
+  return file.exists(filepath) && fs.statSync(filepath).isDirectory();
+};
+
+// True if the path is a file.
+file.isFile = function() {
+  var filepath = path.join.apply(path, arguments);
+  return file.exists(filepath) && fs.statSync(filepath).isFile();
+};
+
+// Is a given file path absolute?
+file.isPathAbsolute = function() {
+  var filepath = path.join.apply(path, arguments);
+  return path.isAbsolute(filepath);
+};
+
+// Do all the specified paths refer to the same path?
+file.arePathsEquivalent = function(first) {
+  first = path.resolve(first);
+  for (var i = 1; i < arguments.length; i++) {
+    if (first !== path.resolve(arguments[i])) { return false; }
+  }
+  return true;
+};
+
+// Are descendant path(s) contained within ancestor path? Note: does not test
+// if paths actually exist.
+file.doesPathContain = function(ancestor) {
+  ancestor = path.resolve(ancestor);
+  var relative;
+  for (var i = 1; i < arguments.length; i++) {
+    relative = path.relative(path.resolve(arguments[i]), ancestor);
+    if (relative === '' || /\w+/.test(relative)) { return false; }
+  }
+  return true;
+};
+
+// Test to see if a filepath is the CWD.
+file.isPathCwd = function() {
+  var filepath = path.join.apply(path, arguments);
+  try {
+    return file.arePathsEquivalent(fs.realpathSync(process.cwd()), fs.realpathSync(filepath));
+  } catch (e) {
+    return false;
+  }
+};
+
+// Test to see if a filepath is contained within the CWD.
+file.isPathInCwd = function() {
+  var filepath = path.join.apply(path, arguments);
+  try {
+    return file.doesPathContain(fs.realpathSync(process.cwd()), fs.realpathSync(filepath));
+  } catch (e) {
+    return false;
+  }
+};
diff --git a/node_modules/grunt/lib/grunt/help.js b/node_modules/grunt/lib/grunt/help.js
new file mode 100644
index 0000000..d761ef5
--- /dev/null
+++ b/node_modules/grunt/lib/grunt/help.js
@@ -0,0 +1,120 @@
+'use strict';
+
+var grunt = require('../grunt');
+
+// Nodejs libs.
+var path = require('path');
+
+// Set column widths.
+var col1len = 0;
+exports.initCol1 = function(str) {
+  col1len = Math.max(col1len, str.length);
+};
+exports.initWidths = function() {
+  // Widths for options/tasks table output.
+  var commandWidth = Math.max(col1len + 20, 76);
+  exports.widths = [1, col1len, 2, commandWidth - col1len];
+};
+
+// Render an array in table form.
+exports.table = function(arr) {
+  arr.forEach(function(item) {
+    grunt.log.writetableln(exports.widths, ['', grunt.util._.pad(item[0], col1len), '', item[1]]);
+  });
+};
+
+// Methods to run, in-order.
+exports.queue = [
+  'initOptions',
+  'initTasks',
+  'initWidths',
+  'header',
+  'usage',
+  'options',
+  'optionsFooter',
+  'tasks',
+  'footer',
+];
+
+// Actually display stuff.
+exports.display = function() {
+  exports.queue.forEach(function(name) { exports[name](); });
+};
+
+// Header.
+exports.header = function() {
+  grunt.log.writeln('Grunt: The JavaScript Task Runner (v' + grunt.version + ')');
+};
+
+// Usage info.
+exports.usage = function() {
+  grunt.log.header('Usage');
+  grunt.log.writeln(' ' + path.basename(process.argv[1]) + ' [options] [task [task ...]]');
+};
+
+// Options.
+exports.initOptions = function() {
+  // Build 2-column array for table view.
+  exports._options = Object.keys(grunt.cli.optlist).map(function(long) {
+    var o = grunt.cli.optlist[long];
+    var col1 = '--' + (o.negate ? 'no-' : '') + long + (o.short ? ', -' + o.short : '');
+    exports.initCol1(col1);
+    return [col1, o.info];
+  });
+};
+
+exports.options = function() {
+  grunt.log.header('Options');
+  exports.table(exports._options);
+};
+
+exports.optionsFooter = function() {
+  grunt.log.writeln().writelns(
+    'Options marked with * have methods exposed via the grunt API and should ' +
+    'instead be specified inside the Gruntfile wherever possible.'
+  );
+};
+
+// Tasks.
+exports.initTasks = function() {
+  // Initialize task system so that the tasks can be listed.
+  grunt.task.init([], {help: true});
+
+  // Build object of tasks by info (where they were loaded from).
+  exports._tasks = [];
+  Object.keys(grunt.task._tasks).forEach(function(name) {
+    exports.initCol1(name);
+    var task = grunt.task._tasks[name];
+    exports._tasks.push(task);
+  });
+};
+
+exports.tasks = function() {
+  grunt.log.header('Available tasks');
+  if (exports._tasks.length === 0) {
+    grunt.log.writeln('(no tasks found)');
+  } else {
+    exports.table(exports._tasks.map(function(task) {
+      var info = task.info;
+      if (task.multi) { info += ' *'; }
+      return [task.name, info];
+    }));
+
+    grunt.log.writeln().writelns(
+      'Tasks run in the order specified. Arguments may be passed to tasks that ' +
+      'accept them by using colons, like "lint:files". Tasks marked with * are ' +
+      '"multi tasks" and will iterate over all sub-targets if no argument is ' +
+      'specified.'
+    );
+  }
+
+  grunt.log.writeln().writelns(
+    'The list of available tasks may change based on tasks directories or ' +
+    'grunt plugins specified in the Gruntfile or via command-line options.'
+  );
+};
+
+// Footer.
+exports.footer = function() {
+  grunt.log.writeln().writeln('For more information, see http://gruntjs.com/');
+};
diff --git a/node_modules/grunt/lib/grunt/option.js b/node_modules/grunt/lib/grunt/option.js
new file mode 100644
index 0000000..bfb4074
--- /dev/null
+++ b/node_modules/grunt/lib/grunt/option.js
@@ -0,0 +1,38 @@
+'use strict';
+
+// The actual option data.
+var data = {};
+
+// Get or set an option value.
+var option = module.exports = function(key, value) {
+  var no = key.match(/^no-(.+)$/);
+  if (arguments.length === 2) {
+    return (data[key] = value);
+  } else if (no) {
+    return data[no[1]] === false;
+  } else {
+    return data[key];
+  }
+};
+
+// Initialize option data.
+option.init = function(obj) {
+  return (data = obj || {});
+};
+
+// List of options as flags.
+option.flags = function() {
+  return Object.keys(data).filter(function(key) {
+    // Don't display empty arrays.
+    return !(Array.isArray(data[key]) && data[key].length === 0);
+  }).map(function(key) {
+    var val = data[key];
+    return '--' + (val === false ? 'no-' : '') + key +
+      (typeof val === 'boolean' ? '' : '=' + val);
+  });
+};
+
+// Get all option keys
+option.keys = function() {
+  return Object.keys(data);
+};
diff --git a/node_modules/grunt/lib/grunt/task.js b/node_modules/grunt/lib/grunt/task.js
new file mode 100644
index 0000000..ffd119e
--- /dev/null
+++ b/node_modules/grunt/lib/grunt/task.js
@@ -0,0 +1,471 @@
+'use strict';
+
+// Keep track of the number of log.error() calls and the last specified tasks message.
+var errorcount, lastInfo;
+
+var grunt = require('../grunt');
+
+// Nodejs libs.
+var path = require('path');
+
+// Extend generic "task" util lib.
+var parent = grunt.util.task.create();
+
+// The module to be exported.
+var task = module.exports = Object.create(parent);
+
+// A temporary registry of tasks and metadata.
+var registry = {tasks: [], untasks: [], meta: {}};
+
+// Number of levels of recursion when loading tasks in collections.
+var loadTaskDepth = 0;
+
+// Override built-in registerTask.
+task.registerTask = function(name) {
+  // Add task to registry.
+  registry.tasks.push(name);
+  // Register task.
+  parent.registerTask.apply(task, arguments);
+  // This task, now that it's been registered.
+  var thisTask = task._tasks[name];
+  // Metadata about the current task.
+  thisTask.meta = grunt.util._.clone(registry.meta);
+  // Override task function.
+  var _fn = thisTask.fn;
+  thisTask.fn = function(arg) {
+    // Guaranteed to always be the actual task name.
+    var name = thisTask.name;
+    // Initialize the errorcount for this task.
+    errorcount = grunt.fail.errorcount;
+    // Return the number of errors logged during this task.
+    Object.defineProperty(this, 'errorCount', {
+      enumerable: true,
+      get: function() {
+        return grunt.fail.errorcount - errorcount;
+      }
+    });
+    // Expose task.requires on `this`.
+    this.requires = task.requires.bind(task);
+    // Expose config.requires on `this`.
+    this.requiresConfig = grunt.config.requires;
+    // Return an options object with the specified defaults overwritten by task-
+    // specific overrides, via the "options" property.
+    this.options = function() {
+      var args = [{}].concat(grunt.util.toArray(arguments)).concat([
+        grunt.config([name, 'options'])
+      ]);
+      var options = grunt.util._.extend.apply(null, args);
+      grunt.verbose.writeflags(options, 'Options');
+      return options;
+    };
+    // If this task was an alias or a multi task called without a target,
+    // only log if in verbose mode.
+    var logger = _fn.alias || (thisTask.multi && (!arg || arg === '*')) ? 'verbose' : 'log';
+    // Actually log.
+    grunt[logger].header('Running "' + this.nameArgs + '"' +
+      (this.name !== this.nameArgs ? ' (' + this.name + ')' : '') + ' task');
+    // If --debug was specified, log the path to this task's source file.
+    grunt[logger].debug('Task source: ' + thisTask.meta.filepath);
+    // Actually run the task.
+    return _fn.apply(this, arguments);
+  };
+  return task;
+};
+
+// Multi task targets can't start with _ or be a reserved property (options).
+function isValidMultiTaskTarget(target) {
+  return !/^_|^options$/.test(target);
+}
+
+// Normalize multi task files.
+task.normalizeMultiTaskFiles = function(data, target) {
+  var prop, obj;
+  var files = [];
+  if (grunt.util.kindOf(data) === 'object') {
+    if ('src' in data || 'dest' in data) {
+      obj = {};
+      for (prop in data) {
+        if (prop !== 'options') {
+          obj[prop] = data[prop];
+        }
+      }
+      files.push(obj);
+    } else if (grunt.util.kindOf(data.files) === 'object') {
+      for (prop in data.files) {
+        files.push({src: data.files[prop], dest: grunt.config.process(prop)});
+      }
+    } else if (Array.isArray(data.files)) {
+      grunt.util._.flattenDeep(data.files).forEach(function(obj) {
+        var prop;
+        if ('src' in obj || 'dest' in obj) {
+          files.push(obj);
+        } else {
+          for (prop in obj) {
+            files.push({src: obj[prop], dest: grunt.config.process(prop)});
+          }
+        }
+      });
+    }
+  } else {
+    files.push({src: data, dest: grunt.config.process(target)});
+  }
+
+  // If no src/dest or files were specified, return an empty files array.
+  if (files.length === 0) {
+    grunt.verbose.writeln('File: ' + '[no files]'.yellow);
+    return [];
+  }
+
+  // Process all normalized file objects.
+  files = grunt.util._(files).chain().forEach(function(obj) {
+    if (!('src' in obj) || !obj.src) { return; }
+    // Normalize .src properties to flattened array.
+    if (Array.isArray(obj.src)) {
+      obj.src = grunt.util._.flatten(obj.src);
+    } else {
+      obj.src = [obj.src];
+    }
+  }).map(function(obj) {
+    // Build options object, removing unwanted properties.
+    var expandOptions = grunt.util._.extend({}, obj);
+    delete expandOptions.src;
+    delete expandOptions.dest;
+
+    // Expand file mappings.
+    if (obj.expand) {
+      return grunt.file.expandMapping(obj.src, obj.dest, expandOptions).map(function(mapObj) {
+        // Copy obj properties to result.
+        var result = grunt.util._.extend({}, obj);
+        // Make a clone of the orig obj available.
+        result.orig = grunt.util._.extend({}, obj);
+        // Set .src and .dest, processing both as templates.
+        result.src = grunt.config.process(mapObj.src);
+        result.dest = grunt.config.process(mapObj.dest);
+        // Remove unwanted properties.
+        ['expand', 'cwd', 'flatten', 'rename', 'ext'].forEach(function(prop) {
+          delete result[prop];
+        });
+        return result;
+      });
+    }
+
+    // Copy obj properties to result, adding an .orig property.
+    var result = grunt.util._.extend({}, obj);
+    // Make a clone of the orig obj available.
+    result.orig = grunt.util._.extend({}, obj);
+
+    if ('src' in result) {
+      // Expose an expand-on-demand getter method as .src.
+      Object.defineProperty(result, 'src', {
+        enumerable: true,
+        get: function fn() {
+          var src;
+          if (!('result' in fn)) {
+            src = obj.src;
+            // If src is an array, flatten it. Otherwise, make it into an array.
+            src = Array.isArray(src) ? grunt.util._.flatten(src) : [src];
+            // Expand src files, memoizing result.
+            fn.result = grunt.file.expand(expandOptions, src);
+          }
+          return fn.result;
+        }
+      });
+    }
+
+    if ('dest' in result) {
+      result.dest = obj.dest;
+    }
+
+    return result;
+  }).flatten().value();
+
+  // Log this.file src and dest properties when --verbose is specified.
+  if (grunt.option('verbose')) {
+    files.forEach(function(obj) {
+      var output = [];
+      if ('src' in obj) {
+        output.push(obj.src.length > 0 ? grunt.log.wordlist(obj.src) : '[no src]'.yellow);
+      }
+      if ('dest' in obj) {
+        output.push('-> ' + (obj.dest ? String(obj.dest).cyan : '[no dest]'.yellow));
+      }
+      if (output.length > 0) {
+        grunt.verbose.writeln('Files: ' + output.join(' '));
+      }
+    });
+  }
+
+  return files;
+};
+
+// This is the most common "multi task" pattern.
+task.registerMultiTask = function(name, info, fn) {
+  // If optional "info" string is omitted, shuffle arguments a bit.
+  if (fn == null) {
+    fn = info;
+    info = 'Custom multi task.';
+  }
+  // Store a reference to the task object, in case the task gets renamed.
+  var thisTask;
+  task.registerTask(name, info, function(target) {
+    // Guaranteed to always be the actual task name.
+    var name = thisTask.name;
+    // Arguments (sans target) as an array.
+    this.args = grunt.util.toArray(arguments).slice(1);
+    // If a target wasn't specified, run this task once for each target.
+    if (!target || target === '*') {
+      return task.runAllTargets(name, this.args);
+    } else if (!isValidMultiTaskTarget(target)) {
+      throw new Error('Invalid target "' + target + '" specified.');
+    }
+    // Fail if any required config properties have been omitted.
+    this.requiresConfig([name, target]);
+    // Return an options object with the specified defaults overwritten by task-
+    // and/or target-specific overrides, via the "options" property.
+    this.options = function() {
+      var targetObj = grunt.config([name, target]);
+      var args = [{}].concat(grunt.util.toArray(arguments)).concat([
+        grunt.config([name, 'options']),
+        grunt.util.kindOf(targetObj) === 'object' ? targetObj.options : {}
+      ]);
+      var options = grunt.util._.extend.apply(null, args);
+      grunt.verbose.writeflags(options, 'Options');
+      return options;
+    };
+    // Expose the current target.
+    this.target = target;
+    // Recreate flags object so that the target isn't set as a flag.
+    this.flags = {};
+    this.args.forEach(function(arg) { this.flags[arg] = true; }, this);
+    // Expose data on `this` (as well as task.current).
+    this.data = grunt.config([name, target]);
+    // Expose normalized files object.
+    this.files = task.normalizeMultiTaskFiles(this.data, target);
+    // Expose normalized, flattened, uniqued array of src files.
+    Object.defineProperty(this, 'filesSrc', {
+      enumerable: true,
+      get: function() {
+        return grunt.util._(this.files).chain().map('src').flatten().uniq().value();
+      }.bind(this)
+    });
+    // Call original task function, passing in the target and any other args.
+    return fn.apply(this, this.args);
+  });
+
+  thisTask = task._tasks[name];
+  thisTask.multi = true;
+};
+
+// Init tasks don't require properties in config, and as such will preempt
+// config loading errors.
+task.registerInitTask = function(name, info, fn) {
+  task.registerTask(name, info, fn);
+  task._tasks[name].init = true;
+};
+
+// Override built-in renameTask to use the registry.
+task.renameTask = function(oldname, newname) {
+  var result;
+  try {
+    // Actually rename task.
+    result = parent.renameTask.apply(task, arguments);
+    // Add and remove task.
+    registry.untasks.push(oldname);
+    registry.tasks.push(newname);
+    // Return result.
+    return result;
+  } catch (e) {
+    grunt.log.error(e.message);
+  }
+};
+
+// If a property wasn't passed, run all task targets in turn.
+task.runAllTargets = function(taskname, args) {
+  // Get an array of sub-property keys under the given config object.
+  var targets = Object.keys(grunt.config.getRaw(taskname) || {});
+  // Remove invalid target properties.
+  targets = targets.filter(isValidMultiTaskTarget);
+  // Fail if there are no actual properties to iterate over.
+  if (targets.length === 0) {
+    grunt.log.error('No "' + taskname + '" targets found.');
+    return false;
+  }
+  // Iterate over all targets, running a task for each.
+  targets.forEach(function(target) {
+    // Be sure to pass in any additionally specified args.
+    task.run([taskname, target].concat(args || []).join(':'));
+  });
+};
+
+// Load tasks and handlers from a given tasks file.
+var loadTaskStack = [];
+function loadTask(filepath) {
+  // In case this was called recursively, save registry for later.
+  loadTaskStack.push(registry);
+  // Reset registry.
+  registry = {tasks: [], untasks: [], meta: {info: lastInfo, filepath: filepath}};
+  var filename = path.basename(filepath);
+  var msg = 'Loading "' + filename + '" tasks...';
+  var regCount = 0;
+  var fn;
+  try {
+    // Load taskfile.
+    fn = require(path.resolve(filepath));
+    if (typeof fn === 'function') {
+      fn.call(grunt, grunt);
+    }
+    grunt.verbose.write(msg).ok();
+    // Log registered/renamed/unregistered tasks.
+    ['un', ''].forEach(function(prefix) {
+      var list = grunt.util._.chain(registry[prefix + 'tasks']).uniq().sort().value();
+      if (list.length > 0) {
+        regCount++;
+        grunt.verbose.writeln((prefix ? '- ' : '+ ') + grunt.log.wordlist(list));
+      }
+    });
+    if (regCount === 0) {
+      grunt.verbose.warn('No tasks were registered or unregistered.');
+    }
+  } catch (e) {
+    // Something went wrong.
+    grunt.log.write(msg).error().verbose.error(e.stack).or.error(e);
+  }
+  // Restore registry.
+  registry = loadTaskStack.pop() || {};
+}
+
+// Log a message when loading tasks.
+function loadTasksMessage(info) {
+  // Only keep track of names of top-level loaded tasks and collections,
+  // not sub-tasks.
+  if (loadTaskDepth === 0) { lastInfo = info; }
+  grunt.verbose.subhead('Registering ' + info + ' tasks.');
+}
+
+// Load tasks and handlers from a given directory.
+function loadTasks(tasksdir) {
+  try {
+    var files = grunt.file.glob.sync('*.{js,coffee}', {cwd: tasksdir, maxDepth: 1});
+    // Load tasks from files.
+    files.forEach(function(filename) {
+      loadTask(path.join(tasksdir, filename));
+    });
+  } catch (e) {
+    grunt.log.verbose.error(e.stack).or.error(e);
+  }
+}
+
+// Load tasks and handlers from a given directory.
+task.loadTasks = function(tasksdir) {
+  loadTasksMessage('"' + tasksdir + '"');
+  if (grunt.file.exists(tasksdir)) {
+    loadTasks(tasksdir);
+  } else {
+    grunt.log.error('Tasks directory "' + tasksdir + '" not found.');
+  }
+};
+
+// Load tasks and handlers from a given locally-installed Npm module (installed
+// relative to the base dir).
+task.loadNpmTasks = function(name) {
+  loadTasksMessage('"' + name + '" local Npm module');
+  var root = path.resolve('node_modules');
+  var pkgpath = path.join(root, name);
+  var pkgfile = path.join(pkgpath, 'package.json');
+  // If package does not exist where grunt expects it to be,
+  // try to find it using Node's package path resolution mechanism
+  if (!grunt.file.exists(pkgpath)) {
+    var nameParts = name.split('/');
+    // In case name points to directory inside module,
+    // get real name of the module with respect to scope (if any)
+    var normailzedName = (name[0] === '@' ? nameParts.slice(0,2).join('/') : nameParts[0]);
+    try {
+      pkgfile = require.resolve(normailzedName + '/package.json');
+      root = pkgfile.substr(0, pkgfile.length - normailzedName.length - '/package.json'.length);
+    } catch (err) {
+      grunt.log.error('Local Npm module "' + normailzedName + '" not found. Is it installed?');
+      return;
+    }
+  }
+  var pkg = grunt.file.exists(pkgfile) ? grunt.file.readJSON(pkgfile) : {keywords: []};
+
+  // Process collection plugins.
+  if (pkg.keywords && pkg.keywords.indexOf('gruntcollection') !== -1) {
+    loadTaskDepth++;
+    Object.keys(pkg.dependencies).forEach(function(depName) {
+      // Npm sometimes pulls dependencies out if they're shared, so find
+      // upwards if not found locally.
+      var filepath = grunt.file.findup('node_modules/' + depName, {
+        cwd: path.resolve('node_modules', name),
+        nocase: true
+      });
+      if (filepath) {
+        // Load this task plugin recursively.
+        task.loadNpmTasks(path.relative(root, filepath));
+      }
+    });
+    loadTaskDepth--;
+    return;
+  }
+
+  // Process task plugins.
+  var tasksdir = path.join(root, name, 'tasks');
+  if (grunt.file.exists(tasksdir)) {
+    loadTasks(tasksdir);
+  } else {
+    grunt.log.error('Local Npm module "' + name + '" not found. Is it installed?');
+  }
+};
+
+// Initialize tasks.
+task.init = function(tasks, options) {
+  if (!options) { options = {}; }
+
+  // Were only init tasks specified?
+  var allInit = tasks.length > 0 && tasks.every(function(name) {
+    var obj = task._taskPlusArgs(name).task;
+    return obj && obj.init;
+  });
+
+  // Get any local Gruntfile or tasks that might exist. Use --gruntfile override
+  // if specified, otherwise search the current directory or any parent.
+  var gruntfile, msg;
+  if (allInit || options.gruntfile === false) {
+    gruntfile = null;
+  } else {
+    gruntfile = grunt.option('gruntfile') ||
+      grunt.file.findup('Gruntfile.{js,coffee}', {nocase: true});
+    msg = 'Reading "' + (gruntfile ? path.basename(gruntfile) : '???') + '" Gruntfile...';
+  }
+
+  if (options.gruntfile === false) {
+    // Grunt was run as a lib with {gruntfile: false}.
+  } else if (gruntfile && grunt.file.exists(gruntfile)) {
+    grunt.verbose.writeln().write(msg).ok();
+    // Change working directory so that all paths are relative to the
+    // Gruntfile's location (or the --base option, if specified).
+    process.chdir(grunt.option('base') || path.dirname(gruntfile));
+    // Load local tasks, if the file exists.
+    loadTasksMessage('Gruntfile');
+    loadTask(gruntfile);
+  } else if (options.help || allInit) {
+    // Don't complain about missing Gruntfile.
+  } else if (grunt.option('gruntfile')) {
+    // If --config override was specified and it doesn't exist, complain.
+    grunt.log.writeln().write(msg).error();
+    grunt.fatal('Unable to find "' + gruntfile + '" Gruntfile.', grunt.fail.code.MISSING_GRUNTFILE);
+  } else if (!grunt.option('help')) {
+    grunt.verbose.writeln().write(msg).error();
+    grunt.log.writelns(
+      'A valid Gruntfile could not be found. Please see the getting ' +
+      'started guide for more information on how to configure grunt: ' +
+      'http://gruntjs.com/getting-started'
+    );
+    grunt.fatal('Unable to find Gruntfile.', grunt.fail.code.MISSING_GRUNTFILE);
+  }
+
+  // Load all user-specified --npm tasks.
+  (grunt.option('npm') || []).map(String).forEach(task.loadNpmTasks);
+  // Load all user-specified --tasks.
+  (grunt.option('tasks') || []).map(String).forEach(task.loadTasks);
+};
diff --git a/node_modules/grunt/lib/grunt/template.js b/node_modules/grunt/lib/grunt/template.js
new file mode 100644
index 0000000..15e1fe1
--- /dev/null
+++ b/node_modules/grunt/lib/grunt/template.js
@@ -0,0 +1,90 @@
+'use strict';
+
+var grunt = require('../grunt');
+
+// The module to be exported.
+var template = module.exports = {};
+
+// External libs.
+template.date = require('dateformat');
+
+// Format today's date.
+template.today = function(format) {
+  var now = new Date();
+  if (process.env.SOURCE_DATE_EPOCH) {
+    now = new Date((process.env.SOURCE_DATE_EPOCH * 1000) + (now.getTimezoneOffset() * 60000));
+  }
+  return template.date(now, format);
+};
+
+// Template delimiters.
+var allDelimiters = {};
+
+// Initialize template delimiters.
+template.addDelimiters = function(name, opener, closer) {
+  var delimiters = allDelimiters[name] = {};
+  // Used by grunt.
+  delimiters.opener = opener;
+  delimiters.closer = closer;
+  // Generate RegExp patterns dynamically.
+  var a = delimiters.opener.replace(/(.)/g, '\\$1');
+  var b = '([\\s\\S]+?)' + delimiters.closer.replace(/(.)/g, '\\$1');
+  // Used by Lo-Dash.
+  delimiters.lodash = {
+    evaluate: new RegExp(a + b, 'g'),
+    interpolate: new RegExp(a + '=' + b, 'g'),
+    escape: new RegExp(a + '-' + b, 'g')
+  };
+};
+
+// The underscore default template syntax should be a pretty sane default for
+// the config system.
+template.addDelimiters('config', '<%', '%>');
+
+// Set Lo-Dash template delimiters.
+template.setDelimiters = function(name) {
+  // Get the appropriate delimiters.
+  var delimiters = allDelimiters[name in allDelimiters ? name : 'config'];
+  // Tell Lo-Dash which delimiters to use.
+  grunt.util._.extend(grunt.util._.templateSettings, delimiters.lodash);
+  // Return the delimiters.
+  return delimiters;
+};
+
+// Process template + data with Lo-Dash.
+template.process = function(tmpl, options) {
+  if (!options) { options = {}; }
+  // Set delimiters, and get a opening match character.
+  var delimiters = template.setDelimiters(options.delimiters);
+  // Clone data, initializing to config data or empty object if omitted.
+  var data = Object.create(options.data || grunt.config.data || {});
+  // Expose grunt so that grunt utilities can be accessed, but only if it
+  // doesn't conflict with an existing .grunt property.
+  if (!('grunt' in data)) { data.grunt = grunt; }
+  // Keep track of last change.
+  var last = tmpl;
+  try {
+    // As long as tmpl contains template tags, render it and get the result,
+    // otherwise just use the template string.
+    while (tmpl.indexOf(delimiters.opener) >= 0) {
+      tmpl = grunt.util._.template(tmpl, options)(data);
+      // Abort if template didn't change - nothing left to process!
+      if (tmpl === last) { break; }
+      last = tmpl;
+    }
+  } catch (e) {
+    // In upgrading to Lo-Dash (or Underscore.js 1.3.3), \n and \r in template
+    // tags now causes an exception to be thrown. Warn the user why this is
+    // happening. https://github.com/documentcloud/underscore/issues/553
+    if (String(e) === 'SyntaxError: Unexpected token ILLEGAL' && /\n|\r/.test(tmpl)) {
+      grunt.log.errorlns('A special character was detected in this template. ' +
+        'Inside template tags, the \\n and \\r special characters must be ' +
+        'escaped as \\\\n and \\\\r. (grunt 0.4.0+)');
+    }
+    // Slightly better error message.
+    e.message = 'An error occurred while processing a template (' + e.message + ').';
+    grunt.warn(e, grunt.fail.code.TEMPLATE_ERROR);
+  }
+  // Normalize linefeeds and return.
+  return grunt.util.normalizelf(tmpl);
+};
diff --git a/node_modules/grunt/lib/util/task.js b/node_modules/grunt/lib/util/task.js
new file mode 100644
index 0000000..d39ce1b
--- /dev/null
+++ b/node_modules/grunt/lib/util/task.js
@@ -0,0 +1,335 @@
+(function(exports) {
+
+  'use strict';
+
+  var grunt = require('../grunt');
+
+  // Construct-o-rama.
+  function Task() {
+    // Information about the currently-running task.
+    this.current = {};
+    // Tasks.
+    this._tasks = {};
+    // Task queue.
+    this._queue = [];
+    // Queue placeholder (for dealing with nested tasks).
+    this._placeholder = {placeholder: true};
+    // Queue marker (for clearing the queue programmatically).
+    this._marker = {marker: true};
+    // Options.
+    this._options = {};
+    // Is the queue running?
+    this._running = false;
+    // Success status of completed tasks.
+    this._success = {};
+  }
+
+  // Expose the constructor function.
+  exports.Task = Task;
+
+  // Create a new Task instance.
+  exports.create = function() {
+    return new Task();
+  };
+
+  // If the task runner is running or an error handler is not defined, throw
+  // an exception. Otherwise, call the error handler directly.
+  Task.prototype._throwIfRunning = function(obj) {
+    if (this._running || !this._options.error) {
+      // Throw an exception that the task runner will catch.
+      throw obj;
+    } else {
+      // Not inside the task runner. Call the error handler and abort.
+      this._options.error.call({name: null}, obj);
+    }
+  };
+
+  // Register a new task.
+  Task.prototype.registerTask = function(name, info, fn) {
+    // If optional "info" string is omitted, shuffle arguments a bit.
+    if (fn == null) {
+      fn = info;
+      info = null;
+    }
+    // String or array of strings was passed instead of fn.
+    var tasks;
+    if (typeof fn !== 'function') {
+      // Array of task names.
+      tasks = this.parseArgs([fn]);
+      // This task function just runs the specified tasks.
+      fn = this.run.bind(this, fn);
+      fn.alias = true;
+      // Generate an info string if one wasn't explicitly passed.
+      if (!info) {
+        info = 'Alias for "' + tasks.join('", "') + '" task' +
+          (tasks.length === 1 ? '' : 's') + '.';
+      }
+    } else if (!info) {
+      info = 'Custom task.';
+    }
+    // Add task into cache.
+    this._tasks[name] = {name: name, info: info, fn: fn};
+    // Make chainable!
+    return this;
+  };
+
+  // Is the specified task an alias?
+  Task.prototype.isTaskAlias = function(name) {
+    return !!this._tasks[name].fn.alias;
+  };
+
+  // Has the specified task been registered?
+  Task.prototype.exists = function(name) {
+    return name in this._tasks;
+  };
+
+  // Rename a task. This might be useful if you want to override the default
+  // behavior of a task, while retaining the old name. This is a billion times
+  // easier to implement than some kind of in-task "super" functionality.
+  Task.prototype.renameTask = function(oldname, newname) {
+    if (!this._tasks[oldname]) {
+      throw new Error('Cannot rename missing "' + oldname + '" task.');
+    }
+    // Rename task.
+    this._tasks[newname] = this._tasks[oldname];
+    // Update name property of task.
+    this._tasks[newname].name = newname;
+    // Remove old name.
+    delete this._tasks[oldname];
+    // Make chainable!
+    return this;
+  };
+
+  // Argument parsing helper. Supports these signatures:
+  //  fn('foo')                 // ['foo']
+  //  fn('foo', 'bar', 'baz')   // ['foo', 'bar', 'baz']
+  //  fn(['foo', 'bar', 'baz']) // ['foo', 'bar', 'baz']
+  Task.prototype.parseArgs = function(args) {
+    // Return the first argument if it's an array, otherwise return an array
+    // of all arguments.
+    return Array.isArray(args[0]) ? args[0] : [].slice.call(args);
+  };
+
+  // Split a colon-delimited string into an array, unescaping (but not
+  // splitting on) any \: escaped colons.
+  Task.prototype.splitArgs = function(str) {
+    if (!str) { return []; }
+    // Store placeholder for \\ followed by \:
+    str = str.replace(/\\\\/g, '\uFFFF').replace(/\\:/g, '\uFFFE');
+    // Split on :
+    return str.split(':').map(function(s) {
+      // Restore place-held : followed by \\
+      return s.replace(/\uFFFE/g, ':').replace(/\uFFFF/g, '\\');
+    });
+  };
+
+  // Given a task name, determine which actual task will be called, and what
+  // arguments will be passed into the task callback. "foo" -> task "foo", no
+  // args. "foo:bar:baz" -> task "foo:bar:baz" with no args (if "foo:bar:baz"
+  // task exists), otherwise task "foo:bar" with arg "baz" (if "foo:bar" task
+  // exists), otherwise task "foo" with args "bar" and "baz".
+  Task.prototype._taskPlusArgs = function(name) {
+    // Get task name / argument parts.
+    var parts = this.splitArgs(name);
+    // Start from the end, not the beginning!
+    var i = parts.length;
+    var task;
+    do {
+      // Get a task.
+      task = this._tasks[parts.slice(0, i).join(':')];
+      // If the task doesn't exist, decrement `i`, and if `i` is greater than
+      // 0, repeat.
+    } while (!task && --i > 0);
+    // Just the args.
+    var args = parts.slice(i);
+    // Maybe you want to use them as flags instead of as positional args?
+    var flags = {};
+    args.forEach(function(arg) { flags[arg] = true; });
+    // The task to run and the args to run it with.
+    return {task: task, nameArgs: name, args: args, flags: flags};
+  };
+
+  // Append things to queue in the correct spot.
+  Task.prototype._push = function(things) {
+    // Get current placeholder index.
+    var index = this._queue.indexOf(this._placeholder);
+    if (index === -1) {
+      // No placeholder, add task+args objects to end of queue.
+      this._queue = this._queue.concat(things);
+    } else {
+      // Placeholder exists, add task+args objects just before placeholder.
+      [].splice.apply(this._queue, [index, 0].concat(things));
+    }
+  };
+
+  // Enqueue a task.
+  Task.prototype.run = function() {
+    // Parse arguments into an array, returning an array of task+args objects.
+    var things = this.parseArgs(arguments).map(this._taskPlusArgs, this);
+    // Throw an exception if any tasks weren't found.
+    var fails = things.filter(function(thing) { return !thing.task; });
+    if (fails.length > 0) {
+      this._throwIfRunning(new Error('Task "' + fails[0].nameArgs + '" not found.'));
+      return this;
+    }
+    // Append things to queue in the correct spot.
+    this._push(things);
+    // Make chainable!
+    return this;
+  };
+
+  // Add a marker to the queue to facilitate clearing it programmatically.
+  Task.prototype.mark = function() {
+    this._push(this._marker);
+    // Make chainable!
+    return this;
+  };
+
+  // Run a task function, handling this.async / return value.
+  Task.prototype.runTaskFn = function(context, fn, done, asyncDone) {
+    // Async flag.
+    var async = false;
+
+    // Update the internal status object and run the next task.
+    var complete = function(success) {
+      var err = null;
+      if (success === false) {
+        // Since false was passed, the task failed generically.
+        err = new Error('Task "' + context.nameArgs + '" failed.');
+      } else if (success instanceof Error || {}.toString.call(success) === '[object Error]') {
+        // An error object was passed, so the task failed specifically.
+        err = success;
+        success = false;
+      } else {
+        // The task succeeded.
+        success = true;
+      }
+      // The task has ended, reset the current task object.
+      this.current = {};
+      // A task has "failed" only if it returns false (async) or if the
+      // function returned by .async is passed false.
+      this._success[context.nameArgs] = success;
+      // If task failed, call error handler.
+      if (!success && this._options.error) {
+        this._options.error.call({name: context.name, nameArgs: context.nameArgs}, err);
+      }
+      // only call done async if explicitly requested to
+      // see: https://github.com/gruntjs/grunt/pull/1026
+      if (asyncDone) {
+        process.nextTick(function() {
+          done(err, success);
+        });
+      } else {
+        done(err, success);
+      }
+    }.bind(this);
+
+    // When called, sets the async flag and returns a function that can
+    // be used to continue processing the queue.
+    context.async = function() {
+      async = true;
+      // The returned function should execute asynchronously in case
+      // someone tries to do this.async()(); inside a task (WTF).
+      return grunt.util._.once(function(success) {
+        setTimeout(function() { complete(success); }, 1);
+      });
+    };
+
+    // Expose some information about the currently-running task.
+    this.current = context;
+
+    try {
+      // Get the current task and run it, setting `this` inside the task
+      // function to be something useful.
+      var success = fn.call(context);
+      // If the async flag wasn't set, process the next task in the queue.
+      if (!async) {
+        complete(success);
+      }
+    } catch (err) {
+      complete(err);
+    }
+  };
+
+  // Begin task queue processing. Ie. run all tasks.
+  Task.prototype.start = function(opts) {
+    if (!opts) {
+      opts = {};
+    }
+    // Abort if already running.
+    if (this._running) { return false; }
+    // Actually process the next task.
+    var nextTask = function() {
+      // Get next task+args object from queue.
+      var thing;
+      // Skip any placeholders or markers.
+      do {
+        thing = this._queue.shift();
+      } while (thing === this._placeholder || thing === this._marker);
+      // If queue was empty, we're all done.
+      if (!thing) {
+        this._running = false;
+        if (this._options.done) {
+          this._options.done();
+        }
+        return;
+      }
+      // Add a placeholder to the front of the queue.
+      this._queue.unshift(this._placeholder);
+
+      // Expose some information about the currently-running task.
+      var context = {
+        // The current task name plus args, as-passed.
+        nameArgs: thing.nameArgs,
+        // The current task name.
+        name: thing.task.name,
+        // The current task arguments.
+        args: thing.args,
+        // The current arguments, available as named flags.
+        flags: thing.flags
+      };
+
+      // Actually run the task function (handling this.async, etc)
+      this.runTaskFn(context, function() {
+        return thing.task.fn.apply(this, this.args);
+      }, nextTask, !!opts.asyncDone);
+
+    }.bind(this);
+
+    // Update flag.
+    this._running = true;
+    // Process the next task.
+    nextTask();
+  };
+
+  // Clear remaining tasks from the queue.
+  Task.prototype.clearQueue = function(options) {
+    if (!options) { options = {}; }
+    if (options.untilMarker) {
+      this._queue.splice(0, this._queue.indexOf(this._marker) + 1);
+    } else {
+      this._queue = [];
+    }
+    // Make chainable!
+    return this;
+  };
+
+  // Test to see if all of the given tasks have succeeded.
+  Task.prototype.requires = function() {
+    this.parseArgs(arguments).forEach(function(name) {
+      var success = this._success[name];
+      if (!success) {
+        throw new Error('Required task "' + name +
+          '" ' + (success === false ? 'failed' : 'must be run first') + '.');
+      }
+    }.bind(this));
+  };
+
+  // Override default options.
+  Task.prototype.options = function(options) {
+    Object.keys(options).forEach(function(name) {
+      this._options[name] = options[name];
+    }.bind(this));
+  };
+
+}(typeof exports === 'object' && exports || this));