<commit_before>/**
 * Spawns a child process using the arguments that were passed in.
 * This is done asynchronously. It will run the callback function
 * upon completion.
 * @param  {string}   args     List of arguments to pass in
 * @param  {function} callback Optional callback function
 */
function runCommand (args, callback) {
    if (!args || typeof(args) !== 'string') {
        throw 'You must pass in a string containing the arguments to be sent to the command line.';
    } else if (callback && typeof(callback) !== 'function') {
        throw 'The second argument to the runCommand method is meant to be a callback function.';
    }

    var executablePath = require('./executablepath.js');
    var exec = require('child_process').exec;
    var flif = executablePath();
    var executableAndArgs = flif + ' ' + args;

    var child = exec(executableAndArgs,
        function (error) {
            if (error !== null) {
                throw 'Executable Error: ' + error;
            }
        }
    );
    // Return data from command line
    child.stdout.on('data', function (chunk) {
        if (callback) {
            callback(chunk);
        }
    });
}

module.exports = runCommand;
<commit_msg>Improve runCommand callbac<commit_after>/**
 * Spawns a child process using the arguments that were passed in.
 * This is done asynchronously. It will run the callback function
 * upon completion.
 * @param  {string}   args     List of arguments to pass in
 * @param  {function} callback Optional callback function
 */
function runCommand (args, callback) {
    if (!args || typeof(args) !== 'string') {
        throw 'You must pass in a string containing the arguments to be sent to the command line.';
    } else if (callback && typeof(callback) !== 'function') {
        throw 'The second argument to the runCommand method is meant to be a callback function.';
    }

    var executablePath = require('./executablepath.js');
    var exec = require('child_process').exec;
    var flif = executablePath();
    var executableAndArgs = flif + ' ' + args;

    exec(executableAndArgs, function (error, stdout) {
        if (error !== null) {
            throw 'Executable Error: ' + error;
        }
        if (stdout && callback) {
            callback(stdout);
        } else if (callback) {
            callback();
        }
    });
}

module.exports = runCommand;
<|endoftext|>


<commit_before>var workQueue = require('./workQueue');
var Promise = require('unexpected-bluebird');
module.exports = function oathbreaker(value) {
    if (!value || typeof value.then !== 'function') {
        return value;
    }

    if (!value.isRejected) {
        // this is not a bluebird promise
        return value;
    }

    if (value.isFulfilled()) {
        return value;
    }

    if (value.isRejected()) {
        value.caught(function () {
            // Ignore - already handled
        });

        throw value.reason();
    }

    var onResolve = function () {};
    var onReject = function () {};

    var evaluated = false;
    var error;
    value.then(function (obj) {
        evaluated = true;
        onResolve(value);
    }, function (err) {
        evaluated = true;
        error = err;
        onReject(err);
    });

    workQueue.drain();

    if (evaluated && error) {
        if (error._isUnexpected && Error.captureStackTrace) {
            Error.captureStackTrace(error);
        }
        throw error;
    } else if (evaluated) {
        return value;
    } else {
        value._captureStackTrace(true);
    }

    return new Promise(function (resolve, reject) {
        onResolve = resolve;
        onReject = reject;
    });
};
<commit_msg>oathbreaker: Avoid breaking with non-Bluebird promises.<commit_after>var workQueue = require('./workQueue');
var Promise = require('unexpected-bluebird');
module.exports = function oathbreaker(value) {
    if (!value || typeof value.then !== 'function') {
        return value;
    }

    if (!value.isRejected) {
        // this is not a bluebird promise
        return value;
    }

    if (value.isFulfilled()) {
        return value;
    }

    if (value.isRejected()) {
        value.caught(function () {
            // Ignore - already handled
        });

        throw value.reason();
    }

    var onResolve = function () {};
    var onReject = function () {};

    var evaluated = false;
    var error;
    value.then(function (obj) {
        evaluated = true;
        onResolve(value);
    }, function (err) {
        evaluated = true;
        error = err;
        onReject(err);
    });

    workQueue.drain();

    if (evaluated && error) {
        if (error._isUnexpected && Error.captureStackTrace) {
            Error.captureStackTrace(error);
        }
        throw error;
    } else if (evaluated) {
        return value;
    } else if (value._captureStackTrace) {
        value._captureStackTrace(true);
    }

    return new Promise(function (resolve, reject) {
        onResolve = resolve;
        onReject = reject;
    });
};
<|endoftext|>


<commit_before>var sax = require('sax')
  , request = require('request')
  , url = require('url')
  , events = require('events')
  , util = require('util');

if(!Object.merge) Object.merge = function(a, b, force){
  if (a && b) {
    if (a !== Object(a) || b !== Object(b)) {
      throw new TypeError('Object.merge called on non-object');
    }
    for (var key in b) {
      if(force || !a.hasOwnProperty(key)) {
        a[key] = b[key];
      }
    }
  }
  return a;
};

function getValue(obj, subkey) {
  if (!subkey)
    subkey = '#';
  if (obj && obj[subkey])
    return obj[subkey];
  else
    return null;
}

function handleMeta (node) {
  if (!node) return {};

  var meta = {
    'title': null
    , updated: null
    , author: null
    , links: []
    , icon: null
  };

  Object.keys(node).forEach(function(name) {
    var el = node[name];

    switch(name) {
      case 'title':
        meta.title = getValue(el);
        break;
      case 'updated':
        meta.updated = getValue(el);
        break;
      case 'author':
        function getAuthor (el, callback) {
          var author = {};

          ['name', 'email', 'uri'].forEach(function(p) {
            if (el.hasOwnProperty(p)) {
              author[p] = el[p]['#'];
            } else {
              author[p] = null;
            }
          });

          callback(author);
        }

        getAuthor(el, function(author) {
          meta.author = author;
        })
        break;
      case 'link':
        if (Array.isArray(el)) {
          meta.links = el;
        } else {
          meta.links.push(el);
        }
        break;
      case 'icon':
        meta.icon = getValue(el);
        break;
    }
  })

  return meta;
}

function handleEntry (node) {
  if (!node) return {};

  var entry = {
    id: null
    , title: ''
    , updated: null
    , links: []
    , authors: []
    , rights: null
    , summary: null
    , content: null
    , category: []
    , "dc:issued": null
    , identifiers: []
    , published: null
    , contributors: []
    , "dc:language": null
    , "dc:publisher": null
    , "dc:subtitle": null
  }

  Object.keys(node).forEach(function(name) {
    var el = node[name];

    switch (name) {
      case 'id':
        entry.id = getValue(el);
        break;
      case 'title':
        entry.title = getValue(el);
      case 'updated':
        entry.updated = getValue(el);
        break;
      case 'link':
        if (Array.isArray(el)) {
          entry.links = el;
        } else {
          entry.links.push(el);
        }
        break;
      case 'author':
        function getAuthor (el, callback) {
          var author = {};

          ['name', 'email', 'uri'].forEach(function(p) {
            if (el.hasOwnProperty(p)) {
              author[p] = el[p]['#'];
            } else {
              author[p] = null;
            }
          });

          callback(author);
        }

        if (Array.isArray(el)) {
          el.forEach(function(o) {
            getAuthor(o, function(author) {
              entry.authors.push(author);
            })
          })
        } else {
          getAuthor(el, function(author) {
            entry.authors.push(author);
          })
        }
        break;
      case 'rights':
        entry.rights = getValue(el);
        break;
      case 'summary':
        entry.summary = getValue(el);
        break;
      case 'content':
        entry.content = el['#'];
        break;
      case 'category':
        function getCategory (el, callback) {
          var category = {};
          ['term', 'scheme', 'label'].forEach(function(p) {
            if (el.hasOwnProperty(p)) {
              category[p] = el[p]['#'];
            } else {
              category[p] = null;
            }
          })
        }

        if (Array.isArray(el)) {
          el.forEach(function(o) {
            getCategory(o, function(category) {
              entry.categories.push(category);
            })
          })
        } else {
          getCategory(o, function(category) {
            entry.categories.push(category);
          })
        }
        break;
      case 'dc:issued':
        entry['dc:issued'] = getValue(el);
        break;
      case 'dc:identifier':
        if (Array.isArray(el)) {
          el.forEach(function(e) {
            entry.identifiers.push(getValue(e));
          })
        } else {
          entry.identifiers.push(getValue(el));
        }
        break;
      case 'published':
        entry.published = getValue(el);
        break;
      case 'contributor':
        function getContributor (el, callback) {
          var contributor = {};

          ['name', 'email', 'uri'].forEach(function(p) {
            if (el.hasOwnProperty(p)) {
              contributor[p] = el[p]['#'];
            } else {
              contributor[p] = null;
            }
          });

          callback(contributor);
        }

        if (Array.isArray(el)) {
          el.forEach(function(o) {
            getContributor(o, function(contributor) {
              entry.contributors.push(contributor);
            })
          })
        } else {
          getContributor(o, function(contributor) {
            entry.contributors.push(contributor);
          })
        }
        break;
      case 'dc:language':
        entry['dc:language'] = getValue(el);
        break;
      case "dc:publisher":
        entry['dc:publisher'] = getValue(el);
        break;
      case "dc:subtitle":
        entry["dc:subtitle"] = getValue(el);
        break;
    }
  });

  return entry;
}

function OpdsParser () {
  var self = this;

  self._reset();

  self.stream = sax.createStream(false, {lowercasetags: true, normalize: true, trim: true});

  self.stream.on('error', function(e) {
    self.handleSaxError(e, self);
  });
  self.stream.on('opentag', function(n) {
    self.handleOpenTag(n, self);
  });
  self.stream.on('closetag', function(el) {
    self.handleCloseTag(el, self);
  });
  self.stream.on('text', function(text) {
    self.handleText(text, self);
  })
  self.stream.on('cdata', function(text) {
    self.handleText(text, self);
  });
  self.stream.on('end', function() {
    self.handleEnd(self);
  });
  events.EventEmitter.call(this);
}

util.inherits(OpdsParser, events.EventEmitter);

OpdsParser.prototype.handleOpenTag = function(node, scope) {
  var self = scope
    , n = {
      '#name': node.name
      , '@': {}
      , '#': ''
    }

  if (self.stack.length == 0) {
    if (!(node.name == 'feed' || node.name == 'entry')) {
      self.handleError('not an atom.', scope);
    }
  }

  function handleAttributes (attrs, el) {
    Object.keys(attrs).forEach(function(name) {
      if (self.xmlbase.length && (name == 'href' || name == 'src' || name == 'uri')) {
        attrs[name] = url.resolve(self.xmlbase[0]['#'], attrs[name]);
      } else if (name == 'xml:base') {
        if (self.xmlbase.length) {
          attrs[name] = url.resolve(self.xmlbase[0]['#'], attrs[name]);
        }
        self.xmlbase.unshift({ '#name': el, '#': attrs[name]});
      } else if (name == 'type' && (attrs['type'] == 'xhtml' || attrs['type'] == 'html')) {
        self.inHtml = true;
        self.html = {'#name': el, '#': ''};
      }
    })

    return attrs;
  }

  if (Object.keys(node.attributes).length) {
    n['@'] = handleAttributes(node.attributes, n['#name']);
  }

  if (self.inHtml && self.html['#name'] != n['#name']) {
    self.html['#'] += '<' + n['#name'];
    Object.keys(n['@']).forEach(function(name) {
      self.html['#'] += ' ' + name + '="' + n['@'][name] + '"';
    });
    self.html['#'] += '>';
  } else if (self.stack.length == 0) {
    self.meta['@'] = [];
    Object.keys(n['@']).forEach(function(name) {
      var o = {};
      o[name] = n['@'][name];
      self.meta['@'].push(o);
    })
  }

  self.stack.unshift(n);
}

