<commit_before>/**
 * @license
 * Copyright 2016 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview Number input field
 * @author fenichel@google.com (Rachel Fenichel)
 */
'use strict';

goog.module('Blockly.FieldNumber');
goog.module.declareLegacyNamespace();

const FieldTextInput = goog.require('Blockly.FieldTextInput');
const aria = goog.require('Blockly.utils.aria');
const {inherits} = goog.require('Blockly.utils.object');
const {register} = goog.require('Blockly.fieldRegistry');

/**
 * Class for an editable number field.
 * @param {string|number=} opt_value The initial value of the field. Should cast
 *    to a number. Defaults to 0.
 * @param {?(string|number)=} opt_min Minimum value.
 * @param {?(string|number)=} opt_max Maximum value.
 * @param {?(string|number)=} opt_precision Precision for value.
 * @param {?Function=} opt_validator A function that is called to validate
 *    changes to the field's value. Takes in a number & returns a validated
 *    number, or null to abort the change.
 * @param {Object=} opt_config A map of options used to configure the field.
 *    See the [field creation documentation]{@link https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/number#creation}
 *    for a list of properties this parameter supports.
 * @extends {FieldTextInput}
 * @constructor
 */
const FieldNumber = function(opt_value, opt_min, opt_max, opt_precision,
    opt_validator, opt_config) {

  /**
   * The minimum value this number field can contain.
   * @type {number}
   * @protected
   */
  this.min_ = -Infinity;

  /**
   * The maximum value this number field can contain.
   * @type {number}
   * @protected
   */
  this.max_ = Infinity;

  /**
   * The multiple to which this fields value is rounded.
   * @type {number}
   * @protected
   */
  this.precision_ = 0;

  /**
   * The number of decimal places to allow, or null to allow any number of
   * decimal digits.
   * @type {?number}
   * @private
   */
  this.decimalPlaces_ = null;

  FieldNumber.superClass_.constructor.call(
      this, opt_value, opt_validator, opt_config);

  if (!opt_config) {  // Only do one kind of configuration or the other.
    this.setConstraints(opt_min, opt_max, opt_precision);
  }
};
inherits(FieldNumber, FieldTextInput);

/**
 * The default value for this field.
 * @type {*}
 * @protected
 */
FieldNumber.prototype.DEFAULT_VALUE = 0;

/**
 * Construct a FieldNumber from a JSON arg object.
 * @param {!Object} options A JSON object with options (value, min, max, and
 *                          precision).
 * @return {!FieldNumber} The new field instance.
 * @package
 * @nocollapse
 */
FieldNumber.fromJson = function(options) {
  // `this` might be a subclass of FieldNumber if that class doesn't override
  // the static fromJson method.
  return new this(options['value'],
      undefined, undefined, undefined, undefined, options);
};

/**
 * Serializable fields are saved by the XML renderer, non-serializable fields
 * are not. Editable fields should also be serializable.
 * @type {boolean}
 */
FieldNumber.prototype.SERIALIZABLE = true;

/**
 * Configure the field based on the given map of options.
 * @param {!Object} config A map of options to configure the field based on.
 * @protected
 * @override
 */
FieldNumber.prototype.configure_ = function(config) {
  FieldNumber.superClass_.configure_.call(this, config);
  this.setMinInternal_(config['min']);
  this.setMaxInternal_(config['max']);
  this.setPrecisionInternal_(config['precision']);
};

/**
 * Set the maximum, minimum and precision constraints on this field.
 * Any of these properties may be undefined or NaN to be disabled.
 * Setting precision (usually a power of 10) enforces a minimum step between
 * values. That is, the user's value will rounded to the closest multiple of
 * precision. The least significant digit place is inferred from the precision.
 * Integers values can be enforces by choosing an integer precision.
 * @param {?(number|string|undefined)} min Minimum value.
 * @param {?(number|string|undefined)} max Maximum value.
 * @param {?(number|string|undefined)} precision Precision for value.
 */
FieldNumber.prototype.setConstraints = function(min, max, precision) {
  this.setMinInternal_(min);
  this.setMaxInternal_(max);
  this.setPrecisionInternal_(precision);
  this.setValue(this.getValue());
};

/**
 * Sets the minimum value this field can contain. Updates the value to reflect.
 * @param {?(number|string|undefined)} min Minimum value.
 */
FieldNumber.prototype.setMin = function(min) {
  this.setMinInternal_(min);
  this.setValue(this.getValue());
};

/**
 * Sets the minimum value this field can contain. Called internally to avoid
 * value updates.
 * @param {?(number|string|undefined)} min Minimum value.
 * @private
 */
FieldNumber.prototype.setMinInternal_ = function(min) {
  if (min == null) {
    this.min_ = -Infinity;
  } else {
    min = Number(min);
    if (!isNaN(min)) {
      this.min_ = min;
    }
  }
};

/**
 * Returns the current minimum value this field can contain. Default is
 * -Infinity.
 * @return {number} The current minimum value this field can contain.
 */
FieldNumber.prototype.getMin = function() {
  return this.min_;
};

/**
 * Sets the maximum value this field can contain. Updates the value to reflect.
 * @param {?(number|string|undefined)} max Maximum value.
 */
FieldNumber.prototype.setMax = function(max) {
  this.setMaxInternal_(max);
  this.setValue(this.getValue());
};

/**
 * Sets the maximum value this field can contain. Called internally to avoid
 * value updates.
 * @param {?(number|string|undefined)} max Maximum value.
 * @private
 */
FieldNumber.prototype.setMaxInternal_ = function(max) {
  if (max == null) {
    this.max_ = Infinity;
  } else {
    max = Number(max);
    if (!isNaN(max)) {
      this.max_ = max;
    }
  }
};

/**
 * Returns the current maximum value this field can contain. Default is
 * Infinity.
 * @return {number} The current maximum value this field can contain.
 */
FieldNumber.prototype.getMax = function() {
  return this.max_;
};

/**
 * Sets the precision of this field's value, i.e. the number to which the
 * value is rounded. Updates the field to reflect.
 * @param {?(number|string|undefined)} precision The number to which the
 *    field's value is rounded.
 */
FieldNumber.prototype.setPrecision = function(precision) {
  this.setPrecisionInternal_(precision);
  this.setValue(this.getValue());
};

/**
 * Sets the precision of this field's value. Called internally to avoid
 * value updates.
 * @param {?(number|string|undefined)} precision The number to which the
 *    field's value is rounded.
 * @private
 */
FieldNumber.prototype.setPrecisionInternal_ = function(precision) {
  this.precision_ = Number(precision) || 0;
  let precisionString = String(this.precision_);
  if (precisionString.indexOf('e') != -1) {
    // String() is fast.  But it turns .0000001 into '1e-7'.
    // Use the much slower toLocaleString to access all the digits.
    precisionString =
        this.precision_.toLocaleString('en-US', {maximumFractionDigits: 20});
  }
  const decimalIndex = precisionString.indexOf('.');
  if (decimalIndex == -1) {
    // If the precision is 0 (float) allow any number of decimals,
    // otherwise allow none.
    this.decimalPlaces_ = precision ? 0 : null;
  } else {
    this.decimalPlaces_ = precisionString.length - decimalIndex - 1;
  }
};

/**
 * Returns the current precision of this field. The precision being the
 * number to which the field's value is rounded. A precision of 0 means that
 * the value is not rounded.
 * @return {number} The number to which this field's value is rounded.
 */
FieldNumber.prototype.getPrecision = function() {
  return this.precision_;
};

/**
 * Ensure that the input value is a valid number (must fulfill the
 * constraints placed on the field).
 * @param {*=} opt_newValue The input value.
 * @return {?number} A valid number, or null if invalid.
 * @protected
 * @override
 */
FieldNumber.prototype.doClassValidation_ = function(opt_newValue) {
  if (opt_newValue === null) {
    return null;
  }
  // Clean up text.
  let newValue = String(opt_newValue);
  // TODO: Handle cases like 'ten', '1.203,14', etc.
  // 'O' is sometimes mistaken for '0' by inexperienced users.
  newValue = newValue.replace(/O/ig, '0');
  // Strip out thousands separators.
  newValue = newValue.replace(/,/g, '');
  // Ignore case of 'Infinity'.
  newValue = newValue.replace(/infinity/i, 'Infinity');

  // Clean up number.
  let n = Number(newValue || 0);
  if (isNaN(n)) {
    // Invalid number.
    return null;
  }
  // Get the value in range.
  n = Math.min(Math.max(n, this.min_), this.max_);
  // Round to nearest multiple of precision.
  if (this.precision_ && isFinite(n)) {
    n = Math.round(n / this.precision_) * this.precision_;
  }
  // Clean up floating point errors.
  if (this.decimalPlaces_ != null) {
    n = Number(n.toFixed(this.decimalPlaces_));
  }
  return n;
};

/**
 * Create the number input editor widget.
 * @return {!HTMLElement} The newly created number input editor.
 * @protected
 * @override
 */
FieldNumber.prototype.widgetCreate_ = function() {
  const htmlInput = FieldNumber.superClass_.widgetCreate_.call(this);

  // Set the accessibility state
  if (this.min_ > -Infinity) {
    aria.setState(htmlInput,
        aria.State.VALUEMIN, this.min_);
  }
  if (this.max_ < Infinity) {
    aria.setState(htmlInput,
        aria.State.VALUEMAX, this.max_);
  }
  return htmlInput;
};

register('field_number', FieldNumber);

exports = FieldNumber;
<commit_msg>clang-format core/field_number.js<commit_after>/**
 * @license
 * Copyright 2016 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview Number input field
 * @author fenichel@google.com (Rachel Fenichel)
 */
'use strict';

goog.module('Blockly.FieldNumber');
goog.module.declareLegacyNamespace();

const FieldTextInput = goog.require('Blockly.FieldTextInput');
const aria = goog.require('Blockly.utils.aria');
const {inherits} = goog.require('Blockly.utils.object');
const {register} = goog.require('Blockly.fieldRegistry');

/**
 * Class for an editable number field.
 * @param {string|number=} opt_value The initial value of the field. Should cast
 *    to a number. Defaults to 0.
 * @param {?(string|number)=} opt_min Minimum value.
 * @param {?(string|number)=} opt_max Maximum value.
 * @param {?(string|number)=} opt_precision Precision for value.
 * @param {?Function=} opt_validator A function that is called to validate
 *    changes to the field's value. Takes in a number & returns a validated
 *    number, or null to abort the change.
 * @param {Object=} opt_config A map of options used to configure the field.
 *    See the [field creation documentation]{@link
 * https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/number#creation}
 *    for a list of properties this parameter supports.
 * @extends {FieldTextInput}
 * @constructor
 */
