<commit_before>/**
 * DEPENDENCIES
 * ezlog, cls  For colorized logging
 */

/**
 * f_ methods dependencies
 * Just colorized logging related
 */
var Ezlog = require(__dirname + './../node_modules/ezlog'),
    cls = require(__dirname + './../node_modules/opensoars_cls'),
    log = new Ezlog({p:{t:'[f_]',c:'yellow'}}),
    logGreen = new Ezlog({ p:{t:'[f_]',   c:'yellow'}, t: {c:'green'} }),
    logRed = new Ezlog({   p:{t:['[f_]'], c:'yellow'}, t: {c:'red'} });

/**
 * Global color helper functions to make cls calls in code unnecessary
 *
 * @param  t {string}  Text to color
 * @return   {string}  Colorized text
 */
function GREEN(t){ return cls(t, 'green');        }
function RED(t){   return cls(t, 'red'  );        }
function WHITE(t){ return cls(t, 'white');        }
function BLUE(t){  return cls(t, 'blue', 'bold'); }

/**
 * All f_ methods, contain all f_ method logic
 * Will be added to prototype object of toAugment (class) or to a plain
 * object by looping through the methods. Methods bill be prefixed w/ `f_`
 */
module.exports = {

  /**
   * Used internally and from within task lists
   *
   * Simple description of how next works
   *  Starting task?
   *  Finishing task?
   *  Is there a method to call?
   *  Are we exceeding maxTries?
   *  Are we handling a retryMethod cb?
   *  Call onNext if wanted
   *  Call method
   *
   * @public
   */
  next: function (){
    // Are we starting a task?
    if(this.f_i === 0 && this.f_hasInitialized === false){
      this.f_startTime = Date.now();
      this.f_hasInitialized = true;
    }

    if(this.f_i === 0) this.f_tries.wholeList += 1;

    // Are we exceeding the maxTries.wholeList ?
    if(this.f_tries.wholeList > this.f_maxTries.wholeList &&
       this.f_tries.wholeList !== '?')
        return this.f_abort('Max all tries exceeded',
          new Error('this.f_tries.wholeList > this.f_maxTries.wholeList'));

    if(this.f_i === 0 && !this.f_retryMethodCall && this.f_toLog.has('start'))
      log('Starting task '
        + (this.f_desc ? BLUE(' ' + this.f_desc) : '')
        + '  Attempt ' + GREEN(this.f_tries.wholeList)
        + (this.f_maxTries.wholeList !== '?'
          ? '/' + RED(this.f_maxTries.wholeList) : '')
      );

    // Are we done with tasks from functionFlow? If yes finish
    if(this.f_i === this.f_functionFlow.length)
      return this.f_finish();

    // Get method from functionFlow array
    var method = this.f_functionFlow[this.f_i];

    // Is the method actualy there? And is it a function ?
    if(this[method] && typeof this[method] === 'function'){

      // Increment try tries for a method, ALWAYS
      this.f_tries[method] += 1

      // If a maxTries is set for a certain method
      if(this.f_maxTries[method] && this.f_maxTries[method] !== '?'){

        // If the max tries for a certain method is exceeded abort
        if(this.f_tries[method] > this.f_maxTries[method] &&
           this.f_maxTries[method] !== '?')
          return this.f_abort('`' + method + '` maxTries exceeded',
            'this.f_tries[method] > this.f_maxTries[method]');
      }

      // Logs method name. If maxTries is set for a method, log about tries
      if(this.f_toLog.has('next')) log('next: ' + BLUE(method)
        + '\tAttempt ' + GREEN(this.f_tries[method])
        + (this.f_maxTries[method] !== '?'
            ?  '/' + RED( this.f_maxTries[method]) : '')
      );

      // Are we in a retryMethod call?
      if(this.f_retryMethodCall && (this.old_f_i || this.old_f_i === 0)){

        // Set f_i to the saved old value of f_i
        this.f_i = this.old_f_i;

        // Set retryMethod properties back to undefined
        this.f_retryMethodCall = undefined;
        this.old_f_i = undefined;

        if(this.onNext) this.onNext({ method: method, f_i: this.f_i });

        // Is a callback set when retryMethod completes a method?
        // If it's not set, continue f_ task list as normal (f_next)
        if(this.f_retryMethodCb) return this.f_retryMethodCb();
        else return this.f_next();
      }

      this.f_i += 1;

      // When onNext is present, send it the currunt method and the f_i(ndex)
      if(this.onNext) this.onNext({ method: method, f_i: this.f_i });

      // Call the 'next' method
      return this[method]();
    }
    // If we can't call another function from functionFlow, ABORT!
    else return this.f_abort('no next function in functionFlow');
  },

  /**
   * Simple namespace reset function
   * Sets data namespace to an empty object
   *
   * @public
   */
  resetNamespace: function (){
    this[this.f_dataNamespace] = {};
  },

  /**
   * Helper function to add errors to f_errs array
   *
   * @param desc {string}  Error description
   * @param err  {Error}   Node error
   */
  addErr: function (desc, err){
    this.f_errs.push({ desc: (desc || ''), err: (err || {}) });
  },

  /**
   * Error stack logger helper function
   * Pretty prints the error stack
   * Gets called from f_abort (if wanted)
   */
  logErrStack: function (){
    logRed('- f_errs -');
    this.f_errs.forEach(function (err){ console.log(err); });
    logRed('- f_errs -');
  },

  /**
   * Retries all methods
   * Resets data if wanted
   * Resets f_i, starting with functionFlow[0]
   * Calls onRetry if wanted
   *
   * @public
   * @param desc {string} opt.  Error description
   * @param err  {Error}  opt.  Node error
   */
  retryAll: function (desc, err){

    if(desc || err) this.f_addErr(desc, err);

    if(this.f_toLog.has('retry'))
      logRed('retryAll' + (desc ? '  desc: ' + WHITE(desc) : ''))

    // Do we want a data namespace reset ?
    if(this.f_resetOnRetryAll) this.f_resetNamespace();

    if(this.onRetry) this.onRetry({
      type: 'retryAll',
      calledFrom: this.f_functionFlow[this.f_i - 1],
      tries: this.f_tries,
      maxTries: this.f_maxTries
    });

    // Reset f_i so we can retry whole functionFlow
    this.f_i = 0;

    return this.f_next();
  },

  /**
   * Retries method where this method is called from
   * Carries on with functionFlow as if nothing happened
   * Increments maxTries.methodName
   * Calls onRetry if wanted
   * NO auto data reset is done with this method! (can be done by user)
   *
   * @public
   * @param desc {string} opt.  Error description
   * @param err  {Error}  opt.  Node error
   */
  retryThis: function (desc, err){

    if(desc || err) this.f_addErr(desc, err);

    // Use this becuase f_ will make use of 'next' logic (maxTries)
    this.f_i -= 1;

    if(this.f_toLog.has('retry'))
      logRed('retryThis' + (desc ? '  desc: ' + WHITE(desc) : ''));

    if(this.onRetry) this.onRetry({
      type: 'retryThis',
      calledFrom: this.f_functionFlow[this.f_i],
      tries: this.f_tries,
      maxTries: this.f_maxTries
    });

    return this.f_next();
  },

  /**
   * Retries task list from given method
   * Will increment maxTries.methodName
   * If methodIndex = 0, will increment tries.all
   * If the method given is not in functionFlow, log about it and add
   * an error.
   * Calls onRetry if wanted
   * NO auto data reset is done with this method! (can be done by user)
   *
   * @public
   * @param method {string}       Method to retry functionFlow from
   * @param desc   {string} opt.  Error description
   * @param err    {Error}  opt.  Node error
   */
  retryFrom: function (method, desc, err){

    if(desc || err) this.f_addErr(desc, err);

    var methodIndex = this.f_functionFlow.indexOf(method);

    // If the method is not found, log/err it and don't change f_i
    if(methodIndex === -1){
      var descStr = 'could not find method `' + method + '`.';
      this.f_addErr('retryFrom ' + descStr, 'this.methodIndex === -1');

      if(this.f_toLog.has('retry')) logRed('retryFrom  '
        + (desc ? '' + RED('desc: ')  + WHITE(desc) + '\n     ': '')
        + WHITE(descStr + ' Will carry on normaly.')
      );

    }
    // Set f_i to methodIndex, so `next` will carry on from there
    else{
      if(this.f_toLog.has('retry')) logRed('retryFrom'
        + (desc ? '  desc: ' + WHITE(desc) : '')
        + RED('  retry from: ') + WHITE(method)
      );

      this.f_i = methodIndex;
    }

    if(this.onRetry) this.onRetry({
      type: 'retryFrom',
      calledFrom: this.f_functionFlow[this.f_i],
      tries: this.f_tries,
      maxTries: this.f_maxTries,
      method: method,
    });

    // 'next' will carry on from given method if it's found
    // OR it will just act like nothing happened
    return this.f_next();
  },

  /**
   * Retries a specified method
   * Has to use f_next logic!
   * Calls onRetry if wanted
   *
   * @public
   * @param method {string}    Method to retry
   * @param cb     {function}  Function to call when retry is done
   * @param desc   {string}    opt.  Error description
   * @param err    {Error}     opt.  Node error
   */
  retryMethod: function (method, cb, desc, err){

    if(desc || err) this.f_addErr(desc, err);

    if(this.f_toLog.has('retry')) logRed('retryMethod: ' + WHITE(method)
      + (desc ? RED('  desc: ') + WHITE(desc) : '')
    );

    if(this.onRetry) this.onRetry({
      type: 'retryMethod',
      calledFrom: this.f_functionFlow[this.f_i -1],
      tries: this.f_tries,
      maxTries: this.f_maxTries,
      method: method,
    });

    // Is the method actualy present?
    if(this[method] && typeof this[method] === 'function'){

      // Makes f_(next) know we called retryMethod
      this.f_retryMethodCall = true;

      // Save the old f_i value
      this.old_f_i = this.f_i;

      // Set f_i to the index of method in f_functionFlow
      this.f_i = this.f_functionFlow.indexOf(method);

      // Set the cb to call from f_next
      this.f_retryMethodCb = cb;

      // f_next call will result in going to the new f_i index method
      return this.f_next();
    }

    // If the method is not present
    else {
      // Add an error
      this.f_addErr('@retryMethod  Could not find method: ' + method,
        new Error('this[method] && typeof this[method] === \'function\''));

      // And then just normaly call f_next;
      return this.f_next();
    }

  },

  /**
   * f_abort function
   * Calls f_logErrStack if wanted
   * Calls onAbort if it's set
   *
   * @public
   * @param desc {string} opt.  Error description
   * @param err  {Error} opt.   Node error
   */
  abort: function (desc, err){
    this.f_endTime = Date.now();
    this.f_timeTaken = this.f_endTime - this.f_startTime;

    this.f_addErr('ABORT  ' + (desc || ''), (err  || {}));

    if(this.f_toLog.has('abort')){
      logRed('Abort  ' + (desc ? 'desc: ' + WHITE(desc) : ''));
      logRed("Time taken: " + this.f_timeTaken.toString() + " ms");
    }

    if(this.f_toLog.has('errStack')) this.f_logErrStack();

    this.f_status = 'aborted'

    if(this.onAbort) return this.onAbort();
  },

  /**
   * When we are done with f_ task this method gets called
   * Calculates the time taken to complete the task
   * Logs about results if wanted
   * Calls onFinish if it's set
   *
   * @public
   */
  finish: function (){
    this.f_endTime = Date.now();
    this.f_timeTaken = this.f_endTime - this.f_startTime;

    if(this.f_toLog.has('finish'))
      log(GREEN('finish') + '  time taken: '
        + GREEN(this.f_timeTaken.toString()) + ' ms');

    this.f_status = 'finished'

    if(this.onFinish) return this.onFinish();
  },

  /**
   * Cleans up used memory
   * It will NOT be called when f_ finishes
   * This call will be put into a timeout
   *
   * It cleans up this.__proto__
   * It cleans up this.propertyName
   *
   * @public
   * @return null  The wanted instance value so the GC can clean
   */
  cleanup: function (){
    this.__proto__ = null;
    for(var prop in this) delete this[prop];

    return null;
  }

};<commit_msg>maxTries checking comment addition<commit_after>/**
 * DEPENDENCIES
 * ezlog, cls  For colorized logging
 */