OpdsParser.prototype.handleText = function(text, scope) {
  var self = scope;

  if (self.inHtml) {
    self.html['#'] += text;
  } else {
    if (self.stack.length) {
      if ('#' in self.stack[0]) {
        self.stack[0]['#'] += text;
      } else {
        self.stack[0]['#'] = text;
      }
    }
  }
}

OpdsParser.prototype.handleCloseTag = function(el, scope) {
  var self = scope
    , n = self.stack.shift();

  delete n['#name'];

  if (self.xmlbase.length) {
    if (el == 'logo' || el == 'icon') {
      n['#'] = url.resolve(self.xmlbase[0]['#'], n['#']);
    }
    if (el == self.xmlbase[0]['#name']) {
      void self.xmlbase.shift();
    }
  }

  if (self.inHtml) {
    if (el == self.html['#name']) {
      n['#'] += self.html['#'].trim();
      for (var key in n) {
        if (key != '@' && key != '#') {
          delete n[key];
        }
      }

      self.html = {};
      self.inHtml = false;
    } else {
      self.html['#'] += '</' + el + '>';
    }
  }

  if ('#' in n) {
    if (n['#'].match(/^\s*$/)) {
      delete n['#'];
    } else {
      n['#'] = n['#'].trim();
      if (Object.keys(n).length === 1) {
        n = n['#'];
      }
    }
  }

  if (el == 'entry') {
    var entry = handleEntry(n);
    self.emit('entry', entry);
    self.entries.push(entry);
  } else if (el == 'feed') {
    Object.merge(self.meta, handleMeta(n), true);
  }

  if (self.stack.length > 0) {
    if (!self.stack[0].hasOwnProperty(el)) {
      self.stack[0][el] = n;
    } else if (self.stack[0][el] instanceof Array) {
      self.stack[0][el].push(n);
    } else {
      self.stack[0][el] = [self.stack[0][el], n];
    }
  } else {
    self.nodes = n;
  }
}

OpdsParser.prototype.handleEnd = function(scope) {
  var self = scope
    , feed = self.meta
    , entries = self.entries;

  feed.entries = self.entries;

  self.emit('end', feed);

  if ('function' == typeof self.callback) {
    if (self.errors.length) {
      var error = self.errors.pop();
      if (self.errors.length) {
        error.errors = self.errors;
      }
      self.callback(error);
    } else {
      self.callback(null, feed);
    }
  }
  self._reset();
}

OpdsParser.prototype.handleSaxError = function(e, scope) {
  var self = scope;
  self.handleError(e, self);
  if (self._parser) {
    self._parser.error = null;
    self._parser.resume();
  }
}

OpdsParser.prototype.handleError = function(e, scope) {
  var self = scope;

  self.errors.push(e);
  this.handleEnd(scope);
}

OpdsParser.prototype._setCallback = function(callback) {
  this.callback = ('function' == typeof callback) ? callback : undefined;
}

OpdsParser.prototype._reset = function() {
  this.meta = {};
  this.entries = [];
  this.xmlbase = [];
  this.stack = [];
  this.nodes = {};
  this.inHtml = false;
  this.html = {};
  this.errors = [];
  this.callback = undefined;
}

OpdsParser.prototype.parseUrl = function(url, options, callback) {
  var self = this
    , defaultOptions = {
        url: url
        , method: 'GET'
        , followRedirect: true
        , followAllRedirects: false
        , maxRedirects: 3
        , timeout: 30 * 1000
        , jar: false
        , headers: {
          'User-Agent': 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)'
        }
      }
    , requestOptions = Object.merge(defaultOptions, options, true);

  self._setCallback(callback);

  request(requestOptions)
    .on('error', function(e) {
      self.handleError(e, self);
    })
    .pipe(self.stream);
}
<commit_msg>adding parseFile method.<commit_after>var sax = require('sax')
  , request = require('request')
  , fs = require('fs')
  , url = require('url')
  , events = require('events')
  , util = require('util');

if(!Object.merge) Object.merge = function(a, b, force){
  if (a && b) {
    if (a !== Object(a) || b !== Object(b)) {
      throw new TypeError('Object.merge called on non-object');
    }
    for (var key in b) {
      if(force || !a.hasOwnProperty(key)) {
        a[key] = b[key];
      }
    }
  }
  return a;
};

function getValue(obj, subkey) {
  if (!subkey)
    subkey = '#';
  if (obj && obj[subkey])
    return obj[subkey];
  else
    return null;
}

function handleMeta (node) {
  if (!node) return {};

  var meta = {
    'title': null
    , updated: null
    , author: null
    , links: []
    , icon: null
  };

  Object.keys(node).forEach(function(name) {
    var el = node[name];

    switch(name) {
      case 'title':
        meta.title = getValue(el);
        break;
      case 'updated':
        meta.updated = getValue(el);
        break;
      case 'author':
        function getAuthor (el, callback) {
          var author = {};

          ['name', 'email', 'uri'].forEach(function(p) {
            if (el.hasOwnProperty(p)) {
              author[p] = el[p]['#'];
            } else {
              author[p] = null;
            }
          });

          callback(author);
        }

        getAuthor(el, function(author) {
          meta.author = author;
        })
        break;
      case 'link':
        if (Array.isArray(el)) {
          meta.links = el;
        } else {
          meta.links.push(el);
        }
        break;
      case 'icon':
        meta.icon = getValue(el);
        break;
    }
  })

  return meta;
}

function handleEntry (node) {
  if (!node) return {};

  var entry = {
    id: null
    , title: ''
    , updated: null
    , links: []
    , authors: []
    , rights: null
    , summary: null
    , content: null
    , category: []
    , "dc:issued": null
    , identifiers: []
    , published: null
    , contributors: []
    , "dc:language": null
    , "dc:publisher": null
    , "dc:subtitle": null
  }

  Object.keys(node).forEach(function(name) {
    var el = node[name];

    switch (name) {
      case 'id':
        entry.id = getValue(el);
        break;
      case 'title':
        entry.title = getValue(el);
      case 'updated':
        entry.updated = getValue(el);
        break;
      case 'link':
        if (Array.isArray(el)) {
          entry.links = el;
        } else {
          entry.links.push(el);
        }
        break;
      case 'author':
        function getAuthor (el, callback) {
          var author = {};

          ['name', 'email', 'uri'].forEach(function(p) {
            if (el.hasOwnProperty(p)) {
              author[p] = el[p]['#'];
            } else {
              author[p] = null;
            }
          });

          callback(author);
        }

        if (Array.isArray(el)) {
          el.forEach(function(o) {
            getAuthor(o, function(author) {
              entry.authors.push(author);
            })
          })
        } else {
          getAuthor(el, function(author) {
            entry.authors.push(author);
          })
        }
        break;
      case 'rights':
        entry.rights = getValue(el);
        break;
      case 'summary':
        entry.summary = getValue(el);
        break;
      case 'content':
        entry.content = el['#'];
        break;
      case 'category':
        function getCategory (el, callback) {
          var category = {};
          ['term', 'scheme', 'label'].forEach(function(p) {
            if (el.hasOwnProperty(p)) {
              category[p] = el[p]['#'];
            } else {
              category[p] = null;
            }
          })
        }

        if (Array.isArray(el)) {
          el.forEach(function(o) {
            getCategory(o, function(category) {
              entry.categories.push(category);
            })
          })
        } else {
          getCategory(o, function(category) {
            entry.categories.push(category);
          })
        }
        break;
      case 'dc:issued':
        entry['dc:issued'] = getValue(el);
        break;
      case 'dc:identifier':
        if (Array.isArray(el)) {
          el.forEach(function(e) {
            entry.identifiers.push(getValue(e));
          })
        } else {
          entry.identifiers.push(getValue(el));
        }
        break;
      case 'published':
        entry.published = getValue(el);
        break;
      case 'contributor':
        function getContributor (el, callback) {
          var contributor = {};

          ['name', 'email', 'uri'].forEach(function(p) {
            if (el.hasOwnProperty(p)) {
              contributor[p] = el[p]['#'];
            } else {
              contributor[p] = null;
            }
          });

          callback(contributor);
        }

        if (Array.isArray(el)) {
          el.forEach(function(o) {
            getContributor(o, function(contributor) {
              entry.contributors.push(contributor);
            })
          })
        } else {
          getContributor(o, function(contributor) {
            entry.contributors.push(contributor);
          })
        }
        break;
      case 'dc:language':
        entry['dc:language'] = getValue(el);
        break;
      case "dc:publisher":
        entry['dc:publisher'] = getValue(el);
        break;
      case "dc:subtitle":
        entry["dc:subtitle"] = getValue(el);
        break;
    }
  });

  return entry;
}

function OpdsParser () {
  var self = this;

  self._reset();

  self.stream = sax.createStream(false, {lowercasetags: true, normalize: true, trim: true});

  self.stream.on('error', function(e) {
    self.handleSaxError(e, self);
  });
  self.stream.on('opentag', function(n) {
    self.handleOpenTag(n, self);
  });
  self.stream.on('closetag', function(el) {
    self.handleCloseTag(el, self);
  });
  self.stream.on('text', function(text) {
    self.handleText(text, self);
  })
  self.stream.on('cdata', function(text) {
    self.handleText(text, self);
  });
  self.stream.on('end', function() {
    self.handleEnd(self);
  });
  events.EventEmitter.call(this);
}

util.inherits(OpdsParser, events.EventEmitter);

OpdsParser.prototype.handleOpenTag = function(node, scope) {
  var self = scope
    , n = {
      '#name': node.name
      , '@': {}
      , '#': ''
    }

  if (self.stack.length == 0) {
    if (!(node.name == 'feed' || node.name == 'entry')) {
      self.handleError('not an atom.', scope);
    }
  }

  function handleAttributes (attrs, el) {
    Object.keys(attrs).forEach(function(name) {
      if (self.xmlbase.length && (name == 'href' || name == 'src' || name == 'uri')) {
        attrs[name] = url.resolve(self.xmlbase[0]['#'], attrs[name]);
      } else if (name == 'xml:base') {
        if (self.xmlbase.length) {
          attrs[name] = url.resolve(self.xmlbase[0]['#'], attrs[name]);
        }
        self.xmlbase.unshift({ '#name': el, '#': attrs[name]});
      } else if (name == 'type' && (attrs['type'] == 'xhtml' || attrs['type'] == 'html')) {
        self.inHtml = true;
        self.html = {'#name': el, '#': ''};
      }
    })

    return attrs;
  }

  if (Object.keys(node.attributes).length) {
    n['@'] = handleAttributes(node.attributes, n['#name']);
  }

  if (self.inHtml && self.html['#name'] != n['#name']) {
    self.html['#'] += '<' + n['#name'];
    Object.keys(n['@']).forEach(function(name) {
      self.html['#'] += ' ' + name + '="' + n['@'][name] + '"';
    });
    self.html['#'] += '>';
  } else if (self.stack.length == 0) {
    self.meta['@'] = [];
    Object.keys(n['@']).forEach(function(name) {
      var o = {};
      o[name] = n['@'][name];
      self.meta['@'].push(o);
    })
  }

  self.stack.unshift(n);
}

OpdsParser.prototype.handleText = function(text, scope) {
  var self = scope;

  if (self.inHtml) {
    self.html['#'] += text;
  } else {
    if (self.stack.length) {
      if ('#' in self.stack[0]) {
        self.stack[0]['#'] += text;
      } else {
        self.stack[0]['#'] = text;
      }
    }
  }
}