const FieldNumber = function(
    opt_value, opt_min, opt_max, opt_precision, opt_validator, opt_config) {
  /**
   * The minimum value this number field can contain.
   * @type {number}
   * @protected
   */
  this.min_ = -Infinity;

  /**
   * The maximum value this number field can contain.
   * @type {number}
   * @protected
   */
  this.max_ = Infinity;

  /**
   * The multiple to which this fields value is rounded.
   * @type {number}
   * @protected
   */
  this.precision_ = 0;

  /**
   * The number of decimal places to allow, or null to allow any number of
   * decimal digits.
   * @type {?number}
   * @private
   */
  this.decimalPlaces_ = null;

  FieldNumber.superClass_.constructor.call(
      this, opt_value, opt_validator, opt_config);

  if (!opt_config) {  // Only do one kind of configuration or the other.
    this.setConstraints(opt_min, opt_max, opt_precision);
  }
};
inherits(FieldNumber, FieldTextInput);

/**
 * The default value for this field.
 * @type {*}
 * @protected
 */
FieldNumber.prototype.DEFAULT_VALUE = 0;

/**
 * Construct a FieldNumber from a JSON arg object.
 * @param {!Object} options A JSON object with options (value, min, max, and
 *                          precision).
 * @return {!FieldNumber} The new field instance.
 * @package
 * @nocollapse
 */
FieldNumber.fromJson = function(options) {
  // `this` might be a subclass of FieldNumber if that class doesn't override
  // the static fromJson method.
  return new this(
      options['value'], undefined, undefined, undefined, undefined, options);
};

/**
 * Serializable fields are saved by the XML renderer, non-serializable fields
 * are not. Editable fields should also be serializable.
 * @type {boolean}
 */
FieldNumber.prototype.SERIALIZABLE = true;

/**
 * Configure the field based on the given map of options.
 * @param {!Object} config A map of options to configure the field based on.
 * @protected
 * @override
 */
FieldNumber.prototype.configure_ = function(config) {
  FieldNumber.superClass_.configure_.call(this, config);
  this.setMinInternal_(config['min']);
  this.setMaxInternal_(config['max']);
  this.setPrecisionInternal_(config['precision']);
};

/**
 * Set the maximum, minimum and precision constraints on this field.
 * Any of these properties may be undefined or NaN to be disabled.
 * Setting precision (usually a power of 10) enforces a minimum step between
 * values. That is, the user's value will rounded to the closest multiple of
 * precision. The least significant digit place is inferred from the precision.
 * Integers values can be enforces by choosing an integer precision.
 * @param {?(number|string|undefined)} min Minimum value.
 * @param {?(number|string|undefined)} max Maximum value.
 * @param {?(number|string|undefined)} precision Precision for value.
 */
FieldNumber.prototype.setConstraints = function(min, max, precision) {
  this.setMinInternal_(min);
  this.setMaxInternal_(max);
  this.setPrecisionInternal_(precision);
  this.setValue(this.getValue());
};

/**
 * Sets the minimum value this field can contain. Updates the value to reflect.
 * @param {?(number|string|undefined)} min Minimum value.
 */
FieldNumber.prototype.setMin = function(min) {
  this.setMinInternal_(min);
  this.setValue(this.getValue());
};

/**
 * Sets the minimum value this field can contain. Called internally to avoid
 * value updates.
 * @param {?(number|string|undefined)} min Minimum value.
 * @private
 */
FieldNumber.prototype.setMinInternal_ = function(min) {
  if (min == null) {
    this.min_ = -Infinity;
  } else {
    min = Number(min);
    if (!isNaN(min)) {
      this.min_ = min;
    }
  }
};

/**
 * Returns the current minimum value this field can contain. Default is
 * -Infinity.
 * @return {number} The current minimum value this field can contain.
 */
FieldNumber.prototype.getMin = function() {
  return this.min_;
};

/**
 * Sets the maximum value this field can contain. Updates the value to reflect.
 * @param {?(number|string|undefined)} max Maximum value.
 */
FieldNumber.prototype.setMax = function(max) {
  this.setMaxInternal_(max);
  this.setValue(this.getValue());
};

/**
 * Sets the maximum value this field can contain. Called internally to avoid
 * value updates.
 * @param {?(number|string|undefined)} max Maximum value.
 * @private
 */
FieldNumber.prototype.setMaxInternal_ = function(max) {
  if (max == null) {
    this.max_ = Infinity;
  } else {
    max = Number(max);
    if (!isNaN(max)) {
      this.max_ = max;
    }
  }
};

/**
 * Returns the current maximum value this field can contain. Default is
 * Infinity.
 * @return {number} The current maximum value this field can contain.
 */
FieldNumber.prototype.getMax = function() {
  return this.max_;
};

/**
 * Sets the precision of this field's value, i.e. the number to which the
 * value is rounded. Updates the field to reflect.
 * @param {?(number|string|undefined)} precision The number to which the
 *    field's value is rounded.
 */
FieldNumber.prototype.setPrecision = function(precision) {
  this.setPrecisionInternal_(precision);
  this.setValue(this.getValue());
};

/**
 * Sets the precision of this field's value. Called internally to avoid
 * value updates.
 * @param {?(number|string|undefined)} precision The number to which the
 *    field's value is rounded.
 * @private
 */
FieldNumber.prototype.setPrecisionInternal_ = function(precision) {
  this.precision_ = Number(precision) || 0;
  let precisionString = String(this.precision_);
  if (precisionString.indexOf('e') != -1) {
    // String() is fast.  But it turns .0000001 into '1e-7'.
    // Use the much slower toLocaleString to access all the digits.
    precisionString =
        this.precision_.toLocaleString('en-US', {maximumFractionDigits: 20});
  }
  const decimalIndex = precisionString.indexOf('.');
  if (decimalIndex == -1) {
    // If the precision is 0 (float) allow any number of decimals,
    // otherwise allow none.
    this.decimalPlaces_ = precision ? 0 : null;
  } else {
    this.decimalPlaces_ = precisionString.length - decimalIndex - 1;
  }
};

/**
 * Returns the current precision of this field. The precision being the
 * number to which the field's value is rounded. A precision of 0 means that
 * the value is not rounded.
 * @return {number} The number to which this field's value is rounded.
 */
FieldNumber.prototype.getPrecision = function() {
  return this.precision_;
};

/**
 * Ensure that the input value is a valid number (must fulfill the
 * constraints placed on the field).
 * @param {*=} opt_newValue The input value.
 * @return {?number} A valid number, or null if invalid.
 * @protected
 * @override
 */
FieldNumber.prototype.doClassValidation_ = function(opt_newValue) {
  if (opt_newValue === null) {
    return null;
  }
  // Clean up text.
  let newValue = String(opt_newValue);
  // TODO: Handle cases like 'ten', '1.203,14', etc.
  // 'O' is sometimes mistaken for '0' by inexperienced users.
  newValue = newValue.replace(/O/ig, '0');
  // Strip out thousands separators.
  newValue = newValue.replace(/,/g, '');
  // Ignore case of 'Infinity'.
  newValue = newValue.replace(/infinity/i, 'Infinity');

  // Clean up number.
  let n = Number(newValue || 0);
  if (isNaN(n)) {
    // Invalid number.
    return null;
  }
  // Get the value in range.
  n = Math.min(Math.max(n, this.min_), this.max_);
  // Round to nearest multiple of precision.
  if (this.precision_ && isFinite(n)) {
    n = Math.round(n / this.precision_) * this.precision_;
  }
  // Clean up floating point errors.
  if (this.decimalPlaces_ != null) {
    n = Number(n.toFixed(this.decimalPlaces_));
  }
  return n;
};

/**
 * Create the number input editor widget.
 * @return {!HTMLElement} The newly created number input editor.
 * @protected
 * @override
 */
FieldNumber.prototype.widgetCreate_ = function() {
  const htmlInput = FieldNumber.superClass_.widgetCreate_.call(this);

  // Set the accessibility state
  if (this.min_ > -Infinity) {
    aria.setState(htmlInput, aria.State.VALUEMIN, this.min_);
  }
  if (this.max_ < Infinity) {
    aria.setState(htmlInput, aria.State.VALUEMAX, this.max_);
  }
  return htmlInput;
};

register('field_number', FieldNumber);

exports = FieldNumber;
<|endoftext|>


<commit_before>const path = require('path');
const _ = require('lodash');
// updating https://github.com/niftylettuce/email-templates#v3-breaking-changes
// const EmailTemplate = require('email-templates').EmailTemplate;
const Email = require('email-templates');
const nodemailer = require('nodemailer');
const ses = require('nodemailer-ses-transport');
const logger = require('../utilities/logger');

const getTransporter = () => {
  if (process.env.ACCESSKEYID == null || process.env.ACCESSKEYID === '') {
    throw new Error('Missing ACCESSKEYID config value');
  }
  if (process.env.SECRETACCESSKEY == null || process.env.SECRETACCESSKEY === '') {
    throw new Error('Missing SECRETACCESSKEY config value');
  }
  return nodemailer.createTransport(ses({
    accessKeyId: process.env.ACCESSKEYID,
    secretAccessKey: process.env.SECRETACCESSKEY,
    region: 'eu-west-1'
  }));
};

module.exports.signup = (options, data, cb) => {
  // const template = path.join(__dirname, 'mailer/templates', 'signup');
  // const mail = getTransporter().templateSender(new EmailTemplate(template));
  const email = new Email({
    message: {
      from: process.env.MAILFROM,
      name: 'AVnode'
    },
    // uncomment below to send emails in development/test env:
    send: true,
    transport: {
      jsonTransport: true
    }
  });
  console.log('to:' + options.to + ' uuid: ' + data.uuid);
  // html.pug a(href=link+uuid) Confirm Email
  // mailer.signup({ to: user.email }, { uuid: user.confirm }
  email.send({
    template: 'templates/signup',
    message: {
      to: options.to
    },
    locals: {
      link: process.env.BASE + 'user/confirm/',
      uuid: data.uuid
    }
  }).then(info => logger.info('email sent', info)).catch(err => cb(err));

  /*const defaults = {
    from: process.env.MAILFROM,
    subject: 'AVnode.net – Welcome'
  };
  const mailOptions = _.merge(options, defaults);

  const context = _.merge({
    link: process.env.BASE + 'user/confirm/'
  }, data);

  mail(mailOptions, context, (err, info) => {
    if (err) {
      return cb(err);
    }
    logger.info('email sent', info);
    return cb(null, info);
  });*/
};

module.exports.confirmEmail = (options, data, cb) => {
  /*const template = path.join(__dirname, 'mailer/templates', 'confirm-email');
  const mail = getTransporter().templateSender(new EmailTemplate(template));

  const defaults = {
    from: process.env.MAILFROM,
    subject: 'AVnode.net – Confirm Email'
  };
  const mailOptions = _.merge(options, defaults);

  const context = _.merge({
    link: process.env.BASE + 'user/confirm/email/',
  }, data);

  mail(mailOptions, context, (err, info) => {
    if (err) {
      return cb(err);
    }
    logger.info('email sent', info);
    return cb(null, info);
  });*/
};

