<commit_before>'use strict';

const log   = require('debug')('akasha:index');
const error = require('debug')('akasha:error-index');

const filez  = require('./filez');
const render = require('./render');
const util   = require('util');
const fs     = require('fs');
const path   = require('path');
const oembed = require('oembed');
const RSS    = require('rss');

exports.cache = require('./caching');

exports.Configuration = require('./Configuration');

exports.Plugin = require('./Plugin');

exports.Renderer = require('./Renderer');

exports.HTMLRenderer = require('./HTMLRenderer');

exports.render = function(config) { return render.render(config); };
exports.findRendererName = function(name) { return render.findRendererName(name); };
exports.findRendererPath = function(_path) { return render.findRendererPath(_path); };

exports.findRendersTo = filez.findRendersTo;

exports.partial = function(config, partial, attrs) {
    // find the partial
    // based on the partial format - render, using attrs
    // if okay - resolve(rendered) - else reject(err)

    var partialFname;
    var partialText;
    var renderer;

    if (!partial) return Promise.reject(new Error("No partial file name supplied"));

    return filez.find(config.partialDirs, partial)
    .then(partialDir => {
        if (!partialDir) throw new Error(`No partial directory found for ${partial}`);

        partialFname = path.join(partialDir, partial);

        renderer = render.findRendererPath(partialFname);

        // For .html partials, we won't find a Renderer and can
        // short-circuit the process by just reading the file.
        if (!renderer && partialFname.match(/\.html$/) != null) {
            return filez.readFile(partialDir, partial);
        }

        if (!renderer) throw new Error(`No renderer found for ${partialFname}`);
        if (!(renderer instanceof exports.HTMLRenderer)) {
            throw new Error(`Renderer for ${partial} must be HTMLRenderer`);
        }

        return filez.readFile(partialDir, partial);
    })
    .then(text => {
        partialText = text;
        return renderer ? renderer.render(partialText, attrs) : partialText;
    });
};

exports.partialSync = function(config, fname, metadata) {

    const renderer = render.findRendererPath(fname);
    if (!renderer && partialFname.match(/\.html$/) != null) {
        var fnamePartial = filez.findSync(config.partialDirs, fname);
        return fs.readFileSync(fnamePartial, 'utf8');
    }

    if (!renderer) {
        throw new Error(`No renderer for ${fname}`);
    }
    if (!(renderer instanceof exports.HTMLRenderer)) {
        throw new Error(`Renderer for ${partial} must be HTMLRenderer`);
    }

    var fnamePartial = filez.findSync(config.partialDirs, fname);

    // log(`partialSync fname=${fname} fnamePartial=${fnamePartial}`);
    if (fnamePartial === undefined) {
        throw new Error('NO FILE FOUND FOR PARTIAL ' + util.inspect(fname));
    }

    var text = fs.readFileSync(fnamePartial, 'utf8');

    return renderer.renderSync(text, metadata);
};

exports.indexChain = function(config, fname) {

    var ret = [];

    var findParents = function(config, fileName) {
        var newFileName;
        var parentDir;
        // log(`findParents ${fileName}`);
        if (path.dirname(fileName) === '.') {
            return Promise.resolve();
        } else {
            if (path.basename(fileName) === "index.html") {
                parentDir = path.dirname(path.dirname(fileName));
            } else {
                parentDir = path.dirname(fileName);
            }
            var lookFor = path.join(parentDir, "index.html");
            return filez.findRendersTo(config.documentDirs, lookFor)
            .then(found => {
                // log(util.inspect(foundDir));
                if (typeof found !== 'undefined') {
                    ret.push({ foundDir: found.foundDir, foundPath: found.foundPath, filename: lookFor });
                }
                return findParents(config, lookFor);
            });
        }
    };

    let renderer = exports.findRendererPath(fname);
    if (renderer) {
        fname = renderer.filePath(fname);
    }

    return filez.findRendersTo(config.documentDirs, fname)
    .then(found => {
        if (typeof found === 'undefined') {
            throw new Error(`Did not find directory for ${fname}`);
        }
        ret.push({ foundDir: found.foundDir, foundPath: found.foundPath, filename: fname });
        return findParents(config, fname);
    })
    .then(() => { return ret; })
    .catch(err => { error(err.stack); throw err; });
};

exports.documentSearch = function(config, options) {

    // Find all the documents, under rootPath if specified
    // Build up a useful object for each
    return new Promise((resolve, reject) => {
        globfs.operate(
        config.documentDirs,
        options.rootPath ? options.rootPath+"/**/*" : "**/*",
        (basedir, fpath, fini) => {
            fs.stat(path.join(basedir, fpath), (err, stat) => {
                if (err) return fini(err);
                var renderer = exports.findRendererPath(fpath);
                let path = renderer.filePath(fpath);
                fini(undefined, {
                    renderer, basedir, stat,
                    fpath, fname: path.basename(fpath),
                    path, name: path.basename(path),
                    metadata: renderer.metadata(basedir, fpath)
                });
            });
        },
        (err, results) => {
            if (err) reject(err);
            else resolve(results)
        });
    })
    // Then filter the list, potentially removing some items if they
    // do not validate against the filters specified in the options object.
    // These are separate .then calls even though it's not truly necessary.
    // This is for ease of implementation so each phase can be eliminated
    // or new phases added easily.
    .then(documents => {
        if (options.pathmatch) {
            return documents.filter(doc => {
                return doc.fpath.match(options.pathmatch) != null;
            });
        } else return documents;
    })
    .then(documents => {
        if (options.renderers) {
            return documents.filter(doc => {
                var match = false;
                for (let renderer of options.renderers) {
                    if (doc.renderer instanceof renderer) {
                        match = true;
                    }
                }
                return match;
            });
        } else return documents;
    })
    .then(documents => {
        if (options.layouts) {
            return documents.filter(doc => {
                var match = false;
                for (let layout of options.layouts) {
                    if (doc.metadata.layout === layout) {
                        match = true;
                    }
                }
                return match;
            });
        } else return documents;
    })
    .then(documents => {
        if (options.func) {
            return documents.filter(doc => {
                return func(config, options, doc);
            });
        } else return documents;
    });

};

///////////////// RSS Feed Generation

exports.generateRSS = function(config, configrss, feedData, items, renderTo) {

    return new Promise((resolve, reject) => {

        // Construct initial rss object
        var rss = {};
        for (var key in configrss) {
            if (configrss.hasOwnProperty(key)) {
                rss[key] = configrss[key];
            }
        }

        // Then fill in from feedData
        for (var key in feedData) {
            if (feedData.hasOwnProperty(key)) {
                rss[key] = feedData[key];
            }
        }

        var rssfeed = new RSS(rss);

        items.forEach(function(item) { rssfeed.item(item); });

        var xml = rssfeed.xml();
        var renderOut = path.join(config.root_out, renderTo);
        // logger.trace(renderOut +' ===> '+ xml);

        fs.mkdirs(path.dirname(renderOut), err => {
            if (err) { error(err); return reject(err); }
            fs.writeFile(renderOut, xml, { encoding: 'utf8' },
                err2 => {
                    if (err2) { error(err2); reject(err2); }
                    else resolve();
                });
        });
    });

}

// Consider making an external plugin
// https://www.npmjs.com/package/oembed-all
// https://www.npmjs.com/package/embedable
// https://www.npmjs.com/package/media-parser
// https://www.npmjs.com/package/oembetter
module.exports.oEmbedData = function(url) {
    return new Promise((resolve, reject) => {
        oembed.fetch(url, { maxwidth: 6000 },
        (err, result) => {
            if (err) return reject(err);
            else resolve(result);
        }
        );
    })
};

<commit_msg>Add globfs, fix searchDocuments<commit_after>'use strict';

const log   = require('debug')('akasha:index');
const error = require('debug')('akasha:error-index');

const filez  = require('./filez');
const render = require('./render');
const util   = require('util');
const fs     = require('fs');
const path   = require('path');
const oembed = require('oembed');
const RSS    = require('rss');
const globfs = require('globfs');

exports.cache = require('./caching');

exports.Configuration = require('./Configuration');

exports.Plugin = require('./Plugin');

exports.Renderer = require('./Renderer');

exports.HTMLRenderer = require('./HTMLRenderer');

exports.render = function(config) { return render.render(config); };
exports.findRendererName = function(name) { return render.findRendererName(name); };
exports.findRendererPath = function(_path) { return render.findRendererPath(_path); };

exports.findRendersTo = filez.findRendersTo;

