<commit_before>const EventEmitter = require('events');
const kafka = require('kafka-node');
/**
 * @typedef {Object} TopicItem
 *
 * @property {String} name
 */

 /**
  * @typedef {Object} ProducerOption
  *
  * @class KafkaProducer
  * @param {String} name The name of current instance.
  * @param {String=} kafkaHost The host of the broker of kafka, when both of `zookeeperHost` and `kafkaHost` passed, the `kafkaHost` has higher priority.
  * @param {String=} topic The topic where you save data in it.
  * @param {TopicItem[]=} topicList A list of topic items , which you can send a message to all of the topic in the list. When this parameter passed, the `option.topic` and `option.partition` will be ignored.
  * @param {Number=} delayInterval When pass this parameter, messages will publish to kafka every `option.delayInterval` ms, otherwise messages will publish to kafka at once.
  */

/**
 * The class of the producer of Kafka
 * @class KafkaProducer
 * @extends {EventEmitter}
 */
class KafkaProducer extends EventEmitter {
    /**
     * Creates an instance of KafkaProducer.
     *
     * @param {ProducerOption} option
     * @memberof KafkaProducer
     */
    constructor({
        name,
        topic,
        kafkaHost,
        delayInterval,
        topicList
    }) {
        super();
        this.name = name;
        this.producer = null;
        this._clientPromise = null;
        this._clientOk = false;
        if (!kafkaHost) {
            throw new Error('You must giva a  kafkaHost.');
        }
        this._createProducer(kafkaHost);

        this.topic = topic;
        this.delayInterval = delayInterval;
        this.topicList = topicList;
        this._delayData = [];
        if (!this.topicList && !this.topic) {
            throw new Error('You must give a topicList or topic paramemter.');
        }
        if (this.topicList && this.topic) {
            console.warn('The topic parameter will be ignored, as you have give a topicList parameter.');
        }

        this._delayTimer = null;
        if (delayInterval > 0) {
            this._doSendDataTimer();
        }
    }
    /**
     * @private
     *
     * @memberof KafkaProducer
     */
    _cancleTimer() {
        if (this._delayTimer) {
            clearTimeout(this._delayTimer);
        }
    }
    /**
     * @private
     * @param {String} zookeeperHost
     *
     * @memberof KafkaProducer
     */

    _createProducer(kafkaHost) {
        const client = new kafka.KafkaClient({kafkaHost: kafkaHost});
        const _this = this;

        _this._clientPromise = new Promise(function(resolve,reject) {
            client.on('ready',function() {
                _this.emit(KafkaProducer.EVENT_CLIENT_READY);

            });
            client.on('error',function(err) {
                _this._cancleTimer();
                _this._clientOk = false;
                reject(err);
                return _this.emit(KafkaProducer.EVENT_CLIENT_ERROR,err);
            });
            const producer = new kafka.HighLevelProducer(client);
            producer.on('ready', function producerReady() {

                _this.producer = producer;
                _this._clientOk = true;
                resolve();
                return _this.emit(KafkaProducer.EVENT_PRODUCER_READY);

            });
            producer.on('error',function producerError(err) {
                _this._cancleTimer();
                _this._clientOk = false;
                reject(err);
                return _this.emit(KafkaProducer.EVENT_PRODUCER_ERROR,err);
            });
        });
    }

    /**
     * @private
     *
     * @memberof KafkaProducer
     */
    _doSendDataTimer() {
        const _this = this;
        this._delayTimer = setTimeout(function delayProcess() {
            if (!_this.producer) {
                return _this._doSendDataTimer();
            }
            const array = _this._delayData.splice(0);
            if (array.length === 0) {
                return _this._doSendDataTimer();
            }
            _this.producer.send(
                _this._createSendData(array),function(err) {
                    if (err) {
                        _this.emit(KafkaProducer.EVENT_SEND_ERROR,err,array);
                    }
                    _this.emit(KafkaProducer.EVENT_DELAY_MESSAGE_SEND_FINISHED,err,array.length);
                    _this._doSendDataTimer();
                }
            );
        },this.delayInterval);

    }
    /**
     * @private
     *
     * @param {Object} taskData
     * @returns
     * @memberof KafkaProducer
     */
    _createSendData(taskData) {
        const topicList = this.topicList;
        if (!topicList) {
            return [{
                topic: this.topic,
                messages: taskData,
                // partition: this.partition
            }];
        }
        const sendData = new Array(topicList.length);
        for (var i=0,len=topicList.length;i<len;i++) {
            const topic = topicList[i];

            sendData[i] = {
                topic:topic.name,
                // partition:topic.partition,
                messages:taskData
            };
        }
        return sendData;
    }
    _getSendData(taskData) {
        return typeof(taskData) === 'object' ?
        JSON.stringify(taskData):
        taskData;
    }
    /**
     *
     * @private
     * @param {Object} taskData
     * @param {Function} callback
     * @memberof KafkaProducer
     */
    _sendAtOnce(taskData,callback) {
        const _this = this;
        this.producer.send(
            this._createSendData(this._getSendData(taskData)), function(err,data) {
                callback(err,data);
                if (err) {
                    _this.emit(KafkaProducer.EVENT_SEND_ERROR,err,taskData);
                }
            }
        );
    }

    /**
     * Send data to kafka, it will send the data to kafka every `delayInterval` ms when `delayInterval` is set. It will wait the client i
     *
     * @param {Object} taskData
     * @param {Function=} [callback=function() {}]
     * @returns {KafkaProducer}
     * @memberof KafkaProducer
     */
    addData(taskData, callback = function() {}) {
        if (this.delayInterval > 0) {
            this._delayData.push(this._getSendData(taskData));
            return this;
        }
        if (this._clientOk) {
            this._sendAtOnce(taskData,callback);
        } else {
            const _this = this;
            this._clientPromise.then(function() {
                _this._sendAtOnce(taskData,callback)
            }).catch(function(err) {
                callback(err);
            });
        }

        return this;
    }
    /**
     * @deprecated
     *
     * @param {any} taskData
     * @memberof KafkaProducer
     */
    addDataDelay(taskData) {
        this._delayData.push(this._getSendData(taskData));
    }
}
/**
 * The event to notify that the client is ready.
 */
KafkaProducer.EVENT_CLIENT_READY = 'eventClientReady';
/**
 * The event to notify that the client is error.
 */
KafkaProducer.EVENT_CLIENT_ERROR = 'eventClientError';

/**
 * The event to notify that a batch of messages have been sent finished.
 */
KafkaProducer.EVENT_DELAY_MESSAGE_SEND_FINISHED = 'eventDelayMessageSendFinished';
/**
 * The event to notify that the producer is ready.
 */
KafkaProducer.EVENT_PRODUCER_READY = 'eventProducerReady';
/**
 * The event to notify the producer is error.
 */
KafkaProducer.EVENT_PRODUCER_ERROR = 'eventProducerError';

/**
 * The event emitted when an error occurs after sending data to kafka.
 */
KafkaProducer.EVENT_SEND_ERROR = 'eventSendError';

module.exports = KafkaProducer;
<commit_msg>remove the function of addDataDelay form KafkaProducer<commit_after>const EventEmitter = require('events');
const kafka = require('kafka-node');
/**
 * @typedef {Object} TopicItem
 *
 * @property {String} name
 */

 /**
  * @typedef {Object} ProducerOption
  *
  * @class KafkaProducer
  * @param {String} name The name of current instance.
  * @param {String=} kafkaHost The host of the broker of kafka, when both of `zookeeperHost` and `kafkaHost` passed, the `kafkaHost` has higher priority.
  * @param {String=} topic The topic where you save data in it.
  * @param {TopicItem[]=} topicList A list of topic items , which you can send a message to all of the topic in the list. When this parameter passed, the `option.topic` and `option.partition` will be ignored.
  * @param {Number=} delayInterval When pass this parameter, messages will publish to kafka every `option.delayInterval` ms, otherwise messages will publish to kafka at once.
  */

/**
 * The class of the producer of Kafka
 * @class KafkaProducer
 * @extends {EventEmitter}
 */
class KafkaProducer extends EventEmitter {
    /**
     * Creates an instance of KafkaProducer.
     *
     * @param {ProducerOption} option
     * @memberof KafkaProducer
     */
    constructor({
        name,
        topic,
        kafkaHost,
        delayInterval,
        topicList
    }) {
        super();
        this.name = name;
        this.producer = null;
        this._clientPromise = null;
        this._clientOk = false;
        if (!kafkaHost) {
            throw new Error('You must giva a  kafkaHost.');
        }
        this._createProducer(kafkaHost);

        this.topic = topic;
        this.delayInterval = delayInterval;
        this.topicList = topicList;
        this._delayData = [];
        if (!this.topicList && !this.topic) {
            throw new Error('You must give a topicList or topic paramemter.');
        }
        if (this.topicList && this.topic) {
            console.warn('The topic parameter will be ignored, as you have give a topicList parameter.');
        }

        this._delayTimer = null;
        if (delayInterval > 0) {
            this._doSendDataTimer();
        }
    }
    /**
     * @private
     *
     * @memberof KafkaProducer
     */
    _cancleTimer() {
        if (this._delayTimer) {
            clearTimeout(this._delayTimer);
        }
    }
    /**
     * @private
     * @param {String} zookeeperHost
     *
     * @memberof KafkaProducer
     */