module.exports.resetPassword = (options, data, cb) => {
  /*const template = path.join(__dirname, 'mailer/templates', 'reset-password');
  const mail = getTransporter().templateSender(new EmailTemplate(template));

  const defaults = {
    from: process.env.MAILFROM,
    subject: 'AVnode.net – Reset your password'
  };
  const mailOptions = _.merge(options, defaults);

  const context = _.merge({
    link: process.env.BASE + 'password/verify/'+data.email+'/'+data.token,
  }, data);

  mail(mailOptions, context, (err, info) => {
    if (err) {
      return cb(err);
    }
    logger.info('email sent', info);
    return cb(null, info);
  });*/
};

module.exports.confirmNewEmail = (options, data, cb) => {
  /*const template = path.join(__dirname, 'mailer/templates', 'confirm-new-email');
  const mail = getTransporter().templateSender(new EmailTemplate(template));

  const defaults = {
    from: process.env.MAILFROM,
    subject: 'AVnode.net – Confirm New Email'
  };
  const mailOptions = _.merge(options, defaults);

  const context = _.merge({
    link: process.env.BASE + 'user/confirm/email/',
  }, data);

  mail(mailOptions, context, (err, info) => {
    if (err) {
      return cb(err);
    }
    logger.info('email sent', info);
    return cb(null, info);
  });*/
};
<commit_msg>email2<commit_after>const path = require('path');
const _ = require('lodash');
// updating https://github.com/niftylettuce/email-templates#v3-breaking-changes
// const EmailTemplate = require('email-templates').EmailTemplate;
const Email = require('email-templates');
const nodemailer = require('nodemailer');
const ses = require('nodemailer-ses-transport');
const logger = require('../utilities/logger');

const getTransporter = () => {
  if (process.env.ACCESSKEYID == null || process.env.ACCESSKEYID === '') {
    throw new Error('Missing ACCESSKEYID config value');
  }
  if (process.env.SECRETACCESSKEY == null || process.env.SECRETACCESSKEY === '') {
    throw new Error('Missing SECRETACCESSKEY config value');
  }
  return nodemailer.createTransport(ses({
    accessKeyId: process.env.ACCESSKEYID,
    secretAccessKey: process.env.SECRETACCESSKEY,
    region: 'eu-west-1'
  }));
};

module.exports.signup = (options, data, cb) => {
  // const template = path.join(__dirname, 'mailer/templates', 'signup');
  // const mail = getTransporter().templateSender(new EmailTemplate(template));
  const email = new Email({
    message: {
      from: process.env.MAILFROM,
      name: 'AVnode'
    },
    // uncomment below to send emails in development/test env:
    send: true,
    transport: {
      jsonTransport: true
    }
  });
  console.log('to:' + options.to + ' uuid: ' + data.uuid);
  // html.pug a(href=link+uuid) Confirm Email
  // mailer.signup({ to: user.email }, { uuid: user.confirm }
  email.send({
    template: 'templates/signup',
    message: {
      to: options.to
    },
    locals: {
      link: process.env.BASE + 'user/confirm/',
      uuid: data.uuid
    }
  }).then(info => logger.info('email sent', info)).catch(err => cb(err));

  const email2 = new Email({
    message: {
      from: process.env.MAILFROM,
      name: 'AVnode network'
    },
    transport: {
      jsonTransport: true
    }
  });
  email2.send({
    template: 'templates/signup',
    message: {
      to: options.to
    },
    locals: {
      link: process.env.BASE + 'user/confirm/',
      uuid: data.uuid
    }
  }).then(info => logger.info('email2 sent', info)).catch(err => cb(err));

  /*const defaults = {
    from: process.env.MAILFROM,
    subject: 'AVnode.net – Welcome'
  };
  const mailOptions = _.merge(options, defaults);

  const context = _.merge({
    link: process.env.BASE + 'user/confirm/'
  }, data);

  mail(mailOptions, context, (err, info) => {
    if (err) {
      return cb(err);
    }
    logger.info('email sent', info);
    return cb(null, info);
  });*/
};

module.exports.confirmEmail = (options, data, cb) => {
  /*const template = path.join(__dirname, 'mailer/templates', 'confirm-email');
  const mail = getTransporter().templateSender(new EmailTemplate(template));

  const defaults = {
    from: process.env.MAILFROM,
    subject: 'AVnode.net – Confirm Email'
  };
  const mailOptions = _.merge(options, defaults);

  const context = _.merge({
    link: process.env.BASE + 'user/confirm/email/',
  }, data);

  mail(mailOptions, context, (err, info) => {
    if (err) {
      return cb(err);
    }
    logger.info('email sent', info);
    return cb(null, info);
  });*/
};

module.exports.resetPassword = (options, data, cb) => {
  /*const template = path.join(__dirname, 'mailer/templates', 'reset-password');
  const mail = getTransporter().templateSender(new EmailTemplate(template));

  const defaults = {
    from: process.env.MAILFROM,
    subject: 'AVnode.net – Reset your password'
  };
  const mailOptions = _.merge(options, defaults);

  const context = _.merge({
    link: process.env.BASE + 'password/verify/'+data.email+'/'+data.token,
  }, data);

  mail(mailOptions, context, (err, info) => {
    if (err) {
      return cb(err);
    }
    logger.info('email sent', info);
    return cb(null, info);
  });*/
};

module.exports.confirmNewEmail = (options, data, cb) => {
  /*const template = path.join(__dirname, 'mailer/templates', 'confirm-new-email');
  const mail = getTransporter().templateSender(new EmailTemplate(template));

  const defaults = {
    from: process.env.MAILFROM,
    subject: 'AVnode.net – Confirm New Email'
  };
  const mailOptions = _.merge(options, defaults);

  const context = _.merge({
    link: process.env.BASE + 'user/confirm/email/',
  }, data);

  mail(mailOptions, context, (err, info) => {
    if (err) {
      return cb(err);
    }
    logger.info('email sent', info);
    return cb(null, info);
  });*/
};
<|endoftext|>