OpdsParser.prototype.handleCloseTag = function(el, scope) {
  var self = scope
    , n = self.stack.shift();

  delete n['#name'];

  if (self.xmlbase.length) {
    if (el == 'logo' || el == 'icon') {
      n['#'] = url.resolve(self.xmlbase[0]['#'], n['#']);
    }
    if (el == self.xmlbase[0]['#name']) {
      void self.xmlbase.shift();
    }
  }

  if (self.inHtml) {
    if (el == self.html['#name']) {
      n['#'] += self.html['#'].trim();
      for (var key in n) {
        if (key != '@' && key != '#') {
          delete n[key];
        }
      }

      self.html = {};
      self.inHtml = false;
    } else {
      self.html['#'] += '</' + el + '>';
    }
  }

  if ('#' in n) {
    if (n['#'].match(/^\s*$/)) {
      delete n['#'];
    } else {
      n['#'] = n['#'].trim();
      if (Object.keys(n).length === 1) {
        n = n['#'];
      }
    }
  }

  if (el == 'entry') {
    var entry = handleEntry(n);
    self.emit('entry', entry);
    self.entries.push(entry);
  } else if (el == 'feed') {
    Object.merge(self.meta, handleMeta(n), true);
  }

  if (self.stack.length > 0) {
    if (!self.stack[0].hasOwnProperty(el)) {
      self.stack[0][el] = n;
    } else if (self.stack[0][el] instanceof Array) {
      self.stack[0][el].push(n);
    } else {
      self.stack[0][el] = [self.stack[0][el], n];
    }
  } else {
    self.nodes = n;
  }
}

OpdsParser.prototype.handleEnd = function(scope) {
  var self = scope
    , feed = self.meta
    , entries = self.entries;

  feed.entries = self.entries;

  self.emit('end', feed);

  if ('function' == typeof self.callback) {
    if (self.errors.length) {
      var error = self.errors.pop();
      if (self.errors.length) {
        error.errors = self.errors;
      }
      self.callback(error);
    } else {
      self.callback(null, feed);
    }
  }
  self._reset();
}

OpdsParser.prototype.handleSaxError = function(e, scope) {
  var self = scope;
  self.handleError(e, self);
  if (self._parser) {
    self._parser.error = null;
    self._parser.resume();
  }
}

OpdsParser.prototype.handleError = function(e, scope) {
  var self = scope;

  self.errors.push(e);
  this.handleEnd(scope);
}

OpdsParser.prototype._setCallback = function(callback) {
  this.callback = ('function' == typeof callback) ? callback : undefined;
}

OpdsParser.prototype._reset = function() {
  this.meta = {};
  this.entries = [];
  this.xmlbase = [];
  this.stack = [];
  this.nodes = {};
  this.inHtml = false;
  this.html = {};
  this.errors = [];
  this.callback = undefined;
}

OpdsParser.prototype.parseUrl = function(url, options, callback) {
  var self = this
    , defaultOptions = {
        url: url
        , method: 'GET'
        , followRedirect: true
        , followAllRedirects: false
        , maxRedirects: 3
        , timeout: 30 * 1000
        , jar: false
        , headers: {
          'User-Agent': 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)'
        }
      }
    , requestOptions = Object.merge(defaultOptions, options, true);

  self._setCallback(callback);

  request(requestOptions)
    .on('error', function(e) {
      self.handleError(e, self);
    })
    .pipe(self.stream);
}

OpdsParser.prototype.parseFile = function(file, callback) {
  var self = this;

  self._setCallback(callback);

  fs.createReadStream(file)
    .on('error', function(e) {
      self.handleError(e, self);
    })
    .pipe(self.stream);
}

exports = module.exports = OpdsParser;
<|endoftext|>


<commit_before>/**
 * Processes a compact property (not transform) and returns the expanded keyframes.
 *
 * @param {string[]} tokens The values from a compact property.
 *
 * @example
 *    process('color', ['red', 'green'])   //=>
 *
 *    [{step: 0, properties: [{name: 'color', value: 'red'}],
 *     {step: 100, properties: [{name: 'color', value: 'green'}]]
 *
 * @return {Object[]} Array of keyframe objects.
 */
export function process(property, tokens, fn) {
  if (!isString(property)) {
    throw new Error("No property");
  }
  if (tokens.length < 2) {
    throw new Error("Error");
  }
  return steps(property, tokens, fn);
}

export function processMultiple(input) {
  let result = [];

  input.forEach((combinedProperty) => {
    process(...combinedProperty).forEach((keyframe) => {
      let tempKeyFrame = result.find(x => x.step == keyframe.step);

      if (tempKeyFrame) {
        return tempKeyFrame.properties = tempKeyFrame.properties.concat(keyframe.properties);
      }

      result.push(keyframe);
    });
  });

  return result;
}

function isString(value) {
  return typeof value === "string";
}

function keyframe(step, property, value) {
  return {step: step, properties: [{property: property, value: value}]};
}

function keyframeTransform(step, property, value, fn) {
  return {step: step, properties: [{property: property, value: fn + "(" + value + ")"}]};
}

function steps(property, tokens, fn) {
  const size = tokens.length - 1;
  const stepSize = (1 / size) * 100;
  const steps = [];
  let n = 0;
  const keyframeFn = isString(fn) ? keyframeTransform : keyframe;

  for (let i = 0; i <= size; i++) {
    const step = Math.round(n * 100) / 100;
    const value = tokens[i];
    steps.push(keyframeFn(step, property, value, fn));
    n = n + stepSize;
  }

  return steps;
}

function tokeniser(string) {
  return string.split(" ");
}
<commit_msg>Refactor processMultiple to a more terse narrative<commit_after>/**
 * Processes a compact property (not transform) and returns the expanded keyframes.
 *
 * @param {string[]} tokens The values from a compact property.
 *
 * @example
 *    process('color', ['red', 'green'])   //=>
 *
 *    [{step: 0, properties: [{name: 'color', value: 'red'}],
 *     {step: 100, properties: [{name: 'color', value: 'green'}]]
 *
 * @return {Object[]} Array of keyframe objects.
 */
export function process(property, tokens, fn) {
  if (!isString(property)) {
    throw new Error("No property");
  }
  if (tokens.length < 2) {
    throw new Error("Error");
  }
  return steps(property, tokens, fn);
}

/**
 * Receives an array Ai and a function and returns the resulting array (Ar) of
 * mapping Ai over fn collecting its results as a flatten array.
 *
 *    flatMap([1,2,3], x => [x, x * x])  => [1, 1, 2, 4, 3, 9]
 */
function flatMap(array, fn) {
  return array.reduce((xs, x) => xs.concat(fn(x)), []);
};

function isUndefined(x) {
  return typeof x === "undefined";
};

function mergeKeyframes(xs, x) {
  const cachedKeyframe = xs.find(kf => kf.step == x.step);

  if (isUndefined(cachedKeyframe)) {
    xs.push(x);
  } else {
    cachedKeyframe.properties = cachedKeyframe.properties.concat(x.properties);
  }

  return xs;
};

export function processMultiple(input) {
  return flatMap(input, x => process(...x))
           .reduce(mergeKeyframes, []);
}

function isString(value) {
  return typeof value === "string";
}

function keyframe(step, property, value) {
  return {step: step, properties: [{property: property, value: value}]};
}

function keyframeTransform(step, property, value, fn) {
  return {step: step, properties: [{property: property, value: fn + "(" + value + ")"}]};
}

function steps(property, tokens, fn) {
  const size = tokens.length - 1;
  const stepSize = (1 / size) * 100;
  const steps = [];
  let n = 0;
  const keyframeFn = isString(fn) ? keyframeTransform : keyframe;

  for (let i = 0; i <= size; i++) {
    const step = Math.round(n * 100) / 100;
    const value = tokens[i];
    steps.push(keyframeFn(step, property, value, fn));
    n = n + stepSize;
  }

  return steps;
}

function tokeniser(string) {
  return string.split(" ");
}
<|endoftext|>


<commit_before>var assign = require('lodash.assign');
var identity = require('lodash.identity');
var mapKeys = require('lodash.mapkeys');
var mapValues = require('lodash.mapvalues');
var omit = require('lodash.omit');
var path = require('path');
var pick = require('lodash.pick');
var imageSize = require('image-size');
var util = require('util');

var Part = require('./partWithRels');
var templates = require('./templates');
var utilities = require('../utilities');

function Image(data, parent) {
    this.parent = parent;
    this.importData(data);
    Part.call(this, {
        drawingml: 'a',
        spreadsheetDrawing: 'xdr'
    }, templates.image);
};
module.exports = Image;
util.inherits(Image, Part);

assign(Image.prototype, {
    importData: function(data) {
        if (!data) throw new TypeError('data must be an Object');
        if (!(data.image instanceof Buffer)) {
            throw new TypeError('data.image must be of type: Buffer. data.image passed as: ' + typeof data.image);
        }
        assign(this.imageData, data);

        if (!data.origin) {
            this.imageData.origin = {
                x: 0,
                y: 0
            };
        }

        assign(this.imageData, this.getImageSize());
        this.rels.addImage({ path: this.imageFile.path });
    },
    getFilename: function () {
        return this.imageData.filename || [
            utilities.slugify(this.parent ? this.parent.name : ''),
            'image' + this.id,
            this.imageData.type
        ].filter(identity).join('.');
    },
    getImageSize: function(image) {
        return imageSize(this.image, this.imageData.filename);
    },
    pxToEMU: function (px) {
        return px * 10270;
    }
});

Object.defineProperties(Image.prototype, utilities.defineProps({
    constant: {type: 'drawing'},
    unsettable: {
        'data': {
            getter: function() {
                var data = assign(pick(this.imageData, 'filename', 'width', 'height', 'type', 'name', 'description'), {
                    extent: {
                        cx: this.pxToEMU(this.imageData.width),
                        cy: this.pxToEMU(this.imageData.height)
                    },
                    origin: {
                        x: this.pxToEMU(this.imageData.origin.x),
                        y: this.pxToEMU(this.imageData.origin.y)
                    },
                    id: this.id
                });
                return data;
            }
        },
        filename: {
            getter: function() { return this.type + this.id + '.xml'; }
        },
        id: {
            getter: function() { return this.parent && this.parent.rels ? this.parent.rels.getId(this) : 0; }
        },
        image: {
            getter: function() { return this.imageData.image; }
        },
        imageData: { ctor: Object },
        path: {
            getter: function() { return 'xl/drawings/' + this.filename; }
        },
        imageFile: {
            getter: function () {
                return {
                    path: 'xl/media/' + this.getFilename(),
                    data: this.imageData.image
                };
            }
        }
    }
}));<commit_msg>use Buffer.isBuffer in place of instanceof Buffer<commit_after>var assign = require('lodash.assign');
var identity = require('lodash.identity');
var mapKeys = require('lodash.mapkeys');
var mapValues = require('lodash.mapvalues');
var omit = require('lodash.omit');
var path = require('path');
var pick = require('lodash.pick');
var imageSize = require('image-size');
var util = require('util');

var Part = require('./partWithRels');
var templates = require('./templates');
var utilities = require('../utilities');

function Image(data, parent) {
    this.parent = parent;
    this.importData(data);
    Part.call(this, {
        drawingml: 'a',
        spreadsheetDrawing: 'xdr'
    }, templates.image);
};
module.exports = Image;
util.inherits(Image, Part);

assign(Image.prototype, {
    importData: function(data) {
        if (!data) throw new TypeError('data must be an Object');
        if (!Buffer.isBuffer(data.image)) {
            throw new TypeError('data.image must be of type: Buffer. data.image passed as: ' + typeof data.image);
        }
        assign(this.imageData, data);

        if (!data.origin) {
            this.imageData.origin = {
                x: 0,
                y: 0
            };
        }

        assign(this.imageData, this.getImageSize());
        this.rels.addImage({ path: this.imageFile.path });
    },
    getFilename: function () {
        return this.imageData.filename || [
            utilities.slugify(this.parent ? this.parent.name : ''),
            'image' + this.id,
            this.imageData.type
        ].filter(identity).join('.');
    },
    getImageSize: function(image) {
        return imageSize(this.image, this.imageData.filename);
    },
    pxToEMU: function (px) {
        return px * 10270;
    }
});