    _createProducer(kafkaHost) {
        const client = new kafka.KafkaClient({kafkaHost: kafkaHost});
        const _this = this;

        _this._clientPromise = new Promise(function(resolve,reject) {
            client.on('ready',function() {
                _this.emit(KafkaProducer.EVENT_CLIENT_READY);

            });
            client.on('error',function(err) {
                _this._cancleTimer();
                _this._clientOk = false;
                reject(err);
                return _this.emit(KafkaProducer.EVENT_CLIENT_ERROR,err);
            });
            const producer = new kafka.HighLevelProducer(client);
            producer.on('ready', function producerReady() {

                _this.producer = producer;
                _this._clientOk = true;
                resolve();
                return _this.emit(KafkaProducer.EVENT_PRODUCER_READY);

            });
            producer.on('error',function producerError(err) {
                _this._cancleTimer();
                _this._clientOk = false;
                reject(err);
                return _this.emit(KafkaProducer.EVENT_PRODUCER_ERROR,err);
            });
        });
    }

    /**
     * @private
     *
     * @memberof KafkaProducer
     */
    _doSendDataTimer() {
        const _this = this;
        this._delayTimer = setTimeout(function delayProcess() {
            if (!_this.producer) {
                return _this._doSendDataTimer();
            }
            const array = _this._delayData.splice(0);
            if (array.length === 0) {
                return _this._doSendDataTimer();
            }
            _this.producer.send(
                _this._createSendData(array),function(err) {
                    if (err) {
                        _this.emit(KafkaProducer.EVENT_SEND_ERROR,err,array);
                    }
                    _this.emit(KafkaProducer.EVENT_DELAY_MESSAGE_SEND_FINISHED,err,array.length);
                    _this._doSendDataTimer();
                }
            );
        },this.delayInterval);

    }
    /**
     * @private
     *
     * @param {Object} taskData
     * @returns
     * @memberof KafkaProducer
     */
    _createSendData(taskData) {
        const topicList = this.topicList;
        if (!topicList) {
            return [{
                topic: this.topic,
                messages: taskData,
                // partition: this.partition
            }];
        }
        const sendData = new Array(topicList.length);
        for (var i=0,len=topicList.length;i<len;i++) {
            const topic = topicList[i];

            sendData[i] = {
                topic:topic.name,
                // partition:topic.partition,
                messages:taskData
            };
        }
        return sendData;
    }
    _getSendData(taskData) {
        return typeof(taskData) === 'object' ?
        JSON.stringify(taskData):
        taskData;
    }
    /**
     *
     * @private
     * @param {Object} taskData
     * @param {Function} callback
     * @memberof KafkaProducer
     */
    _sendAtOnce(taskData,callback) {
        const _this = this;
        this.producer.send(
            this._createSendData(this._getSendData(taskData)), function(err,data) {
                callback(err,data);
                if (err) {
                    _this.emit(KafkaProducer.EVENT_SEND_ERROR,err,taskData);
                }
            }
        );
    }

    /**
     * Send data to kafka, it will send the data to kafka every `delayInterval` ms when `delayInterval` is set. It will wait the client i
     *
     * @param {Object} taskData
     * @param {Function=} [callback=function() {}]
     * @returns {KafkaProducer}
     * @memberof KafkaProducer
     */
    addData(taskData, callback = function() {}) {
        if (this.delayInterval > 0) {
            this._delayData.push(this._getSendData(taskData));
            return this;
        }
        if (this._clientOk) {
            this._sendAtOnce(taskData,callback);
        } else {
            const _this = this;
            this._clientPromise.then(function() {
                _this._sendAtOnce(taskData,callback)
            }).catch(function(err) {
                callback(err);
            });
        }

        return this;
    }

}
/**
 * The event to notify that the client is ready.
 */
KafkaProducer.EVENT_CLIENT_READY = 'eventClientReady';
/**
 * The event to notify that the client is error.
 */
KafkaProducer.EVENT_CLIENT_ERROR = 'eventClientError';

/**
 * The event to notify that a batch of messages have been sent finished.
 */
KafkaProducer.EVENT_DELAY_MESSAGE_SEND_FINISHED = 'eventDelayMessageSendFinished';
/**
 * The event to notify that the producer is ready.
 */
KafkaProducer.EVENT_PRODUCER_READY = 'eventProducerReady';
/**
 * The event to notify the producer is error.
 */
KafkaProducer.EVENT_PRODUCER_ERROR = 'eventProducerError';

/**
 * The event emitted when an error occurs after sending data to kafka.
 */
KafkaProducer.EVENT_SEND_ERROR = 'eventSendError';

module.exports = KafkaProducer;
<|endoftext|>


<commit_before>/**
 * The LanguageArray is a container for locales. It'll make sure that it
 * only contains locales. Also the instance is constant, meaning that it's
 * content will not changed (unless on private access).
 *
 * It is mostly used to store a language and fallbacks.
 *
 * @constructor
 * @param {string|Locale|Array.<(string|Locale)>|LanguageArray}
 * @param {boolean=} unsafe if true locales will be filtered
 */
LanguageArray = function (locales, unsafe) {
  var self = this;

  self._locales = [];

  if (locales instanceof LanguageArray) {
    locales = locales._locales;
  } else if (!_.isArray(locales)) {
    locales = locales != null ? [locales] : [];
  }

  _.each(locales, function (locale) {
    try {
      var isLocale = locale instanceof Locale;
      if (! isLocale) {
        locale = new Locale(locale);
      }
      self._locales.push(locale);
    } catch (e) {
      // if errors weren't expected throw it further
      if (! unsafe) throw e;
    }
  });

  //var hasLocales = self._locales.length > 0;
  //if (! hasLocales) {
  //  console.warn("no locales specified, fallback to en_US");
  //  self._locales.push(new Locale());
  //  self._isFallenBack = true;
  //}
};

/**
 * This little shortcut makes sure that what you got is a LanguageArray.
 * If it is not it will create one. If that fails it'l throw an exception.
 *
 * @param {string|Locale|Array.<(string|Locale)>|LanguageArray}
 */
LanguageArray.make = function (language) {
  if (language instanceof LanguageArray) {
    return language;
  } else {
    return new LanguageArray(language);
  }
};

/**
 * @return {!Array.<Locale>}
 */
LanguageArray.prototype.getLocales = function () {
  return this._locales.slice(); // copy to make the array safe
};
/**
 * @return {Locale}
 */
LanguageArray.prototype.getLocale = function () {
  return this._locales[0];
};

/**
 * @return {boolean}
 */
LanguageArray.prototype.isEmpty = function () {
  return this._locales.length === 0;
};

/**
 * @return {string}
 */
LanguageArray.prototype.toString = function () {
  return "[" + this._locales.join(",") + "]";
}

/**
 * @return {!Locale}
 */
LanguageArray.prototype.clone = function () {
  return new LanguageArray(this._locales);
};
/**
 * @return {string}
 */
LanguageArray.prototype.toJSONValue = function () {
  return this._locales.join(' '); // shortcut for toString() on all
};
/**
 * @return {string}
 */
LanguageArray.prototype.typeName = function () {
  return "Translator.LanguageArray";
};
/**
 * @param {object}
 * @return {boolean}
 */
LanguageArray.prototype.equals = function (other) {
  var self = this;

  return other instanceof self.constructor // same instance
    && other._locales.length == this._locales.length // same amount of locales
    && _.every(other._locales, function (locale, index) { // same locales
      return self._locales[index].equals(locale);
    });
};

EJSON.addType(LanguageArray.prototype.typeName(), function (locales) {
  return new LanguageArray(locales.split(' '));
});
<commit_msg>removed old commented out code<commit_after>/**
 * The LanguageArray is a container for locales. It'll make sure that it
 * only contains locales. Also the instance is constant, meaning that it's
 * content will not changed (unless on private access).
 *
 * It is mostly used to store a language and fallbacks.
 *
 * @constructor
 * @param {string|Locale|Array.<(string|Locale)>|LanguageArray}
 * @param {boolean=} unsafe if true locales will be filtered
 */
LanguageArray = function (locales, unsafe) {
  var self = this;

  self._locales = [];

  if (locales instanceof LanguageArray) {
    locales = locales._locales;
  } else if (!_.isArray(locales)) {
    locales = locales != null ? [locales] : [];
  }

  _.each(locales, function (locale) {
    try {
      var isLocale = locale instanceof Locale;
      if (! isLocale) {
        locale = new Locale(locale);
      }
      self._locales.push(locale);
    } catch (e) {
      // if errors weren't expected throw it further
      if (! unsafe) throw e;
    }
  });

};

/**
 * This little shortcut makes sure that what you got is a LanguageArray.
 * If it is not it will create one. If that fails it'l throw an exception.
 *
 * @param {string|Locale|Array.<(string|Locale)>|LanguageArray}
 */
LanguageArray.make = function (language) {
  if (language instanceof LanguageArray) {
    return language;
  } else {
    return new LanguageArray(language);
  }
};

/**
 * @return {!Array.<Locale>}
 */
LanguageArray.prototype.getLocales = function () {
  return this._locales.slice(); // copy to make the array safe
};
/**
 * @return {Locale}
 */
LanguageArray.prototype.getLocale = function () {
  return this._locales[0];
};

/**
 * @return {boolean}
 */
LanguageArray.prototype.isEmpty = function () {
  return this._locales.length === 0;
};

/**
 * @return {string}
 */
LanguageArray.prototype.toString = function () {
  return "[" + this._locales.join(",") + "]";
}