<commit_before>(function (env) {
    "use strict";

    function getUrl(watchable) {
        return 'http://gowatchit.com' + (watchable.url ? watchable.url : "");
    }

    env.ddg_spice_go_watch_it = function(api_result) {
        // Check if the data that we get is sane.
        if (!api_result || api_result.error || !DDG.getProperty(api_result, 'search.movies') ||
            !DDG.getProperty(api_result, 'search.shows') || api_result.search.movies.length === 0 &&
            api_result.search.shows.length === 0) {
            return Spice.failed('go_watch_it');
        }

        // Get the first result.
        // This probably shouldn't be the way to do it, but it works for now.
        var movie = api_result.search.movies[0],
            show  = api_result.search.shows[0],
            watchable = movie || show;

        var title = watchable.title;

        // Exit if there are no available streaming sources.
        if(watchable.availabilities.length === 0) {
            return Spice.failed('go_watch_it');
        }

        // Check the relevancy of the title and be strict about it.
        var skipArray = ["watch", "stream", "demand", "now", "online", "buy", "rent", "movie", "show", "tv"];
        if(!DDG.isRelevant(title, skipArray, undefined, true)) {
            return Spice.failed('go_watch_it');
        }

        var streaming_providers = {
            "YouTube": 1,
            "Google Play": 1,
            "Disney Movies Anywhere": 1,
            "Target Ticket": 1
        };

        var purchase_providers = {
            "Best Buy": 1,
            "Walmart": 1,
            "Target": 1,
            "Fandango": 1,
            "Amazon": 1
        };

        var provider_icons = {
            3: {
                dark: DDG.get_asset_path('go_watch_it', 'itunes.png'),
                light: DDG.get_asset_path('go_watch_it', 'itunes-alt.png')
            },
            5: {
                dark: DDG.get_asset_path('go_watch_it', 'vudu.png'),
                light: DDG.get_asset_path('go_watch_it', 'vudu.png')
            },
            7: {
                dark: DDG.get_asset_path('go_watch_it', 'amazon.png'),
                light: DDG.get_asset_path('go_watch_it', 'amazon-alt.png')
            },
            12: {
                dark: DDG.get_asset_path('go_watch_it', 'youtube.png'),
                light: DDG.get_asset_path('go_watch_it', 'youtube-alt.png')
            },
            18: {
                dark: DDG.get_asset_path('go_watch_it', 'googleplay.png'),
                light: DDG.get_asset_path('go_watch_it', 'googleplay-alt.png')
            },
            19: {
                dark: DDG.get_asset_path('go_watch_it', 'xboxvideo.png'),
                light: DDG.get_asset_path('go_watch_it', 'xboxvideo.png')
            },
            64: {
                dark: DDG.get_asset_path('go_watch_it', 'flixster.png'),
                light: DDG.get_asset_path('go_watch_it', 'flixster.png')
            },
            20: {
                dark: DDG.get_asset_path('go_watch_it', 'sony.png'),
                light: DDG.get_asset_path('go_watch_it', 'sony-alt.png')
            },
            76: {
                dark: DDG.get_asset_path('go_watch_it', 'disney.png'),
                light: DDG.get_asset_path('go_watch_it', 'disney-alt.png')
            }
        };

        var skip_providers = {
            // Movies on Demand
            10: 1,
            // Filmmaker Direct Streaming
            46: 1
        };

        Spice.add({
            id: "go_watch_it",
            name: "How to Watch",
            data: watchable.availabilities,
            meta: {
                sourceName: 'GoWatchIt',
                sourceUrl: getUrl(watchable),
                itemType: 'Providers for ' + title
            },
            normalize: function(item) {
                if(item.provider_format_id in skip_providers) {
                    return null;
                }

                // If the provider is in this hash, it means that they provide
                // streaming if they don't buy or sell stuff.
                if(item.provider_name in streaming_providers &&
                   item.buy_line === "" && item.rent_line === "") {
                    item.buy_line = "Available for Streaming";
                }

                if(item.provider_name in purchase_providers &&
                   item.buy_line === "" && item.rent_line === "") {
                    item.buy_line = "Available for Purchase";
                }

                // Change the format line to match the other tiles.
                if(item.format_line === "DVD & Blu-ray") {
                    item.format_line = "DVD / Blu-ray";
                }

                // Replace the icon if we can.
                if(item.provider_format_id in provider_icons) {
                    item.provider_format_logos = provider_icons[item.provider_format_id];
                }

                return {
                    url: item.watch_now_url
                }
            },

            templates: {
                item: 'base_item',
                options: {
                    content: Spice.go_watch_it.content
                }
            }
        });
    };

    Spice.registerHelper("buyOrRent", function(buy_line, rent_line, options) {
        if(buy_line && buy_line !== "") {
            this.line = buy_line;
            return options.fn(this);
        }

        this.line = rent_line;
        return options.fn(this);
    });

    // Check to see if both buy_line and rent_line are present.
    Spice.registerHelper("gwi_ifHasBothBuyAndRent", function(buy_line, rent_line, options) {
        if (buy_line && buy_line !== "" && rent_line && rent_line !== "") {
            return options.fn(this);
        } else {
            return options.inverse(this);
        }
    });

    // Check to see if the buy_line/rent_line includes a price.
    // This is because some provider formats (like Netflix) have
    // 'Included with Subscription' in their buy line.
    Spice.registerHelper("gwi_ifHasPrice", function(line, options) {
        if (line.split("$").length > 1) {
            return options.fn(this);
        } else {
            return options.inverse(this);
        }
    });

    // Grab dollar amount from 'Rent from $X.XX' string.
    Spice.registerHelper("gwi_price", function(line, options) {
        var strings = line.split("$")
        return "$" + strings[strings.length - 1]
    });

    // Get the class for the footer element based on whether or not both the buy_line
    // and rent_line are present.
    Spice.registerHelper("gwi_footerClass", function(buy_line, rent_line, options) {
        var klass = 'gwi-footer';
        if (buy_line && buy_line != '' && rent_line && rent_line != '') {
            klass += ' double';
        }

        return klass;
    });

}(this));<commit_msg>GoWatchIt: Replace Hulu icon.<commit_after>(function (env) {
    "use strict";

    function getUrl(watchable) {
        return 'http://gowatchit.com' + (watchable.url ? watchable.url : "");
    }

    env.ddg_spice_go_watch_it = function(api_result) {
        // Check if the data that we get is sane.
        if (!api_result || api_result.error || !DDG.getProperty(api_result, 'search.movies') ||
            !DDG.getProperty(api_result, 'search.shows') || api_result.search.movies.length === 0 &&
            api_result.search.shows.length === 0) {
            return Spice.failed('go_watch_it');
        }

        // Get the first result.
        // This probably shouldn't be the way to do it, but it works for now.
        var movie = api_result.search.movies[0],
            show  = api_result.search.shows[0],
            watchable = movie || show;

        var title = watchable.title;

        // Exit if there are no available streaming sources.
        if(watchable.availabilities.length === 0) {
            return Spice.failed('go_watch_it');
        }

        // Check the relevancy of the title and be strict about it.
        var skipArray = ["watch", "stream", "demand", "now", "online", "buy", "rent", "movie", "show", "tv"];
        if(!DDG.isRelevant(title, skipArray, undefined, true)) {
            return Spice.failed('go_watch_it');
        }

        var streaming_providers = {
            "YouTube": 1,
            "Google Play": 1,
            "Disney Movies Anywhere": 1,
            "Target Ticket": 1,
            "Hulu": 1
        };

        var purchase_providers = {
            "Best Buy": 1,
            "Walmart": 1,
            "Target": 1,
            "Fandango": 1,
            "Amazon": 1
        };

        var provider_icons = {
            3: {
                dark: DDG.get_asset_path('go_watch_it', 'itunes.png'),
                light: DDG.get_asset_path('go_watch_it', 'itunes-alt.png')
            },
            5: {
                dark: DDG.get_asset_path('go_watch_it', 'vudu.png'),
                light: DDG.get_asset_path('go_watch_it', 'vudu.png')
            },
            7: {
                dark: DDG.get_asset_path('go_watch_it', 'amazon.png'),
                light: DDG.get_asset_path('go_watch_it', 'amazon-alt.png')
            },
            12: {
                dark: DDG.get_asset_path('go_watch_it', 'youtube.png'),
                light: DDG.get_asset_path('go_watch_it', 'youtube-alt.png')
            },
            18: {
                dark: DDG.get_asset_path('go_watch_it', 'googleplay.png'),
                light: DDG.get_asset_path('go_watch_it', 'googleplay-alt.png')
            },
            19: {
                dark: DDG.get_asset_path('go_watch_it', 'xboxvideo.png'),
                light: DDG.get_asset_path('go_watch_it', 'xboxvideo.png')
            },
            64: {
                dark: DDG.get_asset_path('go_watch_it', 'flixster.png'),
                light: DDG.get_asset_path('go_watch_it', 'flixster.png')
            },
            20: {
                dark: DDG.get_asset_path('go_watch_it', 'sony.png'),
                light: DDG.get_asset_path('go_watch_it', 'sony-alt.png')
            },
            76: {
                dark: DDG.get_asset_path('go_watch_it', 'disney.png'),
                light: DDG.get_asset_path('go_watch_it', 'disney-alt.png')
            },
            21: {
                dark: DDG.get_asset_path('go_watch_it', 'hulu.png'),
                light: DDG.get_asset_path('go_watch_it', 'hulu.png')
            }
        };

        var skip_providers = {
            // Movies on Demand
            10: 1,
            // Filmmaker Direct Streaming
            46: 1
        };

        Spice.add({
            id: "go_watch_it",
            name: "How to Watch",
            data: watchable.availabilities,
            meta: {
                sourceName: 'GoWatchIt',
                sourceUrl: getUrl(watchable),
                itemType: 'Providers for ' + title
            },
            normalize: function(item) {
                if(item.provider_format_id in skip_providers) {
                    return null;
                }

                // If the provider is in this hash, it means that they provide
                // streaming if they don't buy or sell stuff.
                if(item.provider_name in streaming_providers &&
                   item.buy_line === "" && item.rent_line === "") {
                    item.buy_line = "Available for Streaming";
                }

                if(item.provider_name in purchase_providers &&
                   item.buy_line === "" && item.rent_line === "") {
                    item.buy_line = "Available for Purchase";
                }

                // Change the format line to match the other tiles.
                if(item.format_line === "DVD & Blu-ray") {
                    item.format_line = "DVD / Blu-ray";
                }

                // Replace the icon if we can.
                if(item.provider_format_id in provider_icons) {
                    item.provider_format_logos = provider_icons[item.provider_format_id];
                }

                return {
                    url: item.watch_now_url
                }
            },

            templates: {
                item: 'base_item',
                options: {
                    content: Spice.go_watch_it.content
                }
            }
        });
    };

    Spice.registerHelper("buyOrRent", function(buy_line, rent_line, options) {
        if(buy_line && buy_line !== "") {
            this.line = buy_line;
            return options.fn(this);
        }

        this.line = rent_line;
        return options.fn(this);
    });

    // Check to see if both buy_line and rent_line are present.
    Spice.registerHelper("gwi_ifHasBothBuyAndRent", function(buy_line, rent_line, options) {
        if (buy_line && buy_line !== "" && rent_line && rent_line !== "") {
            return options.fn(this);
        } else {
            return options.inverse(this);
        }
    });

    // Check to see if the buy_line/rent_line includes a price.
    // This is because some provider formats (like Netflix) have
    // 'Included with Subscription' in their buy line.
    Spice.registerHelper("gwi_ifHasPrice", function(line, options) {
        if (line.split("$").length > 1) {
            return options.fn(this);
        } else {
            return options.inverse(this);
        }
    });

    // Grab dollar amount from 'Rent from $X.XX' string.
    Spice.registerHelper("gwi_price", function(line, options) {
        var strings = line.split("$")
        return "$" + strings[strings.length - 1]
    });

    // Get the class for the footer element based on whether or not both the buy_line
    // and rent_line are present.
    Spice.registerHelper("gwi_footerClass", function(buy_line, rent_line, options) {
        var klass = 'gwi-footer';
        if (buy_line && buy_line != '' && rent_line && rent_line != '') {
            klass += ' double';
        }

        return klass;
    });

}(this));<|endoftext|>


<commit_before>(function(env) {
    "use strict";

    env.ddg_spice_indeed_jobs = function(api_result) {
        if (api_result.error || !api_result.results.length) {
            return Spice.failed('indeed_jobs');
        }

        var q = api_result.query || '',
            loc = api_result.location || '',
            re = /^http:\/\/([a-z.]+)\//,
            rematch = api_result.results[0].url.match(re),
            www_domain = rematch ? rematch[0] : "http://www.indeed.com/";

        Spice.add({
            id: "indeed_jobs",
            name: "Jobs",
            data: api_result.results,
            meta: {
                sourceName: "Indeed",
                sourceUrl: www_domain + 'jobs?q=' + encodeURIComponent(q) + '&l=' + encodeURIComponent(loc)
            },
            normalize: function(item) {
                return {
                    url: item.url,
                    title: item.jobtitle,
                    subtitle: item.company,
                    description: DDG.strip_html(item.snippet)
                };
            },
            templates: {
                group: 'text',
                detail: false,
                item_detail: false,
                options: {
                    footer: Spice.indeed_jobs.footer
                },
                variants: {
                    tileTitle: '2line',
                    tileSnippet: 'large',
                    tileFooter: '2line'
                }
            }
        });
    };
}(this));
<commit_msg>IndeedJobs: Add relevancy check<commit_after>(function(env) {
    "use strict";

    env.ddg_spice_indeed_jobs = function(api_result) {
        if (api_result.error || !api_result.results.length) {
            return Spice.failed('indeed_jobs');
        }

        var q = api_result.query || '',
            loc = api_result.location || '',
            re = /^http:\/\/([a-z.]+)\//,
            rematch = api_result.results[0].url.match(re),
            www_domain = rematch ? rematch[0] : "http://www.indeed.com/";

        Spice.add({
            id: "indeed_jobs",
            name: "Jobs",
            data: api_result.results,
            meta: {
                sourceName: "Indeed",
                sourceUrl: www_domain + 'jobs?q=' + encodeURIComponent(q) + '&l=' + encodeURIComponent(loc)
            },
            normalize: function(item) {
                // ensure job is relevant to query
                if (q.length && !DDG.stringsRelevant(item.jobtitle, q)){
                    return null;
                }

                return {
                    url: item.url,
                    title: item.jobtitle,
                    subtitle: item.company,
                    description: DDG.strip_html(item.snippet)
                };
            },
            templates: {
                group: 'text',
                detail: false,
                item_detail: false,
                options: {
                    footer: Spice.indeed_jobs.footer
                },
                variants: {
                    tileTitle: '2line',
                    tileSnippet: 'large',
                    tileFooter: '2line'
                }
            }
        });
    };
}(this));
<|endoftext|>


<commit_before>nrj("soundmanager2/script/soundmanager2-nodebug-jsmin.js", 1);