Object.defineProperties(Image.prototype, utilities.defineProps({
    constant: {type: 'drawing'},
    unsettable: {
        'data': {
            getter: function() {
                var data = assign(pick(this.imageData, 'filename', 'width', 'height', 'type', 'name', 'description'), {
                    extent: {
                        cx: this.pxToEMU(this.imageData.width),
                        cy: this.pxToEMU(this.imageData.height)
                    },
                    origin: {
                        x: this.pxToEMU(this.imageData.origin.x),
                        y: this.pxToEMU(this.imageData.origin.y)
                    },
                    id: this.id
                });
                return data;
            }
        },
        filename: {
            getter: function() { return this.type + this.id + '.xml'; }
        },
        id: {
            getter: function() { return this.parent && this.parent.rels ? this.parent.rels.getId(this) : 0; }
        },
        image: {
            getter: function() { return this.imageData.image; }
        },
        imageData: { ctor: Object },
        path: {
            getter: function() { return 'xl/drawings/' + this.filename; }
        },
        imageFile: {
            getter: function () {
                return {
                    path: 'xl/media/' + this.getFilename(),
                    data: this.imageData.image
                };
            }
        }
    }
}));<|endoftext|>


<commit_before>'use strict'
const config = require('../config')
const logger = require('winston')

module.exports = () => {
  function check(client, msg, cmd, params = []) {
    if (msg.channel.type === 'dm') {
      logger.debug('Command was run in a DM channel. No restrictions')
      return true
    }
    // The bot owner can run any command
    if (msg.author.id === config.owner_id) {
      logger.debug('Command was run by bot owner. No restrictions')
      return true
    }
    // The guild owner can run any command
    if (msg.author.id === msg.guild.ownerID) {
      logger.debug('Command was run by guild owner. No restrictions')
      return true
    }
    if (cmd === 'perms' && params.length === 1) {
      logger.debug("'perms check' has no restrictions")
      return true
    }
    logger.debug(
      'Checking Permissions:',
      msg.author.username,
      msg.guild.id,
      cmd,
    )

    const permCode = `${msg.guild.id}-${cmd}`
    if (!client.commandPerms.has(permCode)) {
      // Allowed if we have not set a permission for it
      logger.debug(
        `There were no permission results found for cmd ${cmd}, allowed`,
      )
      return true
    }

    const denied = client.commandPerms.get(permCode).deny
    const allowed = client.commandPerms.get(permCode).allow

    // Deny if the member is explicitly denied
    if (denied.members.indexOf(msg.author.id) !== -1) {
      logger.debug('Member DENIED')
      return false
    }

    // Allow if the member is explicitly allowed
    if (allowed.members.indexOf(msg.author.id) !== -1) {
      logger.debug('Member ALLOWED')
      return true
    }

    // Deny if the command is run in an explicitly denied channel
    if (denied.channels.indexOf(msg.channel.id) !== -1) {
      logger.debug('Channel DENIED')
      return false
    }

    // Allow if the command is run in an explicitly allowed channel
    if (allowed.channels.indexOf(msg.channel.id) !== -1) {
      logger.debug('Channel ALLOWED')
      return true
    }

    const roles = msg.member.roles
    let foundRole = null
    roles.forEach((r) => {
      // Deny if the message owner has a role that is explicitly denied
      if (denied.roles.indexOf(r.id) !== -1) {
        logger.debug('Role DENIED')
        foundRole = false
      }
      // Allow if the message owner has a role that is explicitly allowed
      if (allowed.roles.indexOf(r.id) !== -1) {
        logger.debug('Role ALLOWED')
        foundRole = true
      }
    })

    if (foundRole !== null) {
      return foundRole
    }

    logger.debug('No Permissions found, allowing')
    return true
  }

  return {
    check,
  }
}
<commit_msg>role cache<commit_after>'use strict'
const config = require('../config')
const logger = require('winston')

module.exports = () => {
  function check(client, msg, cmd, params = []) {
    if (msg.channel.type === 'dm') {
      logger.debug('Command was run in a DM channel. No restrictions')
      return true
    }
    // The bot owner can run any command
    if (msg.author.id === config.owner_id) {
      logger.debug('Command was run by bot owner. No restrictions')
      return true
    }
    // The guild owner can run any command
    if (msg.author.id === msg.guild.ownerID) {
      logger.debug('Command was run by guild owner. No restrictions')
      return true
    }
    if (cmd === 'perms' && params.length === 1) {
      logger.debug("'perms check' has no restrictions")
      return true
    }
    logger.debug(
      'Checking Permissions:',
      msg.author.username,
      msg.guild.id,
      cmd,
    )

    const permCode = `${msg.guild.id}-${cmd}`
    if (!client.commandPerms.has(permCode)) {
      // Allowed if we have not set a permission for it
      logger.debug(
        `There were no permission results found for cmd ${cmd}, allowed`,
      )
      return true
    }

    const denied = client.commandPerms.get(permCode).deny
    const allowed = client.commandPerms.get(permCode).allow

    // Deny if the member is explicitly denied
    if (denied.members.indexOf(msg.author.id) !== -1) {
      logger.debug('Member DENIED')
      return false
    }

    // Allow if the member is explicitly allowed
    if (allowed.members.indexOf(msg.author.id) !== -1) {
      logger.debug('Member ALLOWED')
      return true
    }

    // Deny if the command is run in an explicitly denied channel
    if (denied.channels.indexOf(msg.channel.id) !== -1) {
      logger.debug('Channel DENIED')
      return false
    }

    // Allow if the command is run in an explicitly allowed channel
    if (allowed.channels.indexOf(msg.channel.id) !== -1) {
      logger.debug('Channel ALLOWED')
      return true
    }

    const roles = msg.member.roles
    let foundRole = null
    roles.cache.forEach((r) => {
      // Deny if the message owner has a role that is explicitly denied
      if (denied.roles.indexOf(r.id) !== -1) {
        logger.debug('Role DENIED')
        foundRole = false
      }
      // Allow if the message owner has a role that is explicitly allowed
      if (allowed.roles.indexOf(r.id) !== -1) {
        logger.debug('Role ALLOWED')
        foundRole = true
      }
    })

    if (foundRole !== null) {
      return foundRole
    }

    logger.debug('No Permissions found, allowing')
    return true
  }

  return {
    check,
  }
}
<|endoftext|>


<commit_before>'use strict';
const
  device  = require('./../utils/device');

var phantomTask = function(attributes) {
    var running  = false;
    var size     = undefined;

    var page     = attributes.page;
    var hash     = attributes.hash;
    var request  = attributes.request;
    var response = attributes.response;

    var execute = function() {
        running = true;
        _fetchAndRenderPage();
    };

    // FETCH AND RENDER PAGE (RECURSIVE),
    var _fetchAndRenderPage = function() {
        console.log("Swayzee has is now stalking ", hash);
        size = device.calcScreenSize(request);
        page.set('viewportSize', size);

        page.evaluate(function(hash){
            var previousTitle = document.title;

            window.prerenderReady = false;
            window.location.hash = hash;

            function isRenderReady() {
                console.log(hash+" is render Ready ? "+window.prerenderReady)
                if( window.prerenderReady === false ) {
                    setTimeout(isRenderReady, 50);
                } else {
                    setTimeout(function() {
                        console.log("PEF: "+hash+" -@- "+document.documentElement.innerHTML);
                        document.title = document.title+' processed '+Date.now()
                        return;
                    }, 1500);
                }
            }
            isRenderReady();

        }, function (result) {}, hash);
    };

    return {
        hash     : hash,
        running  : running,
        request  : request,
        response : response,
        execute  : execute
    }
}
module.exports = phantomTask;<commit_msg>Update phantomTask.js<commit_after>'use strict';
const
  device  = require('./../utils/device');

var phantomTask = function(attributes) {
    var running  = false;
    var size     = undefined;

    var page     = attributes.page;
    var hash     = attributes.hash;
    var request  = attributes.request;
    var response = attributes.response;

    var execute = function() {
        running = true;
        _fetchAndRenderPage();
    };

    // FETCH AND RENDER PAGE (RECURSIVE),
    var _fetchAndRenderPage = function() {
        console.log("Swayzee has is now stalking ", hash);
        size = device.calcScreenSize(request);
        page.set('viewportSize', size);

        page.evaluate(function(hash){
            var previousTitle = document.title;

            window.prerenderReady = false;
            window.location.hash = hash;

            function isRenderReady() {
                console.log(hash+" is render Ready ? "+window.prerenderReady)
                if( window.prerenderReady === false ) {
                    setTimeout(isRenderReady, 50);
                } else {
                    setTimeout(function() {
                        console.log("PEF: "+hash+" -@- "+document.documentElement.innerHTML);
                        window.location.hash = "";
                        return;
                    }, 1500);
                }
            }
            isRenderReady();

        }, function (result) {}, hash);
    };

    return {
        hash     : hash,
        running  : running,
        request  : request,
        response : response,
        execute  : execute
    }
}
module.exports = phantomTask;
<|endoftext|>


<commit_before>
/*!
 * Cluster - cli
 * Copyright (c) 2011 LearnBoost <dev@learnboost.com>
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var fs = require('fs')
  , Log = require('log')
  , ESRCH = process.version >= 'v0.4.0'
    ? require('constants').ESRCH
    : process.ESRCH

/**
 * Commands.
 */

var commands = [];

/**
 * Adds a command-line interface to your cluster.
 *
 * This plugin requires that you use `pidfiles()`
 * above `cli()`, so that the pidfile directory
 * is exposed.
 *
 * Examples:
 *
 *     cluster(server)
 *       .use(cluster.pidfiles())
 *       .use(cluster.cli())
 *       .listen(3000);
 *
 * Once set up our server script serves as both
 * the master, and the CLI. For example we may
 * still launch the server(s) as shown below.
 *
 *      $ nohup node server.js &
 *
 * However now we may also utilize commands
 * provided by this plugin.
 *
 *     $ node server.js status
 *
 *       master 3281 dead
 *       worker 0 3282 dead
 *
 * For more command information use `--help`.
 *
 *     $ node server.js --help
 *
 * @return {Function}
 * @api public
 */

exports = module.exports = function(){
  return function(master){
    requirePIDs(master);

    var args = process.argv.slice(2)
      , len = commands.length
      , command
      , arg;

    // augment master
    master.getPID = function(){
      var dir = master.pidfiles
        , path = dir + '/master.pid'
        , pid = fs.readFileSync(path, 'ascii');
      return parseInt(pid, 10);
    };

    // parse arguments
    while (args.length) {
      arg = args.shift();
      for (var i = 0; i < len; ++i) {
        command = commands[i];
        if (~command.flags.indexOf(arg)) {
          command.callback(master);
        }
      }
    }
  }
};

/**
 * Define command `name` with the given callback `fn(master)`
 * and a short `desc`.
 *
 * @param {String} name
 * @param {Function} fn
 * @param {String} desc
 * @return {Object} exports for chaining
 * @api public
 */

var define = exports.define = function(name, fn, desc){
  commands.push({
      flags: name.split(/ *, */)
    , desc: desc
    , callback: fn
  });
  return exports;
};

/**
 * Report master / worker status based on
 * the PID files saved by the pidfiles()
 * plugin.
 */

define('-s, --status, status', function(master){
  var dir = master.pidfiles
    , files = fs.readdirSync(dir);

  console.log('status is temporarily disabled until a bug in node is fixed');
  process.exit();

  console.log();

  // only pids
  files.filter(function(file){
    return file.match(/\.pid$/);
  // check status
  }).forEach(function(file){
    var path = dir + '/' + file
      , pid = fs.readFileSync(path, 'ascii')
      , pid = parseInt(pid, 10)
      , name = file.replace('.pid', '').replace('.', ' ')
      , color
      , status;

    try {
      process.kill(pid, 0);
      status = 'alive';
      color = '36';
    } catch (err) {
      if (ESRCH == err.errno) {
        color = '31';
        status = 'dead';
      } else {
        throw err;
      }
    }

    console.log('  %s\033[90m %d\033[0m \033[' + color + 'm%s\033[0m'
      , name
      , pid
      , status);
  });

  console.log();
  process.exit();
}, 'Output cluster status');