exports.partial = function(config, partial, attrs) {
    // find the partial
    // based on the partial format - render, using attrs
    // if okay - resolve(rendered) - else reject(err)

    var partialFname;
    var partialText;
    var renderer;

    if (!partial) return Promise.reject(new Error("No partial file name supplied"));

    return filez.find(config.partialDirs, partial)
    .then(partialDir => {
        if (!partialDir) throw new Error(`No partial directory found for ${partial}`);

        partialFname = path.join(partialDir, partial);

        renderer = render.findRendererPath(partialFname);

        // For .html partials, we won't find a Renderer and can
        // short-circuit the process by just reading the file.
        if (!renderer && partialFname.match(/\.html$/) != null) {
            return filez.readFile(partialDir, partial);
        }

        if (!renderer) throw new Error(`No renderer found for ${partialFname}`);
        if (!(renderer instanceof exports.HTMLRenderer)) {
            throw new Error(`Renderer for ${partial} must be HTMLRenderer`);
        }

        return filez.readFile(partialDir, partial);
    })
    .then(text => {
        partialText = text;
        return renderer ? renderer.render(partialText, attrs) : partialText;
    });
};

exports.partialSync = function(config, fname, metadata) {

    const renderer = render.findRendererPath(fname);
    if (!renderer && partialFname.match(/\.html$/) != null) {
        var fnamePartial = filez.findSync(config.partialDirs, fname);
        return fs.readFileSync(fnamePartial, 'utf8');
    }

    if (!renderer) {
        throw new Error(`No renderer for ${fname}`);
    }
    if (!(renderer instanceof exports.HTMLRenderer)) {
        throw new Error(`Renderer for ${partial} must be HTMLRenderer`);
    }

    var fnamePartial = filez.findSync(config.partialDirs, fname);

    // log(`partialSync fname=${fname} fnamePartial=${fnamePartial}`);
    if (fnamePartial === undefined) {
        throw new Error('NO FILE FOUND FOR PARTIAL ' + util.inspect(fname));
    }

    var text = fs.readFileSync(fnamePartial, 'utf8');

    return renderer.renderSync(text, metadata);
};

exports.indexChain = function(config, fname) {

    var ret = [];

    var findParents = function(config, fileName) {
        var newFileName;
        var parentDir;
        // log(`findParents ${fileName}`);
        if (path.dirname(fileName) === '.') {
            return Promise.resolve();
        } else {
            if (path.basename(fileName) === "index.html") {
                parentDir = path.dirname(path.dirname(fileName));
            } else {
                parentDir = path.dirname(fileName);
            }
            var lookFor = path.join(parentDir, "index.html");
            return filez.findRendersTo(config.documentDirs, lookFor)
            .then(found => {
                // log(util.inspect(foundDir));
                if (typeof found !== 'undefined') {
                    ret.push({ foundDir: found.foundDir, foundPath: found.foundPath, filename: lookFor });
                }
                return findParents(config, lookFor);
            });
        }
    };

    let renderer = exports.findRendererPath(fname);
    if (renderer) {
        fname = renderer.filePath(fname);
    }

    return filez.findRendersTo(config.documentDirs, fname)
    .then(found => {
        if (typeof found === 'undefined') {
            throw new Error(`Did not find directory for ${fname}`);
        }
        ret.push({ foundDir: found.foundDir, foundPath: found.foundPath, filename: fname });
        return findParents(config, fname);
    })
    .then(() => { return ret; })
    .catch(err => { error(err.stack); throw err; });
};

exports.documentSearch = function(config, options) {

    // Find all the documents, under rootPath if specified
    // Build up a useful object for each
    return new Promise((resolve, reject) => {
        globfs.operate(
        config.documentDirs,
        options.rootPath ? options.rootPath+"/**/*" : "**/*",
        (basedir, fpath, fini) => {
            fs.stat(path.join(basedir, fpath), (err, stat) => {
                if (err) return fini(err);
                var renderer = exports.findRendererPath(fpath);
                let filepath = renderer ? renderer.filePath(fpath) : undefined;
                fini(undefined, {
                    renderer, basedir, stat,
                    fpath, fname: path.basename(fpath),
                    path,
                    name: filepath ? path.basename(filepath) : path.basename(fpath),
                    metadata: renderer ? renderer.metadata(basedir, fpath) : undefined
                });
            });
        },
        (err, results) => {
            if (err) reject(err);
            else resolve(results)
        });
    })
    // Then filter the list, potentially removing some items if they
    // do not validate against the filters specified in the options object.
    // These are separate .then calls even though it's not truly necessary.
    // This is for ease of implementation so each phase can be eliminated
    // or new phases added easily.
    .then(documents => {
        if (options.pathmatch) {
            return documents.filter(doc => {
                return doc.fpath.match(options.pathmatch) != null;
            });
        } else return documents;
    })
    .then(documents => {
        if (options.renderers) {
            return documents.filter(doc => {
                var match = false;
                for (let renderer of options.renderers) {
                    if (doc.renderer instanceof renderer) {
                        match = true;
                    }
                }
                return match;
            });
        } else return documents;
    })
    .then(documents => {
        if (options.layouts) {
            return documents.filter(doc => {
                var match = false;
                for (let layout of options.layouts) {
                    if (doc.metadata.layout === layout) {
                        match = true;
                    }
                }
                return match;
            });
        } else return documents;
    })
    .then(documents => {
        if (options.func) {
            return documents.filter(doc => {
                return func(config, options, doc);
            });
        } else return documents;
    });

};

///////////////// RSS Feed Generation

exports.generateRSS = function(config, configrss, feedData, items, renderTo) {

    return new Promise((resolve, reject) => {

        // Construct initial rss object
        var rss = {};
        for (var key in configrss) {
            if (configrss.hasOwnProperty(key)) {
                rss[key] = configrss[key];
            }
        }

        // Then fill in from feedData
        for (var key in feedData) {
            if (feedData.hasOwnProperty(key)) {
                rss[key] = feedData[key];
            }
        }

        var rssfeed = new RSS(rss);

        items.forEach(function(item) { rssfeed.item(item); });

        var xml = rssfeed.xml();
        var renderOut = path.join(config.root_out, renderTo);
        // logger.trace(renderOut +' ===> '+ xml);

        fs.mkdirs(path.dirname(renderOut), err => {
            if (err) { error(err); return reject(err); }
            fs.writeFile(renderOut, xml, { encoding: 'utf8' },
                err2 => {
                    if (err2) { error(err2); reject(err2); }
                    else resolve();
                });
        });
    });

}

// Consider making an external plugin
// https://www.npmjs.com/package/oembed-all
// https://www.npmjs.com/package/embedable
// https://www.npmjs.com/package/media-parser
// https://www.npmjs.com/package/oembetter
module.exports.oEmbedData = function(url) {
    return new Promise((resolve, reject) => {
        oembed.fetch(url, { maxwidth: 6000 },
        (err, result) => {
            if (err) return reject(err);
            else resolve(result);
        }
        );
    })
};

<|endoftext|>


<commit_before>'use strict';
var EventEmitter = require('events').EventEmitter
var sqlite3      = require('sqlite3')
var inherits     = require('inherits')
var Readable     = require('stream').Readable

var adapter = exports

adapter.name = 'sqlite3'

adapter.verbose = sqlite3.verbose;

adapter.createQuery = function (text, values, callback) {
  if (text instanceof SQLite3Query) return text
  return new SQLite3Query(text, values, callback)
}

adapter.createConnection = function (opts, callback) {
  var filename;

  if (opts.host) {
    filename = opts.host + (opts.database || '')
  } else {
    filename = opts.database
  }

  if (!filename || filename == '/:memory') filename = ':memory:'

  var mode = Object.keys(opts).map(function (k) {
    return (k == k.toUpperCase() && sqlite3[k]) | 0;
  }).reduce(function (mode, flag) {
    return mode | flag;
  }, 0)

  if (!mode) mode = (sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE)

  var db = new sqlite3.Database(filename, mode)
  db.serialize()
  return new SQLite3Connection(db, callback)
}

inherits(SQLite3Connection, EventEmitter)
function SQLite3Connection(db, callback) {
  EventEmitter.call(this)
  this._db = db
  var self = this
  this._db.on('open',  function () { self.emit('open') })
  this._db.on('close', function ()    { self.emit('end') })
  this._db.on('error', function (err) { self.emit('error', err) })

  if (callback) {
    self.once('error', callback)
    self.once('open', function handleOpenEvent () {
      self.removeListener('error', callback)
      callback(null, self)
    })
  }
}

SQLite3Connection.prototype.adapter = adapter