(function(env) {
    "use strict"

    var SOUNDCLOUD_CLIENT_ID = 'df14a65559c0e555d9f9fd950c2d5b17',
        script = $('[src*="/js/spice/sound_cloud/"]')[0],
        source = $(script).attr("src"),
        query = source.match(/sound_cloud\/([^\/]*)/)[1],

        // Blacklist some adult results.
        skip_ids = {
            80320921: 1,
            75349402: 1
        };

    // global SM var:
    window.SM2_DEFER = true;

    /**
     * Tile Audio player
     *
     * I kept this separate from the spice and self contained so
     * we could potentially refactor it out at some point.
     *
     * If we create another spice with
     * playable audio tiles, we're probably going to want a
     * single controller to manage audio playback
     * across tabs/spices.
     */
    var player = {
        // queue up commands to run once
        // sound manager is ready:
        _onReady: [],

        // flipped to 1 on ready:
        _ready: 0,

        init: function() {
            var self = this;

            // don't init more than once:
            if (this._ready) { return; }

            // make sure the lib is loaded and in memory,
            // otherwise poll for it until it is:
            if (!window["SoundManager"]) {
                return setTimeout(this.init.bind(this),50);
            }

            window.soundManager = new SoundManager();
            soundManager.url = "/soundmanager2/swf";
            soundManager.flashVersion = 9;
            soundManager.useFlashBlock = false;
            soundManager.useHTML5Audio = true;
            //soundManager.onerror(function(){
            //    console.log("error loading soundmanager",arguments);
            //});
            soundManager.ontimeout(function(){
                console.log("timeout loading soundmanager",arguments);
            });
            soundManager.beginDelayedInit();
            soundManager.onready(function() {
                for (var i=0,evt; evt=self._onReady[i]; i++) {
                    self[evt[0]](evt[1]);
                }
                self._ready = 1;
                self._onReady = [];
            });
        },

        play: function(item) {
            if (!this._ready) {
                return this._onReady.push(['play',item]);
            }

            // if a different item is playing, stop it:
            if (this._curItem && this._curItem.id !== item.id) {
                this.stop();
            }

            if (item.sound) {
                item.sound.play();
            } else {
                item.$action = item.$html.find('.audio-controls__action');
                item.$time = item.$html.find('.audio-controls__time');
                item.$progress = item.$html.find('.audio-controls__progress');
                item.$loadProgress = item.$html.find('.audio-controls__progress-loading');
                item.$loadProgressFill = item.$html.find('.audio-controls__progress-loading .rotated-fill');
                item.$playProgress = item.$html.find('.audio-controls__progress-playback');
                item.$playProgressFill = item.$html.find('.audio-controls__progress-playback .rotated-fill');

                item.sound = soundManager.createSound({
                    id: item.id,
                    url: item.streamURL,
                    autoPlay: true,
                    whileloading: this._onLoadProgress.bind(this,item),
                    whileplaying: this._onPlayProgress.bind(this,item),
                    onload: this._onLoadFinished.bind(this,item),
                    onfinish: this._onPlayFinished.bind(this,item)
                });
            }

            item.$html.removeClass('is-paused');
            item.$action.text('║');

            // update the UI

            this._curItem = item;
        },

        pause: function(item) {
            if (!this._ready) {
                return this._onReady.push(['pause',item]);
            }

            if (this._curItem && item && this._curItem.id === item.id && item.sound) {
                item.sound.pause();
                item.$html.addClass('is-paused');
                item.$action.text('►');
            }

            // update the UI
        },

        stop: function() {
            if (!this._ready) {
                return this._onReady.push(['stop']);
            }

            soundManager.stopAll();

            if (this._curItem) {
                // update the UI
                this._curItem.$html.removeClass('is-paused');
                this._curItem.$action.text('►');

                // don't want to clear the 'Stream Unavailable text, only the -0:00 time:
                if (!this._curItem.$html.hasClass('is-unavailable')) {
                    this._curItem.$time.text('');
                }
                this._curItem.$loadProgress.removeClass('gt50');
                this._curItem.$playProgress.removeClass('gt50');
                this._updateProgress(this._curItem.$loadProgressFill,0);
                this._updateProgress(this._curItem.$playProgressFill,0);

                // free up resources:
                this._curItem.sound.destruct();
                delete this._curItem.sound
                delete this._curItem;
            }
        },

        _updateProgress: function(elem,pct) {
            var deg = (360 / 100 * pct) + 'deg';

            elem.css({
                '-moz-transform': 'rotate(' + deg + ')',
                '-webkit-transform': 'rotate(' + deg + ')',
                '-o-transform': 'rotate(' + deg + ')',
                'transform': 'rotate(' + deg + ')'
            });
        },

        _onLoadProgress: function(item) {
            if (!item || !item.sound || !item.$loadProgress) { return; }

            var pctLoaded = (item.sound.bytesLoaded / item.sound.bytesTotal) * 100;

            // hacking for now fill to 100 after 50%,
            // not sure how to handle
            // the circle with opacity
            if (pctLoaded > 50) {
                item.$loadProgress.addClass('gt50');
                return this._updateProgress(item.$loadProgressFill, 100);
            }

            this._updateProgress(item.$loadProgressFill, pctLoaded);
        },

        _onLoadFinished: function(item, success) {
            if (success || !item || !item.$time || !item.$html) { return; }

            // handle load error:
            item.$time.text('Stream Unavailable');
            item.$html.addClass('is-unavailable');

            this.stop();

            if (this._autoplaying) {
                Spice.selectNextItem('soundcloud');
            }
        },

        _onPlayProgress: function(item) {
            if (!item || !item.sound || !item.$time || !item.duration) { return; }

            var msLeft = item.duration - item.sound.position,
                pctPlayed = (item.sound.position / item.duration) * 100;

            // update text label:
            item.$time.text( '-' + DDG.formatDuration(msLeft));

            if (pctPlayed > 50) {
                item.$playProgress.addClass('gt50');
            }

            this._updateProgress(item.$playProgressFill, pctPlayed);
        },

        _onPlayFinished: function(item) {
            this.stop();
            this._autoplaying = 1;
            Spice.selectNextItem('soundcloud');
        }
    };

    env.ddg_spice_sound_cloud = function(api_result) {
        Spice.add({
            id: 'soundcloud',
            name: 'SoundCloud',
            data: api_result,
            meta: {
                sourceName: 'SoundCloud',
                sourceUrl: 'https://soundcloud.com/search?q=' + query,
                sourceIcon: true,
                itemType: 'Tracks'
            },
            templates: {
                item_custom: Spice.sound_cloud.item
            },
            normalize: function(o) {
                var image = o.artwork_url || o.user.avatar_url,
                    usingWaveformImage = 0;

                // Check if it's using the default avatar, if
                // so switch to waveform and set the flag
                if (/default_avatar_large/.test(image)) {
                    image = o.waveform_url;
                    usingWaveformImage = 1;
                } else {
                    // Get the larger image for our IA.
                    image = image.replace(/large\.jpg/, "t200x200.jpg");
                }

                // skip items that can't be streamed or explicit id's we
                // want to skip for adult content:
                if (!o.stream_url || skip_ids[o.id]) {
                    return;
                }

                var streamURL = '/audio?u=' + o.stream_url + '?client_id=' + SOUNDCLOUD_CLIENT_ID;

                return {
                    image: image,
                    usingWaveformImage: usingWaveformImage,
                    hearts: o.favoritings_count,
                    duration: o.duration,
                    link: o.permalink_url,
                    title: o.title,
                    streamURL: streamURL
                };
            },
            onItemSelected: function(item) {
                player.play(item);
            },
            onItemUnselected: function(item) {
                player.pause(item);
            },
            onShow: function() {
                player.init();
            },
            onHide: function() {
                // could kill the player here, not sure, kind
                // of nice that it still plays while you flip to other tabs?
            }
        });
    };
}(this));
<commit_msg>make sure there's always a number next to hearts<commit_after>nrj("soundmanager2/script/soundmanager2-nodebug-jsmin.js", 1);

(function(env) {
    "use strict"

    var SOUNDCLOUD_CLIENT_ID = 'df14a65559c0e555d9f9fd950c2d5b17',
        script = $('[src*="/js/spice/sound_cloud/"]')[0],
        source = $(script).attr("src"),
        query = source.match(/sound_cloud\/([^\/]*)/)[1],

        // Blacklist some adult results.
        skip_ids = {
            80320921: 1,
            75349402: 1
        };

    // global SM var:
    window.SM2_DEFER = true;

    /**
     * Tile Audio player
     *
     * I kept this separate from the spice and self contained so
     * we could potentially refactor it out at some point.
     *
     * If we create another spice with
     * playable audio tiles, we're probably going to want a
     * single controller to manage audio playback
     * across tabs/spices.
     */
    var player = {
        // queue up commands to run once
        // sound manager is ready:
        _onReady: [],

        // flipped to 1 on ready:
        _ready: 0,

        init: function() {
            var self = this;

            // don't init more than once:
            if (this._ready) { return; }

            // make sure the lib is loaded and in memory,
            // otherwise poll for it until it is:
            if (!window["SoundManager"]) {
                return setTimeout(this.init.bind(this),50);
            }

            window.soundManager = new SoundManager();
            soundManager.url = "/soundmanager2/swf";
            soundManager.flashVersion = 9;
            soundManager.useFlashBlock = false;
            soundManager.useHTML5Audio = true;
            //soundManager.onerror(function(){
            //    console.log("error loading soundmanager",arguments);
            //});
            soundManager.ontimeout(function(){
                console.log("timeout loading soundmanager",arguments);
            });
            soundManager.beginDelayedInit();
            soundManager.onready(function() {
                for (var i=0,evt; evt=self._onReady[i]; i++) {
                    self[evt[0]](evt[1]);
                }
                self._ready = 1;
                self._onReady = [];
            });
        },

        play: function(item) {
            if (!this._ready) {
                return this._onReady.push(['play',item]);
            }

            // if a different item is playing, stop it:
            if (this._curItem && this._curItem.id !== item.id) {
                this.stop();
            }

            if (item.sound) {
                item.sound.play();
            } else {
                item.$action = item.$html.find('.audio-controls__action');
                item.$time = item.$html.find('.audio-controls__time');
                item.$progress = item.$html.find('.audio-controls__progress');
                item.$loadProgress = item.$html.find('.audio-controls__progress-loading');
                item.$loadProgressFill = item.$html.find('.audio-controls__progress-loading .rotated-fill');
                item.$playProgress = item.$html.find('.audio-controls__progress-playback');
                item.$playProgressFill = item.$html.find('.audio-controls__progress-playback .rotated-fill');

                item.sound = soundManager.createSound({
                    id: item.id,
                    url: item.streamURL,
                    autoPlay: true,
                    whileloading: this._onLoadProgress.bind(this,item),
                    whileplaying: this._onPlayProgress.bind(this,item),
                    onload: this._onLoadFinished.bind(this,item),
                    onfinish: this._onPlayFinished.bind(this,item)
                });
            }

            item.$html.removeClass('is-paused');
            item.$action.text('║');

            // update the UI

            this._curItem = item;
        },

        pause: function(item) {
            if (!this._ready) {
                return this._onReady.push(['pause',item]);
            }

            if (this._curItem && item && this._curItem.id === item.id && item.sound) {
                item.sound.pause();
                item.$html.addClass('is-paused');
                item.$action.text('►');
            }

            // update the UI
        },

        stop: function() {
            if (!this._ready) {
                return this._onReady.push(['stop']);
            }

            soundManager.stopAll();

            if (this._curItem) {
                // update the UI
                this._curItem.$html.removeClass('is-paused');
                this._curItem.$action.text('►');

                // don't want to clear the 'Stream Unavailable text, only the -0:00 time:
                if (!this._curItem.$html.hasClass('is-unavailable')) {
                    this._curItem.$time.text('');
                }
                this._curItem.$loadProgress.removeClass('gt50');
                this._curItem.$playProgress.removeClass('gt50');
                this._updateProgress(this._curItem.$loadProgressFill,0);
                this._updateProgress(this._curItem.$playProgressFill,0);

                // free up resources:
                this._curItem.sound.destruct();
                delete this._curItem.sound
                delete this._curItem;
            }
        },

        _updateProgress: function(elem,pct) {
            var deg = (360 / 100 * pct) + 'deg';

            elem.css({
                '-moz-transform': 'rotate(' + deg + ')',
                '-webkit-transform': 'rotate(' + deg + ')',
                '-o-transform': 'rotate(' + deg + ')',
                'transform': 'rotate(' + deg + ')'
            });
        },

        _onLoadProgress: function(item) {
            if (!item || !item.sound || !item.$loadProgress) { return; }

            var pctLoaded = (item.sound.bytesLoaded / item.sound.bytesTotal) * 100;

            // hacking for now fill to 100 after 50%,
            // not sure how to handle
            // the circle with opacity
            if (pctLoaded > 50) {
                item.$loadProgress.addClass('gt50');
                return this._updateProgress(item.$loadProgressFill, 100);
            }

            this._updateProgress(item.$loadProgressFill, pctLoaded);
        },

        _onLoadFinished: function(item, success) {
            if (success || !item || !item.$time || !item.$html) { return; }

            // handle load error:
            item.$time.text('Stream Unavailable');
            item.$html.addClass('is-unavailable');

            this.stop();

            if (this._autoplaying) {
                Spice.selectNextItem('soundcloud');
            }
        },

        _onPlayProgress: function(item) {
            if (!item || !item.sound || !item.$time || !item.duration) { return; }

            var msLeft = item.duration - item.sound.position,
                pctPlayed = (item.sound.position / item.duration) * 100;

            // update text label:
            item.$time.text( '-' + DDG.formatDuration(msLeft));

            if (pctPlayed > 50) {
                item.$playProgress.addClass('gt50');
            }

            this._updateProgress(item.$playProgressFill, pctPlayed);
        },

        _onPlayFinished: function(item) {
            this.stop();
            this._autoplaying = 1;
            Spice.selectNextItem('soundcloud');
        }
    };

    env.ddg_spice_sound_cloud = function(api_result) {
        Spice.add({
            id: 'soundcloud',
            name: 'SoundCloud',
            data: api_result,
            meta: {
                sourceName: 'SoundCloud',
                sourceUrl: 'https://soundcloud.com/search?q=' + query,
                sourceIcon: true,
                itemType: 'Tracks'
            },
            templates: {
                item_custom: Spice.sound_cloud.item
            },
            normalize: function(o) {
                var image = o.artwork_url || o.user.avatar_url,
                    usingWaveformImage = 0;

                // Check if it's using the default avatar, if
                // so switch to waveform and set the flag
                if (/default_avatar_large/.test(image)) {
                    image = o.waveform_url;
                    usingWaveformImage = 1;
                } else {
                    // Get the larger image for our IA.
                    image = image.replace(/large\.jpg/, "t200x200.jpg");
                }

                // skip items that can't be streamed or explicit id's we
                // want to skip for adult content:
                if (!o.stream_url || skip_ids[o.id]) {
                    return;
                }

                var streamURL = '/audio?u=' + o.stream_url + '?client_id=' + SOUNDCLOUD_CLIENT_ID;

                return {
                    image: image,
                    usingWaveformImage: usingWaveformImage,
                    hearts: o.favoritings_count || 0,
                    duration: o.duration,
                    link: o.permalink_url,
                    title: o.title,
                    streamURL: streamURL
                };
            },
            onItemSelected: function(item) {
                player.play(item);
            },
            onItemUnselected: function(item) {
                player.pause(item);
            },
            onShow: function() {
                player.init();
            },
            onHide: function() {
                // could kill the player here, not sure, kind
                // of nice that it still plays while you flip to other tabs?
            }
        });
    };
}(this));
<|endoftext|>