/**
 * Restart workers.
 */

define('-r, --restart, restart', function(master){
  process.kill(master.getPID(), 'SIGUSR2');
  process.exit();
}, 'Restart master by sending the SIGUSR2 signal');

/**
 * Graceful shutdown.
 */

define('-s, --shutdown, shutdown', function(master){
  process.kill(master.getPID(), 'SIGQUIT');
  process.exit();
}, 'Graceful shutdown by sending the SIGQUIT signal');

/**
 * Hard shutdown.
 */

define('-S, --stop, stop', function(master){
  process.kill(master.getPID(), 'SIGTERM');
  process.exit();
}, 'Hard shutdown by sending the SIGTERM signal');

/**
 * Display help information.
 */

define('-h, --help, help', function(master){
  console.log('\n  Usage: node <file> <command>\n');
  commands.forEach(function(command){
    console.log('    '
      + command.flags.join(', ')
      + '\n    '
      + '\033[90m' + command.desc + '\033[0m'
      + '\n');
  });
  console.log();
  process.exit();
}, 'Show help information');

/**
 * Output cluster version.
 */

define('-v, --version', function(master){
  console.log(require('../cluster').version);
  process.exit();
}, 'Output cluster version');

/**
 * Require `pidfiles()` plugin.
 *
 * @param {Master} master
 * @api private
 */

function requirePIDs(master) {
  if (master.pidfiles) return;
  throw new Error('cli() plugin requires pidfiles(), please add pidfiles() before cli()');
}<commit_msg>output error on status when node < 0.4.1<commit_after>
/*!
 * Cluster - cli
 * Copyright (c) 2011 LearnBoost <dev@learnboost.com>
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var fs = require('fs')
  , Log = require('log')
  , ESRCH = process.version >= 'v0.4.0'
    ? require('constants').ESRCH
    : process.ESRCH

/**
 * Commands.
 */

var commands = [];

/**
 * Adds a command-line interface to your cluster.
 *
 * This plugin requires that you use `pidfiles()`
 * above `cli()`, so that the pidfile directory
 * is exposed.
 *
 * Examples:
 *
 *     cluster(server)
 *       .use(cluster.pidfiles())
 *       .use(cluster.cli())
 *       .listen(3000);
 *
 * Once set up our server script serves as both
 * the master, and the CLI. For example we may
 * still launch the server(s) as shown below.
 *
 *      $ nohup node server.js &
 *
 * However now we may also utilize commands
 * provided by this plugin.
 *
 *     $ node server.js status
 *
 *       master 3281 dead
 *       worker 0 3282 dead
 *
 * For more command information use `--help`.
 *
 *     $ node server.js --help
 *
 * @return {Function}
 * @api public
 */

exports = module.exports = function(){
  return function(master){
    requirePIDs(master);

    var args = process.argv.slice(2)
      , len = commands.length
      , command
      , arg;

    // augment master
    master.getPID = function(){
      var dir = master.pidfiles
        , path = dir + '/master.pid'
        , pid = fs.readFileSync(path, 'ascii');
      return parseInt(pid, 10);
    };

    // parse arguments
    while (args.length) {
      arg = args.shift();
      for (var i = 0; i < len; ++i) {
        command = commands[i];
        if (~command.flags.indexOf(arg)) {
          command.callback(master);
        }
      }
    }
  }
};

/**
 * Define command `name` with the given callback `fn(master)`
 * and a short `desc`.
 *
 * @param {String} name
 * @param {Function} fn
 * @param {String} desc
 * @return {Object} exports for chaining
 * @api public
 */

var define = exports.define = function(name, fn, desc){
  commands.push({
      flags: name.split(/ *, */)
    , desc: desc
    , callback: fn
  });
  return exports;
};

/**
 * Report master / worker status based on
 * the PID files saved by the pidfiles()
 * plugin.
 */

define('-s, --status, status', function(master){
  var dir = master.pidfiles
    , files = fs.readdirSync(dir);

  // null signal failed previous
  // to this release
  if (process.version < 'v0.4.1') {
    console.log('status will not work with node < 0.4.1');
    console.log('due to SIGTERM globbering the null signal');
    process.exit(1);
  }

  console.log();

  // only pids
  files.filter(function(file){
    return file.match(/\.pid$/);
  // check status
  }).forEach(function(file){
    var path = dir + '/' + file
      , pid = fs.readFileSync(path, 'ascii')
      , pid = parseInt(pid, 10)
      , name = file.replace('.pid', '').replace('.', ' ')
      , color
      , status;

    try {
      process.kill(pid, 0);
      status = 'alive';
      color = '36';
    } catch (err) {
      if (ESRCH == err.errno) {
        color = '31';
        status = 'dead';
      } else {
        throw err;
      }
    }

    console.log('  %s\033[90m %d\033[0m \033[' + color + 'm%s\033[0m'
      , name
      , pid
      , status);
  });

  console.log();
  process.exit();
}, 'Output cluster status');

/**
 * Restart workers.
 */

define('-r, --restart, restart', function(master){
  process.kill(master.getPID(), 'SIGUSR2');
  process.exit();
}, 'Restart master by sending the SIGUSR2 signal');

/**
 * Graceful shutdown.
 */

define('-s, --shutdown, shutdown', function(master){
  process.kill(master.getPID(), 'SIGQUIT');
  process.exit();
}, 'Graceful shutdown by sending the SIGQUIT signal');

/**
 * Hard shutdown.
 */

define('-S, --stop, stop', function(master){
  process.kill(master.getPID(), 'SIGTERM');
  process.exit();
}, 'Hard shutdown by sending the SIGTERM signal');

/**
 * Display help information.
 */

define('-h, --help, help', function(master){
  console.log('\n  Usage: node <file> <command>\n');
  commands.forEach(function(command){
    console.log('    '
      + command.flags.join(', ')
      + '\n    '
      + '\033[90m' + command.desc + '\033[0m'
      + '\n');
  });
  console.log();
  process.exit();
}, 'Show help information');

/**
 * Output cluster version.
 */

define('-v, --version', function(master){
  console.log(require('../cluster').version);
  process.exit();
}, 'Output cluster version');

/**
 * Require `pidfiles()` plugin.
 *
 * @param {Master} master
 * @api private
 */

function requirePIDs(master) {
  if (master.pidfiles) return;
  throw new Error('cli() plugin requires pidfiles(), please add pidfiles() before cli()');
}<|endoftext|>


<commit_before>
/*!
 * Cluster - cli
 * Copyright (c) 2011 LearnBoost <dev@learnboost.com>
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var fs = require('fs')
  , Log = require('log');

/**
 * Commands.
 */

var commands = [];

/**
 * Adds a command-line interface to your cluster.
 *
 * This plugin requires that you use `pidfiles()`
 * above `cli()`, so that the pidfile directory
 * is exposed.
 *
 * Examples:
 *
 *     cluster(server)
 *       .use(cluster.pidfiles())
 *       .use(cluster.cli())
 *       .listen(3000);
 *
 * Once set up our server script serves as both
 * the master, and the CLI. For example we may
 * still launch the server(s) as shown below.
 *
 *      $ nohup node server.js &
 *
 * However now we may also utilize commands
 * provided by this plugin.
 *
 *     $ node server.js status
 *
 *       master 3281 dead
 *       worker 0 3282 dead
 *
 * For more command information use `--help`.
 *
 *     $ node server.js --help
 *
 * @return {Function}
 * @api public
 */

exports = module.exports = function(){
  return function(master){
    requirePIDs(master);

    // augment master
    master.killall = function(sig){
      var pid = master.pidof('master');
      try {
        // signal master
        process.kill(pid, sig);
      } catch (err) {
        if ('ESRCH' != err.code) throw err;
        // signal children individually
        master.workerpids().forEach(function(pid){
          try {
            process.kill(pid, sig);
          } catch (err) {
            if ('ESRCH' != err.code) throw err;
          }
        });
      }
    };

    var args = process.argv.slice(2)
      , len = commands.length
      , command
      , arg;

    // parse arguments
    while (args.length) {
      arg = args.shift();
      for (var i = 0; i < len; ++i) {
        command = commands[i];
        if (~command.flags.indexOf(arg)) {
          command.callback(master);
          process.exit(0);
        }
      }
    }
  }
};

/**
 * Define command `name` with the given callback `fn(master)`
 * and a short `desc`.
 *
 * @param {String} name
 * @param {Function} fn
 * @param {String} desc
 * @return {Object} exports for chaining
 * @api public
 */

var define = exports.define = function(name, fn, desc){
  commands.push({
      flags: name.split(/ *, */)
    , desc: desc
    , callback: fn
  });
  return exports;
};

/**
 * Report master / worker status based on
 * the PID files saved by the pidfiles()
 * plugin.
 */

define('-s, --status, status', function(master){
  var dir = master.pidfiles
    , files = fs.readdirSync(dir);

  // null signal failed previous
  // to this release
  if (process.version < 'v0.4.1') {
    console.log('status will not work with node < 0.4.1');
    console.log('due to SIGTERM globbering the null signal');
    process.exit(1);
  }

  console.log();

  // only pids
  files.filter(function(file){
    return file.match(/\.pid$/);
  // check status
  }).forEach(function(file){
    var name = file.replace('.pid', '')
      , pid = master.pidof(name)
      , name = name.replace('.', ' ')
      , color
      , status;

    try {
      process.kill(pid, 0);
      status = 'alive';
      color = '36';
    } catch (err) {
      if ('ESRCH' == err.code) {
        color = '31';
        status = 'dead';
      } else {
        throw err;
      }
    }

    console.log('  %s\033[90m %d\033[0m \033[' + color + 'm%s\033[0m'
      , name
      , pid
      , status);
  });

  console.log();
}, 'Output cluster status');

/**
 * Restart workers.
 */

define('-r, --restart, restart', function(master){
  master.killall('SIGUSR2');
}, 'Restart master by sending the SIGUSR2 signal');

/**
 * Graceful shutdown.
 */

define('-g, --shutdown, shutdown', function(master){
  master.killall('SIGQUIT');
}, 'Graceful shutdown by sending the SIGQUIT signal');

/**
 * Hard shutdown.
 */

define('-S, --stop, stop', function(master){
  master.killall('SIGTERM');
}, 'Hard shutdown by sending the SIGTERM signal');

/**
 * Display help information.
 */

define('-h, --help, help', function(master){
  console.log('\n  Usage: node <file> <command>\n');
  commands.forEach(function(command){
    console.log('    '
      + command.flags.join(', ')
      + '\n    '
      + '\033[90m' + command.desc + '\033[0m'
      + '\n');
  });
  console.log();
}, 'Show help information');

/**
 * Output cluster version.
 */

define('-v, --version', function(master){
  console.log(require('../cluster').version);
}, 'Output cluster version');

/**
 * Require `pidfiles()` plugin.
 *
 * @param {Master} master
 * @api private
 */

function requirePIDs(master) {
  if (master.pidfiles) return;
  throw new Error('cli() plugin requires pidfiles(), please add pidfiles() before cli()');
}<commit_msg>use preventDefault instead of exit() in cli()<commit_after>
/*!
 * Cluster - cli
 * Copyright (c) 2011 LearnBoost <dev@learnboost.com>
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var fs = require('fs')
  , Log = require('log');

/**
 * Commands.
 */

var commands = [];