SQLite3Connection.prototype.query = function (text, values, callback) {
  var query = adapter.createQuery(text, values, callback)

  this.emit('query', query)

  if (query.text.match(/^\s*(insert|update)\s+/i)) {
    this._db.run(query.text,
                 query.values,
                 function (err) { query.complete(err, this.changes, this.lastID) })
  } else {
    this._db.each(query.text,
                  query.values,
                  query.onRow.bind(query),
                  query.complete.bind(query))
  }

  return query
}

SQLite3Connection.prototype.end = function (callback) {
  if (callback) this.on('end', callback)
  this._db.close()
}

inherits(SQLite3Query, Readable)
function SQLite3Query(text, values, callback) {
  Readable.call(this, {objectMode: true})
  this.text = text
  this._fields = null
  this._result = { rows: [] }
  this._errored = false
  if (typeof values == 'function') {
    callback = values
    values = []
  }
  this.values = values || []
  if (this.callback = callback) {
    this.on('error', this.callback).on('data', function (row) {
      this._result.rows.push(row)
    })
  }
}

SQLite3Query.prototype._read = function () {}

SQLite3Query.prototype.onRow = function (err, row) {
  if (this._errored) return
  this._gotData = true
  if (err) {
    this._errored = true
    this.emit('close')
    this.emit('error', err)
  }
  if (!this._result.fields) {
    this._result.fields = Object.keys(row).map(function (name) {
      return { name: name }
    })
    this.emit('fields', this._result.fields)
  }
  this.push(row)
}

SQLite3Query.prototype.complete = function (err, count, lastId) {
  this.push(null)
  if (this._errored) return // we've emitted an error from the row callback
  if (!err && !this._gotData) this.emit('fields', [])
  this.emit('close')
  if (err) return this.emit('error', err)
  this._result.rowCount = count
  this._result.lastInsertId = lastId
  if (this.callback) {
    this.callback(null, this._result)
  }
}
<commit_msg>Get the number of affected rows<commit_after>'use strict';
var EventEmitter = require('events').EventEmitter
var sqlite3      = require('sqlite3')
var inherits     = require('inherits')
var Readable     = require('stream').Readable

var adapter = exports

adapter.name = 'sqlite3'

adapter.verbose = sqlite3.verbose;

adapter.createQuery = function (text, values, callback) {
  if (text instanceof SQLite3Query) return text
  return new SQLite3Query(text, values, callback)
}

adapter.createConnection = function (opts, callback) {
  var filename;

  if (opts.host) {
    filename = opts.host + (opts.database || '')
  } else {
    filename = opts.database
  }

  if (!filename || filename == '/:memory') filename = ':memory:'

  var mode = Object.keys(opts).map(function (k) {
    return (k == k.toUpperCase() && sqlite3[k]) | 0;
  }).reduce(function (mode, flag) {
    return mode | flag;
  }, 0)

  if (!mode) mode = (sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE)

  var db = new sqlite3.Database(filename, mode)
  db.serialize()
  return new SQLite3Connection(db, callback)
}

inherits(SQLite3Connection, EventEmitter)
function SQLite3Connection(db, callback) {
  EventEmitter.call(this)
  this._db = db
  var self = this
  this._db.on('open',  function () { self.emit('open') })
  this._db.on('close', function ()    { self.emit('end') })
  this._db.on('error', function (err) { self.emit('error', err) })

  if (callback) {
    self.once('error', callback)
    self.once('open', function handleOpenEvent () {
      self.removeListener('error', callback)
      callback(null, self)
    })
  }
}

SQLite3Connection.prototype.adapter = adapter

SQLite3Connection.prototype.query = function (text, values, callback) {
  var query = adapter.createQuery(text, values, callback)

  this.emit('query', query)

  if (query.text.match(/^\s*insert|update|delete\s+/i))
    this._db.run(query.text,
                 query.values,
                 function (err) {
                   query.complete(err, this.changes, this.lastID)
                 })
  } else {
    this._db.each(query.text,
                  query.values,
                  query.onRow.bind(query),
                  query.complete.bind(query))
  }

  return query
}

SQLite3Connection.prototype.end = function (callback) {
  if (callback) this.on('end', callback)
  this._db.close()
}

inherits(SQLite3Query, Readable)
function SQLite3Query(text, values, callback) {
  Readable.call(this, {objectMode: true})
  this.text = text
  this._fields = null
  this._result = { rows: [] }
  this._errored = false
  if (typeof values == 'function') {
    callback = values
    values = []
  }
  this.values = values || []
  if (this.callback = callback) {
    this.on('error', this.callback).on('data', function (row) {
      this._result.rows.push(row)
    })
  }
}

SQLite3Query.prototype._read = function () {}

SQLite3Query.prototype.onRow = function (err, row) {
  if (this._errored) return
  this._gotData = true
  if (err) {
    this._errored = true
    this.emit('close')
    this.emit('error', err)
  }
  if (!this._result.fields) {
    this._result.fields = Object.keys(row).map(function (name) {
      return { name: name }
    })
    this.emit('fields', this._result.fields)
  }
  this.push(row)
}

SQLite3Query.prototype.complete = function (err, count, lastId, affectedRows) {
  this.push(null)
  if (this._errored) return // we've emitted an error from the row callback
  if (!err && !this._gotData) this.emit('fields', [])
  this.emit('close')
  if (err) return this.emit('error', err)
  this._result.rowCount = count
  this._result.lastInsertId = lastId
  if(affectedRows) {
    this._result.affectedRows = affectedRows
    this._result.changedRows = affectedRows
  }

  if (this.callback) {
    this.callback(null, this._result)
  }
}
<|endoftext|>


<commit_before>var loggingKey = require('./package.json').name;

module.exports = coerce;

function coerce(args) {
  if (!args) {
    throw new Error(loggingKey + ': missing args argument');
  }

  if (!Array.isArray(args.parameters)) {
    throw new Error(loggingKey + ': args.parameters must be an Array');
  }

  var coerceHeaders = buildCoercer(args.parameters.filter(byLocation('header')),
      true);
  var coerceParams = buildCoercer(args.parameters.filter(byLocation('path')));
  var coerceQuery = buildCoercer(args.parameters.filter(byLocation('query')));

  return function(req, res, next) {
    if (req.headers && coerceHeaders) {
      coerceHeaders(req.headers);
    }

    if (req.params && coerceParams) {
      coerceParams(req.params);
    }

    if (req.query && coerceQuery) {
      coerceQuery(req.query);
    }

    next();
  };
}

var COERCION_STRATEGIES = {
  array: function(itemCoercer, input) {
    if (Array.isArray(input)) {
      input.forEach(function(v, i) {
        input[i] = itemCoercer(v);
      });

      return input;
    } else {
      return [itemCoercer(input)];
    }
  },

  boolean: function(input) {
    if (input === 'false') {
      return false;
    } else {
      return true;
    }
  },

  integer: function(input) {
    return Math.floor(Number(input));
  },

  number: function(input) {
    return Number(input);
  },

  string: function(input) {
    return String(input);
  }
};

function buildCoercer(params, isHeaders) {
  var coercion;
  var l = isHeaders ?
      function(name) {
        return name.toLowerCase();
      } :
      function(name) {
        return name;
      };

  if (params.length) {
    var coercers = {};

    params.forEach(function(param) {
      var name = param.name;
      var coercer;
      var itemCoercer;
      var type = param.type;

      if (type === 'array') {
        if (!param.items) {
          throw new Error(loggingKey + ': items is a required property with type array');
        }

        if (param.items.type === 'array') {
          throw new Error(loggingKey + ': nested arrays are not allowed (items was of type array)');
        }

        itemCoercer = getCoercer(param.items.type);

        coercer = COERCION_STRATEGIES.array.bind(null, itemCoercer);
      } else {
        coercer = getCoercer(param.type);
      }

      if (coercer) {
        coercers[l(name)] = coercer;
      }
    });

    coercion = function(obj) {
      for (var paramName in obj) {
        if (paramName in coercers) {
          obj[paramName] = coercers[paramName](obj[paramName]);
        }
      }
    };
  }

  return coercion;
}

function byLocation(location) {
  return function(param) {
    return param.in === location;
  };
}

function getCoercer(type) {
  return COERCION_STRATEGIES[type];
}
<commit_msg>Added support for collectionFormat to array coercion<commit_after>var loggingKey = require('./package.json').name;

module.exports = coerce;