<commit_before>var ddg_spice_sound_cloud = function(api_result) {
    if(api_result.length === 0) {
        return;
    }

    var image_proxy = "/iu/?u=";
    var query = "";

    // Get the original query.
    // We're going to pass this to the More at SoundCloud link.
    var matched, result;
    $("script").each(function() {
        matched = $(this).attr("src");
        if(matched) {
            result = matched.match(/\/js\/spice\/sound_cloud\/(.+)/);
            if(result) {
                query = result[1];
            }
        }
    });

    // Display the plugin.
    Spice.render({
        data             : api_result,
        header1          : "Sound Cloud",
        source_url       : "https://soundcloud.com/search?q=" + query,
        source_name      : "SoundCloud",
        image_url        : image_proxy + api_result[0].artwork_url,
        template_normal  : "sound_cloud",
        force_big_header : true
    });

    // Get the size of the screen.
    // If the screen is small, don't display the image.
    var zero_click_image = $("#zero_click_image");
    var checkWidth = function(width) {
        if(width < 750) {
            zero_click_image.addClass("hide");
        } else {
            zero_click_image.removeClass("hide");
        }
    };

    // Monitor the size of the window.
    checkWidth($(window).width());
    $(window).resize(function() {
        checkWidth($(window).width());
    });

    // Initialize SoundManager2.
    window.SM2_DEFER = true;
    var soundSetup = function() {
        window.soundManager = new SoundManager();
        soundManager.url = "/soundmanager2/swf/";
        soundManager.flashVersion = 9;
        soundManager.useFlashBlock = false;
        soundManager.useHTML5Audio = false;
        soundManager.beginDelayedInit();
        soundManager.onready(function() {
            // Prevent the user from clicking the link.
            // This copies the attribute "data-stream" to "href."
            $(".soundcloud-audio").each(function() {
                var stream = $(this).attr("data-stream");
                $(this).attr("href", stream);
            });
            var pagePlayer = new PagePlayer();
            pagePlayer.init(typeof PP_CONFIG !== 'undefined' ? PP_CONFIG : null);
        });
    };

    var ready = [false, false];
    var checkReady = function(toggle) {
        ready[toggle] = true;
        if(ready[0] && ready[1]) {
            soundSetup();
        }
    };

    // Load page-player.js. This JS file converts a list of MP3s into a playlist.
    $.getScript("/soundmanager2/script/page-player.js", function() {
        checkReady(0);
    });

    // Load SoundManager2. This JS file handles our audio.
    $.getScript("/soundmanager2/script/soundmanager2-nodebug-jsmin.js", function() {
        checkReady(1);
    });
};

Handlebars.registerHelper("chooseImage", function(first, second) {
    if(first) {
        return first;
    }
    return second;
})<commit_msg>SoundCloud: Shortened chooseImage function.<commit_after>var ddg_spice_sound_cloud = function(api_result) {
    if(api_result.length === 0) {
        return;
    }

    var image_proxy = "/iu/?u=";
    var query = "";

    // Get the original query.
    // We're going to pass this to the More at SoundCloud link.
    var matched, result;
    $("script").each(function() {
        matched = $(this).attr("src");
        if(matched) {
            result = matched.match(/\/js\/spice\/sound_cloud\/(.+)/);
            if(result) {
                query = result[1];
            }
        }
    });

    // Display the plugin.
    Spice.render({
        data             : api_result,
        header1          : "Sound Cloud",
        source_url       : "https://soundcloud.com/search?q=" + query,
        source_name      : "SoundCloud",
        image_url        : image_proxy + api_result[0].artwork_url,
        template_normal  : "sound_cloud",
        force_big_header : true
    });

    // Get the size of the screen.
    // If the screen is small, don't display the image.
    var zero_click_image = $("#zero_click_image");
    var checkWidth = function(width) {
        if(width < 750) {
            zero_click_image.addClass("hide");
        } else {
            zero_click_image.removeClass("hide");
        }
    };

    // Monitor the size of the window.
    checkWidth($(window).width());
    $(window).resize(function() {
        checkWidth($(window).width());
    });

    // Initialize SoundManager2.
    window.SM2_DEFER = true;
    var soundSetup = function() {
        window.soundManager = new SoundManager();
        soundManager.url = "/soundmanager2/swf/";
        soundManager.flashVersion = 9;
        soundManager.useFlashBlock = false;
        soundManager.useHTML5Audio = false;
        soundManager.beginDelayedInit();
        soundManager.onready(function() {
            // Prevent the user from clicking the link.
            // This copies the attribute "data-stream" to "href."
            $(".soundcloud-audio").each(function() {
                var stream = $(this).attr("data-stream");
                $(this).attr("href", stream);
            });
            var pagePlayer = new PagePlayer();
            pagePlayer.init(typeof PP_CONFIG !== 'undefined' ? PP_CONFIG : null);
        });
    };

    var ready = [false, false];
    var checkReady = function(toggle) {
        ready[toggle] = true;
        if(ready[0] && ready[1]) {
            soundSetup();
        }
    };

    // Load page-player.js. This JS file converts a list of MP3s into a playlist.
    $.getScript("/soundmanager2/script/page-player.js", function() {
        checkReady(0);
    });

    // Load SoundManager2. This JS file handles our audio.
    $.getScript("/soundmanager2/script/soundmanager2-nodebug-jsmin.js", function() {
        checkReady(1);
    });
};

Handlebars.registerHelper("chooseImage", function(first, second) {
    return first || second;
});<|endoftext|>


<commit_before>const Discord = require('discord.js');
const client = new Discord.Client();

var callAndResponse = [
	["I love you", "I know"],
	["ping", "pong"],
	["This is madness!", "Madness?  **THIS IS SPARTA!**"],
	["There isn't enough road", "Where we're going, we don't need roads."],
	["May the force be with you", "And also with you"],
	["Is this Tony Stank\\?", "Yes, this is the Tony Stank"],
	["where [ia][sr]e? .+\\?", "Somewhere, over the rainbow"],
	["what is .+", "42"],
	["Why did the chicken cross the road\?", "To get to the other side"],
	["LICENSE", "MIT License at https://github.com/Arc676/ChatBot"],
	["Watcha doin\\?", "Eatin' chocolate"],
	["Whered'ya get it\\?", "A dog dropped it!"],
	["This isn't your average, everyday (.+)", "This is **ADVANCED $**"],
	["How many (\\w+)", "ALL THE $"]
];

var name = "io"
var active = true;

client.on('ready', () => {
	console.log(`Logged in as ${client.user.username}`);
//	client.sendMessage("ChatBot has arrived!");
});

function getReply(msg){

	//Loop through array and get the appropriate response to the call
	for (var i = 0; i < callAndResponse.length; i++){
		var regexp = new RegExp(callAndResponse[i][0], 'i');
		if (regexp.test(msg)){
			response = callAndResponse[i][1];
			if (callAndResponse[i][0].indexOf("(") !== -1) {
				var match = regexp.exec(msg);
				response = response.replace("$", match[1]);
			}
			return response;
		}
	}

	return "";
}