/**
 * Adds a command-line interface to your cluster.
 *
 * This plugin requires that you use `pidfiles()`
 * above `cli()`, so that the pidfile directory
 * is exposed.
 *
 * Examples:
 *
 *     cluster(server)
 *       .use(cluster.pidfiles())
 *       .use(cluster.cli())
 *       .listen(3000);
 *
 * Once set up our server script serves as both
 * the master, and the CLI. For example we may
 * still launch the server(s) as shown below.
 *
 *      $ nohup node server.js &
 *
 * However now we may also utilize commands
 * provided by this plugin.
 *
 *     $ node server.js status
 *
 *       master 3281 dead
 *       worker 0 3282 dead
 *
 * For more command information use `--help`.
 *
 *     $ node server.js --help
 *
 * @return {Function}
 * @api public
 */

exports = module.exports = function(){
  return function(master){
    requirePIDs(master);

    // augment master
    master.killall = function(sig){
      var pid = master.pidof('master');
      try {
        // signal master
        process.kill(pid, sig);
      } catch (err) {
        if ('ESRCH' != err.code) throw err;
        // signal children individually
        master.workerpids().forEach(function(pid){
          try {
            process.kill(pid, sig);
          } catch (err) {
            if ('ESRCH' != err.code) throw err;
          }
        });
      }
    };

    var args = process.argv.slice(2)
      , len = commands.length
      , command
      , arg;

    // parse arguments
    while (args.length) {
      arg = args.shift();
      for (var i = 0; i < len; ++i) {
        command = commands[i];
        if (~command.flags.indexOf(arg)) {
          command.callback(master);
          master.preventDefault = true;
        }
      }
    }
  }
};

/**
 * Define command `name` with the given callback `fn(master)`
 * and a short `desc`.
 *
 * @param {String} name
 * @param {Function} fn
 * @param {String} desc
 * @return {Object} exports for chaining
 * @api public
 */

var define = exports.define = function(name, fn, desc){
  commands.push({
      flags: name.split(/ *, */)
    , desc: desc
    , callback: fn
  });
  return exports;
};

/**
 * Report master / worker status based on
 * the PID files saved by the pidfiles()
 * plugin.
 */

define('-s, --status, status', function(master){
  var dir = master.pidfiles
    , files = fs.readdirSync(dir);

  // null signal failed previous
  // to this release
  if (process.version < 'v0.4.1') {
    console.log('status will not work with node < 0.4.1');
    console.log('due to SIGTERM globbering the null signal');
    process.exit(1);
  }

  console.log();

  // only pids
  files.filter(function(file){
    return file.match(/\.pid$/);
  // check status
  }).forEach(function(file){
    var name = file.replace('.pid', '')
      , pid = master.pidof(name)
      , name = name.replace('.', ' ')
      , color
      , status;

    try {
      process.kill(pid, 0);
      status = 'alive';
      color = '36';
    } catch (err) {
      if ('ESRCH' == err.code) {
        color = '31';
        status = 'dead';
      } else {
        throw err;
      }
    }

    console.log('  %s\033[90m %d\033[0m \033[' + color + 'm%s\033[0m'
      , name
      , pid
      , status);
  });

  console.log();
}, 'Output cluster status');

/**
 * Restart workers.
 */

define('-r, --restart, restart', function(master){
  master.killall('SIGUSR2');
}, 'Restart master by sending the SIGUSR2 signal');

/**
 * Graceful shutdown.
 */

define('-g, --shutdown, shutdown', function(master){
  master.killall('SIGQUIT');
}, 'Graceful shutdown by sending the SIGQUIT signal');

/**
 * Hard shutdown.
 */

define('-S, --stop, stop', function(master){
  master.killall('SIGTERM');
}, 'Hard shutdown by sending the SIGTERM signal');

/**
 * Display help information.
 */

define('-h, --help, help', function(master){
  console.log('\n  Usage: node <file> <command>\n');
  commands.forEach(function(command){
    console.log('    '
      + command.flags.join(', ')
      + '\n    '
      + '\033[90m' + command.desc + '\033[0m'
      + '\n');
  });
  console.log();
}, 'Show help information');

/**
 * Output cluster version.
 */

define('-v, --version', function(master){
  console.log(require('../cluster').version);
}, 'Output cluster version');

/**
 * Require `pidfiles()` plugin.
 *
 * @param {Master} master
 * @api private
 */

function requirePIDs(master) {
  if (master.pidfiles) return;
  throw new Error('cli() plugin requires pidfiles(), please add pidfiles() before cli()');
}<|endoftext|>


<commit_before>/*
 *  Copyright 2011 Rackspace
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

var async = require('async');
var irc = require('irc');

var sprintf = require('util/sprintf');

exports.run = function(dreadnot) {
  var clients = {},
      joined = {},
      config = dreadnot.config.plugins.irc;

  async.forEach(Object.keys(config.channels), function(network, callback) {
    var client = new irc.Client(network, config.nick);

    client.on('registered', function() {
      var i;

      clients[network] = client;
      joined[network] = [];

      client.on('join', function(channel, nick) {
        if (nick === config.nick) {
          joined[network].push(channel);
        }
      });

      for (i = 0; i < config.channels[network].length; i++) {
        clients[network].join(config.channels[network][i]);
      }
    });
  });

  dreadnot.emitter.on('deployments', function(deployment) {
    var link = sprintf('%s/stacks/%s/regions/%s/deployments/%s', dreadnot.config.default_url, deployment.stack, deployment.region,
                          deployment.deployment),
        msg = sprintf('%s is deploying %s to %s:%s - %s', deployment.user, deployment.stack, dreadnot.config.env,
                          deployment.region, link),
        endPath = ['stacks', deployment.stack, 'regions', deployment.region, 'deployments', deployment.deployment, 'end'].join('.'),
        i;
    onEach(clients, joined, function(client, channel) {
      client.notice(channel, msg);
    });

    dreadnot.emitter.once(endPath, function(success) {
      var endMsg = sprintf('deployment #%s of %s to %s:%s %s', deployment.deployment, deployment.stack, dreadnot.config.env,
          deployment.region, success ? 'succeeded' : '\u0002FAILED\u000f');
      onEach(clients, joined, function(client, channel) {
        client.notice(channel, endMsg);
      });
    });
  });
};

function onEach(clients, joined, fn) {
  var network, i;
  for (network in joined) {
    for (i = 0; i < joined[network].length; i++) {
      fn(clients[network], joined[network][i]);
    }
  }
}
<commit_msg>allow specification of irc client options<commit_after>/*
 *  Copyright 2011 Rackspace
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

var async = require('async');
var irc = require('irc');

var sprintf = require('util/sprintf');

exports.run = function(dreadnot) {
  var clients = {},
      joined = {},
      config = dreadnot.config.plugins.irc;

  async.forEach(Object.keys(config.channels), function(network, callback) {
    var client = new irc.Client(network, config.nick, config.connect_opts ? config.connect_opts[network] : undefined);

    client.on('registered', function() {
      var i;

      clients[network] = client;
      joined[network] = [];

      client.on('join', function(channel, nick) {
        if (nick === config.nick) {
          joined[network].push(channel);
        }
      });

      for (i = 0; i < config.channels[network].length; i++) {
        clients[network].join(config.channels[network][i]);
      }
    });
  });

  dreadnot.emitter.on('deployments', function(deployment) {
    var link = sprintf('%s/stacks/%s/regions/%s/deployments/%s', dreadnot.config.default_url, deployment.stack, deployment.region,
                          deployment.deployment),
        msg = sprintf('%s is deploying %s to %s:%s - %s', deployment.user, deployment.stack, dreadnot.config.env,
                          deployment.region, link),
        endPath = ['stacks', deployment.stack, 'regions', deployment.region, 'deployments', deployment.deployment, 'end'].join('.'),
        i;
    onEach(clients, joined, function(client, channel) {
      client.notice(channel, msg);
    });

    dreadnot.emitter.once(endPath, function(success) {
      var endMsg = sprintf('deployment #%s of %s to %s:%s %s', deployment.deployment, deployment.stack, dreadnot.config.env,
          deployment.region, success ? 'succeeded' : '\u0002FAILED\u000f');
      onEach(clients, joined, function(client, channel) {
        client.notice(channel, endMsg);
      });
    });
  });
};

function onEach(clients, joined, fn) {
  var network, i;
  for (network in joined) {
    for (i = 0; i < joined[network].length; i++) {
      fn(clients[network], joined[network][i]);
    }
  }
}
<|endoftext|>


<commit_before>/* eslint-env node */
"use strict";

/*
  This is the beginning of an experimental code transform for cardstack templates.

  It runs on any template that appears under
  templatse/components/cardstack. It transforms direct property
  bindings like:

    {{content.title}}

  Into simple cs-field calls like:

    {{cs-field content "title"}}

  It also transforms attribute assigments into cs-field calls. For example:

  becomes

    {{#cs-field content "imageUrl" as |param1|}}{{/cs-field}}

  It does this in order to annotate those fields for editable by the cardstack tools.

*/

function BindTransform({ moduleName }) {
  this.moduleName = moduleName;
  this.syntax = null;
}

BindTransform.prototype.transform = function(ast) {
  if (/\btemplates\/components\/cardstack\//.test(this.moduleName) ||
      /\btemplates\/(embedded|isolated).hbs/.test(this.moduleName)){
    var b = this.syntax.builders;
    var foundBlockParams = collectBlockParams(ast);

    this.syntax.traverse(ast, {
      ElementNode(node) {
        var contentProperty,
            unusedBlockParam,
            foundDynamicContent = false,
            newAttributes = [];

        var tag = node.tag;
        for (var i=0; i < node.attributes.length; i++) {
          var nodeAttributes = node.attributes[i];
          var name = nodeAttributes.name;
          var value = nodeAttributes.value;
          if (!value) {
            break;
          }

          if (value.type === 'MustacheStatement') {
            var path = value.path;
            var parts = path.parts;
            if (parts.length === 2 && parts[0] === 'content') {
              foundDynamicContent = true;
              // contentProperty is the property that is looked up on content
              // (e.g `imageUrl` in the case of `content.imageUrl`)
              unusedBlockParam = getUnusedBlockParam(foundBlockParams);
              contentProperty = parts[1];
              newAttributes.push(b.attr(name, b.mustache(b.path(unusedBlockParam))));
            } else {
              newAttributes.push(b.attr(name, value));
            }
          } else if (nodeAttributes.type === 'AttrNode') {
            newAttributes.push(b.attr(name, value));
          } else if (nodeAttributes.type === 'TextNode') {
            newAttributes.push(b.attr(name, value.chars));
          }
        }

        if (foundDynamicContent) {
          var newTag = b.element(tag, newAttributes, []);
          var blockWithParam = b.program([newTag], [unusedBlockParam]);
          let block = b.block(b.path('cs-field'), [
            b.path("content"), b.string(contentProperty)
          ], b.hash(), blockWithParam);
          return block;
        }

        return node;
      },

      MustacheStatement(node) {
        if (node.path.parts.length === 2 && node.path.parts[0] === 'content') {
          return b.mustache(b.path("cs-field"), [b.path("content"), b.string(node.path.parts[1])]);
        }
      }
    });
  }
  return ast;
};

function getUnusedBlockParam(foundNames) {
  var foundName = false;
  var i = 0;
  var paramName;

  do {
    i++;
    paramName = 'param' + i;
    if (foundNames.indexOf(paramName) === -1) {
      foundName = true;
    }
  } while (!foundName);

  return paramName;
}

function collectBlockParams(ast, foundBlockParams) {
  if (!foundBlockParams) {
    foundBlockParams = [];
  }

  for (var i=0; i<ast.body.length; i++) {
    var tree = ast.body[i];
    var program = tree.program;
    if (program) {
      foundBlockParams = foundBlockParams.concat(program.blockParams);
      return collectBlockParams(program, foundBlockParams);
    }
  }

  return foundBlockParams;
}

module.exports = BindTransform;
<commit_msg>rendering: Convert `BindTransform` to ES6 class<commit_after>/* eslint-env node */
"use strict";