/**
 * f_ methods dependencies
 * Just colorized logging related
 */
var Ezlog = require(__dirname + './../node_modules/ezlog'),
    cls = require(__dirname + './../node_modules/opensoars_cls'),
    log = new Ezlog({p:{t:'[f_]',c:'yellow'}}),
    logGreen = new Ezlog({ p:{t:'[f_]',   c:'yellow'}, t: {c:'green'} }),
    logRed = new Ezlog({   p:{t:['[f_]'], c:'yellow'}, t: {c:'red'} });

/**
 * Global color helper functions to make cls calls in code unnecessary
 *
 * @param  t {string}  Text to color
 * @return   {string}  Colorized text
 */
function GREEN(t){ return cls(t, 'green');        }
function RED(t){   return cls(t, 'red'  );        }
function WHITE(t){ return cls(t, 'white');        }
function BLUE(t){  return cls(t, 'blue', 'bold'); }

/**
 * All f_ methods, contain all f_ method logic
 * Will be added to prototype object of toAugment (class) or to a plain
 * object by looping through the methods. Methods bill be prefixed w/ `f_`
 */
module.exports = {

  /**
   * Used internally and from within task lists
   *
   * Simple description of how next works
   *  Starting task?
   *  Finishing task?
   *  Is there a method to call?
   *  Are we exceeding maxTries?
   *  Are we handling a retryMethod cb?
   *  Call onNext if wanted
   *  Call method
   *
   * @public
   */
  next: function (){
    // Are we starting a task?
    if(this.f_i === 0 && this.f_hasInitialized === false){
      this.f_startTime = Date.now();
      this.f_hasInitialized = true;
    }

    if(this.f_i === 0) this.f_tries.wholeList += 1;

    // Are we exceeding the maxTries.wholeList?
    // If yes, abort. If no, continue
    if(this.f_tries.wholeList > this.f_maxTries.wholeList &&
       this.f_tries.wholeList !== '?')
        return this.f_abort('Max all tries exceeded',
          new Error('this.f_tries.wholeList > this.f_maxTries.wholeList'));

    if(this.f_i === 0 && !this.f_retryMethodCall && this.f_toLog.has('start'))
      log('Starting task '
        + (this.f_desc ? BLUE(' ' + this.f_desc) : '')
        + '  Attempt ' + GREEN(this.f_tries.wholeList)
        + (this.f_maxTries.wholeList !== '?'
          ? '/' + RED(this.f_maxTries.wholeList) : '')
      );

    // Are we done with tasks from functionFlow? If yes finish
    if(this.f_i === this.f_functionFlow.length)
      return this.f_finish();

    // Get method from functionFlow array
    var method = this.f_functionFlow[this.f_i];

    // Is the method actualy there? And is it a function ?
    if(this[method] && typeof this[method] === 'function'){

      // Increment try tries for a method, ALWAYS
      this.f_tries[method] += 1

      // If a maxTries is set for a certain method
      if(this.f_maxTries[method] && this.f_maxTries[method] !== '?'){

        // If the max tries for a certain method is exceeded abort
        if(this.f_tries[method] > this.f_maxTries[method] &&
           this.f_maxTries[method] !== '?')
          return this.f_abort('`' + method + '` maxTries exceeded',
            'this.f_tries[method] > this.f_maxTries[method]');
      }

      // Logs method name. If maxTries is set for a method, log about tries
      if(this.f_toLog.has('next')) log('next: ' + BLUE(method)
        + '\tAttempt ' + GREEN(this.f_tries[method])
        + (this.f_maxTries[method] !== '?'
            ?  '/' + RED( this.f_maxTries[method]) : '')
      );

      // Are we in a retryMethod call?
      if(this.f_retryMethodCall && (this.old_f_i || this.old_f_i === 0)){

        // Set f_i to the saved old value of f_i
        this.f_i = this.old_f_i;

        // Set retryMethod properties back to undefined
        this.f_retryMethodCall = undefined;
        this.old_f_i = undefined;

        if(this.onNext) this.onNext({ method: method, f_i: this.f_i });

        // Is a callback set when retryMethod completes a method?
        // If it's not set, continue f_ task list as normal (f_next)
        if(this.f_retryMethodCb) return this.f_retryMethodCb();
        else return this.f_next();
      }

      this.f_i += 1;

      // When onNext is present, send it the currunt method and the f_i(ndex)
      if(this.onNext) this.onNext({ method: method, f_i: this.f_i });

      // Call the 'next' method
      return this[method]();
    }
    // If we can't call another function from functionFlow, ABORT!
    else return this.f_abort('no next function in functionFlow');
  },

  /**
   * Simple namespace reset function
   * Sets data namespace to an empty object
   *
   * @public
   */
  resetNamespace: function (){
    this[this.f_dataNamespace] = {};
  },

  /**
   * Helper function to add errors to f_errs array
   *
   * @param desc {string}  Error description
   * @param err  {Error}   Node error
   */
  addErr: function (desc, err){
    this.f_errs.push({ desc: (desc || ''), err: (err || {}) });
  },

  /**
   * Error stack logger helper function
   * Pretty prints the error stack
   * Gets called from f_abort (if wanted)
   */
  logErrStack: function (){
    logRed('- f_errs -');
    this.f_errs.forEach(function (err){ console.log(err); });
    logRed('- f_errs -');
  },

  /**
   * Retries all methods
   * Resets data if wanted
   * Resets f_i, starting with functionFlow[0]
   * Calls onRetry if wanted
   *
   * @public
   * @param desc {string} opt.  Error description
   * @param err  {Error}  opt.  Node error
   */
  retryAll: function (desc, err){

    if(desc || err) this.f_addErr(desc, err);

    if(this.f_toLog.has('retry'))
      logRed('retryAll' + (desc ? '  desc: ' + WHITE(desc) : ''))

    // Do we want a data namespace reset ?
    if(this.f_resetOnRetryAll) this.f_resetNamespace();

    if(this.onRetry) this.onRetry({
      type: 'retryAll',
      calledFrom: this.f_functionFlow[this.f_i - 1],
      tries: this.f_tries,
      maxTries: this.f_maxTries
    });

    // Reset f_i so we can retry whole functionFlow
    this.f_i = 0;

    return this.f_next();
  },

  /**
   * Retries method where this method is called from
   * Carries on with functionFlow as if nothing happened
   * Increments maxTries.methodName
   * Calls onRetry if wanted
   * NO auto data reset is done with this method! (can be done by user)
   *
   * @public
   * @param desc {string} opt.  Error description
   * @param err  {Error}  opt.  Node error
   */
  retryThis: function (desc, err){

    if(desc || err) this.f_addErr(desc, err);

    // Use this becuase f_ will make use of 'next' logic (maxTries)
    this.f_i -= 1;

    if(this.f_toLog.has('retry'))
      logRed('retryThis' + (desc ? '  desc: ' + WHITE(desc) : ''));

    if(this.onRetry) this.onRetry({
      type: 'retryThis',
      calledFrom: this.f_functionFlow[this.f_i],
      tries: this.f_tries,
      maxTries: this.f_maxTries
    });

    return this.f_next();
  },

  /**
   * Retries task list from given method
   * Will increment maxTries.methodName
   * If methodIndex = 0, will increment tries.all
   * If the method given is not in functionFlow, log about it and add
   * an error.
   * Calls onRetry if wanted
   * NO auto data reset is done with this method! (can be done by user)
   *
   * @public
   * @param method {string}       Method to retry functionFlow from
   * @param desc   {string} opt.  Error description
   * @param err    {Error}  opt.  Node error
   */
  retryFrom: function (method, desc, err){

    if(desc || err) this.f_addErr(desc, err);

    var methodIndex = this.f_functionFlow.indexOf(method);

    // If the method is not found, log/err it and don't change f_i
    if(methodIndex === -1){
      var descStr = 'could not find method `' + method + '`.';
      this.f_addErr('retryFrom ' + descStr, 'this.methodIndex === -1');

      if(this.f_toLog.has('retry')) logRed('retryFrom  '
        + (desc ? '' + RED('desc: ')  + WHITE(desc) + '\n     ': '')
        + WHITE(descStr + ' Will carry on normaly.')
      );

    }
    // Set f_i to methodIndex, so `next` will carry on from there
    else{
      if(this.f_toLog.has('retry')) logRed('retryFrom'
        + (desc ? '  desc: ' + WHITE(desc) : '')
        + RED('  retry from: ') + WHITE(method)
      );

      this.f_i = methodIndex;
    }

    if(this.onRetry) this.onRetry({
      type: 'retryFrom',
      calledFrom: this.f_functionFlow[this.f_i],
      tries: this.f_tries,
      maxTries: this.f_maxTries,
      method: method,
    });

    // 'next' will carry on from given method if it's found
    // OR it will just act like nothing happened
    return this.f_next();
  },

  /**
   * Retries a specified method
   * Has to use f_next logic!
   * Calls onRetry if wanted
   *
   * @public
   * @param method {string}    Method to retry
   * @param cb     {function}  Function to call when retry is done
   * @param desc   {string}    opt.  Error description
   * @param err    {Error}     opt.  Node error
   */
  retryMethod: function (method, cb, desc, err){

    if(desc || err) this.f_addErr(desc, err);

    if(this.f_toLog.has('retry')) logRed('retryMethod: ' + WHITE(method)
      + (desc ? RED('  desc: ') + WHITE(desc) : '')
    );

    if(this.onRetry) this.onRetry({
      type: 'retryMethod',
      calledFrom: this.f_functionFlow[this.f_i -1],
      tries: this.f_tries,
      maxTries: this.f_maxTries,
      method: method,
    });

    // Is the method actualy present?
    if(this[method] && typeof this[method] === 'function'){

      // Makes f_(next) know we called retryMethod
      this.f_retryMethodCall = true;

      // Save the old f_i value
      this.old_f_i = this.f_i;

      // Set f_i to the index of method in f_functionFlow
      this.f_i = this.f_functionFlow.indexOf(method);

      // Set the cb to call from f_next
      this.f_retryMethodCb = cb;

      // f_next call will result in going to the new f_i index method
      return this.f_next();
    }

    // If the method is not present
    else {
      // Add an error
      this.f_addErr('@retryMethod  Could not find method: ' + method,
        new Error('this[method] && typeof this[method] === \'function\''));

      // And then just normaly call f_next;
      return this.f_next();
    }

  },

  /**
   * f_abort function
   * Calls f_logErrStack if wanted
   * Calls onAbort if it's set
   *
   * @public
   * @param desc {string} opt.  Error description
   * @param err  {Error} opt.   Node error
   */
  abort: function (desc, err){
    this.f_endTime = Date.now();
    this.f_timeTaken = this.f_endTime - this.f_startTime;

    this.f_addErr('ABORT  ' + (desc || ''), (err  || {}));

    if(this.f_toLog.has('abort')){
      logRed('Abort  ' + (desc ? 'desc: ' + WHITE(desc) : ''));
      logRed("Time taken: " + this.f_timeTaken.toString() + " ms");
    }

    if(this.f_toLog.has('errStack')) this.f_logErrStack();

    this.f_status = 'aborted'

    if(this.onAbort) return this.onAbort();
  },

  /**
   * When we are done with f_ task this method gets called
   * Calculates the time taken to complete the task
   * Logs about results if wanted
   * Calls onFinish if it's set
   *
   * @public
   */
  finish: function (){
    this.f_endTime = Date.now();
    this.f_timeTaken = this.f_endTime - this.f_startTime;

    if(this.f_toLog.has('finish'))
      log(GREEN('finish') + '  time taken: '
        + GREEN(this.f_timeTaken.toString()) + ' ms');

    this.f_status = 'finished'

    if(this.onFinish) return this.onFinish();
  },

  /**
   * Cleans up used memory
   * It will NOT be called when f_ finishes
   * This call will be put into a timeout
   *
   * It cleans up this.__proto__
   * It cleans up this.propertyName
   *
   * @public
   * @return null  The wanted instance value so the GC can clean
   */
  cleanup: function (){
    this.__proto__ = null;
    for(var prop in this) delete this[prop];

    return null;
  }

};<|endoftext|>