client.on('message', message => {
	if (message.author.bot) return;
	if (!active) {
		if (message.content === 'Io come back') {
			message.reply("I'm back");
			active = true;
		}
		return;
	}
	if (message.content === name) {
		message.reply("Hi there!");
	} else if (message.content.startsWith(name + ", ")) {
		if (message.content.endsWith(' go away')) { //Makes chatbot leave
			message.reply('OK :(');
			active = false
		} else if (message.content.endsWith(' help')) {
			message.reply("Recognized commands:\n\thelp\n\tgo away\n\tcome back\n\tdie\n\t(various movie quotes)");
		} else if (message.content.endsWith(' die')) {
			client.destroy();
			process.exit(0);
		} else {
			message.reply("Yes?");
		}
	} else { //Runs commands and performs call and response
		var response = getReply(message.content);
		if (response !== "") {
			message.reply(response);
		}
	}
});

client.login('MzQ0NDMwNzk0NzYyMzU0Njg5.DGsnyA.J9vVgXAqO7YRz4rZGOyAmu9dTFE');
<commit_msg>Added response to thanks<commit_after>const Discord = require('discord.js');
const client = new Discord.Client();

var callAndResponse = [
	["I love you", "I know"],
	["ping", "pong"],
	["This is madness!", "Madness?  **THIS IS SPARTA!**"],
	["There isn't enough road", "Where we're going, we don't need roads."],
	["May the force be with you", "And also with you"],
	["Is this Tony Stank\\?", "Yes, this is the Tony Stank"],
	["where [ia][sr]e? .+\\?", "Somewhere, over the rainbow"],
	["what is .+", "42"],
	["Why did the chicken cross the road\?", "To get to the other side"],
	["LICENSE", "MIT License at https://github.com/Arc676/ChatBot"],
	["Watcha doin\\?", "Eatin' chocolate"],
	["Whered'ya get it\\?", "A dog dropped it!"],
	["This isn't your average, everyday (.+)", "This is **ADVANCED $**"],
	["How many (\\w+)", "ALL THE $"],
	["thank.+\\Wio\\W|thank.+\\Wio$", "You are very welcome"]
];

var name = "io"
var active = true;

client.on('ready', () => {
	console.log(`Logged in as ${client.user.username}`);
//	client.sendMessage("ChatBot has arrived!");
});

function getReply(msg){

	//Loop through array and get the appropriate response to the call
	for (var i = 0; i < callAndResponse.length; i++){
		var regexp = new RegExp(callAndResponse[i][0], 'i');
		if (regexp.test(msg)){
			response = callAndResponse[i][1];
			if (callAndResponse[i][0].indexOf("(") !== -1) {
				var match = regexp.exec(msg);
				response = response.replace("$", match[1]);
			}
			return response;
		}
	}

	return "";
}

client.on('message', message => {
	if (message.author.bot) return;
	if (!active) {
		if (message.content === 'Io come back') {
			message.reply("I'm back");
			active = true;
		}
		return;
	}
	if (message.content === name) {
		message.reply("Hi there!");
	} else if (message.content.startsWith(name + ", ")) {
		if (message.content.endsWith(' go away')) { //Makes chatbot leave
			message.reply('OK :(');
			active = false
		} else if (message.content.endsWith(' help')) {
			message.reply("Recognized commands:\n\thelp\n\tgo away\n\tcome back\n\tdie\n\t(various movie quotes)");
		} else if (message.content.endsWith(' die')) {
			client.destroy();
			process.exit(0);
		} else {
			message.reply("Yes?");
		}
	} else { //Runs commands and performs call and response
		var response = getReply(message.content);
		if (response !== "") {
			message.reply(response);
		}
	}
});

client.login('MzQ0NDMwNzk0NzYyMzU0Njg5.DGsnyA.J9vVgXAqO7YRz4rZGOyAmu9dTFE');
<|endoftext|>


<commit_before>angular.module('chooser', [
	'chooser.dropdown',
	'chooser.single',
	'chooser.multiple',
	'chooser.tags'
]).filter('chooserLabelFilter', function() {
	'use strict';

	return function(item, labelKey) {
		if (angular.isUndefined(labelKey)) {
			return item;
		}

		return angular.isObject(item) && item.hasOwnProperty(labelKey) ? item[labelKey] : item;
	};
});

angular.module('chooser.dropdown', [
	]).directive('chooserDropdown', function($document, $location, $filter, $rootScope) {
	'use strict';
	/* jshint maxstatements:20, maxcomplexity:10 */

	var closeMenu = angular.noop,
		keyboardListener = angular.noop,
		openChooser;

	return {
		restrict: 'E',
		require: ['?^chooser', '?^chooserMultiple', '?^chooserTags'],
		templateUrl: 'partials/chooser.dropdown.tpl.html',
		link: function(scope, element, attrs, ctrls) {
			var chooserCtrl = ctrls[0] || ctrls[1] || ctrls[2];
			if (!chooserCtrl) {
				throw new Error('No controller was found to use for the chooser dropdown.');
			}

			var filterItems = function() {
				var filteredItems = $filter('filter')(scope.items, scope.$search);

				if (angular.isArray(scope.model)) {
					filteredItems = _.difference(scope.items, scope.model);
				}
				scope.filteredItems = filteredItems;
			};

			scope.$watch('$search', filterItems);
			scope.$watch('model', filterItems, true);
			scope.$watch('items', filterItems, true);

			// Set model value when a new option is chosen
			scope.selectOption = function (event, option) {
				chooserCtrl.chooseOption(option);
				closeMenu(event);
			};

			scope.highlightedIndex = 0;

			var upArrow = function() {
				if (scope.highlightedIndex > 0) {
					scope.$apply(function() {
						scope.highlightedIndex--;
					});
				}
			};

			var downArrow = function() {
				if (scope.highlightedIndex < (scope.filteredItems.length - 1)) {
					scope.$apply(function() {
						scope.highlightedIndex++;
					});
				}
			};

			var highlightDefault = function() {
				var highlightedIndex = 0;
				if (scope.model && !angular.isArray(scope.model)) {
					for (var i = 0; i < scope.filteredItems.length; i++) {
						var comparison = scope.valueKey ? scope.filteredItems[i][scope.valueKey] : scope.filteredItems[i];
						if (angular.equals(scope.model, comparison)) {
							highlightedIndex = i;
							break;
						}
					}
				}
				scope.highlightedIndex = highlightedIndex;
			};

			scope.$on('$locationChangeStart', closeMenu);

			scope.openMenu = function(event) {
				var chooserElement = element.closest('.chooser'),
					wasOpen = chooserElement.hasClass('open'),
					input = element.find('input').eq(0);

				if (event.preventDefault) {
					event.preventDefault();
				}

				if (event.stopPropagation) {
					event.stopPropagation();
				}

				if (wasOpen) {
					closeMenu();
				} else if (!chooserElement.hasClass('disabled') && !chooserElement.attr('disabled')) {
					if (openChooser) {
						$rootScope.$broadcast('closeChooser_' + openChooser);
					}
					openChooser = scope.$id;
					chooserElement.addClass('open');
					input[0].focus();

					highlightDefault();
					scope.$search = '';

					closeMenu = function (event) {
						if (event && event.preventDefault) {
							event.preventDefault();
						}

						if (event && event.stopPropagation) {
							event.stopPropagation();
						}
						$document.off('click', closeMenu);
						$document.off('keydown', keyboardListener);
						chooserElement.removeClass('open');

						// Clean Up
						closeMenu = angular.noop;
						keyboardListener = angular.noop;
					};
					$document.on('click', closeMenu);

					keyboardListener = function(event) {
						switch(event.which) {
							case 38: // up
								upArrow();
								break;
							case 40: // down
								downArrow();
								break;
							case 13: //enter
								var selected = scope.filteredItems[scope.highlightedIndex];
								if (selected) {
									scope.$apply(function() {
										scope.selectOption(event, selected);
									});
								} else {
									closeMenu(event);
								}
								break;
							case 9: // tab
							case 27: // escape
								closeMenu(event);
								break;
						}
					};
					$document.on('keydown', keyboardListener);
				}

				scope.$on('closeChooser_' + scope.$id, function() {
					// Wrapping in anonymous function to suppress event argument
					closeMenu();
				});
			};
		}
	};
});

angular.module('chooser.single', [
	'chooser.dropdown'
]).directive('chooser', function($filter) {
	'use strict';

	return {
		restrict: 'E',
		replace: true,
		templateUrl: 'partials/chooser.single.tpl.html',
		scope: {
			labelKey: '@',
			valueKey: '@',
			items: '=options',
			model: '=ngModel',
			placeholder: '=placeholder'
		},
		controller: function($scope) {
			this.chooseOption = function(option) {
				$scope.selectedItem = option;
				if (!$scope.valueKey) {
					$scope.model = $scope.selectedItem;
				} else {
					$scope.model = $scope.selectedItem[$scope.valueKey];
				}
			};
		},
		link: function(scope, element) {

			scope.map = {};

			var updateText = function() {
				var modelLabel = $filter('chooserLabelFilter')(scope.selectedItem, scope.labelKey),
					labelExists = modelLabel && modelLabel.length,
					text = labelExists ? modelLabel : scope.placeholder || 'Select';

				element.find('.chosen-text').removeClass('invalid').toggleClass('placeholder', !labelExists).html(text);
			};

			scope.$watch('model', function(model) {
				scope.selectedItem = !scope.valueKey ? model : scope.map[model];
				updateText();
			});

			scope.$watch('placeholder', function(model) {
				updateText();
			});

			scope.$watch('items', function(items) {
				if (scope.valueKey && items) {
					for (var i = 0; i < items.length; i++) {
						scope.map[items[i][scope.valueKey]] = items[i];
					}
					scope.selectedItem = scope.map[scope.model];
				}
				updateText();
			});
		}
	};
});

angular.module('chooser.multiple', [
	'chooser.dropdown'
]).directive('chooserMultiple', function() {
	'use strict';

	return {
		restrict: 'E',
		templateUrl: 'partials/chooser.multiple.tpl.html',
		scope: {
			labelKey: '@',
			items: '=options',
			model: '=ngModel',
			placeholder: '@placeholder'
		},
		controller: function($scope) {
			this.chooseOption = function(option) {
				$scope.model = angular.isArray($scope.model) ?
						$scope.model.concat([option]) : [option];
			};
		},
		link: function(scope) {
			scope.removeOption = function(event, option) {
				if (event) {
					event.preventDefault();
					event.stopPropagation();
				}

				var index = scope.model.indexOf(option);
				if (index !== -1) {
					scope.model.splice(index, 1);
				}
			};
		}
	};
});