/*
  This is the beginning of an experimental code transform for cardstack templates.

  It runs on any template that appears under
  templatse/components/cardstack. It transforms direct property
  bindings like:

    {{content.title}}

  Into simple cs-field calls like:

    {{cs-field content "title"}}

  It also transforms attribute assigments into cs-field calls. For example:

  becomes

    {{#cs-field content "imageUrl" as |param1|}}{{/cs-field}}

  It does this in order to annotate those fields for editable by the cardstack tools.

*/

class BindTransform {
  constructor({ moduleName }) {
    this.moduleName = moduleName;
    this.syntax = null;
  }

  transform(ast) {
    if (/\btemplates\/components\/cardstack\//.test(this.moduleName) ||
      /\btemplates\/(embedded|isolated).hbs/.test(this.moduleName)){
      var b = this.syntax.builders;
      var foundBlockParams = collectBlockParams(ast);

      this.syntax.traverse(ast, {
        ElementNode(node) {
          var contentProperty,
            unusedBlockParam,
            foundDynamicContent = false,
            newAttributes = [];

          var tag = node.tag;
          for (var i=0; i < node.attributes.length; i++) {
            var nodeAttributes = node.attributes[i];
            var name = nodeAttributes.name;
            var value = nodeAttributes.value;
            if (!value) {
              break;
            }

            if (value.type === 'MustacheStatement') {
              var path = value.path;
              var parts = path.parts;
              if (parts.length === 2 && parts[0] === 'content') {
                foundDynamicContent = true;
                // contentProperty is the property that is looked up on content
                // (e.g `imageUrl` in the case of `content.imageUrl`)
                unusedBlockParam = getUnusedBlockParam(foundBlockParams);
                contentProperty = parts[1];
                newAttributes.push(b.attr(name, b.mustache(b.path(unusedBlockParam))));
              } else {
                newAttributes.push(b.attr(name, value));
              }
            } else if (nodeAttributes.type === 'AttrNode') {
              newAttributes.push(b.attr(name, value));
            } else if (nodeAttributes.type === 'TextNode') {
              newAttributes.push(b.attr(name, value.chars));
            }
          }

          if (foundDynamicContent) {
            var newTag = b.element(tag, newAttributes, []);
            var blockWithParam = b.program([newTag], [unusedBlockParam]);
            let block = b.block(b.path('cs-field'), [
              b.path("content"), b.string(contentProperty)
            ], b.hash(), blockWithParam);
            return block;
          }

          return node;
        },

        MustacheStatement(node) {
          if (node.path.parts.length === 2 && node.path.parts[0] === 'content') {
            return b.mustache(b.path("cs-field"), [b.path("content"), b.string(node.path.parts[1])]);
          }
        }
      });
    }
    return ast;
  }
}

function getUnusedBlockParam(foundNames) {
  var foundName = false;
  var i = 0;
  var paramName;

  do {
    i++;
    paramName = 'param' + i;
    if (foundNames.indexOf(paramName) === -1) {
      foundName = true;
    }
  } while (!foundName);

  return paramName;
}

function collectBlockParams(ast, foundBlockParams) {
  if (!foundBlockParams) {
    foundBlockParams = [];
  }

  for (var i=0; i<ast.body.length; i++) {
    var tree = ast.body[i];
    var program = tree.program;
    if (program) {
      foundBlockParams = foundBlockParams.concat(program.blockParams);
      return collectBlockParams(program, foundBlockParams);
    }
  }

  return foundBlockParams;
}

module.exports = BindTransform;
<|endoftext|>


<commit_before>const Session = require('@cardstack/plugin-utils/session');
const { get, uniq } = require('lodash');

async function getRoute(searchers, router, branch, path, applicationCard) {
  let query;
  let { route: matchedRoute,
    remainingPath,
    params,
    routingCard,
    routingCardsCache,
    allowedQueryParams } = await routeThatMatchesPath(searchers, router, branch, path, applicationCard);

  if (!matchedRoute) { return; }

  routingCard = routingCard || applicationCard;

  if (matchedRoute.query) {
    query = resolveReplacementTagsFromPath(matchedRoute, path, JSON.stringify(matchedRoute.query));
    if (routingCard) {
      query = resolveRoutingCardReplacementTags(routingCard, query, routingCardsCache);
    }
  }

  let routeStack = [ `${routingCard.data.type}/${routingCard.data.id}` ].concat(matchedRoute.routeStack.map(({contentType}) => {
    let card = routingCardsCache[`card::${contentType}`];
    if (!card) { return; }
    return `${card.data.type}/${card.data.id}`;
  }));

  if (routeStack.filter(r => !r).length) {
    // interior routing card cannot be found, consider the route unmatched
    return;
  }

  return {
    query: query && JSON.parse(query),
    matchedRoute,
    remainingPath,
    params,
    allowedQueryParams,
    routingCard,
    routeStack
  };
}

function isCanonicalRoute(route) {
  if (!route.query ||
      route.path === '/' ||
      typeof route.query.filter !== 'object' ||
      Object.keys(route.query.filter).length !== 2) { return false; }

  let filter = route.query.filter;
  let typePredicate = filter.type;
  let idPredicate = filter.id;

  if (!typePredicate || !typePredicate.exact) { return false; }

  if (idPredicate && idPredicate.exact) { return true; }

  let uniqueFieldName = Object.keys(filter).find(i => i !== 'type');

  return uniqueFieldName.exact === ':friendly_id';
}

async function buildRoutingCardsCache({ searchers, branch, resolvedPath, context, applicationCard, routeStack=[] }) {
  if (!searchers || !branch) { return {}; }

  let routingCards = {};
  let typeRegex = /:(?!card:)[\w-]+\[([^\]]+)\]/g;
  let types = uniq((context.match(typeRegex) || []).map(m => m.replace(typeRegex, '$1')));

  for (let type of types) {
    if (!routingCards[type]) {
      let cardRoute;
      let stackIndex = routeStack.findIndex(r => r.contentType === type && r.query);
      let fieldRoute = routeStack[stackIndex];

      if (stackIndex === routeStack.length - 1) {
        routingCards[`card::${type}`] = applicationCard;
      } else {
        cardRoute = routeStack[stackIndex + 1];
      }

      for (let route of [cardRoute, fieldRoute].filter(i => Boolean(i))) {
        if (!route.query) { continue; }

        let query = resolvedPath ? resolveReplacementTagsFromPath(route, resolvedPath, JSON.stringify(route.query)) : JSON.stringify(route.query);
        if (query.includes(':card:')) {
          query = resolveRoutingCardReplacementTags(routingCards[`card::${route.contentType}`], query, routingCards);
        }
        let { data: cards } = await searchers.search(Session.INTERNAL_PRIVILEGED, branch, JSON.parse(query));
        if (cards.length) {
          if (route === cardRoute) {
            routingCards[`card::${type}`] = { data: cards[0] };
          } else {
            routingCards[`field::${type}`] = { data: cards[0] };
          }
        }
      }
    }
  }

  return routingCards;
}

function replaceNamespacedField(routingCard, routingCards, keyPrefix) {
  return (match, field, typeTag, type) => {
    let card = routingCards[keyPrefix + type] || routingCard;
    if (!card) { return match; }

    if (field === 'id') {
      return card.data.id;
    } else if (field === 'type') {
      return card.data.type;
    }
    let attr = get(card, `data.attributes.${field}`);
    if (attr != null) {
      return attr;
    } else {
      return match;
    }
  };
}

function resolveRoutingCardReplacementTags(routingCard, stringToResolve, routingCards={}) {
  if (!stringToResolve) { return stringToResolve; }

  return stringToResolve
    .replace(/:card:([\w-]+)(\[([^\]]+)\])?/g, replaceNamespacedField(routingCard, routingCards, 'card::'))
    .replace(/:([\w-]+)(\[([^\]]+)\])/g, replaceNamespacedField(routingCard, routingCards, 'field::'));
}

function resolveReplacementTagsFromPath(route, path, string) {
  let result = string;
  let dictionary = buildSubstitutionDictionary(route, path);

  for (let dynamicSegmentName of Object.keys(dictionary)) {
    result = result.replace(new RegExp(`:${dynamicSegmentName.replace(/([[\]])/g, '\\$1')}`, 'g'), dictionary[dynamicSegmentName]);
  }
  return result;
}

async function getRoutingCardForRoute(searchers, route, branch, path, routingCardsCache={}) {
  for (let parentRoute of route.routeStack) {
    if (!parentRoute.query) { continue; }

    let query = resolveReplacementTagsFromPath(parentRoute, path, JSON.stringify(parentRoute.query));
    let routingCardType = route.routeStack.length ? route.routeStack[0].contentType : null;
    query = resolveRoutingCardReplacementTags(routingCardType && routingCardsCache[`card::${routingCardType}`], query, routingCardsCache);
    let { data:routingCards } = await searchers.search(Session.INTERNAL_PRIVILEGED, branch, JSON.parse(query));
    return routingCards.length ? { data: routingCards[0] } : null;
  }
}

function buildSubstitutionDictionary(route, path) {
  let dictionary = {};
  let type = route.contentType;
  let pathPattern = route.namespacedPath;
  let pathSegments = path.split('?')[0].split('/');
  let routeSegments = pathPattern.split('?')[0].split('/');

  // process the path part of the URL
  for (let i = 0; i < routeSegments.length; i++) {
    let routeSegment = routeSegments[i];
    if (routeSegment.charAt(0) === ':') {
      dictionary[routeSegment.replace(':', '')] = decodeURIComponent(pathSegments[i]);
    }
  }

  // process the query param part of the URL
  if (pathPattern.includes('?') && path.includes('?')) {
    let queryParams = path.split('?')[1].split('&');
    let routeQueryParams = pathPattern.split('?')[1].split('&');
    for (let routeQueryParam of routeQueryParams) {
      let nameValuePair, queryParamSplit, name;
      if ((nameValuePair = routeQueryParam.split('=')).length > 1 && (name = nameValuePair[1]).charAt(0) === ':') {
        let queryParamRegex = new RegExp(`${type}\\[${nameValuePair[0]}\\]=`);
        let queryParam = queryParams.find(i => i.match(queryParamRegex));
        if (queryParam && (queryParamSplit = queryParam.split('=')).length > 1) {
          let [, value] = queryParamSplit;
          dictionary[name.replace(':', '')] = decodeURIComponent(value);
        }
      }
    }
  }

  return dictionary;
}

