Demo for query storing

Change-Id: I947bcac841992c3f6cfd01ab337c265b0d01cb70
diff --git a/node_modules/tiny-lr/lib/client.js b/node_modules/tiny-lr/lib/client.js
new file mode 100644
index 0000000..548e42b
--- /dev/null
+++ b/node_modules/tiny-lr/lib/client.js
@@ -0,0 +1,94 @@
+import events       from 'events';
+import WebSocket    from 'faye-websocket';
+import objectAssign from 'object-assign';
+
+const debug = require('debug')('tinylr:client');
+
+let idCounter = 0;
+
+export default class Client extends events.EventEmitter {
+
+  constructor (req, socket, head, options = {}) {
+    super();
+    this.options = options;
+    this.ws = new WebSocket(req, socket, head);
+    this.ws.onmessage = this.message.bind(this);
+    this.ws.onclose = this.close.bind(this);
+    this.id = this.uniqueId('ws');
+  }
+
+  message (event) {
+    let data = this.data(event);
+    if (this[data.command]) return this[data.command](data);
+  }
+
+  close (event) {
+    if (this.ws) {
+      this.ws.close();
+      this.ws = null;
+    }
+
+    this.emit('end', event);
+  }
+
+  // Commands
+  hello () {
+    this.send({
+      command: 'hello',
+      protocols: [
+        'http://livereload.com/protocols/official-7'
+      ],
+      serverName: 'tiny-lr'
+    });
+  }
+
+  info (data) {
+    if (data) {
+      debug('Info', data);
+      this.emit('info', objectAssign({}, data, { id: this.id }));
+      this.plugins = data.plugins;
+      this.url = data.url;
+    }
+
+    return objectAssign({}, data || {}, { id: this.id, url: this.url });
+  }
+
+  // Server commands
+  reload (files) {
+    files.forEach(function (file) {
+      this.send({
+        command: 'reload',
+        path: file,
+        liveCSS: this.options.liveCSS !== false,
+        reloadMissingCSS: this.options.reloadMissingCSS !== false,
+        liveImg: this.options.liveImg !== false
+      });
+    }, this);
+  }
+
+  alert (message) {
+    this.send({
+      command: 'alert',
+      message: message
+    });
+  }
+
+  // Utilities
+  data (event) {
+    let data = {};
+    try {
+      data = JSON.parse(event.data);
+    } catch (e) {}
+    return data;
+  }
+
+  send (data) {
+    if (!this.ws) return;
+    this.ws.send(JSON.stringify(data));
+  }
+
+  uniqueId (prefix) {
+    let id = idCounter++;
+    return prefix ? prefix + id : id;
+  }
+}
diff --git a/node_modules/tiny-lr/lib/index.js b/node_modules/tiny-lr/lib/index.js
new file mode 100644
index 0000000..484c407
--- /dev/null
+++ b/node_modules/tiny-lr/lib/index.js
@@ -0,0 +1,46 @@
+import Server from './server';
+import Client from './client';
+
+const debug = require('debug')('tinylr');
+
+// Need to keep track of LR servers when notifying
+const servers = [];
+
+export default tinylr;
+
+// Expose Server / Client objects
+tinylr.Server = Server;
+tinylr.Client = Client;
+
+// and the middleware helpers
+tinylr.middleware = middleware;
+tinylr.changed = changed;
+
+// Main entry point
+function tinylr (opts) {
+  const srv = new Server(opts);
+  servers.push(srv);
+  return srv;
+}
+
+// A facade to Server#handle
+function middleware (opts) {
+  const srv = new Server(opts);
+  servers.push(srv);
+  return function tinylr (req, res, next) {
+    srv.handler(req, res, next);
+  };
+}
+
+// Changed helper, helps with notifying the server of a file change
+function changed (done) {
+  const files = [].slice.call(arguments);
+  if (typeof files[files.length - 1] === 'function') done = files.pop();
+  done = typeof done === 'function' ? done : () => {};
+  debug('Notifying %d servers - Files: ', servers.length, files);
+  servers.forEach(srv => {
+    const params = { params: { files: files } };
+    srv && srv.changed(params);
+  });
+  done();
+}
diff --git a/node_modules/tiny-lr/lib/server.js b/node_modules/tiny-lr/lib/server.js
new file mode 100644
index 0000000..2e74a55
--- /dev/null
+++ b/node_modules/tiny-lr/lib/server.js
@@ -0,0 +1,322 @@
+import fs      from 'fs';
+import http    from 'http';
+import https   from 'https';
+import events  from 'events';
+import {parse} from 'url';
+import Client  from './client';
+import config  from '../package.json';
+import anybody from 'body/any';
+import qs      from 'qs';
+
+const debug = require('debug')('tinylr:server');
+
+const CONTENT_TYPE = 'content-type';
+const FORM_TYPE = 'application/x-www-form-urlencoded';
+
+function buildRootPath (prefix = '/') {
+  let rootUrl = prefix;
+
+  // Add trailing slash
+  if (prefix[prefix.length - 1] !== '/') {
+    rootUrl = `${rootUrl}/`;
+  }
+
+  // Add leading slash
+  if (prefix[0] !== '/') {
+    rootUrl = `/${rootUrl}`;
+  }
+
+  return rootUrl;
+}
+
+class Server extends events.EventEmitter {
+  constructor (options = {}) {
+    super();
+
+    this.options = options;
+
+    options.livereload = options.livereload || require.resolve('livereload-js/dist/livereload.js');
+
+    // todo: change falsy check to allow 0 for random port
+    options.port = parseInt(options.port || 35729, 10);
+
+    if (options.errorListener) {
+      this.errorListener = options.errorListener;
+    }
+
+    this.rootPath = buildRootPath(options.prefix);
+
+    this.clients = {};
+    this.configure(options.app);
+    this.routes(options.app);
+  }
+
+  routes () {
+    if (!this.options.dashboard) {
+      this.on(`GET ${this.rootPath}`, this.index.bind(this));
+    }
+
+    this.on(`GET ${this.rootPath}changed`, this.changed.bind(this));
+    this.on(`POST ${this.rootPath}changed`, this.changed.bind(this));
+    this.on(`POST ${this.rootPath}alert`, this.alert.bind(this));
+    this.on(`GET ${this.rootPath}livereload.js`, this.livereload.bind(this));
+    this.on(`GET ${this.rootPath}kill`, this.close.bind(this));
+  }
+
+  configure (app) {
+    debug('Configuring %s', app ? 'connect / express application' : 'HTTP server');
+
+    let handler = this.options.handler || this.handler;
+
+    if (!app) {
+      if ((this.options.key && this.options.cert) || this.options.pfx) {
+        this.server = https.createServer(this.options, handler.bind(this));
+      } else {
+        this.server = http.createServer(handler.bind(this));
+      }
+
+      this.server.on('upgrade', this.websocketify.bind(this));
+      this.server.on('error', this.error.bind(this));
+      return this;
+    }
+
+    this.app = app;
+    this.app.listen = (port, done) => {
+      done = done || function () {};
+      if (port !== this.options.port) {
+        debug('Warn: LiveReload port is not standard (%d). You are listening on %d', this.options.port, port);
+        debug('You\'ll need to rely on the LiveReload snippet');
+        debug('> http://feedback.livereload.com/knowledgebase/articles/86180-how-do-i-add-the-script-tag-manually-');
+      }
+
+      let srv = this.server = http.createServer(app);
+      srv.on('upgrade', this.websocketify.bind(this));
+      srv.on('error', this.error.bind(this));
+      srv.on('close', this.close.bind(this));
+      return srv.listen(port, done);
+    };
+
+    return this;
+  }
+
+  handler (req, res, next) {
+    let middleware = typeof next === 'function';
+    debug('LiveReload handler %s (middleware: %s)', req.url, middleware ? 'on' : 'off');
+
+    next = next || this.defaultHandler.bind(this, res);
+    req.headers[CONTENT_TYPE] = req.headers[CONTENT_TYPE] || FORM_TYPE;
+    return anybody(req, res, (err, body) => {
+      if (err) return next(err);
+      req.body = body;
+
+      if (!req.query) {
+        req.query = req.url.indexOf('?') !== -1
+          ? qs.parse(parse(req.url).query)
+          : {};
+      }
+
+      return this.handle(req, res, next);
+    });
+  }
+
+  index (req, res) {
+    res.setHeader('Content-Type', 'application/json');
+    res.write(JSON.stringify({
+      tinylr: 'Welcome',
+      version: config.version
+    }));
+
+    res.end();
+  }
+
+  handle (req, res, next) {
+    let url = parse(req.url);
+    debug('Request:', req.method, url.href);
+    let middleware = typeof next === 'function';
+
+    // do the routing
+    let route = req.method + ' ' + url.pathname;
+    let respond = this.emit(route, req, res);
+    if (respond) return;
+
+    if (middleware) return next();
+
+    // Only apply content-type on non middleware setup #70
+    return this.notFound(res);
+  }
+
+  defaultHandler (res, err) {
+    if (!err) return this.notFound(res);
+
+    this.error(err);
+    res.setHeader('Content-Type', 'text/plain');
+    res.statusCode = 500;
+    res.end('Error: ' + err.stack);
+  }
+
+  notFound (res) {
+    res.setHeader('Content-Type', 'application/json');
+    res.writeHead(404);
+    res.write(JSON.stringify({
+      error: 'not_found',
+      reason: 'no such route'
+    }));
+    res.end();
+  }
+
+  websocketify (req, socket, head) {
+    let client = new Client(req, socket, head, this.options);
+    this.clients[client.id] = client;
+
+    // handle socket error to prevent possible app crash, such as ECONNRESET
+    socket.on('error', (e) => {
+      // ignore frequent ECONNRESET error (seems inevitable when refresh)
+      if (e.code === 'ECONNRESET') return;
+      this.error(e);
+    });
+
+    client.once('info', (data) => {
+      debug('Create client %s (url: %s)', data.id, data.url);
+      this.emit('MSG /create', data.id, data.url);
+    });
+
+    client.once('end', () => {
+      debug('Destroy client %s (url: %s)', client.id, client.url);
+      this.emit('MSG /destroy', client.id, client.url);
+      delete this.clients[client.id];
+    });
+  }
+
+  listen (port, host, fn) {
+    port = port || this.options.port;
+
+    // Last used port for error display
+    this.port = port;
+
+    if (typeof host === 'function') {
+      fn = host;
+      host = undefined;
+    }
+
+    this.server.listen(port, host, fn);
+  }
+
+  close (req, res) {
+    Object.keys(this.clients).forEach(function (id) {
+      this.clients[id].close();
+    }, this);
+
+    if (this.server._handle) this.server.close(this.emit.bind(this, 'close'));
+
+    if (res) res.end();
+  }
+
+  error (e) {
+    if (this.errorListener) {
+      this.errorListener(e);
+      return;
+    }
+
+    console.error();
+    if (typeof e === 'undefined') {
+      console.error('... Uhoh. Got error %s ...', e);
+    } else {
+      console.error('... Uhoh. Got error %s ...', e.message);
+      console.error(e.stack);
+
+      if (e.code !== 'EADDRINUSE') return;
+      console.error();
+      console.error('You already have a server listening on %s', this.port);
+      console.error('You should stop it and try again.');
+      console.error();
+    }
+  }
+
+  // Routes
+
+  livereload (req, res) {
+    res.setHeader('Content-Type', 'application/javascript');
+    fs.createReadStream(this.options.livereload).pipe(res);
+  }
+
+  changed (req, res) {
+    let files = this.param('files', req);
+
+    debug('Changed event (Files: %s)', files.join(' '));
+    let clients = this.notifyClients(files);
+
+    if (!res) return;
+
+    res.setHeader('Content-Type', 'application/json');
+    res.write(JSON.stringify({
+      clients: clients,
+      files: files
+    }));
+
+    res.end();
+  }
+
+  alert (req, res) {
+    let message = this.param('message', req);
+
+    debug('Alert event (Message: %s)', message);
+    let clients = this.alertClients(message);
+
+    if (!res) return;
+
+    res.setHeader('Content-Type', 'application/json');
+    res.write(JSON.stringify({
+      clients: clients,
+      message: message
+    }));
+
+    res.end();
+  }
+
+  notifyClients (files) {
+    let clients = Object.keys(this.clients).map(function (id) {
+      let client = this.clients[id];
+      debug('Reloading client %s (url: %s)', client.id, client.url);
+      client.reload(files);
+      return {
+        id: client.id,
+        url: client.url
+      };
+    }, this);
+
+    return clients;
+  };
+
+  alertClients (message) {
+    let clients = Object.keys(this.clients).map(function (id) {
+      let client = this.clients[id];
+      debug('Alert client %s (url: %s)', client.id, client.url);
+      client.alert(message);
+      return {
+        id: client.id,
+        url: client.url
+      };
+    }, this);
+
+    return clients;
+  }
+
+  // Lookup param from body / params / query.
+  param (name, req) {
+    let param;
+    if (req.body && req.body[name]) param = req.body[name];
+    else if (req.params && req.params[name]) param = req.params[name];
+    else if (req.query && req.query[name]) param = req.query[name];
+
+    // normalize files array
+    if (name === 'files') {
+      param = Array.isArray(param) ? param
+        : typeof param === 'string' ? param.split(/[\s,]/)
+        : [];
+    }
+
+    return param;
+  }
+}
+
+export default Server;