function coerce(args) {
  if (!args) {
    throw new Error(loggingKey + ': missing args argument');
  }

  if (!Array.isArray(args.parameters)) {
    throw new Error(loggingKey + ': args.parameters must be an Array');
  }

  var coerceHeaders = buildCoercer(args.parameters.filter(byLocation('header')),
      true);
  var coerceParams = buildCoercer(args.parameters.filter(byLocation('path')));
  var coerceQuery = buildCoercer(args.parameters.filter(byLocation('query')));

  return function(req, res, next) {
    if (req.headers && coerceHeaders) {
      coerceHeaders(req.headers);
    }

    if (req.params && coerceParams) {
      coerceParams(req.params);
    }

    if (req.query && coerceQuery) {
      coerceQuery(req.query);
    }

    next();
  };
}

var COERCION_STRATEGIES = {
  array: function(itemCoercer, collectionFormat, input) {
    if (!Array.isArray(input)) {
      var sep = pathsep(collectionFormat || 'csv');
      input = input.split(sep);
    }

    if (Array.isArray(input)) {
      input.forEach(function(v, i) {
        input[i] = itemCoercer(v);
      });

      return input;
    } else {
      return [itemCoercer(input)];
    }
  },

  boolean: function(input) {
    if (input === 'false') {
      return false;
    } else {
      return true;
    }
  },

  integer: function(input) {
    return Math.floor(Number(input));
  },

  number: function(input) {
    return Number(input);
  },

  string: function(input) {
    return String(input);
  }
};

function buildCoercer(params, isHeaders) {
  var coercion;
  var l = isHeaders ?
      function(name) {
        return name.toLowerCase();
      } :
      function(name) {
        return name;
      };

  if (params.length) {
    var coercers = {};

    params.forEach(function(param) {
      var name = param.name;
      var coercer;
      var itemCoercer;
      var type = param.type;

      if (type === 'array') {
        if (!param.items) {
          throw new Error(loggingKey + ': items is a required property with type array');
        }

        if (param.items.type === 'array') {
          throw new Error(loggingKey + ': nested arrays are not allowed (items was of type array)');
        }

        itemCoercer = getCoercer(param.items.type);

        coercer = COERCION_STRATEGIES.array.bind(null, itemCoercer, param.collectionFormat);
      } else {
        coercer = getCoercer(param.type);
      }

      if (coercer) {
        coercers[l(name)] = coercer;
      }
    });

    coercion = function(obj) {
      for (var paramName in obj) {
        if (paramName in coercers) {
          obj[paramName] = coercers[paramName](obj[paramName]);
        }
      }
    };
  }

  return coercion;
}

function byLocation(location) {
  return function(param) {
    return param.in === location;
  };
}

function getCoercer(type) {
  return COERCION_STRATEGIES[type];
}

function pathsep(format) {
  switch (format) {
    case 'csv':
      return ',';
    case 'ssv':
      return ' ';
    case 'tsv':
      return '\t';
    case 'pipes':
      return '|';
    case 'multi':
      return '&';
  }
}
<|endoftext|>


<commit_before>#!/usr/bin/env node

var _ = require('underscore');
var request = require('request');
var fs = require('fs');
var Duration = require('duration');

var mkdirp = require('mkdirp');
var root = process.env.HOME + '/.partyplay-client';
mkdirp.sync(root + '/temp');
mkdirp.sync(root + '/playlists');

var tempResultsPath = root + '/temp/searchresults.json';

var userConfig = require(process.env.HOME + '/.partyplayConfig.js');
var defaultConfig = require(__dirname + '/partyplayConfigDefaults.js');
var config = _.defaults(userConfig, defaultConfig);

var tlsOpts = {
    key: fs.readFileSync(config.tlsKey),
    cert: fs.readFileSync(config.tlsCert),
    ca: fs.readFileSync(config.tlsCa),
    rejectUnauthorized: config.rejectUnauthorized
};

var usageText = '';
usageText += 'show and manipulate the partyplay queue.\n\n';
usageText += 'commands\n';
usageText += '========\n';
usageText += 'search for songs:\n';
usageText += '  -l            show queue (default action)\n';
usageText += '  -p            list playlists\n';
usageText += '  -p [ID]       list contents of playlist ID\n';
usageText += '  -s [QUERY]    perform search matching QUERY\n';
usageText += 'manipulate playback/queue:\n';
usageText += '  -a [ID]       append song with ID\n';
usageText += '  -d [ID]       delete song with ID\n';
usageText += '  -g [CNT]      skip CNT songs, can be negative to go back\n';
usageText += 'misc:\n';
usageText += '  -n            show now playing song\n';
usageText += '  -h            show this help and quit\n';

var yargs = require('yargs')
    .boolean('s');
var argv = yargs.argv;
if(argv.h) {
    console.log(usageText);
    process.exit(0);
}

var printSong = function(song, id) {
    if(!_.isUndefined(id)) {
        process.stdout.write('  ' + id + ': ');
    }
    console.log(song.artist + ' - ' + song.title + ' (' + song.album + ')');
};

var onexit = function() {
    process.stdout.write('\x1b[?25h'); // enable cursor
    process.exit();
};

var parseRange = function(str) {
    var res = str.match(/(\d*)\.\.(\d*)/);
    if(!res)
        return null;

    var range = [];

    range[0] = res[1] || 0;
    range[1] = res[2] || 9999999999999;

    return range;
};

var url = config.hostname + ':' + config.port;

if (argv.h) {
    console.log(usageText);
} else if (argv.s) {
    request.post({
        url: url + '/search',
        json: {terms: argv._.join(' ')},
        agentOptions: tlsOpts
    }, function(err, res, body) {
        if(!err) {
            var results = [];

            var id = 0;
            _.each(body, function(backend, backendName) {
                console.log(backendName + ':');
                _.each(backend.songs, function(song) {
                    printSong(song, id);
                    results.push(song);
                    id++;
                });
            });

            fs.writeFileSync(tempResultsPath, JSON.stringify(results));
        } else {
            console.log('error: ' + err);
        }
    });
} else if (!_.isUndefined(argv.g)) {
    // "goto"
    if(argv.g === true)
        argv.g = 1;

    request.post({
        url: url + '/playctl',
        json: {
            action: 'skip',
            cnt: argv.g
        },
        agentOptions: tlsOpts
    }, function(err, res, body) {
        console.log(body);
    });
} else if (!_.isUndefined(argv.d)) {
    var cnt = 1;
    var range;
    var start = argv.d;
    if(argv.d === true)
        start = 0;

    if(_.isString(argv.d))
        range = parseRange(argv.d);
    if(range) {
        start = range[0];
        cnt = range[1] - range[0] + 1;
    }
    request.del({
        url: url + '/queue/' + start,
        json: {
            cnt: cnt
        },
        agentOptions: tlsOpts
    }, function(err, res, songs) {
        console.log("deleted songs:");
        _.each(songs, function(song) {
            printSong(song);
        });
    });
} else if (!_.isUndefined(argv.a)) {
    if(fs.existsSync(tempResultsPath)) {
        var tempResults = require(tempResultsPath);
        var matches = [];
        var range;

        if(_.isString(argv.a))
            range = parseRange(argv.a);

        if(argv.a === true) {
            // entire playlist
            matches = tempResults;
        } else if(range) {
            // x..y range
            matches = tempResults.filter(function(song, i) {
                return (i >= range[0] && i <= range[1]);
            });
        } else {
            // by id
            matches = [tempResults[argv.a]];
        }

        request.post({
            url: url + '/queue',
            json: {songs: matches},
            agentOptions: tlsOpts
        }, function(err, res, body) {
            if(!err) {
                console.log('songs queued:');
                _.each(matches, function(song) {
                    printSong(song);
                });
            } else {
                console.log('error: ' + err);
            }
        });
    } else {
        console.log('no search results');
    }
} else if(!_.isUndefined(argv.p)) {
    // without parameters: list all playlists
    if(argv.p === true) {
        var playlists = fs.readdirSync(root + '/playlists');
        playlists.sort();

        var id = 0;
        _.each(playlists, function(playlistName) {
            console.log('  ' + id + ': ' + playlistName);
            id++;
        });
    } else {
        var playlists = fs.readdirSync(root + '/playlists');
        playlists.sort();

        // loop over playlists to find the requested one
        var playlist;
        var id = 0;
        _.each(playlists, function(playlistName) {
            if(id === argv.p) {
                playlist = require(root + '/playlists/' + playlistName);
            }
            id++;
        });

        // loop over songs (in reverse order) and print them
        playlist.reverse();
        id = 0;
        _.each(playlist, function(song) {
            printSong(song, id);
            id++;
        });

        // store song list
        fs.writeFileSync(tempResultsPath, JSON.stringify(playlist));
    }
} else if(argv.n) {
    var socket = require('socket.io-client')(config.hostname + ':' + config.port, tlsOpts);
    var zpad = require('zpad');
    var npInterval = null;
    var playbackInfo = {};
    var playbackInfoTime = 0;

    process.stdin.setRawMode(true); // hide input
    process.stdout.write('\x1b[?25l'); // hide cursor
    process.on('SIGINT', onexit);
    // q or ctrl-c pressed: run onexit
    process.stdin.on('data', function(key) {
        if(key == 'q' || key == '\u0003') onexit();
    });

    var printSongTime = function() {
        process.stdout.write('\r');
        process.stdout.write('\033[2K');

        if(!_.isEmpty(playbackInfo)) {
            var curTime = new Date().getTime();
            var position = new Duration(new Date(playbackInfoTime), new Date(curTime + (playbackInfo.position || 0)));
            var duration = new Duration(new Date(curTime), new Date(curTime + parseInt(playbackInfo.duration)));

            process.stdout.write('[');
            process.stdout.write(String(position.minutes) + ':' + zpad(position.seconds % 60, 2));
            process.stdout.write('/');
            process.stdout.write(String(duration.minutes) + ':' + zpad(duration.seconds % 60, 2));
            process.stdout.write(']');
        } else {
            process.stdout.write('[0:00/0:00]');
        }
    };

    socket.on('playback', function(newPlaybackInfo) {
        playbackInfo = newPlaybackInfo;
        playbackInfoTime = new Date().getTime();
    });
    socket.on('queue', function(queue) {
        if(npInterval)
            clearInterval(npInterval);

        var nowPlaying = queue[0];
        queue.shift();
        queue.reverse();

        process.stdout.write('\u001B[2J\u001B[0;0f'); // clear terminal
        var id = queue.length; // note we already shifted nowPlaying out
        _.each(queue, function(song) {
            printSong(song, id);
            id--;
        });
        console.log('--- Queue ---\n');

        process.stdout.write('Now playing: ');
        if(nowPlaying)
            printSong(nowPlaying);

        if(queue.length || nowPlaying) {
            npInterval = setInterval(function() {
                printSongTime();
            }, 1000);
        }
        printSongTime();
    });
} else {
    request.get({
        url: url + '/queue',
        agentOptions: tlsOpts
    }, function(err, res, body) {
        if(err) {
            console.log(err);
            return;
        }
        console.log('Queue:');
        if(body) {
            var queue = JSON.parse(body);
            queue.reverse();
            var id = queue.length - 1;
            _.each(queue, function(song) {
                printSong(song, id);
                id--;
            });
        }
    });
}
<commit_msg>feature: store current queue on disk<commit_after>#!/usr/bin/env node