/**
 * @return {!Locale}
 */
LanguageArray.prototype.clone = function () {
  return new LanguageArray(this._locales);
};
/**
 * @return {string}
 */
LanguageArray.prototype.toJSONValue = function () {
  return this._locales.join(' '); // shortcut for toString() on all
};
/**
 * @return {string}
 */
LanguageArray.prototype.typeName = function () {
  return "Translator.LanguageArray";
};
/**
 * @param {object}
 * @return {boolean}
 */
LanguageArray.prototype.equals = function (other) {
  var self = this;

  return other instanceof self.constructor // same instance
    && other._locales.length == this._locales.length // same amount of locales
    && _.every(other._locales, function (locale, index) { // same locales
      return self._locales[index].equals(locale);
    });
};

EJSON.addType(LanguageArray.prototype.typeName(), function (locales) {
  return new LanguageArray(locales.split(' '));
});
<|endoftext|>


<commit_before>// -----------------------------------------------------------------------------
// Get rid of `copyProperties`
function rmCopyProperties(file, api, options) {
  if (!options.filters) {
    options.filters = [];
  }

  const j = api.jscodeshift;

  const {getRequireCall, removeRequire} = require('./utils/require')(j);

  const printOptions = options.printOptions || {quote: 'single'};
  const root = j(file.source);

  const flatten = a => Array.isArray(a) ? [].concat(...a.map(flatten)) : a;

  const getObject = node => {
    if (
      options.optionsOrConfig &&
      node.type == 'LogicalExpression' &&
      node.operator == '||' &&
      isOptionsOrConfig(node)
    ) {
      return node.left;
    }
    return node;
  };

  const inlineObjectExpression = path =>
    j(path).replaceWith(j.objectExpression(
      flatten(path.value.arguments.map(getObject).map(p =>
        p.type == 'ObjectExpression' ? p.properties : j.spreadProperty(p)
      ))
    ));

  const isOptionsOrConfig = node => {
    if (node.type == 'Identifier' &&
      (
        node.name == 'options' ||
        node.name == 'config' ||
        node.name == 'defaults' ||
        node.name == '_defaults'
      )
    ) {
      return true;
    }

    if (node.type == 'LogicalExpression' && node.operator == '||') {
      return (
        isOptionsOrConfig(node.left) &&
        node.right.type == 'ObjectExpression' &&
        !node.right.properties.length
      );
    }

    return false;
  };

  const isStateOrProps = node => (
    node.type == 'MemberExpression' &&
    node.object &&
    node.object.type == 'MemberExpression' &&
    node.object.object &&
    node.object.object.type == 'ThisExpression' &&
    node.object.property &&
    node.object.property.type == 'Identifier' &&
    (
      node.object.property.name == 'state' ||
      node.object.property.name == 'props'
    )
  );

  const checkArguments = path =>
    path.value.arguments.slice(1).every(argument =>
      argument.type == 'ObjectExpression' ||
      (
        options.arbiterMixin &&
        argument.type == 'Identifier' &&
        argument.name == 'ArbiterMixin'
      ) ||
      (
        options.optionsOrConfig &&
        isOptionsOrConfig(argument)
      ) ||
      (
        options.inlineStateAndProps &&
        isStateOrProps(argument)
      )
    );

  const availableFilters = {
    onlyPrototypeAssignments(path) {
      var node = path.value.arguments[0];
      return (
        node.type == 'MemberExpression' &&
        node.object.type == 'Identifier' &&
        node.property.type == 'Identifier' &&
        node.property.name == 'prototype'
      );
    },
    onlyThisExpressions(path) {
      return path.value.arguments[0].type == 'ThisExpression';
    },
    onlyNewExpressions(path) {
      return path.value.arguments[0].type == 'NewExpression';
    },
    onlyCapitalizedIdentifiers(path) {
      var node = path.value.arguments[0];
      return (
        node.type == 'Identifier' &&
        node.name.charAt(0) == node.name.charAt(0).toUpperCase()
      );
    },
    onlyCallExpressions(path) {
      var node = path.parent.value;
      return node.type == 'ExpressionStatement';
    },
    onlyObjectExpressions(path) {
      return path.value.arguments[0].type == 'ObjectExpression';
    }
  };

  const rmCopyPropertyCalls = path => {
    if (availableFilters.onlyObjectExpressions(path)) {
      inlineObjectExpression(path);
    } else {
      j(path).replaceWith(j.callExpression(
        j.memberExpression(
          j.identifier('Object'),
          j.identifier('assign'),
          false
        ),
        path.value.arguments.map(getObject)
      ));
    }
  }

  if (options.inlineStateAndProps) {
    options.filters.push('onlyObjectExpressions');
  }

  const filters =
    options.filters.map(filterName => availableFilters[filterName]);

  const declarator = getRequireCall(root, 'copyProperties');
  if (declarator) {
    const variableName = declarator.value.id.name;
    const didTransform = root
      .find(j.CallExpression, {callee: {name: variableName}})
      .filter(checkArguments)
      .filter(p => filters.every(filter => filter(p)))
      .forEach(rmCopyPropertyCalls)
      .size() > 0;
    if (didTransform) {
      if (!root.find(j.CallExpression, {callee: {name: variableName}}).size()) {
        removeRequire(declarator);
      }
      return root.toSource(printOptions) + '\n';
    }
  }
  return null;
}

module.exports = rmCopyProperties;
<commit_msg>defaults can be the first argument.<commit_after>// -----------------------------------------------------------------------------
// Get rid of `copyProperties`
function rmCopyProperties(file, api, options) {
  if (!options.filters) {
    options.filters = [];
  }

  const j = api.jscodeshift;

  const {getRequireCall, removeRequire} = require('./utils/require')(j);

  const printOptions = options.printOptions || {quote: 'single'};
  const root = j(file.source);

  const flatten = a => Array.isArray(a) ? [].concat(...a.map(flatten)) : a;

  const getObject = node => {
    if (
      options.optionsOrConfig &&
      node.type == 'LogicalExpression' &&
      node.operator == '||' &&
      isOptionsOrConfig(node)
    ) {
      return node.left;
    }
    return node;
  };

  const inlineObjectExpression = path =>
    j(path).replaceWith(j.objectExpression(
      flatten(path.value.arguments.map(getObject).map(p =>
        p.type == 'ObjectExpression' ? p.properties : j.spreadProperty(p)
      ))
    ));

  const isOptionsOrConfig = node => {
    if (node.type == 'Identifier' &&
      (
        node.name == 'options' ||
        node.name == 'config' ||
        node.name == 'defaults' ||
        node.name == '_defaults'
      )
    ) {
      return true;
    }

    if (node.type == 'LogicalExpression' && node.operator == '||') {
      return (
        isOptionsOrConfig(node.left) &&
        node.right.type == 'ObjectExpression' &&
        !node.right.properties.length
      );
    }

    return false;
  };

  const isStateOrProps = node => (
    node.type == 'MemberExpression' &&
    node.object &&
    node.object.type == 'MemberExpression' &&
    node.object.object &&
    node.object.object.type == 'ThisExpression' &&
    node.object.property &&
    node.object.property.type == 'Identifier' &&
    (
      node.object.property.name == 'state' ||
      node.object.property.name == 'props'
    )
  );

  const checkArguments = path =>
    path.value.arguments.slice(1).every(argument =>
      argument.type == 'ObjectExpression' ||
      (
        options.arbiterMixin &&
        argument.type == 'Identifier' &&
        argument.name == 'ArbiterMixin'
      ) ||
      (
        options.optionsOrConfig &&
        isOptionsOrConfig(argument)
      ) ||
      (
        options.inlineStateAndProps &&
        isStateOrProps(argument)
      )
    );

  const availableFilters = {
    onlyPrototypeAssignments(path) {
      var node = path.value.arguments[0];
      return (
        node.type == 'MemberExpression' &&
        node.object.type == 'Identifier' &&
        node.property.type == 'Identifier' &&
        node.property.name == 'prototype'
      );
    },
    onlyThisExpressions(path) {
      return path.value.arguments[0].type == 'ThisExpression';
    },
    onlyNewExpressions(path) {
      return path.value.arguments[0].type == 'NewExpression';
    },
    onlyCapitalizedIdentifiers(path) {
      var node = path.value.arguments[0];
      return (
        node.type == 'Identifier' &&
        node.name.charAt(0) == node.name.charAt(0).toUpperCase()
      );
    },
    onlyCallExpressions(path) {
      var node = path.parent.value;
      return node.type == 'ExpressionStatement';
    },
    onlyObjectExpressions(path) {
      return path.value.arguments[0].type == 'ObjectExpression';
    },
    onlyDefaults(path) {
      var node = path.value.arguments[0];
      return (
        node.type == 'Identifier' &&
        node.name == 'defaults'
      );
    }
  };

  const rmCopyPropertyCalls = path => {
    if (availableFilters.onlyObjectExpressions(path)) {
      inlineObjectExpression(path);
    } else {
      j(path).replaceWith(j.callExpression(
        j.memberExpression(
          j.identifier('Object'),
          j.identifier('assign'),
          false
        ),
        path.value.arguments.map(getObject)
      ));
    }
  }

  if (options.inlineStateAndProps) {
    options.filters.push('onlyObjectExpressions');
  }

  const filters =
    options.filters.map(filterName => availableFilters[filterName]);

  const declarator = getRequireCall(root, 'copyProperties');
  if (declarator) {
    const variableName = declarator.value.id.name;
    const didTransform = root
      .find(j.CallExpression, {callee: {name: variableName}})
      .filter(checkArguments)
      .filter(p => filters.every(filter => filter(p)))
      .forEach(rmCopyPropertyCalls)
      .size() > 0;
    if (didTransform) {
      if (!root.find(j.CallExpression, {callee: {name: variableName}}).size()) {
        removeRequire(declarator);
      }
      return root.toSource(printOptions) + '\n';
    }
  }
  return null;
}