<commit_before>'use strict';

// modules dependencies
var utilsHelper = require('../helpers/utils-helper');
var mongodb = require('mongodb');

/**
 * @namespace Mongoat
 */
var Mongoat = mongodb;

/**
 * @private
 * @constructor
 * @description to initialize some properties
 */
Mongoat.Collection.prototype.init = function () {
  this.datetime = {};
  this.hooks = {};
  this.versioning = {};
  this.hooks = {};
};

(function () {
  var opMethod = ['insert', 'update', 'findAndModify', 'remove'];
  var colPrototype = Mongoat.Collection.prototype;

  colPrototype.init();

  for (var i = 0; i < opMethod.length; ++i) {
    colPrototype[opMethod[i] + 'Method'] = colPrototype[opMethod[i]];
  }
})();

/**
 * @function before
 * @memberOf Mongoat
 * @description add before hook to Collection
 * @param {String} opName - operation name, can be insert/update/findAndModify or remove
 * @param {Function} callback - the callback to run after the before hook
 * @return {Void} Nothing
 */
Mongoat.Collection.prototype.before = function(opName, callback) {
  this.hooks[this.s.namespace] = this.hooks[this.s.namespace] ||
    utilsHelper.getHooksTemplate();

  this.hooks[this.s.namespace].before[opName].push(callback);
};

/**
 * @function after
 * @memberOf Mongoat
 * @description add after hook to Collection
 * @param {String} opName - operation name, can be insert/update/findAndModify or remove
 * @param {Function} callback - the callback to run after the after hook
 * @return {Void} Nothing
 */
Mongoat.Collection.prototype.after = function(opName, callback) {
  this.hooks[this.s.namespace] = this.hooks[this.s.namespace] ||
    utilsHelper.getHooksTemplate();

  this.hooks[this.s.namespace].after[opName].push(callback);
};

/**
 * @function insert
 * @memberOf Mongoat
 * @description overwrite the insert method
 * @param {Object} document - document to insert
 * @param {Object} options - options used on insert
 * @return {Promise} - returned mongodb object after insert
 */
Mongoat.Collection.prototype.insert = function(document, options, callback) {
  return this.query('insert', undefined, undefined, document, options, callback);
};

/**
 * @function update
 * @memberOf Mongoat
 * @description overwrite the update method
 * @param {Object} query - query to match
 * @param {Object} update - document to update
 * @param {Object} options - options used on update
 * @return {Promise} - returned mongodb object after update
 */
Mongoat.Collection.prototype.update = function(query, update, options, callback) {
  return this.query('update', query, undefined, update, options, callback);
};

/**
 * @function findAndModify
 * @memberOf Mongoat
 * @description overwrite the findAndModify method
 * @param {Object} query - query to match
 * @param {Object} sort - sort rules
 * @param {Object} update - document to update
 * @param {Object} options - options used on update
 * @return {Promise} - returned mongodb object after findAndModify
 */
Mongoat.Collection.prototype.findAndModify = function(query, sort, update, options, callback) {
  return this.query('findAndModify', query, sort, update, options, callback);
};

/**
 * @function remove
 * @memberOf Mongoat
 * @description overwrite the remove method
 * @param {Object} query - query to match
 * @param {Object} options - options used on update
 * @return {Promise} - returned mongodb object after remove
 */
Mongoat.Collection.prototype.remove = function(query, options, callback) {
  return this.query('remove', query, undefined, undefined, options, callback);
};

/**
 * @function query
 * @memberOf Mongoat
 * @description to make any query, can be insert/update/findAndModify or remove
 * @param {String} opName - operation name, can be insert/update/findAndModify or remove
 * @param {Object} query - query to match
 * @param {Object} sort - sort rules
 * @param {Object} update - document to update
 * @param {Object} options - options used on update
 * @return {Promise} - returned mongodb object after query depending on opName
 */
Mongoat.Collection.prototype.query = function(opName, query, sort, document, options, callback) {
  var promises = [];
  var _this = this;
  var colName = _this.s.namespace;
  var opName_X;
  var mongObject;

  _this.hooks[colName] = _this.hooks[colName] || utilsHelper.getHooksTemplate();

  if (options && typeof options === 'function') {
    callback = options;
    options = {};
  }

  options = options || {};
  opName_X = (opName !== 'findAndModify') ? opName : 'update';

  utilsHelper.setDatetime(opName_X, _this.datetime[colName], document);
  promises = utilsHelper.promisify(_this.hooks[colName].before[opName_X], query, document);

  if (_this.versioning[colName]) {
    promises.push(_this.commit(opName, query, document, options));
  }

  return Promise.all(promises)
  .then(function (docToProcess) {
    var params = utilsHelper.setParams(opName, query, sort, docToProcess[0], options);

    return _this[opName + 'Method'].apply(_this, params);
  })
  .then(function (_mongObject) {
    mongObject = _mongObject;
    var isAfterHook = (_this.hooks[colName].after[opName_X].length > 0) ? true : false;

    return utilsHelper.processUpdatedDocment(opName, _this, isAfterHook, query, mongObject, options);
  })
  .then(function (obj) {
    promises = [];
    promises = utilsHelper.promisify(_this.hooks[colName].after[opName_X], obj);
    Promise.all(promises);

    if (callback && typeof callback === 'function') {
      callback(null, mongObject);
    }

    return mongObject;
  })
  .catch(function (err) {
    if (callback && typeof callback === 'function') {
      callback(err, null);
    } else {
      throw err;
    }
  });
};

/**
 * @function datetime
 * @memberOf Mongoat
 * @description to enable datetime feature
 * @param {Boolean} isEnabled - to enable or disable feature
 * @return {Void} Nothing
 */
Mongoat.Collection.prototype.datetime = function (isEnabled) {
  if (typeof isEnabled === 'boolean') {
    this.datetime[this.s.namespace] = isEnabled;
  }
};

/**
 * @function version
 * @memberOf Mongoat
 * @description to enable versioning feature
 * @param {Boolean} isEnabled - to enable or disable feature
 * @return {Void} Nothing
 */
Mongoat.Collection.prototype.version = function (isEnabled) {
  if (typeof isEnabled === 'boolean') {
    this.versioning[this.s.namespace] = isEnabled;
  }
};

/**
 * @function commit
 * @memberOf Mongoat
 * @description to commit document version
 * @param {String} opName - operation name, can be insert/update or remove
 * @param {Object} query - query to match
 * @param {Object} document - document to commit
 * @param {Object} options - options used on update
 * @return {Promise} - the document to insert/update or remove
 */
Mongoat.Collection.prototype.commit = function (opName, query, document, options) {
  if (opName === 'insert') {
    document._version = 1;
    return document;
  }

  var _this  = this;
  var shadowCol = _this.s.db.collection(_this.s.name + '.vermongo');
  shadowCol.datetime(_this.datetime[_this.s.namespace]);

  /*jshint maxcomplexity:10 */
  return _this.findOne(query)
  .then(function (docToProcess) {
    if (docToProcess) {
      var id = docToProcess._id;
      docToProcess._version = docToProcess._version || 1;
      docToProcess._id = { _id: id, _version: docToProcess._version };
    } else if (!docToProcess && document && document._version) {
      return document;
    }

    if (opName === 'update' || opName === 'findAndModify') {
      if (!docToProcess && options.upsert) {
        if (!document.$setOnInsert) {
          document._version = 1;
        } else {
          document.$setOnInsert._version = 1;
        }

        return document;
      }
    } else if (opName === 'remove') {
      if (!docToProcess) {
        return query;
      }
      docToProcess._version = 'deleted:' + docToProcess._id._version;
    }

    return shadowCol.insertMethod(docToProcess)
    .then(function () {
      if (opName === 'update' || opName === 'findAndModify') {
        if (!document.$setOnInsert && !document.$set) {
          document._version = docToProcess._id._version + 1;
        } else {
          document.$set = document.$set || {};
          document.$setOnInsert = document.$setOnInsert || {};
          document.$set._version = docToProcess._id._version + 1;
          document.$setOnInsert._version = docToProcess._id._version + 1;
        }
        return document;
      } else if (opName === 'remove') {
        return query;
      }
    });
  });
};

/**
 * @function getVersion
 * @memberOf Mongoat
 * @description to get document by version, can +v => version, 0 => last version or -v => last version - v
 * @param {Object} id - id of the document to restore
 * @param {Number} version - version of the document to restore
 * @return {Promise} - the requested document
 */
Mongoat.Collection.prototype.getVersion = function(id, version) {
  var shadowCol = this.s.db.collection(this.s.name + '.vermongo');
  var aggregatePipeline = [{ $match: { '_id._id' : id } }];

  if (version <= 0) {
    aggregatePipeline.push({ $sort: { '_id._version': -1 } });
    aggregatePipeline.push({ $skip: Math.abs(version) });
  } else {
    aggregatePipeline.push({ $match: { '_id._version' : version } });
  }

  aggregatePipeline.push({ $limit: 1 });

  return shadowCol.aggregate(aggregatePipeline).nextObject();
};

/**
 * @function restore
 * @memberOf Mongoat
 * @description to resotre document by version, can +v => version, 0 => last version or -v => last version - v
 * @param {Object} id - id of the document to restore
 * @param {Number} version - version of the document to restore
 * @return {Promise} - the restored document
 */
Mongoat.Collection.prototype.restore = function (id, version) {
  var col = this.s.db.collection(this.s.name);
  var _this = this;
  var lastVersion;
  var document;

  version = version || 0;

  if (!id) {
    return Promise.reject(new Error('The provided id is null or undefined'));
  }

  return _this.getVersion(id, 0)
  .then(function (lastVersionDoc) {
    lastVersion = lastVersionDoc._id._version;
  })
  .then(function () {
    return _this.getVersion(id, version)
  })
  .then(function (_document) {
    document = _document;

    if (!document) {
      return Promise.reject(new Error('The requested version doesn\'t exist'));
    }

    var id = document._id._id;
    delete document._id;
    document._version = lastVersion + 1;
  })
  .then(function () {
    return col.update({ _id: id }, document, { upsert: true });
  })
  .then(function () {
    return document;
  });
};

// export Mongoat object
module.exports = Mongoat;<commit_msg>Fix jshint issues:  - use _id because id is already defined  - remove useless line break<commit_after>'use strict';

// modules dependencies
var utilsHelper = require('../helpers/utils-helper');
var mongodb = require('mongodb');

/**
 * @namespace Mongoat
 */
var Mongoat = mongodb;

/**
 * @private
 * @constructor
 * @description to initialize some properties
 */
Mongoat.Collection.prototype.init = function () {
  this.datetime = {};
  this.hooks = {};
  this.versioning = {};
  this.hooks = {};
};