var _ = require('underscore');
var request = require('request');
var fs = require('fs');
var Duration = require('duration');

var mkdirp = require('mkdirp');
var root = process.env.HOME + '/.partyplay-client';
mkdirp.sync(root + '/temp');
mkdirp.sync(root + '/playlists');

var tempResultsPath = root + '/temp/searchresults.json';

var userConfig = require(process.env.HOME + '/.partyplayConfig.js');
var defaultConfig = require(__dirname + '/partyplayConfigDefaults.js');
var config = _.defaults(userConfig, defaultConfig);

var tlsOpts = {
    key: fs.readFileSync(config.tlsKey),
    cert: fs.readFileSync(config.tlsCert),
    ca: fs.readFileSync(config.tlsCa),
    rejectUnauthorized: config.rejectUnauthorized
};

var usageText = '';
usageText += 'show and manipulate the partyplay queue.\n\n';
usageText += 'commands\n';
usageText += '========\n';
usageText += 'search for songs:\n';
usageText += '  -l            show queue (default action)\n';
usageText += '  -p            list playlists\n';
usageText += '  -p [ID]       list contents of playlist ID\n';
usageText += '  -s [QUERY]    perform search matching QUERY\n';
usageText += 'manipulate playback/queue:\n';
usageText += '  -a [ID]       append song with ID\n';
usageText += '  -d [ID]       delete song with ID\n';
usageText += '  -g [CNT]      skip CNT songs, can be negative to go back\n';
usageText += 'misc:\n';
usageText += '  -n            show now playing song\n';
usageText += '  -w [FILENAME] write current playlist into FILENAME\n';
usageText += '  -h            show this help and quit\n';

var yargs = require('yargs')
    .boolean('s');
var argv = yargs.argv;
if(argv.h) {
    console.log(usageText);
    process.exit(0);
}

var printSong = function(song, id) {
    if(!_.isUndefined(id)) {
        process.stdout.write('  ' + id + ': ');
    }
    console.log(song.artist + ' - ' + song.title + ' (' + song.album + ')');
};

var onexit = function() {
    process.stdout.write('\x1b[?25h'); // enable cursor
    process.exit();
};

var parseRange = function(str) {
    var res = str.match(/(\d*)\.\.(\d*)/);
    if(!res)
        return null;

    var range = [];

    range[0] = res[1] || 0;
    range[1] = res[2] || 9999999999999;

    return range;
};

var url = config.hostname + ':' + config.port;

if (argv.h) {
    console.log(usageText);
} else if (argv.s) {
    request.post({
        url: url + '/search',
        json: {terms: argv._.join(' ')},
        agentOptions: tlsOpts
    }, function(err, res, body) {
        if(!err) {
            var results = [];

            var id = 0;
            _.each(body, function(backend, backendName) {
                console.log(backendName + ':');
                _.each(backend.songs, function(song) {
                    printSong(song, id);
                    results.push(song);
                    id++;
                });
            });

            fs.writeFileSync(tempResultsPath, JSON.stringify(results));
        } else {
            console.log('error: ' + err);
        }
    });
} else if (!_.isUndefined(argv.g)) {
    // "goto"
    if(argv.g === true)
        argv.g = 1;

    request.post({
        url: url + '/playctl',
        json: {
            action: 'skip',
            cnt: argv.g
        },
        agentOptions: tlsOpts
    }, function(err, res, body) {
        console.log(body);
    });
} else if (!_.isUndefined(argv.d)) {
    var cnt = 1;
    var range;
    var start = argv.d;
    if(argv.d === true)
        start = 0;

    if(_.isString(argv.d))
        range = parseRange(argv.d);
    if(range) {
        start = range[0];
        cnt = range[1] - range[0] + 1;
    }
    request.del({
        url: url + '/queue/' + start,
        json: {
            cnt: cnt
        },
        agentOptions: tlsOpts
    }, function(err, res, songs) {
        console.log("deleted songs:");
        _.each(songs, function(song) {
            printSong(song);
        });
    });
} else if (!_.isUndefined(argv.a)) {
    if(fs.existsSync(tempResultsPath)) {
        var tempResults = require(tempResultsPath);
        var matches = [];
        var range;

        if(_.isString(argv.a))
            range = parseRange(argv.a);

        if(argv.a === true) {
            // entire playlist
            matches = tempResults;
        } else if(range) {
            // x..y range
            matches = tempResults.filter(function(song, i) {
                return (i >= range[0] && i <= range[1]);
            });
        } else {
            // by id
            matches = [tempResults[argv.a]];
        }

        request.post({
            url: url + '/queue',
            json: {songs: matches},
            agentOptions: tlsOpts
        }, function(err, res, body) {
            if(!err) {
                console.log('songs queued:');
                _.each(matches, function(song) {
                    printSong(song);
                });
            } else {
                console.log('error: ' + err);
            }
        });
    } else {
        console.log('no search results');
    }
} else if(!_.isUndefined(argv.p)) {
    // without parameters: list all playlists
    if(argv.p === true) {
        var playlists = fs.readdirSync(root + '/playlists');
        playlists.sort();

        var id = 0;
        _.each(playlists, function(playlistName) {
            console.log('  ' + id + ': ' + playlistName);
            id++;
        });
    } else {
        var playlists = fs.readdirSync(root + '/playlists');
        playlists.sort();

        // loop over playlists to find the requested one
        var playlist;
        var id = 0;
        _.each(playlists, function(playlistName) {
            if(id === argv.p) {
                playlist = require(root + '/playlists/' + playlistName);
            }
            id++;
        });

        // loop over songs (in reverse order) and print them
        playlist.reverse();
        id = 0;
        _.each(playlist, function(song) {
            printSong(song, id);
            id++;
        });

        // store song list
        fs.writeFileSync(tempResultsPath, JSON.stringify(playlist));
    }
} else if(argv.n) {
    var socket = require('socket.io-client')(config.hostname + ':' + config.port, tlsOpts);
    var zpad = require('zpad');
    var npInterval = null;
    var playbackInfo = {};
    var playbackInfoTime = 0;

    process.stdin.setRawMode(true); // hide input
    process.stdout.write('\x1b[?25l'); // hide cursor
    process.on('SIGINT', onexit);
    // q or ctrl-c pressed: run onexit
    process.stdin.on('data', function(key) {
        if(key == 'q' || key == '\u0003') onexit();
    });

    var printSongTime = function() {
        process.stdout.write('\r');
        process.stdout.write('\033[2K');

        if(!_.isEmpty(playbackInfo)) {
            var curTime = new Date().getTime();
            var position = new Duration(new Date(playbackInfoTime), new Date(curTime + (playbackInfo.position || 0)));
            var duration = new Duration(new Date(curTime), new Date(curTime + parseInt(playbackInfo.duration)));

            process.stdout.write('[');
            process.stdout.write(String(position.minutes) + ':' + zpad(position.seconds % 60, 2));
            process.stdout.write('/');
            process.stdout.write(String(duration.minutes) + ':' + zpad(duration.seconds % 60, 2));
            process.stdout.write(']');
        } else {
            process.stdout.write('[0:00/0:00]');
        }
    };

    socket.on('playback', function(newPlaybackInfo) {
        playbackInfo = newPlaybackInfo;
        playbackInfoTime = new Date().getTime();
    });
    socket.on('queue', function(queue) {
        if(npInterval)
            clearInterval(npInterval);

        var nowPlaying = queue[0];
        queue.shift();
        queue.reverse();

        process.stdout.write('\u001B[2J\u001B[0;0f'); // clear terminal
        var id = queue.length; // note we already shifted nowPlaying out
        _.each(queue, function(song) {
            printSong(song, id);
            id--;
        });
        console.log('--- Queue ---\n');

        process.stdout.write('Now playing: ');
        if(nowPlaying)
            printSong(nowPlaying);

        if(queue.length || nowPlaying) {
            npInterval = setInterval(function() {
                printSongTime();
            }, 1000);
        }
        printSongTime();
    });
} else if(_.isString(argv.w)) {
    request.get({
        url: url + '/queue',
        agentOptions: tlsOpts
    }, function(err, res, body) {
        if(err) {
            console.log(err);
            return;
        }
        if(body) {
            var queue = body;
            fs.writeFileSync(root + '/playlists/' + argv.w + '.json', queue)
            console.log('playlist written into ' + argv.w + '.json');
        }
    });
} else {
    request.get({
        url: url + '/queue',
        agentOptions: tlsOpts
    }, function(err, res, body) {
        if(err) {
            console.log(err);
            return;
        }
        console.log('Queue:');
        if(body) {
            var queue = JSON.parse(body);
            queue.reverse();
            var id = queue.length - 1;
            _.each(queue, function(song) {
                printSong(song, id);
                id--;
            });
        }
    });
}
<|endoftext|>