module.exports = rmCopyProperties;
<|endoftext|>


<commit_before>/**
 * Replaces string concatenation with template literals.
 * Adapted from https://vramana.github.io/blog/2015/12/21/codemod-tutorial/
 *
 * Areas of improvement:
 *
 * - Better handling of comments when they are in the middle of string
 *   concatenation. Currently, those are simply removed. Perhaps in these
 *   situations, the string concatenation should be preserved as-is.
 */
module.exports = function templateLiterals(file, api, options) {
  const j = api.jscodeshift;
  const printOptions = options.printOptions || {quote: 'single'};

  function extractNodes(node) {
    if (node.type !== 'BinaryExpression') {
      return [node];
    }

    if (node.operator !== '+') {
      return [node];
    }

    if (node.parenthesizedExpression) {
      return [node];
    }

    if (!isStringishOnLeft(node)) {
      // We need to be careful about not having a stringish node on the left to
      // prevent things like 1 + 2 + 'foo', which should evaluate to '3foo' from
      // becoming '12foo'.
      return [node.left, node.right];
    }

    return [
      ...extractNodes(node.left),
      ...extractNodes(node.right)
    ];
  }

  function isStringNode(node) {
    return node.type === 'Literal' && (typeof node.value === 'string');
  }

  function isTemplateLiteralNode(node) {
    return node.type === 'TemplateLiteral';
  }

  function isBinaryExpressionNode(node) {
    return node.type === 'BinaryExpression';
  }

  function isStringishNode(node) {
    return isStringNode(node) || isTemplateLiteralNode(node);
  }

  function isStringishOnLeft(node) {
    if (isBinaryExpressionNode(node)) {
      if (isBinaryExpressionNode(node.left)) {
        return isStringishOnLeft(node.left);
      } else if (isStringishNode(node.left)) {
        return true;
      } else {
        return isStringishOnLeft(node.right);
      }
    }

    return isStringishNode(node);
  }

  function buildTL(nodes, quasis = [], expressions = [], temp = '') {
    if (nodes.length === 0) {
      // We are done recursing
      const newQuasis = [
        ...quasis,
        j.templateElement({ cooked: temp, raw: temp}, true),
      ];

      return [newQuasis, expressions];
    }

    const [node, ...rest] = nodes;

    if (node.type === 'Literal') {
      let val = node.value;
      if (isStringNode(node)) {
        // Need to escape ${ already used by strings so that we don't introduce
        // new interpolation.
        val = val.replace(/\$\{/, '\\${');
      }
      return buildTL(rest, quasis, expressions, temp + val);
    }

    if (node.type === 'TemplateLiteral') {
      const nodeQuasis = node.quasis.map((q, i) => (
        // If this is the first quasi, we want to prepend the temp value so that
        // it does not get lost. Since there can be multiple quasis, once before
        // and after the expression, we want to only do this on the first one
        // and no others, to prevent the string from repeating.
        j.templateElement({
          cooked: i === 0 ? temp + q.value.cooked : q.value.cooked,
          raw: i === 0 ? temp + q.value.raw : q.value.raw,
        }, false)
      ));

      const newQuasis = quasis.concat(nodeQuasis);
      const newExpressions = expressions.concat(node.expressions);
      return buildTL(rest, newQuasis, newExpressions, '');
    }

    const nextTemplateElement = j.templateElement({ cooked: temp, raw: temp }, false);

    const newQuasis = quasis.concat(nextTemplateElement);
    const newExpressions = expressions.concat(node);

    return buildTL(rest, newQuasis, newExpressions, '');
  }

  function convertToTemplateString(p) {
    const tempNodes = extractNodes(p.node);

    if (!tempNodes.some(isStringishNode)) {
      return p.node;
    }

    const tl = j.templateLiteral(...buildTL(tempNodes));
    if (tl.expressions.length > 0) {
      return tl;
    }

    // There are no expressions, so let's use a regular string.
    const str = tl.quasis.map(q => q.value.raw).join('');
    return j.literal(str);
  }

  return j(file.source)
    .find(j.BinaryExpression, { operator: '+' })
    .replaceWith(convertToTemplateString)
    .toSource(printOptions);
};
<commit_msg>Add note about an edge case to template-literals<commit_after>/**
 * Replaces string concatenation with template literals.
 * Adapted from https://vramana.github.io/blog/2015/12/21/codemod-tutorial/
 *
 * Areas of improvement:
 *
 * - Better handling of comments when they are in the middle of string
 *   concatenation. Currently, those are simply removed. Perhaps in these
 *   situations, the string concatenation should be preserved as-is.
 *
 * - Better handling of code like 1 + 2 + 'foo' + bar which would ideally become
 *   `${1 + 2}foo${bar}`.
 */
module.exports = function templateLiterals(file, api, options) {
  const j = api.jscodeshift;
  const printOptions = options.printOptions || {quote: 'single'};

  function extractNodes(node) {
    if (node.type !== 'BinaryExpression') {
      return [node];
    }

    if (node.operator !== '+') {
      return [node];
    }

    if (node.parenthesizedExpression) {
      return [node];
    }

    if (!isStringishOnLeft(node)) {
      // We need to be careful about not having a stringish node on the left to
      // prevent things like 1 + 2 + 'foo', which should evaluate to '3foo' from
      // becoming '12foo'.
      return [node.left, node.right];
    }

    return [
      ...extractNodes(node.left),
      ...extractNodes(node.right)
    ];
  }

  function isStringNode(node) {
    return node.type === 'Literal' && (typeof node.value === 'string');
  }

  function isTemplateLiteralNode(node) {
    return node.type === 'TemplateLiteral';
  }

  function isBinaryExpressionNode(node) {
    return node.type === 'BinaryExpression';
  }

  function isStringishNode(node) {
    return isStringNode(node) || isTemplateLiteralNode(node);
  }

  function isStringishOnLeft(node) {
    if (isBinaryExpressionNode(node)) {
      if (isBinaryExpressionNode(node.left)) {
        return isStringishOnLeft(node.left);
      } else if (isStringishNode(node.left)) {
        return true;
      } else {
        return isStringishOnLeft(node.right);
      }
    }

    return isStringishNode(node);
  }

  function buildTL(nodes, quasis = [], expressions = [], temp = '') {
    if (nodes.length === 0) {
      // We are done recursing
      const newQuasis = [
        ...quasis,
        j.templateElement({ cooked: temp, raw: temp}, true),
      ];

      return [newQuasis, expressions];
    }

    const [node, ...rest] = nodes;

    if (node.type === 'Literal') {
      let val = node.value;
      if (isStringNode(node)) {
        // Need to escape ${ already used by strings so that we don't introduce
        // new interpolation.
        val = val.replace(/\$\{/, '\\${');
      }
      return buildTL(rest, quasis, expressions, temp + val);
    }

    if (node.type === 'TemplateLiteral') {
      const nodeQuasis = node.quasis.map((q, i) => (
        // If this is the first quasi, we want to prepend the temp value so that
        // it does not get lost. Since there can be multiple quasis, once before
        // and after the expression, we want to only do this on the first one
        // and no others, to prevent the string from repeating.
        j.templateElement({
          cooked: i === 0 ? temp + q.value.cooked : q.value.cooked,
          raw: i === 0 ? temp + q.value.raw : q.value.raw,
        }, false)
      ));

      const newQuasis = quasis.concat(nodeQuasis);
      const newExpressions = expressions.concat(node.expressions);
      return buildTL(rest, newQuasis, newExpressions, '');
    }

    const nextTemplateElement = j.templateElement({ cooked: temp, raw: temp }, false);

    const newQuasis = quasis.concat(nextTemplateElement);
    const newExpressions = expressions.concat(node);

    return buildTL(rest, newQuasis, newExpressions, '');
  }

  function convertToTemplateString(p) {
    const tempNodes = extractNodes(p.node);

    if (!tempNodes.some(isStringishNode)) {
      return p.node;
    }

    const tl = j.templateLiteral(...buildTL(tempNodes));
    if (tl.expressions.length > 0) {
      return tl;
    }

    // There are no expressions, so let's use a regular string.
    const str = tl.quasis.map(q => q.value.raw).join('');
    return j.literal(str);
  }

  return j(file.source)
    .find(j.BinaryExpression, { operator: '+' })
    .replaceWith(convertToTemplateString)
    .toSource(printOptions);
};
<|endoftext|>


<commit_before>'use strict';

var Utils = require('./utils')
  , Promise = require('./promise');

/**
 * Hooks are function that are called before and after  (bulk-) creation/updating/deletion and validation. Hooks can be added to you models in three ways:
 *
 * 1. By specifying them as options in `sequelize.define`
 * 2. By calling `hook()` with a string and your hook handler function
 * 3. By calling the function with the same name as the hook you want

 * ```js
 * // Method 1
 * sequelize.define(name, { attributes }, {
 *   hooks: {
 *     beforeBulkCreate: function () {
 *       // can be a single function
 *     },
 *     beforeValidate: [
 *       function () {},
 *       function() {} // Or an array of several
 *     ]
 *   }
 * })
 *
 * // Method 2
 * Model.hook('afterDestroy', function () {})
 *
 * // Method 3
 * Model.afterBulkUpdate(function () {})
 * ```
 *
 * @see {Sequelize#define}
 * @mixin Hooks
 */

var hookTypes = {
  beforeValidate: {params: 2},
  afterValidate: {params: 2},
  beforeCreate: {params: 2},
  afterCreate: {params: 2},
  beforeDestroy: {params: 2},
  afterDestroy: {params: 2},
  beforeUpdate: {params: 2},
  afterUpdate: {params: 2},
  beforeBulkCreate: {params: 2},
  afterBulkCreate: {params: 2},
  beforeBulkDestroy: {params: 1},
  afterBulkDestroy: {params: 1},
  beforeBulkUpdate: {params: 1},
  afterBulkUpdate: {params: 1},
  beforeFind: {params: 1},
  beforeFindAfterExpandIncludeAll: {params: 1},
  beforeFindAfterOptions: {params: 1},
  afterFind: {params: 2},
  beforeDefine: {params: 2, sync: true},
  afterDefine: {params: 1, sync: true},
  beforeInit: {params: 2, sync: true},
  afterInit: {params: 1, sync: true}
};

var hookAliases = {
  beforeDelete: 'beforeDestroy',
  afterDelete: 'afterDestroy',
  beforeBulkDelete: 'beforeBulkDestroy',
  afterBulkDelete: 'afterBulkDestroy'
};

var Hooks = {
  replaceHookAliases: function(hooks) {
    var realHookName;

    Utils._.each(hooks, function(hooksArray, name) {
      // Does an alias for this hook name exist?
      if (realHookName = hookAliases[name]) {
        // Add the hooks to the actual hook
        hooks[realHookName] = (hooks[realHookName] || []).concat(hooksArray);

        // Delete the alias
        delete hooks[name];
      }
    });

    return hooks;
  },

  runHooks: function(hooks) {
    var self = this
      , fn
      , fnArgs = Array.prototype.slice.call(arguments, 1)
      , hookType;

    if (typeof fnArgs[fnArgs.length - 1] === 'function') {
      fn = fnArgs.pop();
    }

    if (typeof hooks === 'string') {
      hookType = hooks;
      hooks = this.options.hooks[hookType] || [];
      if (!Array.isArray(hooks)) hooks = hooks === undefined ? [] : [hooks];
      if (this.sequelize) hooks = hooks.concat(this.sequelize.options.hooks[hookType] || []);
    }

    if (!Array.isArray(hooks)) {
      hooks = hooks === undefined ? [] : [hooks];
    }

    // run hooks as sync functions if flagged as sync
    if (hookTypes[hookType] && hookTypes[hookType].sync) {
      hooks.forEach(function(hook) {
        if (typeof hook === 'object') hook = hook.fn;
        return hook.apply(self, fnArgs);
      });
      return;
    }

    // run hooks async
    var promise = Promise.each(hooks, function (hook) {
      if (typeof hook === 'object') {
        hook = hook.fn;
      }

      if (hookType && hook.length > hookTypes[hookType].params) {
        hook = Promise.promisify(hook, self);
      }

      return hook.apply(self, fnArgs);
    }).return();

    if (fn) {
      return promise.nodeify(fn);
    }

    return promise;
  },

  hook: function() {
    return Hooks.addHook.apply(this, arguments);
  },

  /**
   * Add a hook to the model
   *
   * @param {String}    hooktype
   * @param {String}    [name]    Provide a name for the hook function. This serves no purpose, other than the ability to be able to order hooks based on some sort of priority system in the future.
   * @param {Function}  fn        The hook function
   *
   * @alias hook
   */
  addHook: function(hookType, name, fn) {
    if (typeof name === 'function') {
      fn = name;
      name = null;
    }

    hookType = hookAliases[hookType] || hookType;

    this.options.hooks[hookType] = this.options.hooks[hookType] || [];
    this.options.hooks[hookType].push(!!name ? {name: name, fn: fn} : fn);
    return this;
  },

  /*
   * Check whether the mode has any hooks of this type
   *
   * @param {String}  hookType
   *
   * @alias hasHooks
   */
  hasHook: function(hookType) {
    return this.options.hooks[hookType] && !!this.options.hooks[hookType].length;
  },
};

Hooks.hasHooks = Hooks.hasHook;

/**
 * A hook that is run before validation
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with instance, options, callback(err)
 */

/**
 * A hook that is run after validation
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with instance, options, callback(err)
 */

/**
 * A hook that is run before creating a single instance
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with attributes, options, callback(err)
 */

/**
 * A hook that is run after creating a single instance
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with attributes, options, callback(err)
 */

/**
 * A hook that is run before destroying a single instance
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with instance, options, callback(err)
 *
 * @alias beforeDelete
 */

/**
 * A hook that is run after destroying a single instance
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with instance, options, callback(err)
 *
 * @alias afterDelete
 */

/**
 * A hook that is run before updating a single instance
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with instance, options, callback(err)
 */

/**
 * A hook that is run after updating a single instance
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with instance, options, callback(err)
 */

/**
 * A hook that is run before creating instances in bulk
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with instances, options, callback(err)
 */

/**
 * A hook that is run after creating instances in bulk
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with instances, options, callback(err)
 */

/**
 * A hook that is run before destroying instances in bulk
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with options, callback(err)
 *
 * @alias beforeBulkDelete
 */

/**
 * A hook that is run after destroying instances in bulk
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with options, callback(err)
 *
 * @alias afterBulkDelete
 */

/**
 * A hook that is run after updating instances in bulk
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with options, callback(err)
 */

/**
 * A hook that is run after updating instances in bulk
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with options, callback(err)
 */

/**
 * A hook that is run before a find (select) query
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with options, callback(err)
 */

/**
 * A hook that is run before a find (select) query, after any { include: {all: ...} } options are expanded
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with options, callback(err)
 */

/**
 * A hook that is run before a find (select) query, after all option parsing is complete
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with options, callback(err)
 */

/**
 * A hook that is run after a find (select) query
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with instance(s), options, callback(err)
 */

/**
 * A hook that is run before a define call
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with attributes, options, callback(err)
 */

/**
 * A hook that is run after a define call
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with factory, callback(err)
 */

/**
 * A hook that is run before Sequelize() call
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with config, options, callback(err)
 */

/**
 * A hook that is run after Sequelize() call
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with sequelize, callback(err)
 */

module.exports = {
  hooks: hookTypes,
  hookAliases: hookAliases,

  applyTo: function(Model) {
    Utils._.mixin(Model, Hooks);
    Utils._.mixin(Model.prototype, Hooks);

    var allHooks = Object.keys(hookTypes).concat(Object.keys(hookAliases));
    allHooks.forEach(function(hook) {
      Model.prototype[hook] = function(callback) {
        return this.addHook(hook, callback);
      };
    });
  }
};
<commit_msg>added @name to hooks so every hook has a name<commit_after>'use strict';

var Utils = require('./utils')
  , Promise = require('./promise');

/**
 * Hooks are function that are called before and after  (bulk-) creation/updating/deletion and validation. Hooks can be added to you models in three ways:
 *
 * 1. By specifying them as options in `sequelize.define`
 * 2. By calling `hook()` with a string and your hook handler function
 * 3. By calling the function with the same name as the hook you want

 * ```js
 * // Method 1
 * sequelize.define(name, { attributes }, {
 *   hooks: {
 *     beforeBulkCreate: function () {
 *       // can be a single function
 *     },
 *     beforeValidate: [
 *       function () {},
 *       function() {} // Or an array of several
 *     ]
 *   }
 * })
 *
 * // Method 2
 * Model.hook('afterDestroy', function () {})
 *
 * // Method 3
 * Model.afterBulkUpdate(function () {})
 * ```
 *
 * @see {Sequelize#define}
 * @mixin Hooks
 */

var hookTypes = {
  beforeValidate: {params: 2},
  afterValidate: {params: 2},
  beforeCreate: {params: 2},
  afterCreate: {params: 2},
  beforeDestroy: {params: 2},
  afterDestroy: {params: 2},
  beforeUpdate: {params: 2},
  afterUpdate: {params: 2},
  beforeBulkCreate: {params: 2},
  afterBulkCreate: {params: 2},
  beforeBulkDestroy: {params: 1},
  afterBulkDestroy: {params: 1},
  beforeBulkUpdate: {params: 1},
  afterBulkUpdate: {params: 1},
  beforeFind: {params: 1},
  beforeFindAfterExpandIncludeAll: {params: 1},
  beforeFindAfterOptions: {params: 1},
  afterFind: {params: 2},
  beforeDefine: {params: 2, sync: true},
  afterDefine: {params: 1, sync: true},
  beforeInit: {params: 2, sync: true},
  afterInit: {params: 1, sync: true}
};

var hookAliases = {
  beforeDelete: 'beforeDestroy',
  afterDelete: 'afterDestroy',
  beforeBulkDelete: 'beforeBulkDestroy',
  afterBulkDelete: 'afterBulkDestroy'
};

var Hooks = {
  replaceHookAliases: function(hooks) {
    var realHookName;

    Utils._.each(hooks, function(hooksArray, name) {
      // Does an alias for this hook name exist?
      if (realHookName = hookAliases[name]) {
        // Add the hooks to the actual hook
        hooks[realHookName] = (hooks[realHookName] || []).concat(hooksArray);

        // Delete the alias
        delete hooks[name];
      }
    });

    return hooks;
  },

  runHooks: function(hooks) {
    var self = this
      , fn
      , fnArgs = Array.prototype.slice.call(arguments, 1)
      , hookType;

    if (typeof fnArgs[fnArgs.length - 1] === 'function') {
      fn = fnArgs.pop();
    }

    if (typeof hooks === 'string') {
      hookType = hooks;
      hooks = this.options.hooks[hookType] || [];
      if (!Array.isArray(hooks)) hooks = hooks === undefined ? [] : [hooks];
      if (this.sequelize) hooks = hooks.concat(this.sequelize.options.hooks[hookType] || []);
    }

    if (!Array.isArray(hooks)) {
      hooks = hooks === undefined ? [] : [hooks];
    }

    // run hooks as sync functions if flagged as sync
    if (hookTypes[hookType] && hookTypes[hookType].sync) {
      hooks.forEach(function(hook) {
        if (typeof hook === 'object') hook = hook.fn;
        return hook.apply(self, fnArgs);
      });
      return;
    }

    // run hooks async
    var promise = Promise.each(hooks, function (hook) {
      if (typeof hook === 'object') {
        hook = hook.fn;
      }

      if (hookType && hook.length > hookTypes[hookType].params) {
        hook = Promise.promisify(hook, self);
      }

      return hook.apply(self, fnArgs);
    }).return();

    if (fn) {
      return promise.nodeify(fn);
    }

    return promise;
  },

  hook: function() {
    return Hooks.addHook.apply(this, arguments);
  },

  /**
   * Add a hook to the model
   *
   * @param {String}    hooktype
   * @param {String}    [name]    Provide a name for the hook function. This serves no purpose, other than the ability to be able to order hooks based on some sort of priority system in the future.
   * @param {Function}  fn        The hook function
   *
   * @alias hook
   */
  addHook: function(hookType, name, fn) {
    if (typeof name === 'function') {
      fn = name;
      name = null;
    }

    hookType = hookAliases[hookType] || hookType;

    this.options.hooks[hookType] = this.options.hooks[hookType] || [];
    this.options.hooks[hookType].push(!!name ? {name: name, fn: fn} : fn);
    return this;
  },

  /*
   * Check whether the mode has any hooks of this type
   *
   * @param {String}  hookType
   *
   * @alias hasHooks
   */
  hasHook: function(hookType) {
    return this.options.hooks[hookType] && !!this.options.hooks[hookType].length;
  },
};

Hooks.hasHooks = Hooks.hasHook;

/**
 * A hook that is run before validation
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with instance, options, callback(err)
 * @name beforeValidate
 */

/**
 * A hook that is run after validation
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with instance, options, callback(err)
 * @name afterValidate
 */

/**
 * A hook that is run before creating a single instance
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with attributes, options, callback(err)
 * @name beforeCreate
 */

/**
 * A hook that is run after creating a single instance
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with attributes, options, callback(err)
 * @name afterCreate
 */

/**
 * A hook that is run before destroying a single instance
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with instance, options, callback(err)
 *
 * @name beforeDestroy
 * @alias beforeDelete
 */

/**
 * A hook that is run after destroying a single instance
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with instance, options, callback(err)
 *
 * @name afterDestroy
 * @alias afterDelete
 */

/**
 * A hook that is run before updating a single instance
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with instance, options, callback(err)
 * @name beforeUpdate
 */

/**
 * A hook that is run after updating a single instance
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with instance, options, callback(err)
 * @name afterUpdate
 */

/**
 * A hook that is run before creating instances in bulk
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with instances, options, callback(err)
 * @name beforeBulkCreate
 */

/**
 * A hook that is run after creating instances in bulk
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with instances, options, callback(err)
 * @name afterBulkCreate
 */

/**
 * A hook that is run before destroying instances in bulk
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with options, callback(err)
 *
 * @name beforeBulkDestroy
 * @alias beforeBulkDelete
 */

/**
 * A hook that is run after destroying instances in bulk
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with options, callback(err)
 *
 * @name afterBulkDestroy
 * @alias afterBulkDelete
 */

/**
 * A hook that is run after updating instances in bulk
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with options, callback(err)
 * @name beforeBulkUpdate
 */

/**
 * A hook that is run after updating instances in bulk
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with options, callback(err)
 * @name afterBulkUpdate
 */

/**
 * A hook that is run before a find (select) query
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with options, callback(err)
 * @name beforeFind
 */

/**
 * A hook that is run before a find (select) query, after any { include: {all: ...} } options are expanded
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with options, callback(err)
 * @name beforeFindAfterExpandIncludeAll
 */

/**
 * A hook that is run before a find (select) query, after all option parsing is complete
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with options, callback(err)
 * @name beforeFindAfterOptions
 */

/**
 * A hook that is run after a find (select) query
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with instance(s), options, callback(err)
 * @name afterFind
 */

/**
 * A hook that is run before a define call
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with attributes, options, callback(err)
 * @name beforeDefine
 */

/**
 * A hook that is run after a define call
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with factory, callback(err)
 * @name afterDefine
 */

/**
 * A hook that is run before Sequelize() call
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with config, options, callback(err)
 * @name beforeInit
 */

/**
 * A hook that is run after Sequelize() call
 * @param {String}   name
 * @param {Function} fn   A callback function that is called with sequelize, callback(err)
 * @name afterInit
 */

module.exports = {
  hooks: hookTypes,
  hookAliases: hookAliases,

  applyTo: function(Model) {
    Utils._.mixin(Model, Hooks);
    Utils._.mixin(Model.prototype, Hooks);

    var allHooks = Object.keys(hookTypes).concat(Object.keys(hookAliases));
    allHooks.forEach(function(hook) {
      Model.prototype[hook] = function(callback) {
        return this.addHook(hook, callback);
      };
    });
  }
};
<|endoftext|>


<commit_before>'use strict';

(function (angular, buildfire, window) {
  angular.module('couponPluginWidget')
    .controller('WidgetSavedCtrl', ['$scope', 'DataStore', 'TAG_NAMES', 'LAYOUTS', '$sce', '$rootScope', 'Buildfire', 'ViewStack', 'UserData', 'PAGINATION', '$modal', '$timeout',
      function ($scope, DataStore, TAG_NAMES, LAYOUTS, $sce, $rootScope, Buildfire, ViewStack, UserData, PAGINATION, $modal, $timeout) {
        var WidgetSaved = this;
        WidgetSaved.busy = false;
        WidgetSaved.items = [];
        WidgetSaved.savedItems = {};
        WidgetSaved.hasAtleastOneSavedItem = false;
        WidgetSaved.listeners = {};
        var searchOptions = {
          skip: 0,
          limit: PAGINATION.itemCount
        };

        //Refresh list of saved items on pulling the tile bar

        buildfire.datastore.onRefresh(function () {
          WidgetSaved.init(function(err){
            if(!err){
              console.log(">>>>>>Refreshed saved list");
              WidgetSaved.items = [];
              searchOptions.skip = 0;
              WidgetSaved.busy = false;
              WidgetSaved.loadMore();
            }
          });
        });

        WidgetSaved.init = function (cb) {
          Buildfire.spinner.show();
          var success = function (result) {
              Buildfire.spinner.hide();
              if (result && result.data) {
                WidgetSaved.data = result.data;
              }
            cb();
            }
            , error = function (err) {
              Buildfire.spinner.hide();
              console.error('Error while getting data', err);
            cb();
            };
          DataStore.get(TAG_NAMES.COUPON_INFO).then(success, error);
        };

        WidgetSaved.getSavedItems = function () {
          for (var item = 0; item < WidgetSaved.items.length; item++) {
            WidgetSaved.items[item].isSaved = false;
            for (var saved in WidgetSaved.savedItems) {
              if (WidgetSaved.items[item].id == WidgetSaved.savedItems[saved].data.itemId) {
                WidgetSaved.hasAtleastOneSaved = true;
                WidgetSaved.items[item].isSaved = true;
                WidgetSaved.items[item].savedId = WidgetSaved.savedItems[saved].id;
              }
            }
          }
          $scope.isFetchedAllData = true;
        };

        WidgetSaved.getItems = function () {
          Buildfire.spinner.show();
          var successAll = function (resultAll) {
              Buildfire.spinner.hide();
              WidgetSaved.items = WidgetSaved.items.length ? WidgetSaved.items.concat(resultAll) : resultAll;
              console.log("***************** ==============", WidgetSaved.items);
              if(WidgetSaved.items.length < 1){
                WidgetSaved.hasAtleastOneSaved = false;
              }
              searchOptions.skip = searchOptions.skip + PAGINATION.itemCount;
              if (resultAll.length == PAGINATION.itemCount) {
                WidgetSaved.busy = false;
              }
              var err = function (error) {
                Buildfire.spinner.hide();
                console.log("============ There is an error in getting data", error);
              }, result = function (result) {
                Buildfire.spinner.hide();
                console.log("===========search", result);
                WidgetSaved.savedItems = result;
                WidgetSaved.getSavedItems();
              };
              UserData.search({}, TAG_NAMES.COUPON_SAVED).then(result, err);
            },
            errorAll = function (error) {
              Buildfire.spinner.hide();
              console.log("error", error)
            };
          DataStore.search(searchOptions, TAG_NAMES.COUPON_ITEMS).then(successAll, errorAll);
        };

        WidgetSaved.removeSavedItem = function (item, index) {
          Buildfire.spinner.show();
          var successRemove = function (result) {
            Buildfire.spinner.hide();
            WidgetSaved.items.splice(index, 1);
            WidgetSaved.getSavedItems();
            if (!$scope.$$phase)
              $scope.$digest();
            var removeSavedItemModal = $modal.open({
              templateUrl: 'templates/Saved_Removed.html',
              size: 'sm',
              backdropClass: "ng-hide"
            });
            $timeout(function () {
              removeSavedItemModal.close();
            }, 3000);
            $rootScope.$broadcast("ITEM_SAVED_UPDATED");

          }, errorRemove = function () {
            Buildfire.spinner.hide();
            return console.error('There was a problem removing your data');
          };
          console.log("****************,", item.savedId, WidgetSaved.currentLoggedInUser._id);
          UserData.delete(item.savedId, TAG_NAMES.COUPON_SAVED, WidgetSaved.currentLoggedInUser._id).then(successRemove, errorRemove)
        };

        WidgetSaved.loadMore = function () {
          console.log("===============In loadmore");
          if (WidgetSaved.busy) return;
          WidgetSaved.busy = true;
          WidgetSaved.getItems();
        };

        WidgetSaved.showListItems = function () {
          ViewStack.popAllViews()
        };

        WidgetSaved.showMapView = function () {
          ViewStack.push({
            template: 'Map',
            params: {
              controller: "WidgetMapCtrl as WidgetMap"
            }
          });
        };

        WidgetSaved.showFilter = function () {
          ViewStack.push({
            template: 'Filter',
            params: {
              controller: "WidgetFilterCtrl as WidgetFilter"
            }
          });
        };

        WidgetSaved.openDetails = function (itemId) {
          ViewStack.push({
            template: 'Item',
            params: {
              controller: "WidgetItemCtrl as WidgetItem",
              itemId: itemId
            }
          });
        };

        /**
         * Check for current logged in user, if not show ogin screen
         */
        buildfire.auth.getCurrentUser(function (err, user) {
          console.log("===========LoggedInUser", user);
          if (user) {
            WidgetSaved.currentLoggedInUser = user;
            $scope.$apply();
          }
        });

        var searchData = function (newValue, tag) {
          Buildfire.spinner.show();
          var searchTerm = '';
          if (typeof newValue === 'undefined') {
            return;
          }
          if (newValue) {
            newValue = newValue.trim();
            if (newValue.indexOf(' ') !== -1) {
              searchTerm = newValue.split(' ');
              searchOptions.filter = {
                "$or": [{
                  "$json.title": {
                    "$regex": searchTerm[0],
                    "$options": "i"
                  }
                }, {
                  "$json.summary": {
                    "$regex": searchTerm[0],
                    "$options": "i"
                  }
                }, {
                  "$json.title": {
                    "$regex": searchTerm[1],
                    "$options": "i"
                  }
                }, {
                  "$json.summary": {
                    "$regex": searchTerm[1],
                    "$options": "i"
                  }
                }
                ]
              };
            } else {
              searchTerm = newValue;
              searchOptions.filter = {
                "$or": [{
                  "$json.title": {
                    "$regex": searchTerm,
                    "$options": "i"
                  }
                }, {"$json.summary": {"$regex": searchTerm, "$options": "i"}}]
              };
            }
          }

          WidgetSaved.busy = false;
          searchOptions.skip = 0;
          WidgetSaved.items = [];
          WidgetSaved.savedItems = {};
          WidgetSaved.loadMore();
        };

        var tmrDelay = null;

        var searchDataWithDelay = function (newObj) {
          if (newObj) {
            if (tmrDelay) {
              clearTimeout(tmrDelay);
            }
            tmrDelay = setTimeout(function () {
              if (newObj)
                searchData(newObj, TAG_NAMES.COUPON_SAVED);
            }, 500);
          }
          else {
            WidgetSaved.busy = false;
            searchOptions.skip = 0;
            searchOptions.filter = {};
            WidgetSaved.items = [];
            WidgetSaved.savedItems = {};
            WidgetSaved.loadMore();
          }
        };

        $scope.$on("$destroy", function () {
          for (var i in WidgetSaved.listeners) {
            if (WidgetSaved.listeners.hasOwnProperty(i)) {
              WidgetSaved.listeners[i]();
            }
          }
        });

        $scope.$watch(function () {
          return WidgetSaved.keyword;
        }, searchDataWithDelay, true);

        WidgetSaved.clearSearchResult = function () {
          WidgetSaved.keyword = null;
          WidgetSaved.busy = false;
          searchOptions.skip = 0;
          searchOptions.filter = {};
          WidgetSaved.items = [];
          WidgetSaved.savedItems = {};
          WidgetSaved.loadMore();
        };

        WidgetSaved.init(function(){});

        WidgetSaved.listeners['CHANGED'] = $rootScope.$on('VIEW_CHANGED', function (e, type, view) {

          if (ViewStack.getCurrentView().template == 'Saved') {
            //bind on refresh again

            buildfire.datastore.onRefresh(function () {
              WidgetSaved.init(function(err){
                if(!err){
                  console.log(">>>>>>Refreshed saved list");
                  WidgetSaved.items = [];
                  searchOptions.skip = 0;
                  WidgetSaved.busy = false;
                  WidgetSaved.loadMore();
                }
              });
            });
          }
        });

      }]);
})(window.angular, window.buildfire, window);
<commit_msg>Fixed issue : Item duplicates when click on close icon in saved item list<commit_after>'use strict';

(function (angular, buildfire, window) {
  angular.module('couponPluginWidget')
    .controller('WidgetSavedCtrl', ['$scope', 'DataStore', 'TAG_NAMES', 'LAYOUTS', '$sce', '$rootScope', 'Buildfire', 'ViewStack', 'UserData', 'PAGINATION', '$modal', '$timeout',
      function ($scope, DataStore, TAG_NAMES, LAYOUTS, $sce, $rootScope, Buildfire, ViewStack, UserData, PAGINATION, $modal, $timeout) {
        var WidgetSaved = this;
        WidgetSaved.busy = false;
        WidgetSaved.items = [];
        WidgetSaved.savedItems = {};
        WidgetSaved.hasAtleastOneSavedItem = false;
        WidgetSaved.listeners = {};
        var searchOptions = {
          skip: 0,
          limit: PAGINATION.itemCount
        };

        //Refresh list of saved items on pulling the tile bar

        buildfire.datastore.onRefresh(function () {
          WidgetSaved.init(function(err){
            if(!err){
              console.log(">>>>>>Refreshed saved list");
              WidgetSaved.items = [];
              searchOptions.skip = 0;
              WidgetSaved.busy = false;
              WidgetSaved.loadMore();
            }
          });
        });

        WidgetSaved.init = function (cb) {
          Buildfire.spinner.show();
          var success = function (result) {
              Buildfire.spinner.hide();
              if (result && result.data) {
                WidgetSaved.data = result.data;
              }
            cb();
            }
            , error = function (err) {
              Buildfire.spinner.hide();
              console.error('Error while getting data', err);
            cb();
            };
          DataStore.get(TAG_NAMES.COUPON_INFO).then(success, error);
        };

        WidgetSaved.getSavedItems = function () {
          for (var item = 0; item < WidgetSaved.items.length; item++) {
            WidgetSaved.items[item].isSaved = false;
            for (var saved in WidgetSaved.savedItems) {
              if (WidgetSaved.items[item].id == WidgetSaved.savedItems[saved].data.itemId) {
                WidgetSaved.hasAtleastOneSaved = true;
                WidgetSaved.items[item].isSaved = true;
                WidgetSaved.items[item].savedId = WidgetSaved.savedItems[saved].id;
              }
            }
          }
          $scope.isFetchedAllData = true;
        };

        WidgetSaved.getItems = function () {
          Buildfire.spinner.show();
          var successAll = function (resultAll) {
              Buildfire.spinner.hide();
              WidgetSaved.items = WidgetSaved.items.length ? WidgetSaved.items.concat(resultAll) : resultAll;
              console.log("***************** ==============", WidgetSaved.items);
              if(WidgetSaved.items.length < 1){
                WidgetSaved.hasAtleastOneSaved = false;
              }
              searchOptions.skip = searchOptions.skip + PAGINATION.itemCount;
              if (resultAll.length == PAGINATION.itemCount) {
                WidgetSaved.busy = false;
              }
              var err = function (error) {
                Buildfire.spinner.hide();
                console.log("============ There is an error in getting data", error);
              }, result = function (result) {
                Buildfire.spinner.hide();
                console.log("===========search", result);
                WidgetSaved.savedItems = result;
                WidgetSaved.getSavedItems();
              };
              UserData.search({}, TAG_NAMES.COUPON_SAVED).then(result, err);
            },
            errorAll = function (error) {
              Buildfire.spinner.hide();
              console.log("error", error)
            };
          DataStore.search(searchOptions, TAG_NAMES.COUPON_ITEMS).then(successAll, errorAll);
        };

        WidgetSaved.removeSavedItem = function (item, index) {
          Buildfire.spinner.show();
          var successRemove = function (result) {
            Buildfire.spinner.hide();
            WidgetSaved.items.splice(index, 1);
            WidgetSaved.getSavedItems();
            if (!$scope.$$phase)
              $scope.$digest();
            var removeSavedItemModal = $modal.open({
              templateUrl: 'templates/Saved_Removed.html',
              size: 'sm',
              backdropClass: "ng-hide"
            });
            $timeout(function () {
              removeSavedItemModal.close();
            }, 3000);
            $rootScope.$broadcast("ITEM_SAVED_UPDATED");

          }, errorRemove = function () {
            Buildfire.spinner.hide();
            return console.error('There was a problem removing your data');
          };
          console.log("****************,", item.savedId, WidgetSaved.currentLoggedInUser._id);
          UserData.delete(item.savedId, TAG_NAMES.COUPON_SAVED, WidgetSaved.currentLoggedInUser._id).then(successRemove, errorRemove)
        };

        WidgetSaved.loadMore = function () {
          console.log("===============In loadmore");
          if (WidgetSaved.busy) return;
          WidgetSaved.busy = true;
          WidgetSaved.getItems();
        };

        WidgetSaved.showListItems = function () {
          ViewStack.popAllViews()
        };

        WidgetSaved.showMapView = function () {
          ViewStack.push({
            template: 'Map',
            params: {
              controller: "WidgetMapCtrl as WidgetMap"
            }
          });
        };

        WidgetSaved.showFilter = function () {
          ViewStack.push({
            template: 'Filter',
            params: {
              controller: "WidgetFilterCtrl as WidgetFilter"
            }
          });
        };

        WidgetSaved.openDetails = function (itemId) {
          ViewStack.push({
            template: 'Item',
            params: {
              controller: "WidgetItemCtrl as WidgetItem",
              itemId: itemId
            }
          });
        };

        /**
         * Check for current logged in user, if not show ogin screen
         */
        buildfire.auth.getCurrentUser(function (err, user) {
          console.log("===========LoggedInUser", user);
          if (user) {
            WidgetSaved.currentLoggedInUser = user;
            $scope.$apply();
          }
        });

        var searchData = function (newValue, tag) {
          Buildfire.spinner.show();
          var searchTerm = '';
          if (typeof newValue === 'undefined') {
            return;
          }
          if (newValue) {
            newValue = newValue.trim();
            if (newValue.indexOf(' ') !== -1) {
              searchTerm = newValue.split(' ');
              searchOptions.filter = {
                "$or": [{
                  "$json.title": {
                    "$regex": searchTerm[0],
                    "$options": "i"
                  }
                }, {
                  "$json.summary": {
                    "$regex": searchTerm[0],
                    "$options": "i"
                  }
                }, {
                  "$json.title": {
                    "$regex": searchTerm[1],
                    "$options": "i"
                  }
                }, {
                  "$json.summary": {
                    "$regex": searchTerm[1],
                    "$options": "i"
                  }
                }
                ]
              };
            } else {
              searchTerm = newValue;
              searchOptions.filter = {
                "$or": [{
                  "$json.title": {
                    "$regex": searchTerm,
                    "$options": "i"
                  }
                }, {"$json.summary": {"$regex": searchTerm, "$options": "i"}}]
              };
            }
          }

          WidgetSaved.busy = false;
          searchOptions.skip = 0;
          WidgetSaved.items = [];
          WidgetSaved.savedItems = {};
          WidgetSaved.loadMore();
        };

        var tmrDelay = null;

        var searchDataWithDelay = function (newObj) {
          if (newObj) {
            if (tmrDelay) {
              clearTimeout(tmrDelay);
            }
            tmrDelay = setTimeout(function () {
              if (newObj)
                searchData(newObj, TAG_NAMES.COUPON_SAVED);
            }, 500);
          }
          else {
            WidgetSaved.busy = false;
            searchOptions.skip = 0;
            searchOptions.filter = {};
            WidgetSaved.items = [];
            WidgetSaved.savedItems = {};
            WidgetSaved.loadMore();
          }
        };

        $scope.$on("$destroy", function () {
          for (var i in WidgetSaved.listeners) {
            if (WidgetSaved.listeners.hasOwnProperty(i)) {
              WidgetSaved.listeners[i]();
            }
          }
        });

        $scope.$watch(function () {
          return WidgetSaved.keyword;
        }, searchDataWithDelay, true);

        WidgetSaved.clearSearchResult = function () {
          WidgetSaved.keyword = null;
        };

        WidgetSaved.init(function(){});

        WidgetSaved.listeners['CHANGED'] = $rootScope.$on('VIEW_CHANGED', function (e, type, view) {

          if (ViewStack.getCurrentView().template == 'Saved') {
            //bind on refresh again

            buildfire.datastore.onRefresh(function () {
              WidgetSaved.init(function(err){
                if(!err){
                  console.log(">>>>>>Refreshed saved list");
                  WidgetSaved.items = [];
                  searchOptions.skip = 0;
                  WidgetSaved.busy = false;
                  WidgetSaved.loadMore();
                }
              });
            });
          }
        });

      }]);
})(window.angular, window.buildfire, window);
<|endoftext|>


<commit_before>/*!
 *
 * Copyright (c) 2013 Sebastian Golasch
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

'use strict';

/**
 * Initializes the timers default values
 *
 * @constructor
 * @class Timer
 */

var Timer = function () {
  this.timerData = [0, 0];
  this.interval = [0, 0];
};

/**
 * Timing module to measure test run times
 *
 * @module DalekJS
 * @class Timer
 * @namespace Dalek
 */

Timer.prototype = {

  /**
   * Starts the timer
   *
   * @method start
   * @chainable
   */

  start: function () {
    this.timerData = process.hrtime();
    return this;
  },

  /**
   * Stops the timer
   *
   * @method stop
   * @chainable
   */

  stop: function () {
    this.interval = process.hrtime(this.timerData);
    return this;
  },

  /**
   * Returns the elapsed time in ms
   *
   * @method getElapsedTime
   * @return {float}
   */

  getElapsedTime: function () {
    return (this.interval[0]*1e3 + this.interval[1]/1e6) / 1000;
  },

  /**
   * Returns an object with test run time informations
   * containing hours, minutes & seconds
   *
   * @method getElapsedTimeFormatted
   * @return {Object}
   */

  getElapsedTimeFormatted: function () {
    var hours, minutes, seconds;
    var elapsedTimeInSeconds = this.getElapsedTime();

    // assign elapsed time (in seconds) to the seconds output
    seconds = elapsedTimeInSeconds;

    // check if the elapsed time in seconds is more than a minute
    // and convert the raw seconds to minutes & seconds
    if (elapsedTimeInSeconds > 60) {
      minutes = Math.floor(elapsedTimeInSeconds / 60);
      seconds = elapsedTimeInSeconds - minutes * 60;
    }

    // check if the elapsed time in minutes is more than an hour
    // and convert the raw seconds to hours, minutes & seconds
    if (minutes > 60) {
      hours = Math.floor(elapsedTimeInSeconds / 3600);
      minutes = elapsedTimeInSeconds - hours * 60;
      seconds = elapsedTimeInSeconds - minutes * 3600;
    }

    return {hours: hours, minutes: minutes, seconds: seconds};
  }
};

// export the timer module
module.exports = Timer;
<commit_msg>Updated dependencies, added coverage badge, added trello board, added npm badges<commit_after>/*!
 *
 * Copyright (c) 2013 Sebastian Golasch
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

'use strict';

/**
 * Initializes the timers default values
 *
 * @constructor
 * @class Timer
 */

var Timer = function () {
  this.timerData = [0, 0];
  this.interval = [0, 0];
};

/**
 * Timing module to measure test run times
 *
 * @module DalekJS
 * @class Timer
 * @namespace Dalek
 * @part Timer
 * @api
 */

Timer.prototype = {

  /**
   * Starts the timer
   *
   * @method start
   * @chainable
   */

  start: function () {
    this.timerData = process.hrtime();
    return this;
  },

  /**
   * Stops the timer
   *
   * @method stop
   * @chainable
   */

  stop: function () {
    this.interval = process.hrtime(this.timerData);
    return this;
  },

  /**
   * Returns the elapsed time in ms
   *
   * @method getElapsedTime
   * @return {float}
   */

  getElapsedTime: function () {
    return (this.interval[0]*1e3 + this.interval[1]/1e6) / 1000;
  },

  /**
   * Returns an object with test run time informations
   * containing hours, minutes & seconds
   *
   * @method getElapsedTimeFormatted
   * @return {Object}
   */

  getElapsedTimeFormatted: function () {
    var hours, minutes, seconds;
    var elapsedTimeInSeconds = this.getElapsedTime();

    // assign elapsed time (in seconds) to the seconds output
    seconds = elapsedTimeInSeconds;

    // check if the elapsed time in seconds is more than a minute
    // and convert the raw seconds to minutes & seconds
    if (elapsedTimeInSeconds > 60) {
      minutes = Math.floor(elapsedTimeInSeconds / 60);
      seconds = elapsedTimeInSeconds - minutes * 60;
    }

    // check if the elapsed time in minutes is more than an hour
    // and convert the raw seconds to hours, minutes & seconds
    if (minutes > 60) {
      hours = Math.floor(elapsedTimeInSeconds / 3600);
      minutes = elapsedTimeInSeconds - hours * 60;
      seconds = elapsedTimeInSeconds - minutes * 3600;
    }

    return {hours: hours, minutes: minutes, seconds: seconds};
  }
};

// export the timer module
module.exports = Timer;
<|endoftext|>