(function () {
  var opMethod = ['insert', 'update', 'findAndModify', 'remove'];
  var colPrototype = Mongoat.Collection.prototype;

  colPrototype.init();

  for (var i = 0; i < opMethod.length; ++i) {
    colPrototype[opMethod[i] + 'Method'] = colPrototype[opMethod[i]];
  }
})();

/**
 * @function before
 * @memberOf Mongoat
 * @description add before hook to Collection
 * @param {String} opName - operation name, can be insert/update/findAndModify or remove
 * @param {Function} callback - the callback to run after the before hook
 * @return {Void} Nothing
 */
Mongoat.Collection.prototype.before = function(opName, callback) {
  this.hooks[this.s.namespace] = this.hooks[this.s.namespace] ||
    utilsHelper.getHooksTemplate();

  this.hooks[this.s.namespace].before[opName].push(callback);
};

/**
 * @function after
 * @memberOf Mongoat
 * @description add after hook to Collection
 * @param {String} opName - operation name, can be insert/update/findAndModify or remove
 * @param {Function} callback - the callback to run after the after hook
 * @return {Void} Nothing
 */
Mongoat.Collection.prototype.after = function(opName, callback) {
  this.hooks[this.s.namespace] = this.hooks[this.s.namespace] ||
    utilsHelper.getHooksTemplate();

  this.hooks[this.s.namespace].after[opName].push(callback);
};

/**
 * @function insert
 * @memberOf Mongoat
 * @description overwrite the insert method
 * @param {Object} document - document to insert
 * @param {Object} options - options used on insert
 * @return {Promise} - returned mongodb object after insert
 */
Mongoat.Collection.prototype.insert = function(document, options, callback) {
  return this.query('insert', undefined, undefined, document, options, callback);
};

/**
 * @function update
 * @memberOf Mongoat
 * @description overwrite the update method
 * @param {Object} query - query to match
 * @param {Object} update - document to update
 * @param {Object} options - options used on update
 * @return {Promise} - returned mongodb object after update
 */
Mongoat.Collection.prototype.update = function(query, update, options, callback) {
  return this.query('update', query, undefined, update, options, callback);
};

/**
 * @function findAndModify
 * @memberOf Mongoat
 * @description overwrite the findAndModify method
 * @param {Object} query - query to match
 * @param {Object} sort - sort rules
 * @param {Object} update - document to update
 * @param {Object} options - options used on update
 * @return {Promise} - returned mongodb object after findAndModify
 */
Mongoat.Collection.prototype.findAndModify = function(query, sort, update, options, callback) {
  return this.query('findAndModify', query, sort, update, options, callback);
};

/**
 * @function remove
 * @memberOf Mongoat
 * @description overwrite the remove method
 * @param {Object} query - query to match
 * @param {Object} options - options used on update
 * @return {Promise} - returned mongodb object after remove
 */
Mongoat.Collection.prototype.remove = function(query, options, callback) {
  return this.query('remove', query, undefined, undefined, options, callback);
};

/**
 * @function query
 * @memberOf Mongoat
 * @description to make any query, can be insert/update/findAndModify or remove
 * @param {String} opName - operation name, can be insert/update/findAndModify or remove
 * @param {Object} query - query to match
 * @param {Object} sort - sort rules
 * @param {Object} update - document to update
 * @param {Object} options - options used on update
 * @return {Promise} - returned mongodb object after query depending on opName
 */
Mongoat.Collection.prototype.query = function(opName, query, sort, document, options, callback) {
  var promises = [];
  var _this = this;
  var colName = _this.s.namespace;
  var opName_X;
  var mongObject;

  _this.hooks[colName] = _this.hooks[colName] || utilsHelper.getHooksTemplate();

  if (options && typeof options === 'function') {
    callback = options;
    options = {};
  }

  options = options || {};
  opName_X = (opName !== 'findAndModify') ? opName : 'update';

  utilsHelper.setDatetime(opName_X, _this.datetime[colName], document);
  promises = utilsHelper.promisify(_this.hooks[colName].before[opName_X], query, document);

  if (_this.versioning[colName]) {
    promises.push(_this.commit(opName, query, document, options));
  }

  return Promise.all(promises)
  .then(function (docToProcess) {
    var params = utilsHelper.setParams(opName, query, sort, docToProcess[0], options);

    return _this[opName + 'Method'].apply(_this, params);
  })
  .then(function (_mongObject) {
    mongObject = _mongObject;
    var isAfterHook = (_this.hooks[colName].after[opName_X].length > 0) ? true : false;

    return utilsHelper.processUpdatedDocment(opName, _this, isAfterHook, query, mongObject, options);
  })
  .then(function (obj) {
    promises = [];
    promises = utilsHelper.promisify(_this.hooks[colName].after[opName_X], obj);
    Promise.all(promises);

    if (callback && typeof callback === 'function') {
      callback(null, mongObject);
    }

    return mongObject;
  })
  .catch(function (err) {
    if (callback && typeof callback === 'function') {
      callback(err, null);
    } else {
      throw err;
    }
  });
};

/**
 * @function datetime
 * @memberOf Mongoat
 * @description to enable datetime feature
 * @param {Boolean} isEnabled - to enable or disable feature
 * @return {Void} Nothing
 */
Mongoat.Collection.prototype.datetime = function (isEnabled) {
  if (typeof isEnabled === 'boolean') {
    this.datetime[this.s.namespace] = isEnabled;
  }
};

/**
 * @function version
 * @memberOf Mongoat
 * @description to enable versioning feature
 * @param {Boolean} isEnabled - to enable or disable feature
 * @return {Void} Nothing
 */
Mongoat.Collection.prototype.version = function (isEnabled) {
  if (typeof isEnabled === 'boolean') {
    this.versioning[this.s.namespace] = isEnabled;
  }
};

/**
 * @function commit
 * @memberOf Mongoat
 * @description to commit document version
 * @param {String} opName - operation name, can be insert/update or remove
 * @param {Object} query - query to match
 * @param {Object} document - document to commit
 * @param {Object} options - options used on update
 * @return {Promise} - the document to insert/update or remove
 */
Mongoat.Collection.prototype.commit = function (opName, query, document, options) {
  if (opName === 'insert') {
    document._version = 1;
    return document;
  }

  var _this  = this;
  var shadowCol = _this.s.db.collection(_this.s.name + '.vermongo');
  shadowCol.datetime(_this.datetime[_this.s.namespace]);

  /*jshint maxcomplexity:10 */
  return _this.findOne(query)
  .then(function (docToProcess) {
    if (docToProcess) {
      var id = docToProcess._id;
      docToProcess._version = docToProcess._version || 1;
      docToProcess._id = { _id: id, _version: docToProcess._version };
    } else if (!docToProcess && document && document._version) {
      return document;
    }

    if (opName === 'update' || opName === 'findAndModify') {
      if (!docToProcess && options.upsert) {
        if (!document.$setOnInsert) {
          document._version = 1;
        } else {
          document.$setOnInsert._version = 1;
        }

        return document;
      }
    } else if (opName === 'remove') {
      if (!docToProcess) {
        return query;
      }
      docToProcess._version = 'deleted:' + docToProcess._id._version;
    }

    return shadowCol.insertMethod(docToProcess)
    .then(function () {
      if (opName === 'update' || opName === 'findAndModify') {
        if (!document.$setOnInsert && !document.$set) {
          document._version = docToProcess._id._version + 1;
        } else {
          document.$set = document.$set || {};
          document.$setOnInsert = document.$setOnInsert || {};
          document.$set._version = docToProcess._id._version + 1;
          document.$setOnInsert._version = docToProcess._id._version + 1;
        }
        return document;
      } else if (opName === 'remove') {
        return query;
      }
    });
  });
};

/**
 * @function getVersion
 * @memberOf Mongoat
 * @description to get document by version, can +v => version, 0 => last version or -v => last version - v
 * @param {Object} id - id of the document to restore
 * @param {Number} version - version of the document to restore
 * @return {Promise} - the requested document
 */
Mongoat.Collection.prototype.getVersion = function(id, version) {
  var shadowCol = this.s.db.collection(this.s.name + '.vermongo');
  var aggregatePipeline = [{ $match: { '_id._id' : id } }];

  if (version <= 0) {
    aggregatePipeline.push({ $sort: { '_id._version': -1 } });
    aggregatePipeline.push({ $skip: Math.abs(version) });
  } else {
    aggregatePipeline.push({ $match: { '_id._version' : version } });
  }

  aggregatePipeline.push({ $limit: 1 });

  return shadowCol.aggregate(aggregatePipeline).nextObject();
};

/**
 * @function restore
 * @memberOf Mongoat
 * @description to resotre document by version, can +v => version, 0 => last version or -v => last version - v
 * @param {Object} id - id of the document to restore
 * @param {Number} version - version of the document to restore
 * @return {Promise} - the restored document
 */
Mongoat.Collection.prototype.restore = function (id, version) {
  var col = this.s.db.collection(this.s.name);
  var _this = this;
  var lastVersion;
  var document;
  var _id;

  version = version || 0;

  if (!id) {
    return Promise.reject(new Error('The provided id is null or undefined'));
  }

  return _this.getVersion(id, 0)
  .then(function (lastVersionDoc) {
    lastVersion = lastVersionDoc._id._version;
  })
  .then(function () {
    return _this.getVersion(id, version);
  })
  .then(function (_document) {
    document = _document;

    if (!document) {
      return Promise.reject(new Error('The requested version doesn\'t exist'));
    }

    _id = document._id._id;
    delete document._id;
    document._version = lastVersion + 1;
  })
  .then(function () {
    return col.update({ _id: _id }, document, { upsert: true });
  })
  .then(function () {
    return document;
  });
};

// export Mongoat object
module.exports = Mongoat;<|endoftext|>


<commit_before>//     telegram-mt-node
//     Copyright 2014 Enrico Stara 'enrico.stara@gmail.com'
//     Released under the MIT License
//     https://github.com/enricostara/telegram-mt-node

//     MTProto module
//
// This module provide the `MTProto classes & method` as defined by the Telegram specification. The MTProto
// is the standard protocol to communicate with the Telegram data-center.

// Export the sub-modules
exports.auth = require('./auth');
exports.message = require('./message');
exports.net = require('./net');
exports.security = require('./security');
exports.utility = require('./utility');
exports.time = require('./time');
exports.SequenceNumber = require('./sequence-number');

// Import dependencies
var TypeBuilder = require('telegram-tl-node').TypeBuilder;
//var PlainMessage = require('./message').PlainMessage;

// MtProto TL schema as provided by Telegram
var mtTlSchema = require('./mt-tlschema.json');

// API TL schema as provided by Telegram, but only the Type functions needed at the protocol level
var mtApiTlSchema = require('./mt-api-tlschema.json');

// Declare the `type` module
var type = {_id: 'mtproto.type'};
// List the `mtproto` constructors
var constructors = ['ResPQ', 'P_Q_inner_data', 'Server_DH_Params', 'Server_DH_inner_data', 'Client_DH_Inner_Data',
    'Set_client_DH_params_answer', 'Message'];
// Build the `type` list
TypeBuilder.buildTypes(mtTlSchema.constructors, constructors, type);
// Export the 'type' module
exports.type = type;

// Declare the `service` module
var service = {_id: 'mtproto.service'};
// List the `mtproto' methods
var methods = ['req_pq', 'req_DH_params', 'set_client_DH_params', 'initConnection', 'invokeWithLayer'];
// Build registered methods
//TypeBuilder.buildTypes(api.methods, methods, service, PlainMessage);
TypeBuilder.buildTypes(mtTlSchema.methods, methods, service, true);
TypeBuilder.buildTypes(mtApiTlSchema.methods, methods, service, true);
// Export the 'service' module
exports.service = service;
<commit_msg>Build all the MTProto Type definitions by the TL schema<commit_after>//     telegram-mt-node
//     Copyright 2014 Enrico Stara 'enrico.stara@gmail.com'
//     Released under the MIT License
//     https://github.com/enricostara/telegram-mt-node