<commit_before>var React = require('react');
var objectAssign = require('object-assign');

module.exports = React.createClass({
  displayName: 'TextAreaAutoSize',

  getInitialState: function() {
    if (this.props.onChange) {
      return {
        "_onChange": this.props.onChange
      };
    }
  },

  componentDidMount: function() {
    this.getDiffSize();
    this.recalculateSize();
  },

  componentWillReceiveProps: function(nextProps) {
    this.dirty = !!nextProps.style;
  },

  componentDidUpdate: function() {
    if (this.dirty) {
      this.getDiffSize();
      this.dirty = false;
    }
  },

  getDiffSize: function() {
    var styles = window.getComputedStyle(this.getDOMNode());

    // If the textarea is set to border-box, it's not necessary to
    // subtract the padding.
    if (styles.getPropertyValue('box-sizing') === "border-box" ||
        styles.getPropertyValue('-moz-box-sizing') === "border-box" ||
        styles.getPropertyValue('-webkit-box-sizing') === "border-box") {
      this.diff = 0;
      return;
    }

    this.diff  = (
      parseInt(styles.getPropertyValue('padding-bottom') || 0, 10) +
      parseInt(styles.getPropertyValue('padding-top') || 0, 10)
    );
  },

  recalculateSize: function() {
    if (!this.isMounted()) {
      return;
    }

    var node = this.getDOMNode();
    node.style.height = 'auto';
    node.style.height = (node.scrollHeight - this.diff) + 'px';
  },

  onChange: function(e) {
    if (this.state._onChange) {
      this.state._onChange(e);
    }

    this.recalculateSize();
  },

  render: function() {
    var props = objectAssign({}, this.props, {
      onChange: this.onChange,
      style: objectAssign({}, this.props.style, {overflow: 'hidden'})
    });

    return React.DOM.textarea(props, this.props.children);
  }
});
<commit_msg>Rearrange methods<commit_after>var React = require('react');
var objectAssign = require('object-assign');

module.exports = React.createClass({
  displayName: 'TextAreaAutoSize',

  render: function() {
    var props = objectAssign({}, this.props, {
      onChange: this.onChange,
      style: objectAssign({}, this.props.style, {overflow: 'hidden'})
    });

    return React.DOM.textarea(props, this.props.children);
  },

  getInitialState: function() {
    if (this.props.onChange) {
      return {
        "_onChange": this.props.onChange
      };
    }
  },

  componentDidMount: function() {
    this.getDiffSize();
    this.recalculateSize();
  },

  componentWillReceiveProps: function(nextProps) {
    this.dirty = !!nextProps.style;
  },

  componentDidUpdate: function() {
    if (this.dirty) {
      this.getDiffSize();
      this.dirty = false;
    }
  },

  onChange: function(e) {
    if (this.state._onChange) {
      this.state._onChange(e);
    }
    this.recalculateSize();
  },

  getDiffSize: function() {
    var styles = window.getComputedStyle(this.getDOMNode());

    // If the textarea is set to border-box, it's not necessary to
    // subtract the padding.
    if (styles.getPropertyValue('box-sizing') === "border-box" ||
        styles.getPropertyValue('-moz-box-sizing') === "border-box" ||
        styles.getPropertyValue('-webkit-box-sizing') === "border-box") {
      this.diff = 0;
      return;
    }

    this.diff  = (
      parseInt(styles.getPropertyValue('padding-bottom') || 0, 10) +
      parseInt(styles.getPropertyValue('padding-top') || 0, 10)
    );
  },

  recalculateSize: function() {
    if (!this.isMounted()) {
      return;
    }

    var node = this.getDOMNode();
    node.style.height = 'auto';
    node.style.height = (node.scrollHeight - this.diff) + 'px';
  }
});
<|endoftext|>


<commit_before>var promise = require( 'bluebird' );
var EventEmitter = require( 'events').EventEmitter;

var curId = Number.MIN_VALUE,
	idToKey = {},
	keyToId = {},
	roomData = {},
	roomUsers = {},
	publicRooms = [],
	keys;

module.exports = storeMemory;

/**
 * storeMemory is used to store data in memory on one process. This is not ideal
 * for clustered servers as each process may have different data. Use another
 * memory store if your server is clustered however use this memory store just
 * to get up and running fast or if your server is not clustered.
 *
 * @return {storeMemory} An new instance of storeMemory
 */
function storeMemory() {

	if( !( this instanceof storeMemory ) )
		return new storeMemory();

	EventEmitter.call( this );

	this.generateKeys();
}

var p = storeMemory.prototype = Object.create( EventEmitter.prototype );

/**
 * This will create a roomID, roomDataObject, and users array for the room
 *
 * @return {Promise} This promise will return a room id once the room is created
 */
p.createRoom = function( userID ) {

	var id = curId;

	curId++;

	if( curId == Number.MAX_VALUE )
		curId = Number.MIN_VALUE;

	roomUsers[ id ] = [];

	return this.setRoomData( id, {} )
		   .then( this.joinRoom.bind( this, userID, id ) )
		   .then( function() {

		   		return id;
		   });
};

/**
 * Join an existing room
 *
 * @param  {String} userID an id for the user whose joining
 * @param  {String} roomID an id for the room the user is joing
 * @return {Promise} A promise which will resolve and return a roomID
 */
p.joinRoom = function( userID, roomID ) {

	if( roomUsers[ roomID ] === undefined ) {

		return promise.reject( 'No room with the id: ' + roomID );
	} else {

		if( roomUsers[ roomID ].indexOf( userID ) != -1 ) {

			return promise.reject( 'User is already in room' );
		} else {

			roomUsers[ roomID ].push( userID );

			this.emit( roomID, {

				action: 'join',
				user: userID,
				users: roomUsers[ roomID ]
			});

			return promise.resolve( roomID );
		}
	}
};

/**
 * Remove a user from a room
 *
 * @param  {String} userID an id for the user whose leaving
 * @param  {String} roomID an id for the room the user is leaving
 * @return {Promise} When this promise resolves it will send the number of users in the room
 */