async function routeThatMatchesPath(searchers, router, branch, path, applicationCard) {
  let matchedRoute, routingCard, routingCardsCache;
  let remainingPath = path.split('?')[0];
  let params = {};
  let allowedQueryParams = [];

  for (let route of router) {
    let { path: routePath } = route;
    let pathRegex = new RegExp(`${routePath.split('?')[0].replace(/(:card)?:[^/?#&]+/g, '[^/?#&]+')}`);
    if (decodeURI(path).match(pathRegex)) {
      routingCardsCache = await buildRoutingCardsCache({
        context: route.namespacedPath,
        resolvedPath: path,
        branch,
        searchers,
        routeStack: route.routeStack,
        applicationCard,
      });
      routingCard = await getRoutingCardForRoute(searchers, route, branch, path, routingCardsCache);

      if (routePath.includes(':card')) {
        routePath = resolveRoutingCardReplacementTags(
          routingCard || applicationCard,
          route.namespacedPath,
          routingCardsCache
        );
        pathRegex = new RegExp(`${routePath.split('?')[0].replace(/(:card)?:[^/?#&]+/g, '[^/?#&]+')}`);
        if (!decodeURI(path).match(pathRegex)) {
          continue;
        }
      }

      if (!route.query && (!routingCard || routingCard.data.type !== route.contentType)) { continue; }

      matchedRoute = route;
      remainingPath = decodeURI(remainingPath).replace(pathRegex, '');
      params = buildSubstitutionDictionary(matchedRoute, path);

      // remove params that were included from antecedant routing cards, the only params a card can use are params that derive from its own route
      let keys = Object.keys(params);
      for (let key of keys) {
        if (key.includes('[')) {
          delete params[key];
        }
      }

      if (matchedRoute.additionalParams) {
        let resolvedAdditionalParams = resolveReplacementTagsFromPath(matchedRoute, path, JSON.stringify(matchedRoute.additionalParams));
        params = Object.assign({}, params, JSON.parse(resolveRoutingCardReplacementTags(routingCard || applicationCard, resolvedAdditionalParams, routingCardsCache)));
      }

      if (matchedRoute.path.includes('?')) {
        let routeQueryParams = routePath.split('?')[1];
        allowedQueryParams = routeQueryParams.split('&').map(i => i.split('=')[0]);
      }

      let routePathSegment = resolveReplacementTagsFromPath(matchedRoute, path, matchedRoute.routePathSegment);
      routePathSegment = resolveRoutingCardReplacementTags(routingCard, routePathSegment, routingCardsCache).split("?")[0];
      let matchedQueryParams = [];
      for (let queryParam of allowedQueryParams) {
        if (params[queryParam]) {
          matchedQueryParams.push(`${queryParam}=${params[queryParam]}`);
        }
      }
      params.path = matchedQueryParams.length ? `${routePathSegment}?${matchedQueryParams.join('&')}` : routePathSegment;

      break;
    }
  }

  return { route: matchedRoute, remainingPath, params, allowedQueryParams, routingCard, routingCardsCache };
}

module.exports = {
  getRoute,
  isCanonicalRoute,
  resolveRoutingCardReplacementTags
};<commit_msg>code cleanup<commit_after>const Session = require('@cardstack/plugin-utils/session');
const { get, uniq } = require('lodash');

async function getRoute(searchers, router, branch, path, applicationCard) {
  let query;
  let { route: matchedRoute,
    remainingPath,
    params,
    routingCard,
    routingCardsCache,
    allowedQueryParams } = await routeThatMatchesPath(searchers, router, branch, path, applicationCard);

  if (!matchedRoute) { return; }

  routingCard = routingCard || applicationCard;

  if (matchedRoute.query) {
    query = resolveReplacementTagsFromPath(matchedRoute, path, JSON.stringify(matchedRoute.query));
    if (routingCard) {
      query = resolveRoutingCardReplacementTags(routingCard, query, routingCardsCache);
    }
  }

  let routeStack = [ `${routingCard.data.type}/${routingCard.data.id}` ].concat(matchedRoute.routeStack.map(({contentType}) => {
    let card = get(routingCardsCache, `antecedantResolution.${contentType}`);
    if (!card) { return; }
    return `${card.data.type}/${card.data.id}`;
  }));

  if (routeStack.filter(r => !r).length) {
    // interior routing card cannot be found, consider the route unmatched
    return;
  }

  return {
    query: query && JSON.parse(query),
    matchedRoute,
    remainingPath,
    params,
    allowedQueryParams,
    routingCard,
    routeStack
  };
}

function isCanonicalRoute(route) {
  if (!route.query ||
      route.path === '/' ||
      typeof route.query.filter !== 'object' ||
      Object.keys(route.query.filter).length !== 2) { return false; }

  let filter = route.query.filter;
  let typePredicate = filter.type;
  let idPredicate = filter.id;

  if (!typePredicate || !typePredicate.exact) { return false; }

  if (idPredicate && idPredicate.exact) { return true; }

  let uniqueFieldName = Object.keys(filter).find(i => i !== 'type');

  return uniqueFieldName.exact === ':friendly_id';
}

async function buildRoutingCardsCache({ searchers, branch, resolvedPath, context, applicationCard, routeStack=[] }) {
  if (!searchers || !branch) { return {}; }

  let routingCards = { antecedantResolution: {}, cardResolution: {} };
  let typeRegex = /:(?!card:)[\w-]+\[([^\]]+)\]/g;
  let types = uniq((context.match(typeRegex) || []).map(m => m.replace(typeRegex, '$1')));

  for (let type of types) {
    if (!routingCards[type]) {
      let cardRoute;
      let stackIndex = routeStack.findIndex(r => r.contentType === type && r.query);
      let fieldRoute = routeStack[stackIndex];

      if (stackIndex === routeStack.length - 1) {
        routingCards.antecedantResolution[type] = applicationCard;
      } else {
        cardRoute = routeStack[stackIndex + 1];
      }

      for (let route of [cardRoute, fieldRoute].filter(i => Boolean(i))) {
        if (!route.query) { continue; }

        let query = resolvedPath ? resolveReplacementTagsFromPath(route, resolvedPath, JSON.stringify(route.query)) : JSON.stringify(route.query);
        if (query.includes(':card:')) {
          query = resolveRoutingCardReplacementTags(get(routingCards, `antecedantResolution.${route.contentType}`), query, routingCards);
        }
        let { data: cards } = await searchers.search(Session.INTERNAL_PRIVILEGED, branch, JSON.parse(query));
        if (cards.length) {
          if (route === cardRoute) {
            routingCards.antecedantResolution[type] = { data: cards[0] };
          } else {
            routingCards.cardResolution[type] = { data: cards[0] };
          }
        }
      }
    }
  }

  return routingCards;
}

function replaceNamespacedField(routingCard, routingCards, resolutionType) {
  return (match, field, typeTag, type) => {
    let card = get(routingCards, `${resolutionType}.${type}`) || routingCard;
    if (!card) { return match; }

    if (field === 'id') {
      return card.data.id;
    } else if (field === 'type') {
      return card.data.type;
    }
    let attr = get(card, `data.attributes.${field}`);
    if (attr != null) {
      return attr;
    } else {
      return match;
    }
  };
}

function resolveRoutingCardReplacementTags(routingCard, stringToResolve, routingCards={}) {
  if (!stringToResolve) { return stringToResolve; }

  return stringToResolve
    .replace(/:card:([\w-]+)(\[([^\]]+)\])?/g, replaceNamespacedField(routingCard, routingCards, 'antecedantResolution'))
    .replace(/:([\w-]+)(\[([^\]]+)\])/g, replaceNamespacedField(routingCard, routingCards, 'cardResolution'));
}

function resolveReplacementTagsFromPath(route, path, string) {
  let result = string;
  let dictionary = buildSubstitutionDictionary(route, path);

  for (let dynamicSegmentName of Object.keys(dictionary)) {
    result = result.replace(new RegExp(`:${dynamicSegmentName.replace(/([[\]])/g, '\\$1')}`, 'g'), dictionary[dynamicSegmentName]);
  }
  return result;
}

async function getRoutingCardForRoute(searchers, route, branch, path, routingCardsCache={}) {
  for (let parentRoute of route.routeStack) {
    if (!parentRoute.query) { continue; }

    let query = resolveReplacementTagsFromPath(parentRoute, path, JSON.stringify(parentRoute.query));
    let routingCardType = route.routeStack.length ? route.routeStack[0].contentType : null;
    query = resolveRoutingCardReplacementTags(get(routingCardsCache, `antecedantResolution.${routingCardType}`), query, routingCardsCache);
    let { data:routingCards } = await searchers.search(Session.INTERNAL_PRIVILEGED, branch, JSON.parse(query));
    return routingCards.length ? { data: routingCards[0] } : null;
  }
}

function buildSubstitutionDictionary(route, path) {
  let dictionary = {};
  let type = route.contentType;
  let pathPattern = route.namespacedPath;
  let pathSegments = path.split('?')[0].split('/');
  let routeSegments = pathPattern.split('?')[0].split('/');

  // process the path part of the URL
  for (let i = 0; i < routeSegments.length; i++) {
    let routeSegment = routeSegments[i];
    if (routeSegment.charAt(0) === ':') {
      dictionary[routeSegment.replace(':', '')] = decodeURIComponent(pathSegments[i]);
    }
  }

  // process the query param part of the URL
  if (pathPattern.includes('?') && path.includes('?')) {
    let queryParams = path.split('?')[1].split('&');
    let routeQueryParams = pathPattern.split('?')[1].split('&');
    for (let routeQueryParam of routeQueryParams) {
      let nameValuePair, queryParamSplit, name;
      if ((nameValuePair = routeQueryParam.split('=')).length > 1 && (name = nameValuePair[1]).charAt(0) === ':') {
        let queryParamRegex = new RegExp(`${type}\\[${nameValuePair[0]}\\]=`);
        let queryParam = queryParams.find(i => i.match(queryParamRegex));
        if (queryParam && (queryParamSplit = queryParam.split('=')).length > 1) {
          let [, value] = queryParamSplit;
          dictionary[name.replace(':', '')] = decodeURIComponent(value);
        }
      }
    }
  }

  return dictionary;
}

async function routeThatMatchesPath(searchers, router, branch, path, applicationCard) {
  let matchedRoute, routingCard, routingCardsCache;
  let remainingPath = path.split('?')[0];
  let params = {};
  let allowedQueryParams = [];

  for (let route of router) {
    let { path: routePath } = route;
    let pathRegex = new RegExp(`${routePath.split('?')[0].replace(/(:card)?:[^/?#&]+/g, '[^/?#&]+')}`);
    if (decodeURI(path).match(pathRegex)) {
      routingCardsCache = await buildRoutingCardsCache({
        context: route.namespacedPath,
        resolvedPath: path,
        branch,
        searchers,
        routeStack: route.routeStack,
        applicationCard,
      });
      routingCard = await getRoutingCardForRoute(searchers, route, branch, path, routingCardsCache);

      if (routePath.includes(':card')) {
        routePath = resolveRoutingCardReplacementTags(
          routingCard || applicationCard,
          route.namespacedPath,
          routingCardsCache
        );
        pathRegex = new RegExp(`${routePath.split('?')[0].replace(/(:card)?:[^/?#&]+/g, '[^/?#&]+')}`);
        if (!decodeURI(path).match(pathRegex)) {
          continue;
        }
      }

      if (!route.query && (!routingCard || routingCard.data.type !== route.contentType)) { continue; }

      matchedRoute = route;
      remainingPath = decodeURI(remainingPath).replace(pathRegex, '');
      params = buildSubstitutionDictionary(matchedRoute, path);

      // remove params that were included from antecedant routing cards, the only params a card can use are params that derive from its own route
      let keys = Object.keys(params);
      for (let key of keys) {
        if (key.includes('[')) {
          delete params[key];
        }
      }

      if (matchedRoute.additionalParams) {
        let resolvedAdditionalParams = resolveReplacementTagsFromPath(matchedRoute, path, JSON.stringify(matchedRoute.additionalParams));
        params = Object.assign({}, params, JSON.parse(resolveRoutingCardReplacementTags(routingCard || applicationCard, resolvedAdditionalParams, routingCardsCache)));
      }

      if (matchedRoute.path.includes('?')) {
        let routeQueryParams = routePath.split('?')[1];
        allowedQueryParams = routeQueryParams.split('&').map(i => i.split('=')[0]);
      }

      let routePathSegment = resolveReplacementTagsFromPath(matchedRoute, path, matchedRoute.routePathSegment);
      routePathSegment = resolveRoutingCardReplacementTags(routingCard, routePathSegment, routingCardsCache).split("?")[0];
      let matchedQueryParams = [];
      for (let queryParam of allowedQueryParams) {
        if (params[queryParam]) {
          matchedQueryParams.push(`${queryParam}=${params[queryParam]}`);
        }
      }
      params.path = matchedQueryParams.length ? `${routePathSegment}?${matchedQueryParams.join('&')}` : routePathSegment;

      break;
    }
  }

  return { route: matchedRoute, remainingPath, params, allowedQueryParams, routingCard, routingCardsCache };
}

module.exports = {
  getRoute,
  isCanonicalRoute,
  resolveRoutingCardReplacementTags
};<|endoftext|>