//     MTProto module
//
// This module provide the `MTProto classes & method` as defined by the Telegram specification. The MTProto
// is the standard protocol to communicate with the Telegram data-center.

// Export the sub-modules
exports.auth = require('./auth');
exports.message = require('./message');
exports.net = require('./net');
exports.security = require('./security');
exports.utility = require('./utility');
exports.time = require('./time');
exports.SequenceNumber = require('./sequence-number');

// Import dependencies
var TypeBuilder = require('telegram-tl-node').TypeBuilder;
//var PlainMessage = require('./message').PlainMessage;

// MtProto TL schema as provided by Telegram
var mtTlSchema = require('./mt-tlschema.json');

// API TL schema as provided by Telegram, but only the Type functions needed at the protocol level
var mtApiTlSchema = require('./mt-api-tlschema.json');

// Declare the `type` module
var type = {_id: 'mtproto.type'};
// Build all the constructors
TypeBuilder.buildTypes(mtTlSchema.constructors, null, type);
// Export the 'type' module
exports.type = type;

// Declare the `service` module
var service = {_id: 'mtproto.service'};
// Build all the methods
TypeBuilder.buildTypes(mtTlSchema.methods, null, service, true);
TypeBuilder.buildTypes(mtApiTlSchema.methods, null, service, true);
// Export the 'service' module
exports.service = service;
<|endoftext|>