p.leaveRoom = function( userID, roomID ) {

	var userCount;

	if( roomUsers[ roomID ] === undefined ) {

		return promise.reject( 'No room with the id: ' + roomID );
	} else {

		var userIDX = roomUsers[ roomID ].indexOf( userID );

		if( userIDX != -1 ) {

			roomUsers[ roomID ].splice( userIDX, 1 );

			userCount = roomUsers[ roomID ].length;

			if( userCount == 0 ) {

				delete roomUsers[ roomID ];
			}

			this.emit( roomID, {

				action: 'leave',
				user: userID,
				users: roomUsers[ roomID ]
			});

			return promise.resolve( userCount );
		} else {

			return promise.reject( 'User ' + userID + ' is not in the room: ' + roomID );
		}
	}
};

/**
 * Get all users for a room
 *
 * @param  {String} roomID room id where to retrieve users
 * @return {Promise} A promise is returned which will return all users in a room
 */
p.getUsers = function( roomID ) {

	var setName = 'roomUsers:' + roomID;

	return promise.resolve( roomUsers[ roomID ] );
};

/**
 * get a key which can be used to enter a room vs entering room via
 * roomID
 *
 * @param  {String} roomID id of the room you'd like a key for
 * @return {Promise} A promise will be returned which will return a roomKey on success
 */
p.getKey = function( roomID ) {

	if( keys.length > 0 ) {

		var randIdx = Math.round( Math.random() * ( keys.length - 1 ) ),
			key = keys.splice( randIdx, 1 )[ 0 ];

		keyToId[ key ] = roomID;
		idToKey[ roomID ] = key;

		return promise.resolve( key );
	} else {

		return promise.reject( 'Run out of keys' );
	}
};

/**
 * return a room key so someone else can use it.
 *
 * @param  {String} roomID id of the room you'll be returning a key for
 * @param  {String} key the key you'd like to return
 * @return {Promise} This promise will succeed when the room key was returned
 */
p.returnKey = function( roomID, key ) {

	return this.getRoomIdForKey( key )
	.then( function( savedRoomId ) {

		if( savedRoomId == roomID ) {

			delete idToKey[ roomID ];
			delete keyToId[ key ];

			keys.push( key );

			return promise.resolve();
		} else {

			return promise.reject( 'roomID and roomID for key do not match' );
		}
	});
};

/**
 * return the room id for the given key
 *
 * @param  {String} key key used to enter the room
 * @return {Promise} This promise will succeed with the room id and fail if no room id exists for key
 */
p.getRoomIdForKey = function( key ) {

	var savedRoomId = keyToId[ key ];

	if( savedRoomId ) {

		return promise.resolve( savedRoomId );
	} else {

		return promise.reject();
	}
};

/**
 * set a variable on the rooms data object
 *
 * @param {String} roomID id for the room whose
 * @param {String} key variable name/key that you want to set
 * @param {*} value Value you'd like to set for the variable
 * @return {Promise} once this promise succeeds the rooms variable will be set
 */
p.setRoomDataVar = function( roomID, key, value ) {

	if( roomData[ roomID ] === undefined )
		roomData[ roomID ] = {};

	roomData[ roomID ][ key ] = value;

	return promise.resolve( value );
};

/**
 * get a variable from the rooms data object
 *
 * @param {String} roomID id for the room
 * @param {String} key variable name/key that you want to get
 * @return {Promise} once this promise succeeds it will return the variable value it will fail if the variable does not exist
 */
p.getRoomDataVar = function( roomID, key ) {

	if( roomData[ roomID ] === undefined ) {

		return promise.reject( 'There is no room by that id' );
	} else {

		return promise.resolve( roomData[ roomID ][ key ] );
	}
};

/**
 * delete a variable from the rooms data object
 *
 * @param {String} roomID id for the room
 * @param {String} key variable name/key that you want to delete
 * @return {Promise} once this promise succeeds it will return the value that was stored before delete
 */
p.delRoomDataVar = function( roomID, key ) {

	var oldVal;

	if( roomData[ roomID ] === undefined ) {

		return promise.reject( 'There is no room by that id' );
	} else {

		oldVal = roomData[ roomID ][ key ];

		delete roomData[ roomID ][ key ];

		return promise.resolve( oldVal );
	}
};

/**
 * Receive the data stored for a room.
 *
 * @param  {String} roomID id for the room you'd like data for
 * @return {Promise} This promise will succeed when data is received for the room
 */
p.getRoomData = function( roomID ) {

	if( roomData[ roomID ] )
		return promise.resolve( roomData[ roomID ] );
	else
		return promise.reject( 'No room data' );
};

/**
 * Set data stored for a room.
 *
 * @param  {String} roomID id for the room you'd like to set data for
 * @return {Promise} This promise will succeed when data is set for the room
 */
p.setRoomData = function( roomID, data ) {

	roomData[ roomID ] = data;

	return promise.resolve( data );
};

/**
 * Set room as public
 *
 * @param  {String} roomID id for the room you'd like to make public
 * @return {Promise} This promise will succeed when the room as been set as public
 */
p.makeRoomPublic = function( roomID ) {

	if( roomData[ roomID ] === undefined ) {

		return promise.reject( 'There is no room by that id' );
	} else {
		publicRooms.push( roomID );
		return promise.resolve( );
	}
};

/**
 * Gets first first available public room
 *
 * @return {Promise} This promise will succeed when the room has been retrieved
 */
p.getPublicRoom = function() {

	if ( publicRooms.length > 0 ) {

		return promise.resolve( publicRooms.shift() );
	} else {

		return promise.reject( 'No available public rooms' );
	}

};

/**
 * generateKeys is a function which will create and store a set of
 * keys which can be used to enter a room instead of a room id
 */
p.generateKeys = function() {

	if( !keys ) {

		keys = [];

		for( var i = 0; i < 9999; i++ ) {

			keys.push( i );
		}
	}
};<commit_msg>Change to manually make private again<commit_after>var promise = require( 'bluebird' );
var EventEmitter = require( 'events').EventEmitter;

var curId = Number.MIN_VALUE,
	idToKey = {},
	keyToId = {},
	roomData = {},
	roomUsers = {},
	publicRooms = [],
	keys;

module.exports = storeMemory;

/**
 * storeMemory is used to store data in memory on one process. This is not ideal
 * for clustered servers as each process may have different data. Use another
 * memory store if your server is clustered however use this memory store just
 * to get up and running fast or if your server is not clustered.
 *
 * @return {storeMemory} An new instance of storeMemory
 */
function storeMemory() {

	if( !( this instanceof storeMemory ) )
		return new storeMemory();

	EventEmitter.call( this );

	this.generateKeys();
}

var p = storeMemory.prototype = Object.create( EventEmitter.prototype );

/**
 * This will create a roomID, roomDataObject, and users array for the room
 *
 * @return {Promise} This promise will return a room id once the room is created
 */
p.createRoom = function( userID ) {

	var id = curId;

	curId++;

	if( curId == Number.MAX_VALUE )
		curId = Number.MIN_VALUE;

	roomUsers[ id ] = [];

	return this.setRoomData( id, {} )
		   .then( this.joinRoom.bind( this, userID, id ) )
		   .then( function() {

		   		return id;
		   });
};

/**
 * Join an existing room
 *
 * @param  {String} userID an id for the user whose joining
 * @param  {String} roomID an id for the room the user is joing
 * @return {Promise} A promise which will resolve and return a roomID
 */
p.joinRoom = function( userID, roomID ) {

	if( roomUsers[ roomID ] === undefined ) {

		return promise.reject( 'No room with the id: ' + roomID );
	} else {

		if( roomUsers[ roomID ].indexOf( userID ) != -1 ) {

			return promise.reject( 'User is already in room' );
		} else {

			roomUsers[ roomID ].push( userID );

			this.emit( roomID, {

				action: 'join',
				user: userID,
				users: roomUsers[ roomID ]
			});

			return promise.resolve( roomID );
		}
	}
};

/**
 * Remove a user from a room
 *
 * @param  {String} userID an id for the user whose leaving
 * @param  {String} roomID an id for the room the user is leaving
 * @return {Promise} When this promise resolves it will send the number of users in the room
 */