angular.module('chooser.tags', [

]).directive('chooserTags', function() {
	'use strict';

	return {
		restrict: 'E',
		require: 'chooserTags',
		templateUrl: 'partials/chooser.tags.tpl.html',
		scope: {
			items: '=options',
			model: '=ngModel',
			placeholder: '@placeholder'
		},
		controller: function($scope) {
			this.chooseOption = function(option) {
				if (angular.isArray($scope.model)) {
					// Don't allow duplicates
					if ($scope.model.indexOf(option) === -1) {
						$scope.model = $scope.model.concat([option]);
					}
				} else {
					$scope.model = [option];
				}
			};
		},
		link: function(scope, element, attrs, tagsCtrl) {
			var input = element.find('input').eq(0);

			var keyboardListener = function(event) {
				switch (event.which) {
					case 13: // enter
						scope.$apply(function() {
							if (input.val().length) {
								tagsCtrl.chooseOption(input.val());
							}
							scope.newTag = '';
						});
						break;
					case 27: // escape
						input.blur();
						break;
				}
			};

			var focusInput = function() {
				input.bind('keydown', keyboardListener);
				input.bind('blur', inputBlurred);
				input[0].focus();
			};
			scope.focusInput = focusInput;

			var inputBlurred = function() {
				scope.newTag = '';
				input.unbind('keydown', keyboardListener);
				input.unbind('blur', inputBlurred);
			};

			scope.removeOption = function(event, option) {
				if (event) {
					event.preventDefault();
					event.stopPropagation();
				}

				var index = scope.model.indexOf(option);
				if (index !== -1) {
					scope.model.splice(index, 1);
				}
			};
		}
	};
});<commit_msg>Simplified option highlighting by using scope.selectedItem<commit_after>angular.module('chooser', [
	'chooser.dropdown',
	'chooser.single',
	'chooser.multiple',
	'chooser.tags'
]).filter('chooserLabelFilter', function() {
	'use strict';

	return function(item, labelKey) {
		if (angular.isUndefined(labelKey)) {
			return item;
		}

		return angular.isObject(item) && item.hasOwnProperty(labelKey) ? item[labelKey] : item;
	};
});

angular.module('chooser.dropdown', [
	]).directive('chooserDropdown', function($document, $location, $filter, $rootScope) {
	'use strict';
	/* jshint maxstatements:20, maxcomplexity:10 */

	var closeMenu = angular.noop,
		keyboardListener = angular.noop,
		openChooser;

	return {
		restrict: 'E',
		require: ['?^chooser', '?^chooserMultiple', '?^chooserTags'],
		templateUrl: 'partials/chooser.dropdown.tpl.html',
		link: function(scope, element, attrs, ctrls) {
			var chooserCtrl = ctrls[0] || ctrls[1] || ctrls[2];
			if (!chooserCtrl) {
				throw new Error('No controller was found to use for the chooser dropdown.');
			}

			var filterItems = function() {
				var filteredItems = $filter('filter')(scope.items, scope.$search);

				if (angular.isArray(scope.model)) {
					filteredItems = _.difference(scope.items, scope.model);
				}
				scope.filteredItems = filteredItems;
			};

			scope.$watch('$search', filterItems);
			scope.$watch('model', filterItems, true);
			scope.$watch('items', filterItems, true);

			// Set model value when a new option is chosen
			scope.selectOption = function (event, option) {
				chooserCtrl.chooseOption(option);
				closeMenu(event);
			};

			scope.highlightedIndex = 0;

			var upArrow = function() {
				if (scope.highlightedIndex > 0) {
					scope.$apply(function() {
						scope.highlightedIndex--;
					});
				}
			};

			var downArrow = function() {
				if (scope.highlightedIndex < (scope.filteredItems.length - 1)) {
					scope.$apply(function() {
						scope.highlightedIndex++;
					});
				}
			};

			var highlightDefault = function() {
				var highlightedIndex = 0;
				if (scope.model && !angular.isArray(scope.model)) {
					for (var i = 0; i < scope.filteredItems.length; i++) {
						if (angular.equals(scope.selectedItem, scope.filteredItems[i])) {
							highlightedIndex = i;
							break;
						}
					}
				}
				scope.highlightedIndex = highlightedIndex;
			};

			scope.$on('$locationChangeStart', closeMenu);

			scope.openMenu = function(event) {
				var chooserElement = element.closest('.chooser'),
					wasOpen = chooserElement.hasClass('open'),
					input = element.find('input').eq(0);

				if (event.preventDefault) {
					event.preventDefault();
				}

				if (event.stopPropagation) {
					event.stopPropagation();
				}

				if (wasOpen) {
					closeMenu();
				} else if (!chooserElement.hasClass('disabled') && !chooserElement.attr('disabled')) {
					if (openChooser) {
						$rootScope.$broadcast('closeChooser_' + openChooser);
					}
					openChooser = scope.$id;
					chooserElement.addClass('open');
					input[0].focus();

					highlightDefault();
					scope.$search = '';

					closeMenu = function (event) {
						if (event && event.preventDefault) {
							event.preventDefault();
						}

						if (event && event.stopPropagation) {
							event.stopPropagation();
						}
						$document.off('click', closeMenu);
						$document.off('keydown', keyboardListener);
						chooserElement.removeClass('open');

						// Clean Up
						closeMenu = angular.noop;
						keyboardListener = angular.noop;
					};
					$document.on('click', closeMenu);

					keyboardListener = function(event) {
						switch(event.which) {
							case 38: // up
								upArrow();
								break;
							case 40: // down
								downArrow();
								break;
							case 13: //enter
								var selected = scope.filteredItems[scope.highlightedIndex];
								if (selected) {
									scope.$apply(function() {
										scope.selectOption(event, selected);
									});
								} else {
									closeMenu(event);
								}
								break;
							case 9: // tab
							case 27: // escape
								closeMenu(event);
								break;
						}
					};
					$document.on('keydown', keyboardListener);
				}

				scope.$on('closeChooser_' + scope.$id, function() {
					// Wrapping in anonymous function to suppress event argument
					closeMenu();
				});
			};
		}
	};
});

angular.module('chooser.single', [
	'chooser.dropdown'
]).directive('chooser', function($filter) {
	'use strict';

	return {
		restrict: 'E',
		replace: true,
		templateUrl: 'partials/chooser.single.tpl.html',
		scope: {
			labelKey: '@',
			valueKey: '@',
			items: '=options',
			model: '=ngModel',
			placeholder: '=placeholder'
		},
		controller: function($scope) {
			this.chooseOption = function(option) {
				$scope.selectedItem = option;
				if (!$scope.valueKey) {
					$scope.model = $scope.selectedItem;
				} else {
					$scope.model = $scope.selectedItem[$scope.valueKey];
				}
			};
		},
		link: function(scope, element) {

			scope.map = {};

			var updateText = function() {
				var modelLabel = $filter('chooserLabelFilter')(scope.selectedItem, scope.labelKey),
					labelExists = modelLabel && modelLabel.length,
					text = labelExists ? modelLabel : scope.placeholder || 'Select';

				element.find('.chosen-text').removeClass('invalid').toggleClass('placeholder', !labelExists).html(text);
			};

			scope.$watch('model', function(model) {
				scope.selectedItem = !scope.valueKey ? model : scope.map[model];
				updateText();
			});

			scope.$watch('placeholder', function(model) {
				updateText();
			});

			scope.$watch('items', function(items) {
				if (scope.valueKey && items) {
					for (var i = 0; i < items.length; i++) {
						scope.map[items[i][scope.valueKey]] = items[i];
					}
					scope.selectedItem = scope.map[scope.model];
				}
				updateText();
			});
		}
	};
});

angular.module('chooser.multiple', [
	'chooser.dropdown'
]).directive('chooserMultiple', function() {
	'use strict';

	return {
		restrict: 'E',
		templateUrl: 'partials/chooser.multiple.tpl.html',
		scope: {
			labelKey: '@',
			items: '=options',
			model: '=ngModel',
			placeholder: '@placeholder'
		},
		controller: function($scope) {
			this.chooseOption = function(option) {
				$scope.model = angular.isArray($scope.model) ?
						$scope.model.concat([option]) : [option];
			};
		},
		link: function(scope) {
			scope.removeOption = function(event, option) {
				if (event) {
					event.preventDefault();
					event.stopPropagation();
				}

				var index = scope.model.indexOf(option);
				if (index !== -1) {
					scope.model.splice(index, 1);
				}
			};
		}
	};
});

angular.module('chooser.tags', [

]).directive('chooserTags', function() {
	'use strict';

	return {
		restrict: 'E',
		require: 'chooserTags',
		templateUrl: 'partials/chooser.tags.tpl.html',
		scope: {
			items: '=options',
			model: '=ngModel',
			placeholder: '@placeholder'
		},
		controller: function($scope) {
			this.chooseOption = function(option) {
				if (angular.isArray($scope.model)) {
					// Don't allow duplicates
					if ($scope.model.indexOf(option) === -1) {
						$scope.model = $scope.model.concat([option]);
					}
				} else {
					$scope.model = [option];
				}
			};
		},
		link: function(scope, element, attrs, tagsCtrl) {
			var input = element.find('input').eq(0);

			var keyboardListener = function(event) {
				switch (event.which) {
					case 13: // enter
						scope.$apply(function() {
							if (input.val().length) {
								tagsCtrl.chooseOption(input.val());
							}
							scope.newTag = '';
						});
						break;
					case 27: // escape
						input.blur();
						break;
				}
			};

			var focusInput = function() {
				input.bind('keydown', keyboardListener);
				input.bind('blur', inputBlurred);
				input[0].focus();
			};
			scope.focusInput = focusInput;

			var inputBlurred = function() {
				scope.newTag = '';
				input.unbind('keydown', keyboardListener);
				input.unbind('blur', inputBlurred);
			};

			scope.removeOption = function(event, option) {
				if (event) {
					event.preventDefault();
					event.stopPropagation();
				}

				var index = scope.model.indexOf(option);
				if (index !== -1) {
					scope.model.splice(index, 1);
				}
			};
		}
	};
});<|endoftext|>


<commit_before>import ObjectPath from "./object-path";

const pPaths = Symbol("paths"),
    mapFindAllIn = function(path) {
        return path.findIn(this);
    };

export default class ObjectPathList {
    construct(...paths) {
        this[pPaths] = paths.map(ObjectPath.from.bind(ObjectPath));
    }

    findAllIn(object) {
        this[pPaths].map(function() {

        });
    }

    get length() {
        return this[pPaths].length;
    }

    set length(length) {
        this[pPaths].length = length;
    }
}

ObjectPathList.parse = function(strings) {
    return new this(...strings.map(ObjectPath.parse.bind(ObjectPath)));
};

ObjectPathList.from = function(iterable) {
    if(iterable instanceof this) {
        return iterable;
    }

    return new this(...iterable);
};
<commit_msg>Fix findAllIn<commit_after>import ObjectPath from "./object-path";

const pPaths = Symbol("paths"),
    mapFindAllIn = function(path) {
        return path.findIn(this);
    };

export default class ObjectPathList {
    construct(...paths) {
        this[pPaths] = paths.map(ObjectPath.from.bind(ObjectPath));
    }

    findAllIn(object) {
        this[pPaths].map(mapFindAllIn, object);
    }

    get length() {
        return this[pPaths].length;
    }

    set length(length) {
        this[pPaths].length = length;
    }
}

ObjectPathList.parse = function(strings) {
    return new this(...strings.map(ObjectPath.parse.bind(ObjectPath)));
};

ObjectPathList.from = function(iterable) {
    if(iterable instanceof this) {
        return iterable;
    }

    return new this(...iterable);
};
<|endoftext|>