<commit_before>(function(root, factory) {
    if (typeof module === 'object' && module.exports) {
        module.exports = factory();
    } else {
        root.nearley = factory();
    }
}(this, function() {

function Rule(name, symbols, postprocess) {
    this.id = ++Rule.highestId;
    this.name = name;
    this.symbols = symbols;        // a list of literal | regex class | nonterminal
    this.postprocess = postprocess;
    return this;
}
Rule.highestId = 0;

Rule.prototype.toString = function(withCursorAt) {
    function stringifySymbolSequence (e) {
        return e.literal ? JSON.stringify(e.literal) :
               e.type ? '%' + e.type : e.toString();
    }
    var symbolSequence = (typeof withCursorAt === "undefined")
                         ? this.symbols.map(stringifySymbolSequence).join(' ')
                         : (   this.symbols.slice(0, withCursorAt).map(stringifySymbolSequence).join(' ')
                             + " ● "
                             + this.symbols.slice(withCursorAt).map(stringifySymbolSequence).join(' ')     );
    return this.name + " → " + symbolSequence;
}

// a State is a rule at a position from a given starting point in the input stream (reference)
function State(rule, dot, reference, wantedBy) {
    this.rule = rule;
    this.dot = dot;
    this.reference = reference;
    this.data = [];
    this.wantedBy = wantedBy;
    this.isComplete = this.dot === rule.symbols.length;
}

State.prototype.toString = function() {
    return "{" + this.rule.toString(this.dot) + "}, from: " + (this.reference || 0);
};

State.prototype.nextState = function(child) {
    var state = new State(this.rule, this.dot + 1, this.reference, this.wantedBy);
    state.left = this;
    state.right = child;
    if (state.isComplete) {
        state.data = state.build();
    }
    return state;
};

State.prototype.build = function() {
    var children = [];
    var node = this;
    do {
        children.push(node.right.data);
        node = node.left;
    } while (node.left);
    children.reverse();
    return children;
};

State.prototype.finish = function() {
    if (this.rule.postprocess) {
        this.data = this.rule.postprocess(this.data, this.reference, Parser.fail);
    }
};

function Column(grammar, index) {
    this.grammar = grammar;
    this.index = index;
    this.states = [];
    this.wants = {}; // states indexed by the non-terminal they expect
    this.scannable = []; // list of states that expect a token
    this.completed = {}; // states that are nullable
}

Column.prototype.process = function(nextColumn) {
    var states = this.states;
    var wants = this.wants;
    var completed = this.completed;

    for (var w = 0; w < states.length; w++) { // nb. we push() during iteration
        var state = states[w];

        if (state.isComplete) {
            state.finish();
            if (state.data !== Parser.fail) {
                // complete
                var wantedBy = state.wantedBy;
                for (var i = wantedBy.length; i--; ) { // this line is hot
                    var left = wantedBy[i];
                    this.complete(left, state);
                }

                // special-case nullables
                if (state.reference === this.index) {
                    // make sure future predictors of this rule get completed.
                    var exp = state.rule.name;
                    (this.completed[exp] = this.completed[exp] || []).push(state);
                }
            }

        } else {
            // queue scannable states
            var exp = state.rule.symbols[state.dot];
            if (typeof exp !== 'string') {
                this.scannable.push(state);
                continue;
            }

            // predict
            if (wants[exp]) {
                wants[exp].push(state);

                if (completed.hasOwnProperty(exp)) {
                    var nulls = completed[exp];
                    for (var i = 0; i < nulls.length; i++) {
                        var right = nulls[i];
                        this.complete(state, right);
                    }
                }
            } else {
                wants[exp] = [state];
                this.predict(exp);
            }
        }
    }
}

Column.prototype.predict = function(exp) {
    var rules = this.grammar.byName[exp] || [];

    for (var i = 0; i < rules.length; i++) {
        var r = rules[i];
        var wantedBy = this.wants[exp];
        var s = new State(r, 0, this.index, wantedBy);
        this.states.push(s);
    }
}

Column.prototype.complete = function(left, right) {
    var inp = right.rule.name;
    if (left.rule.symbols[left.dot] === inp) {
        var copy = left.nextState(right);
        this.states.push(copy);
    }
}

function Grammar(rules, start) {
    this.rules = rules;
    this.start = start || this.rules[0].name;
    var byName = this.byName = {};
    this.rules.forEach(function(rule) {
        if (!byName.hasOwnProperty(rule.name)) {
            byName[rule.name] = [];
        }
        byName[rule.name].push(rule);
    });
}

// So we can allow passing (rules, start) directly to Parser for backwards compatibility
Grammar.fromCompiled = function(rules, start) {
    var lexer = rules.Lexer;
    if (rules.ParserStart) {
      start = rules.ParserStart;
      rules = rules.ParserRules;
    }
    var rules = rules.map(function (r) { return (new Rule(r.name, r.symbols, r.postprocess)); });
    var g = new Grammar(rules, start);
    g.lexer = lexer; // nb. storing lexer on Grammar is iffy, but unavoidable
    return g;
}

function StreamLexer() {
  this.reset("");
}

StreamLexer.prototype.reset = function(data, state) {
    this.buffer = data;
    this.index = 0;
    this.line = state ? state.line : 1;
    this.lastLineBreak = state ? -state.col : 0;
}

StreamLexer.prototype.next = function() {
    if (this.index < this.buffer.length) {
        var ch = this.buffer[this.index++];
        if (ch === '\n') {
          this.line += 1;
          this.lastLineBreak = this.index;
        }
        return {value: ch};
    }
}

StreamLexer.prototype.save = function() {
  return {
    line: this.line,
    col: this.index - this.lastLineBreak,
  }
}

StreamLexer.prototype.formatError = function(token, message) {
    // nb. this gets called after consuming the offending token,
    // so the culprit is index-1
    var buffer = this.buffer;
    if (typeof buffer === 'string') {
        var nextLineBreak = buffer.indexOf('\n', this.index);
        if (nextLineBreak === -1) nextLineBreak = buffer.length;
        var line = buffer.substring(this.lastLineBreak, nextLineBreak)
        var col = this.index - this.lastLineBreak;
        message += " at line " + this.line + " col " + col + ":\n\n";
        message += "  " + line + "\n"
        message += "  " + Array(col).join(" ") + "^"
        return message;
    } else {
        return message + " at index " + (this.index - 1);
    }
}

function Parser(rules, start, options) {
    if (rules instanceof Grammar) {
        var grammar = rules;
        var options = start;
    } else {
        var grammar = Grammar.fromCompiled(rules, start);
    }
    this.grammar = grammar;

    // Read options
    this.options = {
        keepHistory: false,
        lexer: grammar.lexer || new StreamLexer,
    };
    for (var key in (options || {})) {
        this.options[key] = options[key];
    }

    // Setup lexer
    this.lexer = this.options.lexer;
    this.lexerState = undefined;

    // Setup a table
    var column = new Column(grammar, 0);
    var table = this.table = [column];

    // I could be expecting anything.
    column.wants[grammar.start] = [];
    column.predict(grammar.start);
    // TODO what if start rule is nullable?
    column.process();
    this.current = 0; // token index
}

// create a reserved token for indicating a parse fail
Parser.fail = {};

Parser.prototype.feed = function(chunk) {
    var lexer = this.lexer;
    lexer.reset(chunk, this.lexerState);

    while (token = lexer.next()) {
        // We add new states to table[current+1]
        var column = this.table[this.current];

        // GC unused states
        if (!this.options.keepHistory) {
            delete this.table[this.current - 1];
        }

        var n = this.current + 1;
        var nextColumn = new Column(this.grammar, n);
        this.table.push(nextColumn);

        // Advance all tokens that expect the symbol
        var literal = token.value;
        var value = lexer.constructor === StreamLexer ? token.value : token;
        var scannable = column.scannable;
        for (var w = scannable.length; w--; ) {
            var state = scannable[w];
            var expect = state.rule.symbols[state.dot];
            // Try to consume the token
            // either regex or literal
            if (expect.test ? expect.test(value) :
                expect.type ? expect.type === token.type
                            : expect.literal === literal) {
                // Add it
                var next = state.nextState({data: value, token: token, isToken: true, reference: n - 1});
                nextColumn.states.push(next);
            }
        }

        // Next, for each of the rules, we either
        // (a) complete it, and try to see if the reference row expected that
        //     rule
        // (b) predict the next nonterminal it expects by adding that
        //     nonterminal's start state
        // To prevent duplication, we also keep track of rules we have already
        // added

        nextColumn.process();

        // If needed, throw an error:
        if (nextColumn.states.length === 0) {
            // No states at all! This is not good.
            var message = this.lexer.formatError(token, "invalid syntax") + "\n";
            message += "Unexpected " + (token.type ? token.type + " token: " : "");
            message += JSON.stringify(token.value !== undefined ? token.value : token) + "\n";
            var err = new Error(message);
            err.offset = this.current;
            err.token = token;
            throw err;
        }

        // maybe save lexer state
        if (this.options.keepHistory) {
          column.lexerState = lexer.save()
        }

        this.current++;
    }
    if (column) {
      this.lexerState = lexer.save()
    }

    // Incrementally keep track of results
    this.results = this.finish();

    // Allow chaining, for whatever it's worth
    return this;
};

Parser.prototype.save = function() {
    var column = this.table[this.current];
    column.lexerState = this.lexerState;
    return column;
};

Parser.prototype.restore = function(column) {
    var index = column.index;
    this.current = index;
    this.table[index] = column;
    this.table.splice(index + 1);
    this.lexerState = column.lexerState;

    // Incrementally keep track of results
    this.results = this.finish();
};

// nb. deprecated: use save/restore instead!
Parser.prototype.rewind = function(index) {
    if (!this.options.keepHistory) {
        throw new Error('set option `keepHistory` to enable rewinding')
    }
    // nb. recall column (table) indicies fall between token indicies.
    //        col 0   --   token 0   --   col 1
    this.restore(this.table[index]);
};

Parser.prototype.finish = function() {
    // Return the possible parsings
    var considerations = [];
    var start = this.grammar.start;
    var column = this.table[this.table.length - 1]
    column.states.forEach(function (t) {
        if (t.rule.name === start
                && t.dot === t.rule.symbols.length
                && t.reference === 0
                && t.data !== Parser.fail) {
            considerations.push(t);
        }
    });
    return considerations.map(function(c) {return c.data; });
};

return {
    Parser: Parser,
    Grammar: Grammar,
    Rule: Rule,
};

}));
<commit_msg>Small fix to support JS strict mode<commit_after>(function(root, factory) {
    if (typeof module === 'object' && module.exports) {
        module.exports = factory();
    } else {
        root.nearley = factory();
    }
}(this, function() {

function Rule(name, symbols, postprocess) {
    this.id = ++Rule.highestId;
    this.name = name;
    this.symbols = symbols;        // a list of literal | regex class | nonterminal
    this.postprocess = postprocess;
    return this;
}
Rule.highestId = 0;

Rule.prototype.toString = function(withCursorAt) {
    function stringifySymbolSequence (e) {
        return e.literal ? JSON.stringify(e.literal) :
               e.type ? '%' + e.type : e.toString();
    }
    var symbolSequence = (typeof withCursorAt === "undefined")
                         ? this.symbols.map(stringifySymbolSequence).join(' ')
                         : (   this.symbols.slice(0, withCursorAt).map(stringifySymbolSequence).join(' ')
                             + " ● "
                             + this.symbols.slice(withCursorAt).map(stringifySymbolSequence).join(' ')     );
    return this.name + " → " + symbolSequence;
}

// a State is a rule at a position from a given starting point in the input stream (reference)
function State(rule, dot, reference, wantedBy) {
    this.rule = rule;
    this.dot = dot;
    this.reference = reference;
    this.data = [];
    this.wantedBy = wantedBy;
    this.isComplete = this.dot === rule.symbols.length;
}

State.prototype.toString = function() {
    return "{" + this.rule.toString(this.dot) + "}, from: " + (this.reference || 0);
};

State.prototype.nextState = function(child) {
    var state = new State(this.rule, this.dot + 1, this.reference, this.wantedBy);
    state.left = this;
    state.right = child;
    if (state.isComplete) {
        state.data = state.build();
    }
    return state;
};

State.prototype.build = function() {
    var children = [];
    var node = this;
    do {
        children.push(node.right.data);
        node = node.left;
    } while (node.left);
    children.reverse();
    return children;
};

State.prototype.finish = function() {
    if (this.rule.postprocess) {
        this.data = this.rule.postprocess(this.data, this.reference, Parser.fail);
    }
};

function Column(grammar, index) {
    this.grammar = grammar;
    this.index = index;
    this.states = [];
    this.wants = {}; // states indexed by the non-terminal they expect
    this.scannable = []; // list of states that expect a token
    this.completed = {}; // states that are nullable
}

Column.prototype.process = function(nextColumn) {
    var states = this.states;
    var wants = this.wants;
    var completed = this.completed;

    for (var w = 0; w < states.length; w++) { // nb. we push() during iteration
        var state = states[w];

        if (state.isComplete) {
            state.finish();
            if (state.data !== Parser.fail) {
                // complete
                var wantedBy = state.wantedBy;
                for (var i = wantedBy.length; i--; ) { // this line is hot
                    var left = wantedBy[i];
                    this.complete(left, state);
                }

                // special-case nullables
                if (state.reference === this.index) {
                    // make sure future predictors of this rule get completed.
                    var exp = state.rule.name;
                    (this.completed[exp] = this.completed[exp] || []).push(state);
                }
            }

        } else {
            // queue scannable states
            var exp = state.rule.symbols[state.dot];
            if (typeof exp !== 'string') {
                this.scannable.push(state);
                continue;
            }

            // predict
            if (wants[exp]) {
                wants[exp].push(state);

                if (completed.hasOwnProperty(exp)) {
                    var nulls = completed[exp];
                    for (var i = 0; i < nulls.length; i++) {
                        var right = nulls[i];
                        this.complete(state, right);
                    }
                }
            } else {
                wants[exp] = [state];
                this.predict(exp);
            }
        }
    }
}

Column.prototype.predict = function(exp) {
    var rules = this.grammar.byName[exp] || [];

    for (var i = 0; i < rules.length; i++) {
        var r = rules[i];
        var wantedBy = this.wants[exp];
        var s = new State(r, 0, this.index, wantedBy);
        this.states.push(s);
    }
}

Column.prototype.complete = function(left, right) {
    var inp = right.rule.name;
    if (left.rule.symbols[left.dot] === inp) {
        var copy = left.nextState(right);
        this.states.push(copy);
    }
}

function Grammar(rules, start) {
    this.rules = rules;
    this.start = start || this.rules[0].name;
    var byName = this.byName = {};
    this.rules.forEach(function(rule) {
        if (!byName.hasOwnProperty(rule.name)) {
            byName[rule.name] = [];
        }
        byName[rule.name].push(rule);
    });
}

// So we can allow passing (rules, start) directly to Parser for backwards compatibility
Grammar.fromCompiled = function(rules, start) {
    var lexer = rules.Lexer;
    if (rules.ParserStart) {
      start = rules.ParserStart;
      rules = rules.ParserRules;
    }
    var rules = rules.map(function (r) { return (new Rule(r.name, r.symbols, r.postprocess)); });
    var g = new Grammar(rules, start);
    g.lexer = lexer; // nb. storing lexer on Grammar is iffy, but unavoidable
    return g;
}

function StreamLexer() {
  this.reset("");
}

StreamLexer.prototype.reset = function(data, state) {
    this.buffer = data;
    this.index = 0;
    this.line = state ? state.line : 1;
    this.lastLineBreak = state ? -state.col : 0;
}

StreamLexer.prototype.next = function() {
    if (this.index < this.buffer.length) {
        var ch = this.buffer[this.index++];
        if (ch === '\n') {
          this.line += 1;
          this.lastLineBreak = this.index;
        }
        return {value: ch};
    }
}

StreamLexer.prototype.save = function() {
  return {
    line: this.line,
    col: this.index - this.lastLineBreak,
  }
}

StreamLexer.prototype.formatError = function(token, message) {
    // nb. this gets called after consuming the offending token,
    // so the culprit is index-1
    var buffer = this.buffer;
    if (typeof buffer === 'string') {
        var nextLineBreak = buffer.indexOf('\n', this.index);
        if (nextLineBreak === -1) nextLineBreak = buffer.length;
        var line = buffer.substring(this.lastLineBreak, nextLineBreak)
        var col = this.index - this.lastLineBreak;
        message += " at line " + this.line + " col " + col + ":\n\n";
        message += "  " + line + "\n"
        message += "  " + Array(col).join(" ") + "^"
        return message;
    } else {
        return message + " at index " + (this.index - 1);
    }
}

function Parser(rules, start, options) {
    if (rules instanceof Grammar) {
        var grammar = rules;
        var options = start;
    } else {
        var grammar = Grammar.fromCompiled(rules, start);
    }
    this.grammar = grammar;

    // Read options
    this.options = {
        keepHistory: false,
        lexer: grammar.lexer || new StreamLexer,
    };
    for (var key in (options || {})) {
        this.options[key] = options[key];
    }

    // Setup lexer
    this.lexer = this.options.lexer;
    this.lexerState = undefined;

    // Setup a table
    var column = new Column(grammar, 0);
    var table = this.table = [column];

    // I could be expecting anything.
    column.wants[grammar.start] = [];
    column.predict(grammar.start);
    // TODO what if start rule is nullable?
    column.process();
    this.current = 0; // token index
}

// create a reserved token for indicating a parse fail
Parser.fail = {};

Parser.prototype.feed = function(chunk) {
    var lexer = this.lexer;
    lexer.reset(chunk, this.lexerState);

    var token;
    while (token = lexer.next()) {
        // We add new states to table[current+1]
        var column = this.table[this.current];

        // GC unused states
        if (!this.options.keepHistory) {
            delete this.table[this.current - 1];
        }

        var n = this.current + 1;
        var nextColumn = new Column(this.grammar, n);
        this.table.push(nextColumn);

        // Advance all tokens that expect the symbol
        var literal = token.value;
        var value = lexer.constructor === StreamLexer ? token.value : token;
        var scannable = column.scannable;
        for (var w = scannable.length; w--; ) {
            var state = scannable[w];
            var expect = state.rule.symbols[state.dot];
            // Try to consume the token
            // either regex or literal
            if (expect.test ? expect.test(value) :
                expect.type ? expect.type === token.type
                            : expect.literal === literal) {
                // Add it
                var next = state.nextState({data: value, token: token, isToken: true, reference: n - 1});
                nextColumn.states.push(next);
            }
        }

        // Next, for each of the rules, we either
        // (a) complete it, and try to see if the reference row expected that
        //     rule
        // (b) predict the next nonterminal it expects by adding that
        //     nonterminal's start state
        // To prevent duplication, we also keep track of rules we have already
        // added

        nextColumn.process();

        // If needed, throw an error:
        if (nextColumn.states.length === 0) {
            // No states at all! This is not good.
            var message = this.lexer.formatError(token, "invalid syntax") + "\n";
            message += "Unexpected " + (token.type ? token.type + " token: " : "");
            message += JSON.stringify(token.value !== undefined ? token.value : token) + "\n";
            var err = new Error(message);
            err.offset = this.current;
            err.token = token;
            throw err;
        }

        // maybe save lexer state
        if (this.options.keepHistory) {
          column.lexerState = lexer.save()
        }

        this.current++;
    }
    if (column) {
      this.lexerState = lexer.save()
    }

    // Incrementally keep track of results
    this.results = this.finish();

    // Allow chaining, for whatever it's worth
    return this;
};

Parser.prototype.save = function() {
    var column = this.table[this.current];
    column.lexerState = this.lexerState;
    return column;
};

Parser.prototype.restore = function(column) {
    var index = column.index;
    this.current = index;
    this.table[index] = column;
    this.table.splice(index + 1);
    this.lexerState = column.lexerState;

    // Incrementally keep track of results
    this.results = this.finish();
};

// nb. deprecated: use save/restore instead!
Parser.prototype.rewind = function(index) {
    if (!this.options.keepHistory) {
        throw new Error('set option `keepHistory` to enable rewinding')
    }
    // nb. recall column (table) indicies fall between token indicies.
    //        col 0   --   token 0   --   col 1
    this.restore(this.table[index]);
};

Parser.prototype.finish = function() {
    // Return the possible parsings
    var considerations = [];
    var start = this.grammar.start;
    var column = this.table[this.table.length - 1]
    column.states.forEach(function (t) {
        if (t.rule.name === start
                && t.dot === t.rule.symbols.length
                && t.reference === 0
                && t.data !== Parser.fail) {
            considerations.push(t);
        }
    });
    return considerations.map(function(c) {return c.data; });
};

return {
    Parser: Parser,
    Grammar: Grammar,
    Rule: Rule,
};

}));
<|endoftext|>


<commit_before>"use strict";

var Logger = require('./logger')
  , path = require('path')
  , fs = require('fs-extra')

var Network = {

  routePaquets : function () {

  },

  /**
   * Setup the /etc/resolver/{domain} file
   * @private
   */
  setupResolver : function (domain, port, ip) {

    var file = path.join('/etc/resolver', domain)

    var contents = [
      "nameserver " + ip,
      "port " + port + "\n"
    ].join("\n")

    fs.outputFile(file, contents, function () {
      Logger.info('Resolver file %s updated', file)
    })

    process.stdin.resume();

    var onExit = function() {
      Logger.info('Removing resolver file %s', file)
      fs.unlinkSync(file)
      process.exit(0)
    }

    process.on('SIGINT', onExit)
    process.on('SIGTERM', onExit)

    return this
  }
}