p.leaveRoom = function( userID, roomID ) {

	var userCount;

	if( roomUsers[ roomID ] === undefined ) {

		return promise.reject( 'No room with the id: ' + roomID );
	} else {

		var userIDX = roomUsers[ roomID ].indexOf( userID );

		if( userIDX != -1 ) {

			roomUsers[ roomID ].splice( userIDX, 1 );

			userCount = roomUsers[ roomID ].length;

			if( userCount == 0 ) {

				delete roomUsers[ roomID ];
			}

			this.emit( roomID, {

				action: 'leave',
				user: userID,
				users: roomUsers[ roomID ]
			});

			return promise.resolve( userCount );
		} else {

			return promise.reject( 'User ' + userID + ' is not in the room: ' + roomID );
		}
	}
};

/**
 * Get all users for a room
 *
 * @param  {String} roomID room id where to retrieve users
 * @return {Promise} A promise is returned which will return all users in a room
 */
p.getUsers = function( roomID ) {

	var setName = 'roomUsers:' + roomID;

	return promise.resolve( roomUsers[ roomID ] );
};

/**
 * get a key which can be used to enter a room vs entering room via
 * roomID
 *
 * @param  {String} roomID id of the room you'd like a key for
 * @return {Promise} A promise will be returned which will return a roomKey on success
 */
p.getKey = function( roomID ) {

	if( keys.length > 0 ) {

		var randIdx = Math.round( Math.random() * ( keys.length - 1 ) ),
			key = keys.splice( randIdx, 1 )[ 0 ];

		keyToId[ key ] = roomID;
		idToKey[ roomID ] = key;

		return promise.resolve( key );
	} else {

		return promise.reject( 'Run out of keys' );
	}
};

/**
 * return a room key so someone else can use it.
 *
 * @param  {String} roomID id of the room you'll be returning a key for
 * @param  {String} key the key you'd like to return
 * @return {Promise} This promise will succeed when the room key was returned
 */
p.returnKey = function( roomID, key ) {

	return this.getRoomIdForKey( key )
	.then( function( savedRoomId ) {

		if( savedRoomId == roomID ) {

			delete idToKey[ roomID ];
			delete keyToId[ key ];

			keys.push( key );

			return promise.resolve();
		} else {

			return promise.reject( 'roomID and roomID for key do not match' );
		}
	});
};

/**
 * return the room id for the given key
 *
 * @param  {String} key key used to enter the room
 * @return {Promise} This promise will succeed with the room id and fail if no room id exists for key
 */
p.getRoomIdForKey = function( key ) {

	var savedRoomId = keyToId[ key ];

	if( savedRoomId ) {

		return promise.resolve( savedRoomId );
	} else {

		return promise.reject();
	}
};

/**
 * set a variable on the rooms data object
 *
 * @param {String} roomID id for the room whose
 * @param {String} key variable name/key that you want to set
 * @param {*} value Value you'd like to set for the variable
 * @return {Promise} once this promise succeeds the rooms variable will be set
 */
p.setRoomDataVar = function( roomID, key, value ) {

	if( roomData[ roomID ] === undefined )
		roomData[ roomID ] = {};

	roomData[ roomID ][ key ] = value;

	return promise.resolve( value );
};

/**
 * get a variable from the rooms data object
 *
 * @param {String} roomID id for the room
 * @param {String} key variable name/key that you want to get
 * @return {Promise} once this promise succeeds it will return the variable value it will fail if the variable does not exist
 */
p.getRoomDataVar = function( roomID, key ) {

	if( roomData[ roomID ] === undefined ) {

		return promise.reject( 'There is no room by that id' );
	} else {

		return promise.resolve( roomData[ roomID ][ key ] );
	}
};

/**
 * delete a variable from the rooms data object
 *
 * @param {String} roomID id for the room
 * @param {String} key variable name/key that you want to delete
 * @return {Promise} once this promise succeeds it will return the value that was stored before delete
 */
p.delRoomDataVar = function( roomID, key ) {

	var oldVal;

	if( roomData[ roomID ] === undefined ) {

		return promise.reject( 'There is no room by that id' );
	} else {

		oldVal = roomData[ roomID ][ key ];

		delete roomData[ roomID ][ key ];

		return promise.resolve( oldVal );
	}
};

/**
 * Receive the data stored for a room.
 *
 * @param  {String} roomID id for the room you'd like data for
 * @return {Promise} This promise will succeed when data is received for the room
 */
p.getRoomData = function( roomID ) {

	if( roomData[ roomID ] )
		return promise.resolve( roomData[ roomID ] );
	else
		return promise.reject( 'No room data' );
};

/**
 * Set data stored for a room.
 *
 * @param  {String} roomID id for the room you'd like to set data for
 * @return {Promise} This promise will succeed when data is set for the room
 */
p.setRoomData = function( roomID, data ) {

	roomData[ roomID ] = data;

	return promise.resolve( data );
};

/**
 * Set room as public
 *
 * @param  {String} roomID id for the room you'd like to make public
 * @return {Promise} This promise will succeed when the room as been set as public
 */
p.makeRoomPublic = function( roomID ) {

	if( roomData[ roomID ] === undefined ) {

		return promise.reject( 'There is no room by that id' );
	} else {
		publicRooms.push( roomID );
		return promise.resolve( );
	}
};

/**
 * Set room as private, removing it from the publicly available list
 *
 * @param  {String} roomID id for the room you'd like to make private
 * @return {Promise} This promise will succeed when the room as been set as private
 */
p.makeRoomPrivate = function( roomID ) {

	if( roomData[ roomID ] === undefined ) {

		return promise.reject( 'There is no room by that id' );
	} else {
		var roomIndex = publicRooms.indexOf( roomID );
		if (roomIndex >= 0) publicRooms.splice( roomIndex, 1 )
		return promise.resolve( );
	}
};

/**
 * Gets first first available public room
 *
 * @return {Promise} This promise will succeed when the room has been retrieved
 */
p.getPublicRoom = function() {

	if ( publicRooms.length > 0 ) {

		return promise.resolve( publicRooms[0] );
	} else {

		return promise.reject( 'No available public rooms' );
	}

};

/**
 * generateKeys is a function which will create and store a set of
 * keys which can be used to enter a room instead of a room id
 */
p.generateKeys = function() {

	if( !keys ) {

		keys = [];

		for( var i = 0; i < 9999; i++ ) {

			keys.push( i );
		}
	}
};<|endoftext|>


<commit_before>'use strict';

var ip = require('ip-regex').v4().source;
var tlds = require('./tlds.json').join('|');

module.exports = function (opts) {
	opts = opts || {};

	var auth = '(?:\\S+(?::\\S*)?@)?';
	var domain = '(?:\\.(?:xn--[a-z0-9\\-]{1,59}|(?:[a-z\\u00a1-\\uffff0-9]+-?){0,62}[a-z\\u00a1-\\uffff0-9]{1,63}))*';
	var host = '(?:xn--[a-z0-9\\-]{1,59}|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?){0,62}[a-z\\u00a1-\\uffff0-9]{1,63}))';
	var path = '(?:\/[^\\s]*)?';
	var port = '(?::\\d{2,5})?';
	var protocol = '(?:(?:(?:\\w)+:)?\/\/)?';
	var tld = '(?:\\.(?:xn--[a-z0-9\\-]{1,59}|' + tlds + '+))';

	var regex = [
		protocol + auth + '(?:' + ip + '|',
		'(?:localhost)|' + host + domain + tld + ')' + port + path
	].join('');

	return opts.exact ? new RegExp('(?:^' + regex + '$)', 'i') :
						new RegExp('(?:^|\\s)(["\'])?' + regex + '\\1', 'ig');
};
<commit_msg>Don't match spaces<commit_after>'use strict';

var ip = require('ip-regex').v4().source;
var tlds = require('./tlds.json').join('|');

module.exports = function (opts) {
	opts = opts || {};

	var auth = '(?:\\S+(?::\\S*)?@)?';
	var domain = '(?:\\.(?:xn--[a-z0-9\\-]{1,59}|(?:[a-z\\u00a1-\\uffff0-9]+-?){0,62}[a-z\\u00a1-\\uffff0-9]{1,63}))*';
	var host = '(?:xn--[a-z0-9\\-]{1,59}|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?){0,62}[a-z\\u00a1-\\uffff0-9]{1,63}))';
	var path = '(?:\/[^\\s]*)?';
	var port = '(?::\\d{2,5})?';
	var protocol = '(?:(?:(?:\\w)+:)?\/\/)?';
	var tld = '(?:\\.(?:xn--[a-z0-9\\-]{1,59}|' + tlds + '+))';

	var regex = [
		protocol + auth + '(?:' + ip + '|',
		'(?:localhost)|' + host + domain + tld + ')' + port + path
	].join('');

	return opts.exact ? new RegExp('(?:^' + regex + '$)', 'i') :
						new RegExp('(["\'])?' + regex + '\\1', 'ig');
};
<|endoftext|>