module.exports = Network<commit_msg>remove unused method<commit_after>"use strict";

var Logger = require('./logger')
  , path = require('path')
  , fs = require('fs-extra')

var Network = {

  /**
   * Setup the /etc/resolver/{domain} file
   * @private
   */
  setupResolver : function (domain, port, ip) {

    var file = path.join('/etc/resolver', domain)

    var contents = [
      "nameserver " + ip,
      "port " + port + "\n"
    ].join("\n")

    fs.outputFile(file, contents, function () {
      Logger.info('Resolver file %s updated', file)
    })

    process.stdin.resume();

    var onExit = function() {
      Logger.info('Removing resolver file %s', file)
      fs.unlinkSync(file)
      process.exit(0)
    }

    process.on('SIGINT', onExit)
    process.on('SIGTERM', onExit)

    return this
  }
}

module.exports = Network<|endoftext|>


<commit_before>/**
 * Copyright 2015 Mozilla
 *
 * 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.
 */

'use strict';

// Import this first so we can use it to wrap other modules we import.
var promisify = require("promisify-node");

var fs = require('fs');
var path = require('path');
var swPrecache = require('sw-precache');
var gutil = require('gulp-util');

module.exports = promisify(function(config, callback) {
  var rootDir = config.rootDir || './';

  // Ensure root directory ends with slash so we strip leading slash from paths
  // of files to cache, so they're relative paths, and the offline worker
  // will match them regardless of whether the app is deployed to a top-level
  // directory for a custom domain (f.e. https://example.com/) or a subdirectory
  // of a GitHub Pages domain (f.e. https://mykmelez.github.io/example/).
  if (rootDir.lastIndexOf('/') !== rootDir.length -1) {
    rootDir = rootDir + "/";
  }

  var fileGlobs = config.fileGlobs || ['**/*'];

  // Remove the existing service worker, if any, so sw-precache doesn't include
  // it in the list of files to cache.
  try {
    fs.unlinkSync(path.join(rootDir, 'offline-worker.js'));
  } catch(ex) {
    // XXX Only ignore the error if it's a 'file not found' error, i.e.:
    // { [Error: ENOENT, no such file or directory 'dist/offline-worker.js']
    //    errno: -2,
    //    code: 'ENOENT',
    //    path: 'dist/offline-worker.js',
    //    syscall: 'unlink' }
  }

  swPrecache.write(path.join(rootDir, 'offline-worker.js'), {
    staticFileGlobs: fileGlobs.map(function(v) { return path.join(rootDir, v) }),
    stripPrefix: rootDir,
    verbose: true,
    logger: gutil.log,
    importScripts: config.importScripts || [],
  }, callback);
});
<commit_msg>Only ignore errors while removing the offline-worker.js file if it's a 'file not found' error. Fixes #40<commit_after>/**
 * Copyright 2015 Mozilla
 *
 * 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.
 */

'use strict';

// Import this first so we can use it to wrap other modules we import.
var promisify = require("promisify-node");

var fs = require('fs');
var path = require('path');
var swPrecache = require('sw-precache');
var gutil = require('gulp-util');

module.exports = promisify(function(config, callback) {
  var rootDir = config.rootDir || './';

  // Ensure root directory ends with slash so we strip leading slash from paths
  // of files to cache, so they're relative paths, and the offline worker
  // will match them regardless of whether the app is deployed to a top-level
  // directory for a custom domain (f.e. https://example.com/) or a subdirectory
  // of a GitHub Pages domain (f.e. https://mykmelez.github.io/example/).
  if (rootDir.lastIndexOf('/') !== rootDir.length -1) {
    rootDir = rootDir + "/";
  }

  var fileGlobs = config.fileGlobs || ['**/*'];

  // Remove the existing service worker, if any, so sw-precache doesn't include
  // it in the list of files to cache.
  try {
    fs.unlinkSync(path.join(rootDir, 'offline-worker.js'));
  } catch (ex) {
    // Only ignore the error if it's a 'file not found' error.
    if (!ex.code || ex.code !== 'ENOENT') {
      throw ex;
    }
  }

  swPrecache.write(path.join(rootDir, 'offline-worker.js'), {
    staticFileGlobs: fileGlobs.map(function(v) { return path.join(rootDir, v) }),
    stripPrefix: rootDir,
    verbose: true,
    logger: gutil.log,
    importScripts: config.importScripts || [],
  }, callback);
});
<|endoftext|>


<commit_before>//TODO consider OpenHAB item discovery via 'config.HA_server + '/rest/items'

var config = require('../config');
var request = require('request');

/***************** OPENHAB REST API ********************/
// Get state of an OpenHAB item
function getState(itemName, callback) {
    request(config.HA_server + "/rest/items/" + itemName + "/state", function (err, response) {
        if (err) {
            callback(new Error('Cannot reach OpenHAB API @ ' + config.HA_server + "/rest/items/" + itemName + "/state"));
        } else if (response.statusCode !== 200) {
            callback(new Error('Cannot fetch OpenHAB API Data:  ' + config.HA_server + "/rest/items/" + itemName + "/state"));
        } else {
            //console.log('OpenHAB getState Results: ', response.body);
            callback(null, response.body);
        }
    });
}

// Send command or set state of an OpenHAB item
function setState(itemName, state) {
    var url = config.HA_server + "/rest/items/" + itemName;
    request.post({
        uri:url,
        headers:{
            'Content-Type': 'text/plain',
            'Content-Length': state.length
        },
        body:state
    }, function(err,response){
        // DEBUG
        //console.log('setState call: ' + url + '/' + state);
        //console.log('setState response code: ' + response.statusCode);
        //console.log('setState response: ', response);

        // Handle errors or bad OpenHAB HTTP response codes
        if (err) {
            console.log('There was an error during during setState! Error message: ' + err.message);
            return false;
        }
        else if (response.statusCode !== 201) {
            console.log('There was an error during during setState! HTTP response code: ' + response.statusCode);
            return false;
        }
        else {
            // Success!
            return true;
        }
    });
}

/***************** VOICE COMMANDS **********************/
// Execute voice command, wait for server side 'processed' flag, and return the HA response/answer
// TODO make this fail after 10 seconds!
function runVoiceCMD(callback) {
    setTimeout(function () {
        waitTilProcessed(function (msg) {
            if (msg !== 'ON') {
                runVoiceCMD(callback);
            }
            else {
                getState(config.HA_item_answer, function (err,msg) {
                    if (err) {
                        callback('Cannot reach OpenHAB API: ' + err.message, null);
                    } else if (msg) {
                        callback(null, msg);
                    }
                });

            }
        });
    }, 500);
}

// Wait until HA server has processed the response
function waitTilProcessed(callback) {
    var processed = null;

    getState(config.HA_item_processed, function (err,msg) {
        if (err) {
            console.log('Cannot reach OpenHAB API: ' + err.message, null);
        } else if (msg) {
            processed = msg.toString();
            if (processed !== 'ON') {
                callback('OFF');
            }
            else{
                callback('ON');
            }
        }
    });
}

module.exports.getState = getState;
module.exports.setState = setState;
module.exports.runVoiceCMD = runVoiceCMD;

<commit_msg>HTTPS support for Alexa-HA <-> HA server communication<commit_after>//TODO consider OpenHAB item discovery via 'config.HA_server + '/rest/items'

var config = require('../config');
var request = require('request');

// If https is used, allow default and self signed certificates
var agent;
if ( config.HA_protocol == 'https' ) {
    var https = require('https');
    var agentOptions;
    agentOptions = {
        host: config.HA_host,
        port: config.HA_port,
        path: '/',
        rejectUnauthorized: false
    };
    agent = new https.Agent(agentOptions);
}
/***************** OPENHAB REST API ********************/
// Get state of an OpenHAB item
function getState(itemName, callback) {
    var url = config.HA_server + "/rest/items/" + itemName + "/state";
    request({
        uri:url,
        method: 'GET',
        agent: agent
    }, function (err, response) {
        if (err) {
            callback(new Error('Cannot reach OpenHAB API @ ' + config.HA_server + "/rest/items/" + itemName + "/state"));
        } else if (response.statusCode !== 200) {
            callback(new Error('Cannot fetch OpenHAB API Data:  ' + config.HA_server + "/rest/items/" + itemName + "/state"));
        } else {
            //console.log('OpenHAB getState Results: ', response.body);
            callback(null, response.body);
        }
    });
}

// Send command or set state of an OpenHAB item
function setState(itemName, state) {
    var url = config.HA_server + "/rest/items/" + itemName;
    request.post({
        uri:url,
        headers:{
            'Content-Type': 'text/plain',
            'Content-Length': state.length
        },
        body:state,
        agent:agent
    }, function(err,response){
        // DEBUG
        //console.log('setState call: ' + url + '/' + state);
        //console.log('setState response code: ' + response.statusCode);
        //console.log('setState response: ', response);

        // Handle errors or bad OpenHAB HTTP response codes
        if (err) {
            console.log('There was an error during during setState! Error message: ' + err.message);
            return false;
        }
        else if (response.statusCode !== 201) {
            console.log('There was an error during during setState! HTTP response code: ' + response.statusCode);
            return false;
        }
        else {
            // Success!
            return true;
        }
    });
}

/***************** VOICE COMMANDS **********************/
// Execute voice command, wait for server side 'processed' flag, and return the HA response/answer
// TODO make this fail after 10 seconds!
function runVoiceCMD(callback) {
    setTimeout(function () {
        waitTilProcessed(function (msg) {
            if (msg !== 'ON') {
                runVoiceCMD(callback);
            }
            else {
                getState(config.HA_item_answer, function (err,msg) {
                    if (err) {
                        callback('Cannot reach OpenHAB API: ' + err.message, null);
                    } else if (msg) {
                        callback(null, msg);
                    }
                });

            }
        });
    }, 500);
}

// Wait until HA server has processed the response
function waitTilProcessed(callback) {
    var processed = null;

    getState(config.HA_item_processed, function (err,msg) {
        if (err) {
            console.log('Cannot reach OpenHAB API: ' + err.message, null);
        } else if (msg) {
            processed = msg.toString();
            if (processed !== 'ON') {
                callback('OFF');
            }
            else{
                callback('ON');
            }
        }
    });
}

module.exports.getState = getState;
module.exports.setState = setState;
module.exports.runVoiceCMD = runVoiceCMD;

<|endoftext|>


<commit_before>/*jshint node:true, indent:2, curly:false, eqeqeq:true, immed:true, latedef:true, newcap:true, noarg:true,
regexp:true, undef:true, strict:true, trailing:true, white:true */
/*global */

(function () {
  "use strict";

  var _path = require("path"), flag, cwd, path;

  cwd = process.cwd();

  if ((flag = process.argv.indexOf("-c")) > 0) {
    path = _path.join(cwd, process.argv[flag + 1]);
  } else { path = _path.join(cwd, "config.js"); }

  module.exports = require(path);
}());
<commit_msg>Allow config files to live in a dir not relative to the current working dir if the first char is a / then use the config file path directly, otherwise append to the current working directory<commit_after>/*jshint node:true, indent:2, curly:false, eqeqeq:true, immed:true, latedef:true, newcap:true, noarg:true,
regexp:true, undef:true, strict:true, trailing:true, white:true */
/*global */

(function () {
  "use strict";

  var _path = require("path"), flag, cwd, path;

  cwd = process.cwd();

  if ((flag = process.argv.indexOf("-c")) > 0) {
    var configPath = process.argv[flag + 1];
    if (configPath[0] == '/') {
	path = configPath;
    } else {
    	path = _path.join(cwd, process.argv[flag + 1]);
    }
  } else { path = _path.join(cwd, "config.js"); }

  module.exports = require(path);
}());
<|endoftext|>


<commit_before>/**
 * package - Easy package.json exports.
 *
 * Author: Veselin Todorov <hi@vesln.com>
 * Licensed under the MIT License.
 */

/**
 * Dependencies.
 */
var fs = require('fs');
var path = require('path');

/**
 * Package.
 *
 * @param {String|null} location
 * @returns {Object} package.json data
 */
var package = function(location) {
  if (location === Object(location)) {
    location = package.discover(location);
  }
  return package.read(path.normalize(location + '/package.json'));
};

/**
 * Reads and parses a package.json file.
 *
 * @param {String} file
 * @returns {Object} package.json data
 */
package.read = function(file) {
  var data = fs.readFileSync(file, 'utf8');
  return JSON.parse(data);
};

/**
 * Makes an atempt to find package.json file.
 *
 * @returns {Object} package.json data
 */
package.discover = function(module) {
  var location = path.dirname(module.filename);
  var found = null;

  while (!found) {
    if (path.existsSync(location + '/package.json')) {
      found = location;
    } else if (location !== '/') {
      location = path.dirname(location);
    } else {
      throw new Error('package.json can not be located');
    }
  }

  return found;
};

/**
 * Exporting the lib.
 */
module.exports = package;<commit_msg>Fix deprecation warning on v0.7.x<commit_after>/**
 * package - Easy package.json exports.
 *
 * Author: Veselin Todorov <hi@vesln.com>
 * Licensed under the MIT License.
 */

/**
 * Dependencies.
 */
var fs = require('fs');
var path = require('path');
var exists = fs.existsSync || path.existsSync;

/**
 * Package.
 *
 * @param {String|null} location
 * @returns {Object} package.json data
 */
var package = function(location) {
  if (location === Object(location)) {
    location = package.discover(location);
  }
  return package.read(path.normalize(location + '/package.json'));
};

/**
 * Reads and parses a package.json file.
 *
 * @param {String} file
 * @returns {Object} package.json data
 */
package.read = function(file) {
  var data = fs.readFileSync(file, 'utf8');
  return JSON.parse(data);
};

/**
 * Makes an atempt to find package.json file.
 *
 * @returns {Object} package.json data
 */
package.discover = function(module) {
  var location = path.dirname(module.filename);
  var found = null;

  while (!found) {
    if (exists(location + '/package.json')) {
      found = location;
    } else if (location !== '/') {
      location = path.dirname(location);
    } else {
      throw new Error('package.json can not be located');
    }
  }

  return found;
};

/**
 * Exporting the lib.
 */
module.exports = package;
<|endoftext|>


<commit_before>module.exports = function (stream) {
	return [
		require('./opentype.js')(stream),
		require('./woff.js')(stream),
		require('./woff2.js')(stream),
		require('./eot.js')(stream),
		require('./svg.js')(stream)
	];
};<commit_msg>Fix require() in parsers.js<commit_after>var opentype = require('./opentype.js');
var woff = require('./woff.js');
var woff2 = require('./woff2.js');
var eot = require('./eot.js');
var svg = require('./svg.js');

module.exports = function (stream) {
	return [
		opentype(stream),
		woff(stream),
		woff2(stream),
		eot(stream),
		svg(stream)
	];
};<|endoftext|>


<commit_before>var Hooker = require('hooker')
  , utils = require('./utils')
  , debug = require('debug')('plugger')

/*
 Every plugin must follow this protocol /or as specified when creating a Plugger instance
 */
var DEF_PROTOCOL = {
    main: 'init'
  , id: 'name'
  , orderBy: 'priority'
  , contract: {
        init: 'function'
      , priority: 'number'
      , name: 'string'
    }
}

/**
 * Filter to be used for detecting plugin files
*/
function defaultFilter(filename) {
    return /.*\.(js|coffee)$/.test(filename);
}

/**
 * Cache
 */
var slice = Array.prototype.slice;

/**
 * Plugger that plugs the plugins
 * @param modules       List of plugin dirs. Array with each item containing 'path' and 'depth' properties.
 * @param protocol      Protocol to be followed by all plugin files
 * @param callback      Notifies of invocation errors or completion of all invocations
 * @returns {Plugger}
 * @constructor
 */
function Plugger(modules, protocol, callback) {
    if (!(this instanceof Plugger)) return new Plugger(modules, protocol, callback);

    if (arguments.length === 2 && typeof protocol === 'function') {
        callback = protocol;
        protocol = null;
    }

    this._protocol = protocol || DEF_PROTOCOL;
    this._plugins = [];

    this._hooker = new Hooker(function (err) {
        callback(err);
    });

    this.hook = this._hooker.hook.bind(this._hooker);
    this.invoke = this._hooker.invoke.bind(this._hooker);

    var self = this;

    for (var i = 0, l = modules.length; i < l; i++) {
        var module = modules[i];
        var opts = {
            depth: module.depth || 1
        };

        if (typeof module.filter === 'function') {
            opts.filter = module.matcher;
        } else {
            opts.filter = defaultFilter;
        }

        utils.loadModules(module.path, opts, function (err, module, path) {
            if (err) {
                console.warn('Error loading plugin: ' + err + ' | ' + path);
                return;
            }

            if (module) {
                if (self._isValidModule(module)) {
                    debug('Loaded plugin: ' + module[self._protocol.id]);
                    self._plugins.push(module);
                } else {
                    console.warn('Unrecognized plugin protocol: ' + path);
                }
            }
        });
    }

    this._plugins.sort(function (a, b) {
        return a[self._protocol.orderBy] - b[self._protocol.orderBy];
    });
}

/**
 * Initializes all detected plugins by calling the protocol.main function in the order of protocol.orderBy
 * Plugins are expected to register their hooks in protocol.main
 */
Plugger.prototype.init = function () {
    var plugins = this._plugins;
    var protocol = this._protocol;

    var args = slice.call(arguments);
    args.unshift(this._hooker);
    args.unshift(this.hook);

    for (var i = 0, l = plugins.length; i < l; i++) {
        var plugin = plugins[i];
        plugin[protocol.main].apply(plugin, args);
    }
}

/**
 * Retrieve the require'd plugin module by protocol.id
 * @param idValue
 * @returns {*}
 */
Plugger.prototype.getPlugin = function (idValue) {
    var plugins = this._plugins;
    var options = this._options;
    var id = this._protocol.id;

    for (var i = 0, l = plugins.length; i < l; i++) {
        if (plugins[i][id] === idValue) {
            return plugins[i];
        }
    }

    return false;
}

/**
 * Checks if require'd module follows the specified protocol, i.e. module is a plugin
 * @param module        require'd module
 * @returns {boolean}
 * @private
 */
Plugger.prototype._isValidModule = function (module) {
    var protocol = this._protocol;

    if (typeof module[protocol.main] !== 'function') {
        return false;
    }

    if (typeof module[protocol.id] !== 'string' || !module[protocol.id].length) {
        return false;
    }

    if (typeof module[protocol.orderBy] !== 'number' || module[protocol.orderBy] < 0) {
        return false;
    }

    for (var key in protocol.contract) {
        if (protocol.contract.hasOwnProperty(key)) {
            if (typeof module[key] !== protocol.contract[key]) {
                return false;
            }
        }
    }

    return true;
}

/*!
 * Module exports.
 */
module.exports = exports = Plugger;<commit_msg>Expose seal method<commit_after>var Hooker = require('hooker')
  , utils = require('./utils')
  , debug = require('debug')('plugger')

/*
 Every plugin must follow this protocol /or as specified when creating a Plugger instance
 */
var DEF_PROTOCOL = {
    main: 'init'
  , id: 'name'
  , orderBy: 'priority'
  , contract: {
        init: 'function'
      , priority: 'number'
      , name: 'string'
    }
}

/**
 * Filter to be used for detecting plugin files
*/
function defaultFilter(filename) {
    return /.*\.(js|coffee)$/.test(filename);
}

/**
 * Cache
 */
var slice = Array.prototype.slice;

/**
 * Plugger that plugs the plugins
 * @param modules       List of plugin dirs. Array with each item containing 'path' and 'depth' properties.
 * @param protocol      Protocol to be followed by all plugin files
 * @param callback      Notifies of invocation errors or completion of all invocations
 * @returns {Plugger}
 * @constructor
 */
function Plugger(modules, protocol, callback) {
    if (!(this instanceof Plugger)) return new Plugger(modules, protocol, callback);

    if (arguments.length === 2 && typeof protocol === 'function') {
        callback = protocol;
        protocol = null;
    }

    this._protocol = protocol || DEF_PROTOCOL;
    this._plugins = [];

    this._hooker = new Hooker(function (err) {
        callback(err);
    });

    var self = this;

    // expose hooker methods
    [
        'hook'
      , 'invoke'
      , 'seal'
    ].forEach(function (fn) {
        self[fn] = self._hooker[fn].bind(self._hooker);
    });

    // scan for plugins
    for (var i = 0, l = modules.length; i < l; i++) {
        var module = modules[i];
        var opts = {
            depth: module.depth || 1
        };

        if (typeof module.filter === 'function') {
            opts.filter = module.matcher;
        } else {
            opts.filter = defaultFilter;
        }

        utils.loadModules(module.path, opts, function (err, module, path) {
            if (err) {
                console.warn('Error loading plugin: ' + err + ' | ' + path);
                return;
            }

            if (module) {
                if (self._isValidModule(module)) {
                    debug('Loaded plugin: ' + module[self._protocol.id]);
                    self._plugins.push(module);
                } else {
                    console.warn('Unrecognized plugin protocol: ' + path);
                }
            }
        });
    }

    this._plugins.sort(function (a, b) {
        return a[self._protocol.orderBy] - b[self._protocol.orderBy];
    });
}

/**
 * Initializes all detected plugins by calling the protocol.main function in the order of protocol.orderBy
 * Plugins are expected to register their hooks in protocol.main
 */
Plugger.prototype.init = function () {
    debug('init');

    var plugins = this._plugins;
    var protocol = this._protocol;

    var args = slice.call(arguments);
    args.unshift(this._hooker);
    args.unshift(this.hook);

    for (var i = 0, l = plugins.length; i < l; i++) {
        var plugin = plugins[i];
        debug('initialzing plugin: ' + plugin[protocol.id]);
        plugin[protocol.main].apply(plugin, args);
    }
}

/**
 * Retrieve the require'd plugin module by protocol.id
 * @param idValue
 * @returns {*}
 */
Plugger.prototype.getPlugin = function (idValue) {
    var plugins = this._plugins;
    var options = this._options;
    var id = this._protocol.id;

    for (var i = 0, l = plugins.length; i < l; i++) {
        if (plugins[i][id] === idValue) {
            return plugins[i];
        }
    }

    return false;
}

/**
 * Checks if require'd module follows the specified protocol, i.e. module is a plugin
 * @param module        require'd module
 * @returns {boolean}
 * @private
 */
Plugger.prototype._isValidModule = function (module) {
    var protocol = this._protocol;

    if (typeof module[protocol.main] !== 'function') {
        return false;
    }

    if (typeof module[protocol.id] !== 'string' || !module[protocol.id].length) {
        return false;
    }

    if (typeof module[protocol.orderBy] !== 'number' || module[protocol.orderBy] < 0) {
        return false;
    }

    for (var key in protocol.contract) {
        if (protocol.contract.hasOwnProperty(key)) {
            if (typeof module[key] !== protocol.contract[key]) {
                return false;
            }
        }
    }

    return true;
}

/*!
 * Module exports.
 */
module.exports = exports = Plugger;<|endoftext|>