<commit_before>'use strict';

var Validator = require('validator')
  , Utils = require('./utils')
  , sequelizeError = require('./errors')
  , Promise = require('./promise')
  , DataTypes = require('./data-types')
  , _ = require('lodash');

function noop() {}

// Deprecate this.
Validator.notNull = function() {
  throw new Error('Warning "notNull" validation has been deprecated in favor of Schema based "allowNull"');
};

// https://github.com/chriso/validator.js/blob/1.5.0/lib/validators.js

Validator.extend('notEmpty', function(str) {
  return !str.match(/^[\s\t\r\n]*$/);
});

Validator.extend('len', function(str, min, max) {
  return this.isLength(str, min, max);
});

Validator.extend('isUrl', function(str) {
  return this.isURL(str);
});

Validator.extend('isIPv6', function(str) {
  return this.isIP(str, 6);
});

Validator.extend('isIPv4', function(str) {
  return this.isIP(str, 4);
});

Validator.extend('notIn', function(str, values) {
  return !this.isIn(str, values);
});

Validator.extend('regex', function(str, pattern, modifiers) {
  str += '';
  if (Object.prototype.toString.call(pattern).slice(8, -1) !== 'RegExp') {
    pattern = new RegExp(pattern, modifiers);
  }
  return str.match(pattern);
});

Validator.extend('notRegex', function(str, pattern, modifiers) {
  return !this.regex(str, pattern, modifiers);
});

Validator.extend('isDecimal', function(str) {
  return str !== '' && str.match(/^(?:-?(?:[0-9]+))?(?:\.[0-9]*)?(?:[eE][\+\-]?(?:[0-9]+))?$/);
});

Validator.extend('min', function(str, val) {
  var number = parseFloat(str);
  return isNaN(number) || number >= val;
});

Validator.extend('max', function(str, val) {
  var number = parseFloat(str);
  return isNaN(number) || number <= val;
});

Validator.extend('not', function(str, pattern, modifiers) {
  return this.notRegex(str, pattern, modifiers);
});

Validator.extend('contains', function(str, elem) {
  return str.indexOf(elem) >= 0 && !!elem;
});

Validator.extend('notContains', function(str, elem) {
  return !this.contains(str, elem);
});

Validator.extend('is', function(str, pattern, modifiers) {
  return this.regex(str, pattern, modifiers);
});

function extendModelValidations(modelInstance) {
  Validator.extend('isImmutable', function(str, param, field) {
    return (modelInstance.isNewRecord || modelInstance.dataValues[field] === modelInstance._previousDataValues[field]);
  });
}

/**
 * The Main DAO Validator.
 *
 * @param {DAO} modelInstance The model instance.
 * @param {Object} options A dict with options.
 * @constructor
 */
var InstanceValidator = module.exports = function(modelInstance, options) {
  options = options || {};

  // assign defined and default options
  this.options = Utils._.defaults(options, {
    skip: []
  });

  this.modelInstance = modelInstance;

  /**
   * Exposes a reference to validator.js. This allows you to add custom validations using `Validator.extend`
   * @name Validator
   */
  this.Validator = Validator;

  /**
   *  All errors will be stored here from the validations.
   *
   * @type {Array} Will contain keys that correspond to attributes which will
   *   be Arrays of Errors.
   */
  this.errors = [];

  /** @type {boolean} Indicates if validations are in progress */
  this.inProgress = false;

  extendModelValidations(modelInstance);
};

/** @define {string} The error key for arguments as passed by custom validators */
InstanceValidator.RAW_KEY_NAME = '__raw';

/**
 * The main entry point for the Validation module, invoke to start the dance.
 *
 * @return {Promise}
 */
InstanceValidator.prototype.validate = function() {
  if (this.inProgress) {
    throw new Error('Validations already in progress.');
  }
  this.inProgress = true;

  var self = this;
  return Promise.settle([
    self._builtinValidators(),
    self._customValidators()
  ]).then(function() {
    if (self.errors.length) {
      return new sequelizeError.ValidationError('Validation error', self.errors);
    }

    return new Promise(function(resolve) {
      resolve();
    });
  });
};

/**
 * Invoke the Validation sequence:
 *   - Before Validation Model Hooks
 *   - Validation
 *   - After Validation Model Hooks
 *
 * @return {Promise}
 */
InstanceValidator.prototype.hookValidate = function() {
  var self = this;
  return self.modelInstance.Model.runHooks('beforeValidate', self.modelInstance, self.options).then(function() {
    return self.validate().then(function(error) {
      if (error) {
        throw error;
      }
    });
  }).then(function() {
    return self.modelInstance.Model.runHooks('afterValidate', self.modelInstance, self.options);
  }).return(self.modelInstance);
};

/**
 * Will run all the built-in validators.
 *
 * @return {Promise(Array.<Promise.PromiseInspection>)} A promise from .settle().
 * @private
 */
InstanceValidator.prototype._builtinValidators = function() {
  var self = this;

  // promisify all attribute invocations
  var validators = [];
  Utils._.forIn(this.modelInstance.rawAttributes, function(rawAttribute, field) {
    if (self.options.skip.indexOf(field) >= 0) {
      return;
    }

    var value = self.modelInstance.dataValues[field];

    if (!rawAttribute._autoGenerated && !rawAttribute.autoIncrement) {
      // perform validations based on schema
      self._validateSchema(rawAttribute, field, value);
    }

    if (self.modelInstance.validators.hasOwnProperty(field)) {
      validators.push(self._builtinAttrValidate.call(self, value, field));
    }
  });

  return Promise.settle(validators);
};

/**
 * Will run all the custom validators.
 *
 * @return {Promise(Array.<Promise.PromiseInspection>)} A promise from .settle().
 * @private
 */
InstanceValidator.prototype._customValidators = function() {

  var validators = [];
  var self = this;
  Utils._.each(this.modelInstance.__options.validate, function(validator,
    validatorType) {

    var valprom = self._invokeCustomValidator(validator, validatorType)
      // errors are handled in settling, stub this
      .catch(noop);

    validators.push(valprom);
  });

  return Promise.settle(validators);
};

/**
 * Validate a single attribute with all the defined built-in validators.
 *
 * @param {*} value Anything.
 * @param {string} field The field name.
 * @return {Promise} A promise, will always resolve,
 *   auto populates error on this.error local object.
 * @private
 */
InstanceValidator.prototype._builtinAttrValidate = function(value, field) {
  var self = this;
  // check if value is null (if null not allowed the Schema pass will capture it)
  if (value === null || typeof value === 'undefined') {
    return Promise.resolve();
  }

  // Promisify each validator
  var validators = [];
  Utils._.forIn(this.modelInstance.validators[field], function(test,
    validatorType) {
    if (['isUrl', 'isURL'].indexOf(validatorType) !== -1 && test === true) {
      // Preserve backwards compat. Validator.js now expects the second param to isURL to be an object
      test = {};
    }

    // Check for custom validator.
    if (typeof test === 'function') {
      return validators.push(self._invokeCustomValidator(test, validatorType, true, value, field));
    }

    var validatorPromise = self._invokeBuiltinValidator(value, test, validatorType, field);
    // errors are handled in settling, stub this
    validatorPromise.catch(noop);
    validators.push(validatorPromise);
  });

  return Promise.settle(validators)
    .then(this._handleSettledResult.bind(this, field));
};

/**
 * Prepare and invoke a custom validator.
 *
 * @param {Function} validator The custom validator.
 * @param {string} validatorType the custom validator type (name).
 * @param {boolean=} optAttrDefined Set to true if custom validator was defined
 *   from the Attribute
 * @return {Promise} A promise.
 * @private
 */
InstanceValidator.prototype._invokeCustomValidator = Promise.method(function(validator, validatorType, optAttrDefined, optValue, optField) {
  var validatorFunction = null;  // the validation function to call
  var isAsync = false;

  var validatorArity = validator.length;
  // check if validator is async and requires a callback
  var asyncArity = 1;
  var errorKey = validatorType;
  var invokeArgs;
  if (optAttrDefined) {
    asyncArity = 2;
    invokeArgs = optValue;
    errorKey = optField;
  }
  if (validatorArity === asyncArity) {
    isAsync = true;
  }

  if (isAsync) {
    if (optAttrDefined) {
      validatorFunction = Promise.promisify(validator.bind(this.modelInstance, invokeArgs));
    } else {
      validatorFunction = Promise.promisify(validator.bind(this.modelInstance));
    }
    return validatorFunction()
      .catch(this._pushError.bind(this, false, errorKey));
  } else {
    return Promise.try(validator.bind(this.modelInstance, invokeArgs))
      .catch(this._pushError.bind(this, false, errorKey));
  }
});

/**
 * Prepare and invoke a build-in validator.
 *
 * @param {*} value Anything.
 * @param {*} test The test case.
 * @param {string} validatorType One of known to Sequelize validators.
 * @param {string} field The field that is being validated
 * @return {Object} An object with specific keys to invoke the validator.
 * @private
 */
InstanceValidator.prototype._invokeBuiltinValidator = Promise.method(function(value, test, validatorType, field) {

  // check if Validator knows that kind of validation test
  if (typeof Validator[validatorType] !== 'function') {
    throw new Error('Invalid validator function: ' + validatorType);
  }

  // extract extra arguments for the validator
  var validatorArgs = test.hasOwnProperty('args') || test.hasOwnProperty('msg') ? test.args : test;

  // extract the error msg
  var errorMessage = test.hasOwnProperty('msg') && test.msg ? test.msg :
    'Validation ' + validatorType + ' failed';

  if (!Array.isArray(validatorArgs)) {
    validatorArgs = [validatorArgs];
  } else {
    validatorArgs = validatorArgs.slice(0);
  }
  validatorArgs.push(field);
  if (!Validator[validatorType].apply(Validator, [value].concat(validatorArgs))) {
    throw errorMessage;
  }
});

/**
 * Will validate a single field against its schema definition (isnull).
 *
 * @param {Object} rawAttribute As defined in the Schema.
 * @param {string} field The field name.
 * @param {*} value anything.
 * @private
 */
InstanceValidator.prototype._validateSchema = function(rawAttribute, field, value) {
  var error;

  if (rawAttribute.allowNull === false && ((value === null) || (value === undefined))) {
    error = new sequelizeError.ValidationErrorItem(field + ' cannot be null', 'notNull Violation', field, value);
    this.errors.push(error);
  }

  if (rawAttribute.type === DataTypes.STRING || rawAttribute.type instanceof DataTypes.STRING || rawAttribute.type === DataTypes.TEXT) {
    if (Array.isArray(value) || (_.isObject(value) && !value._isSequelizeMethod)) {
      error = new sequelizeError.ValidationErrorItem(field + ' cannot be an array or an object', 'string violation', field, value);
      this.errors.push(error);
    }
  }
};

/**
 * Handles the returned result of a Promise.settle.
 *
 * If errors are found it populates this.error.
 *
 * @param {string} field The attribute name.
 * @param {Array.<Promise.PromiseInspection>} Promise inspection objects.
 * @private
 */
InstanceValidator.prototype._handleSettledResult = function(field, promiseInspections) {
  var self = this;
  promiseInspections.forEach(function(promiseInspection) {
    if (promiseInspection.isRejected()) {
      var rejection = promiseInspection.error();
      self._pushError(true, field, rejection);
    }
  });
};

/**
 * Signs all errors retaining the original.
 *
 * @param {boolean} isBuiltin Determines if error is from builtin validator.
 * @param {string} errorKey The error key to assign on this.errors object.
 * @param {Error|string} rawError The original error.
 * @private
 */
InstanceValidator.prototype._pushError = function(isBuiltin, errorKey, rawError) {
  var message = rawError.message || rawError || 'Validation error';
  var error = new sequelizeError.ValidationErrorItem(message, 'Validation error', errorKey, rawError);
  error[InstanceValidator.RAW_KEY_NAME] = rawError;

  this.errors.push(error);
};
<commit_msg>fix(validation): binary data is ok for strings<commit_after>'use strict';

var Validator = require('validator')
  , Utils = require('./utils')
  , sequelizeError = require('./errors')
  , Promise = require('./promise')
  , DataTypes = require('./data-types')
  , _ = require('lodash');

function noop() {}

// Deprecate this.
Validator.notNull = function() {
  throw new Error('Warning "notNull" validation has been deprecated in favor of Schema based "allowNull"');
};

// https://github.com/chriso/validator.js/blob/1.5.0/lib/validators.js

Validator.extend('notEmpty', function(str) {
  return !str.match(/^[\s\t\r\n]*$/);
});

Validator.extend('len', function(str, min, max) {
  return this.isLength(str, min, max);
});

Validator.extend('isUrl', function(str) {
  return this.isURL(str);
});

Validator.extend('isIPv6', function(str) {
  return this.isIP(str, 6);
});

Validator.extend('isIPv4', function(str) {
  return this.isIP(str, 4);
});

Validator.extend('notIn', function(str, values) {
  return !this.isIn(str, values);
});

Validator.extend('regex', function(str, pattern, modifiers) {
  str += '';
  if (Object.prototype.toString.call(pattern).slice(8, -1) !== 'RegExp') {
    pattern = new RegExp(pattern, modifiers);
  }
  return str.match(pattern);
});

Validator.extend('notRegex', function(str, pattern, modifiers) {
  return !this.regex(str, pattern, modifiers);
});

Validator.extend('isDecimal', function(str) {
  return str !== '' && str.match(/^(?:-?(?:[0-9]+))?(?:\.[0-9]*)?(?:[eE][\+\-]?(?:[0-9]+))?$/);
});

Validator.extend('min', function(str, val) {
  var number = parseFloat(str);
  return isNaN(number) || number >= val;
});

Validator.extend('max', function(str, val) {
  var number = parseFloat(str);
  return isNaN(number) || number <= val;
});

Validator.extend('not', function(str, pattern, modifiers) {
  return this.notRegex(str, pattern, modifiers);
});

Validator.extend('contains', function(str, elem) {
  return str.indexOf(elem) >= 0 && !!elem;
});

Validator.extend('notContains', function(str, elem) {
  return !this.contains(str, elem);
});

Validator.extend('is', function(str, pattern, modifiers) {
  return this.regex(str, pattern, modifiers);
});

function extendModelValidations(modelInstance) {
  Validator.extend('isImmutable', function(str, param, field) {
    return (modelInstance.isNewRecord || modelInstance.dataValues[field] === modelInstance._previousDataValues[field]);
  });
}

/**
 * The Main DAO Validator.
 *
 * @param {DAO} modelInstance The model instance.
 * @param {Object} options A dict with options.
 * @constructor
 */
var InstanceValidator = module.exports = function(modelInstance, options) {
  options = options || {};

  // assign defined and default options
  this.options = Utils._.defaults(options, {
    skip: []
  });

  this.modelInstance = modelInstance;

  /**
   * Exposes a reference to validator.js. This allows you to add custom validations using `Validator.extend`
   * @name Validator
   */
  this.Validator = Validator;

  /**
   *  All errors will be stored here from the validations.
   *
   * @type {Array} Will contain keys that correspond to attributes which will
   *   be Arrays of Errors.
   */
  this.errors = [];

  /** @type {boolean} Indicates if validations are in progress */
  this.inProgress = false;

  extendModelValidations(modelInstance);
};

/** @define {string} The error key for arguments as passed by custom validators */
InstanceValidator.RAW_KEY_NAME = '__raw';

/**
 * The main entry point for the Validation module, invoke to start the dance.
 *
 * @return {Promise}
 */
InstanceValidator.prototype.validate = function() {
  if (this.inProgress) {
    throw new Error('Validations already in progress.');
  }
  this.inProgress = true;

  var self = this;
  return Promise.settle([
    self._builtinValidators(),
    self._customValidators()
  ]).then(function() {
    if (self.errors.length) {
      return new sequelizeError.ValidationError('Validation error', self.errors);
    }

    return new Promise(function(resolve) {
      resolve();
    });
  });
};

/**
 * Invoke the Validation sequence:
 *   - Before Validation Model Hooks
 *   - Validation
 *   - After Validation Model Hooks
 *
 * @return {Promise}
 */
InstanceValidator.prototype.hookValidate = function() {
  var self = this;
  return self.modelInstance.Model.runHooks('beforeValidate', self.modelInstance, self.options).then(function() {
    return self.validate().then(function(error) {
      if (error) {
        throw error;
      }
    });
  }).then(function() {
    return self.modelInstance.Model.runHooks('afterValidate', self.modelInstance, self.options);
  }).return(self.modelInstance);
};

/**
 * Will run all the built-in validators.
 *
 * @return {Promise(Array.<Promise.PromiseInspection>)} A promise from .settle().
 * @private
 */
InstanceValidator.prototype._builtinValidators = function() {
  var self = this;

  // promisify all attribute invocations
  var validators = [];
  Utils._.forIn(this.modelInstance.rawAttributes, function(rawAttribute, field) {
    if (self.options.skip.indexOf(field) >= 0) {
      return;
    }

    var value = self.modelInstance.dataValues[field];

    if (!rawAttribute._autoGenerated && !rawAttribute.autoIncrement) {
      // perform validations based on schema
      self._validateSchema(rawAttribute, field, value);
    }

    if (self.modelInstance.validators.hasOwnProperty(field)) {
      validators.push(self._builtinAttrValidate.call(self, value, field));
    }
  });

  return Promise.settle(validators);
};

/**
 * Will run all the custom validators.
 *
 * @return {Promise(Array.<Promise.PromiseInspection>)} A promise from .settle().
 * @private
 */
InstanceValidator.prototype._customValidators = function() {

  var validators = [];
  var self = this;
  Utils._.each(this.modelInstance.__options.validate, function(validator,
    validatorType) {

    var valprom = self._invokeCustomValidator(validator, validatorType)
      // errors are handled in settling, stub this
      .catch(noop);

    validators.push(valprom);
  });

  return Promise.settle(validators);
};

/**
 * Validate a single attribute with all the defined built-in validators.
 *
 * @param {*} value Anything.
 * @param {string} field The field name.
 * @return {Promise} A promise, will always resolve,
 *   auto populates error on this.error local object.
 * @private
 */
InstanceValidator.prototype._builtinAttrValidate = function(value, field) {
  var self = this;
  // check if value is null (if null not allowed the Schema pass will capture it)
  if (value === null || typeof value === 'undefined') {
    return Promise.resolve();
  }

  // Promisify each validator
  var validators = [];
  Utils._.forIn(this.modelInstance.validators[field], function(test,
    validatorType) {
    if (['isUrl', 'isURL'].indexOf(validatorType) !== -1 && test === true) {
      // Preserve backwards compat. Validator.js now expects the second param to isURL to be an object
      test = {};
    }

    // Check for custom validator.
    if (typeof test === 'function') {
      return validators.push(self._invokeCustomValidator(test, validatorType, true, value, field));
    }

    var validatorPromise = self._invokeBuiltinValidator(value, test, validatorType, field);
    // errors are handled in settling, stub this
    validatorPromise.catch(noop);
    validators.push(validatorPromise);
  });

  return Promise.settle(validators)
    .then(this._handleSettledResult.bind(this, field));
};

/**
 * Prepare and invoke a custom validator.
 *
 * @param {Function} validator The custom validator.
 * @param {string} validatorType the custom validator type (name).
 * @param {boolean=} optAttrDefined Set to true if custom validator was defined
 *   from the Attribute
 * @return {Promise} A promise.
 * @private
 */
InstanceValidator.prototype._invokeCustomValidator = Promise.method(function(validator, validatorType, optAttrDefined, optValue, optField) {
  var validatorFunction = null;  // the validation function to call
  var isAsync = false;

  var validatorArity = validator.length;
  // check if validator is async and requires a callback
  var asyncArity = 1;
  var errorKey = validatorType;
  var invokeArgs;
  if (optAttrDefined) {
    asyncArity = 2;
    invokeArgs = optValue;
    errorKey = optField;
  }
  if (validatorArity === asyncArity) {
    isAsync = true;
  }

  if (isAsync) {
    if (optAttrDefined) {
      validatorFunction = Promise.promisify(validator.bind(this.modelInstance, invokeArgs));
    } else {
      validatorFunction = Promise.promisify(validator.bind(this.modelInstance));
    }
    return validatorFunction()
      .catch(this._pushError.bind(this, false, errorKey));
  } else {
    return Promise.try(validator.bind(this.modelInstance, invokeArgs))
      .catch(this._pushError.bind(this, false, errorKey));
  }
});

/**
 * Prepare and invoke a build-in validator.
 *
 * @param {*} value Anything.
 * @param {*} test The test case.
 * @param {string} validatorType One of known to Sequelize validators.
 * @param {string} field The field that is being validated
 * @return {Object} An object with specific keys to invoke the validator.
 * @private
 */
InstanceValidator.prototype._invokeBuiltinValidator = Promise.method(function(value, test, validatorType, field) {

  // check if Validator knows that kind of validation test
  if (typeof Validator[validatorType] !== 'function') {
    throw new Error('Invalid validator function: ' + validatorType);
  }

  // extract extra arguments for the validator
  var validatorArgs = test.hasOwnProperty('args') || test.hasOwnProperty('msg') ? test.args : test;

  // extract the error msg
  var errorMessage = test.hasOwnProperty('msg') && test.msg ? test.msg :
    'Validation ' + validatorType + ' failed';

  if (!Array.isArray(validatorArgs)) {
    validatorArgs = [validatorArgs];
  } else {
    validatorArgs = validatorArgs.slice(0);
  }
  validatorArgs.push(field);
  if (!Validator[validatorType].apply(Validator, [value].concat(validatorArgs))) {
    throw errorMessage;
  }
});

/**
 * Will validate a single field against its schema definition (isnull).
 *
 * @param {Object} rawAttribute As defined in the Schema.
 * @param {string} field The field name.
 * @param {*} value anything.
 * @private
 */
InstanceValidator.prototype._validateSchema = function(rawAttribute, field, value) {
  var error;

  if (rawAttribute.allowNull === false && ((value === null) || (value === undefined))) {
    error = new sequelizeError.ValidationErrorItem(field + ' cannot be null', 'notNull Violation', field, value);
    this.errors.push(error);
  }

  if (rawAttribute.type === DataTypes.STRING || rawAttribute.type instanceof DataTypes.STRING || rawAttribute.type === DataTypes.TEXT) {
    if (Array.isArray(value) || (_.isObject(value) && !value._isSequelizeMethod) && !Buffer.isBuffer(value)) {
      error = new sequelizeError.ValidationErrorItem(field + ' cannot be an array or an object', 'string violation', field, value);
      this.errors.push(error);
    }
  }
};

/**
 * Handles the returned result of a Promise.settle.
 *
 * If errors are found it populates this.error.
 *
 * @param {string} field The attribute name.
 * @param {Array.<Promise.PromiseInspection>} Promise inspection objects.
 * @private
 */
InstanceValidator.prototype._handleSettledResult = function(field, promiseInspections) {
  var self = this;
  promiseInspections.forEach(function(promiseInspection) {
    if (promiseInspection.isRejected()) {
      var rejection = promiseInspection.error();
      self._pushError(true, field, rejection);
    }
  });
};

/**
 * Signs all errors retaining the original.
 *
 * @param {boolean} isBuiltin Determines if error is from builtin validator.
 * @param {string} errorKey The error key to assign on this.errors object.
 * @param {Error|string} rawError The original error.
 * @private
 */
InstanceValidator.prototype._pushError = function(isBuiltin, errorKey, rawError) {
  var message = rawError.message || rawError || 'Validation error';
  var error = new sequelizeError.ValidationErrorItem(message, 'Validation error', errorKey, rawError);
  error[InstanceValidator.RAW_KEY_NAME] = rawError;

  this.errors.push(error);
};
<|endoftext|>


<commit_before>
import {element, SVGelement} from './dom'
import {batchAnimationFrame} from './renderer'
import {Observable} from './observable'
import {inherits} from './utils'

export function View (options) {
  var isView = this instanceof View
  if (!isView) {
    return new View(options)
  }
  var svg = options && options.svg || false

  View.super.call(this) // init Observable

  this.$el = svg ? SVGelement(options.el || 'svg') : element(options.el || 'div')
  this.attrs = {}
  this.class = {}
  this.data = {}
  this.style = {}

  options && this.setOptions(options, true)
  this.trigger('init', this)
  options.data && this.set(options.data)
  this.trigger('inited', this)
}

inherits(View, Observable)

View.extend = function (superOptions) {
  return function ExtendedView (options) {
    if (!options) {
      return new View(superOptions)
    }
    var currentOptions = {}

    for (var key in superOptions) {
      currentOptions[key] = superOptions[key]
    }
    for (key in options) {
      currentOptions[key] = options[key]
    }
    return new View(currentOptions)
  }
}

View.prototype.mount = function (target) {
  var self = this

  if (target instanceof View) {
    self.parent = target
    self.$root = target.$el
  } else {
    self.$root = target
  }

  batchAnimationFrame(function () {
    if (self.parent && !self.parent.$root) {
      self.parent.on('mount', self.onMount)
      return
    }
    self.trigger('mount')
    self.$root.appendChild(self.$el)
    self.trigger('mounted')
  })
}

View.prototype.onMount = function () {
  this.trigger('mount')
  this.$root.appendChild(this.$el)
  this.trigger('mounted')
}

View.prototype.unmount = function () {
  var self = this
  var $el = self.$el

  if (!self.$root) {
    return
  }

  batchAnimationFrame(function () {
    self.trigger('unmount')
    self.$root.removeChild($el)
    self.$root = null
    self.parent = null
    self.trigger('unmounted')
  })
}

View.prototype.destroy = function () {
  var self = this

  self.trigger('destroy')
  self.off()
  self.unmount()
  self.trigger('destroyed')
}

View.prototype.mountBefore = function (target, before) {
  var $el = this.$el

  this.$root = target

  batchAnimationFrame(function () {
    target.insertBefore($el, before)
  })
}

View.prototype.set = function (data) {
  var self = this
  batchAnimationFrame(function () {
    self.trigger('render')
  })
  self.trigger('update', data)
  for (var key in data) {
    self.data[key] = data[key]
  }
  self.trigger('updated')
  batchAnimationFrame(function () {
    self.trigger('rendered')
  })
}

View.prototype.setOptions = function (options, skipData) {
  if (!options) {
    return
  }
  var key

  for (key in options) {
    if (key === 'attrs') {
      this.setAttributes(options.attrs)
    } else if (key === 'attr') {
      this.setAttributes(options.attr)
    } else if (key === 'href') {
      this.setAttributes({
        href: options.href
      })
    } else if (key === 'id') {
      this.setAttributes({
        id: options.id
      })
    } else if (key === 'data') {
      if (!skipData) {
        this.set(options.data)
      }
    } else if (key === 'style') {
      if (typeof options.style === 'string') {
        this.setAttributes({
          style: options.style
        })
        continue
      }
      this.setStyle(options.style)
    } else if (key === 'class') {
      if (typeof options.class === 'string') {
        this.setAttributes({
          class: options.class
        })
        continue
      }
      this.setClass(options.class)
    } else if (key === 'textContent') {
      this.textContent(options.textContent)
    } else if (key === 'listen') {
      this.addListeners(options.listen)
    } else if (key === 'init') {
      this.on('init', options.init)
    } else if (key === 'update') {
      this.on('update', options.update)
    } else if (key === 'parent') {
      this.mount(options.parent)
    } else if (key === '$root') {
      this.mount(options.$root)
    } else {
      this[key] = options[key]
    }
  }
}

View.prototype.textContent = function (text) {
  var self = this
  var $el = self.$el

  if (text !== self.text) {
    self.text = text

    batchAnimationFrame(function () {
      if (text === self.text) {
        $el.textContent = text
      }
    })
  }
}

View.prototype.addListeners = function (listeners) {
  var self = this
  var $el = self.$el
  var key, value

  for (key in listeners) {
    value = listeners[key]
    addListener(key, value)
  }
  function addListener (key, value) {
    self.on(key, value)
    $el.addEventListener(key, function (e) {
      self.trigger(key, e)
    })
  }
}

View.prototype.setClass = function (classes) {
  var self = this
  var $el = self.$el
  var key, value

  for (key in classes) {
    value = classes[key]
    if (self.class[key] !== value) {
      self.class[key] = value
      setClass(key, value)
    }
  }

  function setClass (key, value) {
    batchAnimationFrame(function () {
      if (self.class[key] === value) {
        if (value) {
          $el.classList.add(key)
        } else {
          $el.classList.remove(key)
        }
      }
    })
  }
}

View.prototype.setStyle = function (style) {
  var self = this
  var $el = self.$el
  var key, value

  for (key in style) {
    value = style[key]
    if (self.style[key] !== value) {
      self.style[key] = value
      setStyle(key, value)
    }
  }

  function setStyle (key, value) {
    batchAnimationFrame(function () {
      if (self.style[key] === style[key]) {
        $el.style[key] = value
      }
    })
  }
}

View.prototype.setAttribute = function (attr, value) {
  var data = {}
  data[attr] = value
  this.setAttributes(data)
}

View.prototype.setAttributes = function (attrs) {
  var self = this
  var $el = self.$el
  var currentAttrs = self.attrs
  var value, attr

  for (attr in attrs) {
    value = attrs[attr]
    if (value !== currentAttrs[attr]) {
      self.attrs[attr] = value

      if (value === self.attrs[attr]) {
        setAttribute(attr, value)
      }
    }
  }

  function setAttribute (attr, value) {
    batchAnimationFrame(function () {
      if (value === self.attrs[attr]) {
        if (value === false || value == null) {
          $el.removeAttribute(attr)
          return
        }
        $el.setAttribute(attr, value)

        if (attr === 'autofocus') {
          $el.focus()
        }
      }
    })
  }
}
<commit_msg>better autofocus support<commit_after>
import {element, SVGelement} from './dom'
import {batchAnimationFrame} from './renderer'
import {Observable} from './observable'
import {inherits} from './utils'

export function View (options) {
  var isView = this instanceof View
  if (!isView) {
    return new View(options)
  }
  var svg = options && options.svg || false

  View.super.call(this) // init Observable

  this.$el = svg ? SVGelement(options.el || 'svg') : element(options.el || 'div')
  this.attrs = {}
  this.class = {}
  this.data = {}
  this.style = {}

  options && this.setOptions(options, true)
  this.trigger('init', this)
  options.data && this.set(options.data)
  this.trigger('inited', this)
}

inherits(View, Observable)

View.extend = function (superOptions) {
  return function ExtendedView (options) {
    if (!options) {
      return new View(superOptions)
    }
    var currentOptions = {}

    for (var key in superOptions) {
      currentOptions[key] = superOptions[key]
    }
    for (key in options) {
      currentOptions[key] = options[key]
    }
    return new View(currentOptions)
  }
}

View.prototype.mount = function (target) {
  var self = this

  if (target instanceof View) {
    self.parent = target
    self.$root = target.$el
  } else {
    self.$root = target
  }

  batchAnimationFrame(function () {
    if (self.parent && !self.parent.$root) {
      self.parent.on('mount', onMount)
      return
    }
    self.trigger('mount')
    self.$root.appendChild(self.$el)
    self.trigger('mounted')
  })
}

function onMount () {
  this.trigger('mount')
  this.$root.appendChild(this.$el)
  this.trigger('mounted')
}

View.prototype.unmount = function () {
  var self = this
  var $el = self.$el

  if (!self.$root) {
    return
  }

  batchAnimationFrame(function () {
    self.trigger('unmount')
    self.$root.removeChild($el)
    self.$root = null
    self.parent = null
    self.trigger('unmounted')
  })
}

View.prototype.destroy = function () {
  var self = this

  self.trigger('destroy')
  self.off()
  self.unmount()
  self.trigger('destroyed')
}

View.prototype.mountBefore = function (target, before) {
  var $el = this.$el

  this.$root = target

  batchAnimationFrame(function () {
    target.insertBefore($el, before)
  })
}

View.prototype.set = function (data) {
  var self = this
  batchAnimationFrame(function () {
    self.trigger('render')
  })
  self.trigger('update', data)
  for (var key in data) {
    self.data[key] = data[key]
  }
  self.trigger('updated')
  batchAnimationFrame(function () {
    self.trigger('rendered')
  })
}

View.prototype.setOptions = function (options, skipData) {
  if (!options) {
    return
  }
  var key

  for (key in options) {
    if (key === 'attrs') {
      this.setAttributes(options.attrs)
    } else if (key === 'attr') {
      this.setAttributes(options.attr)
    } else if (key === 'href') {
      this.setAttributes({
        href: options.href
      })
    } else if (key === 'id') {
      this.setAttributes({
        id: options.id
      })
    } else if (key === 'data') {
      if (!skipData) {
        this.set(options.data)
      }
    } else if (key === 'style') {
      if (typeof options.style === 'string') {
        this.setAttributes({
          style: options.style
        })
        continue
      }
      this.setStyle(options.style)
    } else if (key === 'class') {
      if (typeof options.class === 'string') {
        this.setAttributes({
          class: options.class
        })
        continue
      }
      this.setClass(options.class)
    } else if (key === 'textContent') {
      this.textContent(options.textContent)
    } else if (key === 'listen') {
      this.addListeners(options.listen)
    } else if (key === 'init') {
      this.on('init', options.init)
    } else if (key === 'update') {
      this.on('update', options.update)
    } else if (key === 'parent') {
      this.mount(options.parent)
    } else if (key === '$root') {
      this.mount(options.$root)
    } else {
      this[key] = options[key]
    }
  }
}

View.prototype.textContent = function (text) {
  var self = this
  var $el = self.$el

  if (text !== self.text) {
    self.text = text

    batchAnimationFrame(function () {
      if (text === self.text) {
        $el.textContent = text
      }
    })
  }
}

View.prototype.addListeners = function (listeners) {
  var self = this
  var $el = self.$el
  var key, value

  for (key in listeners) {
    value = listeners[key]
    addListener(key, value)
  }
  function addListener (key, value) {
    self.on(key, value)
    $el.addEventListener(key, function (e) {
      self.trigger(key, e)
    })
  }
}

View.prototype.setClass = function (classes) {
  var self = this
  var $el = self.$el
  var key, value

  for (key in classes) {
    value = classes[key]
    if (self.class[key] !== value) {
      self.class[key] = value
      setClass(key, value)
    }
  }

  function setClass (key, value) {
    batchAnimationFrame(function () {
      if (self.class[key] === value) {
        if (value) {
          $el.classList.add(key)
        } else {
          $el.classList.remove(key)
        }
      }
    })
  }
}

View.prototype.setStyle = function (style) {
  var self = this
  var $el = self.$el
  var key, value

  for (key in style) {
    value = style[key]
    if (self.style[key] !== value) {
      self.style[key] = value
      setStyle(key, value)
    }
  }

  function setStyle (key, value) {
    batchAnimationFrame(function () {
      if (self.style[key] === style[key]) {
        $el.style[key] = value
      }
    })
  }
}

View.prototype.setAttribute = function (attr, value) {
  var data = {}
  data[attr] = value
  this.setAttributes(data)
}

View.prototype.setAttributes = function (attrs) {
  var self = this
  var $el = self.$el
  var currentAttrs = self.attrs
  var value, attr

  for (attr in attrs) {
    value = attrs[attr]
    if (value !== currentAttrs[attr]) {
      self.attrs[attr] = value

      if (value === self.attrs[attr]) {
        setAttribute(attr, value)
      }
    }
  }

  function setAttribute (attr, value) {
    batchAnimationFrame(function () {
      if (value === self.attrs[attr]) {
        if (value === false || value == null) {
          $el.removeAttribute(attr)
          return
        }
        $el.setAttribute(attr, value)

        if (attr === 'autofocus') {
          if (value) {
            $el.focus()
            self.on('mount', onAutofocus)
          } else {
            self.off('mount', onAutofocus)
          }
        }
      }
    })
  }
}

function onAutofocus () {
  this.$el.focus()
}
<|endoftext|>


<commit_before>/**
 * attr dico
 *      partials="html::.... || swig::....  || views::myView || ..."
 *
 *      edit-on-click="true"
 *
 *
 *      data-bind="mp3::id/my/path"
 *
 *      required, minLength, maxLength, .. or any json-schema related stuffs
 *
 *
my custom message

 *
 *
 */
"use strict";
if (typeof define !== 'function') {
	var define = require('amdefine')(module);
}
define(["require", "../deep"], function(require, deep) {

	deep.ui.View = deep.compose.Classes(function(layer) {
		deep.utils.up(layer, this);
	}, {
		//what:"this::!",
		_deep_view_: true,
		how: function(what, routeParams) {
			return this.toString();
		},
		//where:"dom.appendTo::#ng-view",
		done: function(success) {
			// success == { what:.., rendered:"...", placed:jQuery }
			// should seek after particular deep.ui html attributs and tag to interpret as 'partials' or 'data-bind')...
		},
		fail: function(e) {
			console.error("error while rendering : ", e);
		},
		load: function(params) {
			// console.log("LOAD VIEW")
			var self = this;
			if (!this.how || this.condition === false)
				return null;
			if (this.condition)
				if (typeof this.condition === "function" && !this.condition.apply(params))
					return null;

			var promises = [];
			if(this.what)
				promises.push(deep(this).query("./what").load(params, false));
			else
				promises.push(null);

			if(typeof this.how === 'string')
				promises.push(deep(this).query("./how").load(params, false));
			else
				promises.push(this.how);

			if(this.where)
				if(typeof this.where === 'string')
					promises.push(deep(this).query("./where").load(params, false));
				else
					promises.push(this.where);

			// console.log("launch deep load");
			return deep.all(promises)
				.done(function(res) {
					//console.log("view deep load : res  : ", res);
					var res = {
						_deep_render_node_:true,
						what:res[0] || self,
						how:res[1],
						where:res[2],
						params:params,
						controller:self
					}
					//console.log("view deep load done : ", res);
					return res;
				});
		},
		refresh: function(params, loaded) {
			// console.log("view refresh : params : ", params, " - loaded : ", loaded);
			var self = this;
			params = params || this.params;
			if (params && !deep.isNode)
				this.params = params;
			var render = function(loaded) {
				// console.log("view render : ", loaded)
				if (!loaded)
					return;
				var what = loaded.controller, how=null, where=null,rendered = null,placed = null;
				if(loaded.what)
					what = loaded.what;
				if(loaded.how)
					how = loaded.how;
				if(loaded.where)
					where = loaded.where;
				what = (what && what._deep_query_node_) ? what.value : what;
				how = (how && how._deep_query_node_) ? how.value : how;
				where = (where && where._deep_query_node_) ? where.value : where;
				placed = self.placed ? self.placed() : null;

				loaded.placed =  placed;
				loaded.getRoute = function(path) {};
				// console.log("render with : how : ", how, what)
				if(!how)
					return loaded;
				try {
					if(typeof how === 'function')
					{
						what._params = params;
						what._getRoute = loaded.getRoute;
						rendered = loaded.rendered = how.call(self, what);
						delete what._params;
						delete what._getRoute;
					}
					else
						rendered = loaded.rendered = how;
					if (where)
						placed = loaded.placed = where.call(self, loaded.rendered, loaded.placed);
					else
						placed = loaded.placed = null;
				} catch (e) {
					// console.log("error while rendering")
					if (typeof self.fail === 'function')
						return self.fail(e) || e;
					return e;
				}

				if (placed && !deep.isNode)
					self.placed = function() {
						return placed;
					};

				var promises = [];
				if (placed)
					for(var i in deep.ui.View.htmlEnhancers)
					{
						var enhancer = deep.ui.View.htmlEnhancers[i];
						promises.push(enhancer.call(self,loaded));
					}
				// console.log("rendered : ", loaded);
				if(promises.length > 0)
					return deep.all(promises)
					.done(function(){
						if (typeof self.done === "function")
							return self.done(loaded) || loaded;
						return loaded;
					});
				if (typeof self.done === "function")
					return self.done(loaded) || loaded;
				return loaded;
			};
			if (loaded)
				return render(loaded);
			return deep.when(this.load(params))
			.done(render)
			.fail(function(e){
				if (typeof self.fail === 'function')
					return self.fail(e) || e;
			});
		},
		toString: function(pretty) {
			return "default view render.";
		}
	});

	deep.View = function (layer) {
		return new deep.ui.View(layer);
	};

	deep.Chain.addHandle("refresh", function()
	{
		var args= arguments;
		var self = this;
		var func = function(s,e)
		{
			var alls = [];
			deep.chain.each(self, function (v) {
				if(typeof v.refresh === "function")
					alls.push(v.refresh.apply(v,args));
				else
					alls.push(v);
			});
			return deep.all(alls)
			.done(function(alls){
				if(!self._queried)
					return alls.shift();
				return alls;
			});
		};
		func._isDone_ = true;
		deep.chain.addInChain.call(self,func);
		return this;
	});

	deep.ui.View.htmlEnhancers = {
		partials:function(success){
			var promises = [];
			success.placed
			.find("div[partials]") //

			.each(function() {
				console.log("inside a partial tag");

				var node = $(this);
				promises.push(deep.get(node.attr("partials"))
				.done(function(partials) {
					if (typeof partials === 'function')
						node.html(partials());
					node.html(partials);
				}));
			});
			return deep.all(promises);
		},
		"dp-repeat":function(success){
			console.log("DP-REPEAT : descriptor = ", success);
			var promises = [];
			success.placed
			.find("div[dprepeat]") //

			.each(function() {
				console.log("inside a repeat tag");
				var rowNode = $(this);
				var rowHtml = deep.ui.swig(rowNode.outerHTML());

				success.controller.myDatas.forEach(function (data) {
					rowNode.after(rowHtml(data));
				});
			});
		}
	};
	return deep.View;
});

<commit_msg>dp-repeat WiP<commit_after>/**
 * attr dico
 *      partials="html::.... || swig::....  || views::myView || ..."
 *
 *      edit-on-click="true"
 *
 *
 *      data-bind="mp3::id/my/path"
 *
 *      required, minLength, maxLength, .. or any json-schema related stuffs
 *
 *
my custom message

 *
 *
 */
"use strict";
if (typeof define !== 'function') {
	var define = require('amdefine')(module);
}
define(["require", "../deep"], function(require, deep) {

	jQuery.fn.outerHTML = function(s) {
	    return s
	        ? this.before(s).remove()
	        : jQuery("
").append(this.eq(0).clone()).html();
	};

	deep.ui.View = deep.compose.Classes(function(layer) {
		deep.utils.up(layer, this);
	}, {
		//what:"this::!",
		_deep_view_: true,
		how: function(what, routeParams) {
			return this.toString();
		},
		//where:"dom.appendTo::#ng-view",
		done: function(success) {
			// success == { what:.., rendered:"...", placed:jQuery }
			// should seek after particular deep.ui html attributs and tag to interpret as 'partials' or 'data-bind')...
		},
		fail: function(e) {
			console.error("error while rendering : ", e);
		},
		load: function(params) {
			// console.log("LOAD VIEW")
			var self = this;
			if (!this.how || this.condition === false)
				return null;
			if (this.condition)
				if (typeof this.condition === "function" && !this.condition.apply(params))
					return null;

			var promises = [];
			if(this.what)
				promises.push(deep(this).query("./what").load(params, false));
			else
				promises.push(null);

			if(typeof this.how === 'string')
				promises.push(deep(this).query("./how").load(params, false));
			else
				promises.push(this.how);

			if(this.where)
				if(typeof this.where === 'string')
					promises.push(deep(this).query("./where").load(params, false));
				else
					promises.push(this.where);

			// console.log("launch deep load");
			return deep.all(promises)
				.done(function(res) {
					//console.log("view deep load : res  : ", res);
					var res = {
						_deep_render_node_:true,
						what:res[0] || self,
						how:res[1],
						where:res[2],
						params:params,
						controller:self
					}
					//console.log("view deep load done : ", res);
					return res;
				});
		},
		refresh: function(params, loaded) {
			// console.log("view refresh : params : ", params, " - loaded : ", loaded);
			var self = this;
			params = params || this.params;
			if (params && !deep.isNode)
				this.params = params;
			var render = function(loaded) {
				// console.log("view render : ", loaded)
				if (!loaded)
					return;
				var what = loaded.controller, how=null, where=null,rendered = null,placed = null;
				if(loaded.what)
					what = loaded.what;
				if(loaded.how)
					how = loaded.how;
				if(loaded.where)
					where = loaded.where;
				what = (what && what._deep_query_node_) ? what.value : what;
				how = (how && how._deep_query_node_) ? how.value : how;
				where = (where && where._deep_query_node_) ? where.value : where;
				placed = self.placed ? self.placed() : null;

				loaded.placed =  placed;
				loaded.getRoute = function(path) {};
				// console.log("render with : how : ", how, what)
				if(!how)
					return loaded;
				try {
					if(typeof how === 'function')
					{
						what._params = params;
						what._getRoute = loaded.getRoute;
						rendered = loaded.rendered = how.call(self, what);
						delete what._params;
						delete what._getRoute;
					}
					else
						rendered = loaded.rendered = how;
					if (where)
						placed = loaded.placed = where.call(self, loaded.rendered, loaded.placed);
					else
						placed = loaded.placed = null;
				} catch (e) {
					// console.log("error while rendering")
					if (typeof self.fail === 'function')
						return self.fail(e) || e;
					return e;
				}

				if (placed && !deep.isNode)
					self.placed = function() {
						return placed;
					};

				var promises = [];
				if (placed)
				{
					//console.log("View placed");
					for(var i in deep.ui.View.htmlEnhancers)
					{
						//console.log("run enhancer :", i);
						var enhancer = deep.ui.View.htmlEnhancers[i];
						promises.push(enhancer.call(self,loaded));
					}
				}
				// console.log("rendered : ", loaded);
				if(promises.length > 0)
					return deep.all(promises)
					.done(function(){
						if (typeof self.done === "function")
							return self.done(loaded) || loaded;
						return loaded;
					});
				if (typeof self.done === "function")
					return self.done(loaded) || loaded;
				return loaded;
			};
			if (loaded)
				return render(loaded);
			return deep.when(this.load(params))
			.done(render)
			.fail(function(e){
				if (typeof self.fail === 'function')
					return self.fail(e) || e;
			});
		},
		toString: function(pretty) {
			return "default view render.";
		}
	});

	deep.View = function (layer) {
		return new deep.ui.View(layer);
	};

	deep.Chain.addHandle("refresh", function()
	{
		var args= arguments;
		var self = this;
		var func = function(s,e)
		{
			var alls = [];
			deep.chain.each(self, function (v) {
				if(typeof v.refresh === "function")
					alls.push(v.refresh.apply(v,args));
				else
					alls.push(v);
			});
			return deep.all(alls)
			.done(function(alls){
				if(!self._queried)
					return alls.shift();
				return alls;
			});
		};
		func._isDone_ = true;
		deep.chain.addInChain.call(self,func);
		return this;
	});

	deep.ui.View.htmlEnhancers = {
		partials:function(success){
			//console.log("PARTIALS : descriptor = ", success);
			var promises = [];
			success.placed
			.find("div[partials]") //

			.each(function() {
				//console.log("inside a partial tag :", $(this));
				var node = $(this);
				promises.push(deep.get(node.attr("partials"))
				.done(function(partials) {
					if (typeof partials === 'function')
						node.html(partials());
					node.html(partials);
				}));
			});
			return deep.all(promises);
		},
		"dprepeat":function(success){
			//console.log("DP-REPEAT : descriptor = ", success);
			var promises = [];
			success.placed
			.find("div[dprepeat]") //

			.each(function() {
				//console.log("inside a repeat tag");
				var rowNode = $(this);
				var rowHtml = deep.ui.swig(rowNode.outerHTML());

				success.controller.myDatas.forEach(function (data) {
					rowNode.after(rowHtml(data));
				});
			});
		}
	};
	return deep.View;
});

<|endoftext|>


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

var url = require('url');
var querystring = require('querystring');

var logmagic = require('logmagic');
var request = require('request');

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

/**
 * BuildBot Client.
 * @constructor
 * @param {Object} options The options.
 */
function BuildBot(options, log) {
  var parsed = url.parse(options.url);

  // Inject auth into the URL
  delete parsed.host;
  parsed['auth'] = sprintf('%s:%s', options.username, options.password);

  this._url = url.format(parsed);
  this._options = options;
  this.log = log || logmagic.local('buildbot');
}

/**
 * Initiate build on buildbot
 * @param {String} builder The builder to force the build on.
 * @param {String} revision The revision to build.
 * @param {Function} callback The completion callback(err).
 */
BuildBot.prototype.build = function(builder, revision, callback) {
  var self = this,
      reqOpts = {
        method: 'POST',
        uri: sprintf('%s/builders/%s/force', this._url, builder),
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded'
        },
        body: querystring.stringify({
          username: this._options.username,
          revision: revision
        })
      };

  this.log.infof('forcing build of ${revision} on ${builder}', {
    builder: builder,
    revision: revision
  });

  request(reqOpts, function(err, response, body) {
    if (err) {
      callback(err);
    } else if (response.statusCode !== 302) {
      callback(new Error(sprintf('Received %s from %s', response.statusCode, self._options.url)));
    } else {
      callback(null, body);
    }
  });
};

/**
 * Find the oldest build with a given revision.
 */
BuildBot.prototype._findOldestBuild = function(builds, revision) {
  var i, j, number, numbers, build, properties, retrybuild;

  // Get numbers of builds from oldest to newest
  numbers = Object.keys(builds).map(function(numstr) {
    return parseInt(numstr);
  }).sort(function(a, b) {
    return a - b;
  });

  for (i = 0; i < numbers.length; i++) {
    number = numbers[i].toString();

    if (builds.hasOwnProperty(number)) {
      build = builds[number];
      properties = build.properties;

      for (j = 0; j < properties.length; j++) {
        if (properties[j][0] === 'got_revision' && properties[j][1] === revision) {
          // If a build failed due to an exception, see if a subsequent one
          // can be used instead.
          if (build.results === 4) {
            retrybuild = build;
            continue;
          }

          return build;
        }
      }
    }
  }

  return retrybuild;
};

/**
 * Get a build for a specified revision.
 * @param {String} revision The revision to search for.
 * @param {Function} callback A callback fired with (err, build).
 */
BuildBot.prototype.getRevision = function(builder, revision, callback) {
  var self = this,
      selects = [],
      search, i;

  for (i = 1; i <= this._options.num_builds; i++) {
    selects.push(sprintf('-%s', i));
  }

  search = querystring.stringify({select: selects});

  request.get(sprintf('%s/json/builders/%s/builds?%s', this._url, builder, search), function(err, response, body) {
    if (err) {
      callback(err);
    } else {
      try {
        body = JSON.parse(body);
      } catch (e) {
        callback(e);
        return;
      }

      callback(null, self._findOldestBuild(body, revision));
    }
  });
};

/**
 *
 */
BuildBot.prototype.ensureRevisionBuilt = function(builder, revision, callback) {
  var self = this,
      attempts = 0;

  this.log.infof('ensuring revision ${revision} on ${builder}', {
    revision: revision,
    builder: builder
  });

  function attempt(force) {
    attempts++;

    if (attempts > self._options.retries) {
      callback(new Error(sprintf('No build found after %s attempts', self._options.retries)));
      return;
    }

    self.getRevision(builder, revision, function(err, build) {
      if (err) {
        callback(err);
      } else if (!build) {
        if (force) {
          self.build(builder, revision, function(err) {
            if (err) {
              callback(err);
            } else {
              setTimeout(attempt, self._options.delay);
            }
          });
        } else {
          // The build was already forced, but isn't showing up - it is
          // *probably* pending. Alternatively, maybe num_builds happened
          // and we missed it. Oops.
          log.infof('build of ${revision} on ${builder} not available', {
            revision: revision,
            builder: builder
          });
          setTimeout(attempt, self._options.delay);
        }
      } else if (build.times[1] !== null) {
        self.log.infof('build ${number} on ${builder} finished: ${text}', {
          number: build.number,
          result_code: build.results || 0,
          builder: builder,
          text: build.text.join(' ')
        });
        // There is a Buildbot bug where build.results isn't included when it
        // is 0
        if ((build.results || 0) === 0) {
          callback(null, build);
        } else {
          callback(new Error(sprintf('Build %s of %s: %s', build.number, builder, build.text.join(' '))));
        }
      } else {
        self.log.infof('build ${number} on ${builder} ETA is ${eta}s', {
          number: build.number,
          builder: builder,
          eta: build.eta
        });
        // Try again in the greater of half of the ETA or the configured delay
        setTimeout(attempt, Math.max(build.eta * 500, self._options.delay));
      }
    });
  }

  attempt(true);
};

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

var url = require('url');
var querystring = require('querystring');

var logmagic = require('logmagic');
var request = require('request');

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

/**
 * BuildBot Client.
 * @constructor
 * @param {Object} options The options.
 */
function BuildBot(options, log) {
  var parsed = url.parse(options.url);

  // Inject auth into the URL
  delete parsed.host;
  parsed['auth'] = sprintf('%s:%s', options.username, options.password);

  this._url = url.format(parsed);
  this._options = options;
  this.log = log || logmagic.local('buildbot');
}

/**
 * Initiate build on buildbot
 * @param {String} builder The builder to force the build on.
 * @param {String} revision The revision to build.
 * @param {Function} callback The completion callback(err).
 */
BuildBot.prototype.build = function(builder, revision, callback) {
  var self = this,
      reqOpts = {
        method: 'POST',
        uri: sprintf('%s/builders/%s/force', this._url, builder),
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded'
        },
        body: querystring.stringify({
          username: this._options.username,
          revision: revision
        })
      };

  this.log.infof('forcing build of ${revision} on ${builder}', {
    builder: builder,
    revision: revision
  });

  request(reqOpts, function(err, response, body) {
    if (err) {
      callback(err);
    } else if (response.statusCode !== 302) {
      callback(new Error(sprintf('Received %s from %s', response.statusCode, self._options.url)));
    } else {
      callback(null, body);
    }
  });
};

/**
 * Find the oldest build with a given revision.
 */
BuildBot.prototype._findOldestBuild = function(builds, revision) {
  var i, j, number, numbers, build, properties, retrybuild;

  // Get numbers of builds from oldest to newest
  numbers = Object.keys(builds).map(function(numstr) {
    return parseInt(numstr);
  }).sort(function(a, b) {
    return a - b;
  });

  for (i = 0; i < numbers.length; i++) {
    number = numbers[i].toString();

    if (builds.hasOwnProperty(number)) {
      build = builds[number];
      properties = build.properties;

      if (!properties) {
        this.log.warnf('invalid build at ${index}: ${text}', {
          index: number,
          text: build.error
        });
        continue;
      }

      for (j = 0; j < properties.length; j++) {
        if (properties[j][0] === 'got_revision' && properties[j][1] === revision) {
          // If a build failed due to an exception, see if a subsequent one
          // can be used instead.
          if (build.results === 4) {
            retrybuild = build;
            continue;
          }

          return build;
        }
      }
    }
  }

  return retrybuild;
};

/**
 * Get a build for a specified revision.
 * @param {String} revision The revision to search for.
 * @param {Function} callback A callback fired with (err, build).
 */
BuildBot.prototype.getRevision = function(builder, revision, callback) {
  var self = this,
      selects = [],
      search, i;

  for (i = 1; i <= this._options.num_builds; i++) {
    selects.push(sprintf('-%s', i));
  }

  search = querystring.stringify({select: selects});

  request.get(sprintf('%s/json/builders/%s/builds?%s', this._url, builder, search), function(err, response, body) {
    if (err) {
      callback(err);
    } else {
      try {
        body = JSON.parse(body);
      } catch (e) {
        callback(e);
        return;
      }

      callback(null, self._findOldestBuild(body, revision));
    }
  });
};

/**
 *
 */
BuildBot.prototype.ensureRevisionBuilt = function(builder, revision, callback) {
  var self = this,
      attempts = 0;

  this.log.infof('ensuring revision ${revision} on ${builder}', {
    revision: revision,
    builder: builder
  });

  function attempt(force) {
    attempts++;

    if (attempts > self._options.retries) {
      callback(new Error(sprintf('No build found after %s attempts', self._options.retries)));
      return;
    }

    self.getRevision(builder, revision, function(err, build) {
      if (err) {
        callback(err);
      } else if (!build) {
        if (force) {
          self.build(builder, revision, function(err) {
            if (err) {
              callback(err);
            } else {
              setTimeout(attempt, self._options.delay);
            }
          });
        } else {
          // The build was already forced, but isn't showing up - it is
          // *probably* pending. Alternatively, maybe num_builds happened
          // and we missed it. Oops.
          log.infof('build of ${revision} on ${builder} not available', {
            revision: revision,
            builder: builder
          });
          setTimeout(attempt, self._options.delay);
        }
      } else if (build.times[1] !== null) {
        self.log.infof('build ${number} on ${builder} finished: ${text}', {
          number: build.number,
          result_code: build.results || 0,
          builder: builder,
          text: build.text.join(' ')
        });
        // There is a Buildbot bug where build.results isn't included when it
        // is 0
        if ((build.results || 0) === 0) {
          callback(null, build);
        } else {
          callback(new Error(sprintf('Build %s of %s: %s', build.number, builder, build.text.join(' '))));
        }
      } else {
        self.log.infof('build ${number} on ${builder} ETA is ${eta}s', {
          number: build.number,
          builder: builder,
          eta: build.eta
        });
        // Try again in the greater of half of the ETA or the configured delay
        setTimeout(attempt, Math.max(build.eta * 500, self._options.delay));
      }
    });
  }

  attempt(true);
};

/** BuildBot Class */
exports.BuildBot = BuildBot;
<|endoftext|>


<commit_before>var emitter = require('emitter');
var model = require('./model');
var Bindings = require('./bindings');
var each = require('each');

/**
 * Each of the events that are called on the view
 * and have helper methods created for them.
 */

var lifecycleEvents = [
  'construct',
  'created',
  'ready',
  'mounted',
  'unmounted',
  'destroying',
  'destroyed'
];

/**
 * Get a node using element the element itself
 * or a CSS selector
 *
 * @param {Element|String} node
 *
 * @return {Element}
 */

function getNode(node) {
  if (typeof node === 'string') {
    node = document.querySelector(node);
    if (!node) throw new Error('DOM node doesn\'t exist');
  }
  return node;
}

module.exports = function(template) {

  /**
   * Stores all of the directives, views,
   * filters etc. that we might want to share
   * between views.
   *
   * @type {Bindings}
   */
  var bindings = new Bindings();

  /**
   * Stores the state of the view.
   *
   * @type {Function}
   */
  var Model = model();

  /**
   * The view controls the lifecycle of the
   * element that it creates from a template.
   * Each element can only have one view and
   * each view can only have one element.
   */
  function View(options) {
    options = options || {};
    View.emit('construct', this, [options]);
    this.options = options;
    this.children = [];
    this.template = options.template || template;
    this.owner = options.owner;
    this.bindings = options.bindings || bindings;
    this.root = this;
    if (this.owner) {
      this.owner.children.push(this);
      this.root = this.owner.root;
    }
    this.scope = options.scope;
    this.scopeWatchers = {};
    this.model = new Model(View.parse(options));
    this.data = this.model.props;
    View.emit('created', this);
    this.el = this.render();
    View.emit('ready', this);
  }

  /**
   * Mixins
   */
  emitter(View);
  emitter(View.prototype);

  /**
   * Add a directive
   *
   * @param {String|Regex} match
   * @param {Function} fn
   *
   * @return {View}
   */
  View.directive = function(match, fn) {
    bindings.directive(match, fn);
    return this;
  };

  /**
   * Add a component
   *
   * @param {String} match
   * @param {Function} fn
   *
   * @return {View}
   */
  View.compose = function(name, Child) {
    bindings.component(name, Child);
    return this;
  };

  /**
   * Add interpolation filter
   *
   * @param {String} name
   * @param {Function} fn
   *
   * @return {View}
   */
  View.filter = function(name, fn) {
    if (typeof name !== 'string') {
      for(var key in name) {
        View.filter(key, name[key]);
      }
      return;
    }
    bindings.filter(name, fn);
    return this;
  };

  /**
   * Use a plugin
   *
   * @return {View}
   */
  View.use = function(fn, options) {
    fn(View, options);
    return this;
  };

  /**
   * Create helper methods for binding to events
   */
  lifecycleEvents.forEach(function(name) {
    View[name] = function(fn){
      View.on(name, function(view, args){
        fn.apply(view, args);
      });
    };
  });

  /**
   * Parse the options for the initial data
   */
  View.parse = function(options) {
    return options.data;
  };

  /**
   * Set the state off the view. This will trigger
   * refreshes to the UI. If we were previously
   * watching the parent scope for changes to this
   * property, we will remove all of those watchers
   * and then bind them to our model instead.
   *
   * @param {Object} obj
   */
  View.prototype.set = function(key, value) {
    if ( typeof key !== 'string' ) {
      for(var name in key) this.set(name, key[name]);
      return this;
    }
    if (this.scope && this.scopeWatchers[key]) {
      var self = this;
      this.scopeWatchers[key].forEach(function(callback){
        self.scope.unwatch(key, callback);
        self.model.watch(key, callback);
      });
      delete this.scopeWatchers[key];
    }
    this.model.set(key, value);
    return this;
  };

  /**
   * Get some data
   *
   * @param {String} key
   */
  View.prototype.get = function(key) {
    var value = this.model.get(key);
    if (value === undefined && this.scope) {
      return this.scope.get(key);
    }
    return value;
  };

  /**
   * Remove the element from the DOM
   */
  View.prototype.destroy = function() {
    var self = this;
    this.emit('destroying');
    View.emit('destroying', this);
    this.remove();
    this.model.destroy();
    this.off();
    this.children.forEach(function(child){
      child.destroy();
    });
    if (this.owner) {
      var index = this.owner.children.indexOf(this);
      this.owner.children.splice(index, 1);
    }
    each(this.scopeWatchers, function(key, callbacks){
      callbacks.forEach(function(callback){
        self.scope.unwatch(key, callback);
      });
    });
    this.scopeWatchers = null;
    this.scope = null;
    this.el = null;
    this.owner = null;
    this.root = null;
    this.data = null;
    this.emit('destroyed');
    View.emit('destroyed', this);
  };

  /**
   * Is the view mounted in the DOM
   *
   * @return {Boolean}
   */
  View.prototype.isMounted = function() {
    return this.el != null && this.el.parentNode != null;
  };

  /**
   * Render the view to an element. This should
   * only ever render the element once.
   */
  View.prototype.render = function() {
    return this.bindings.bind(this);
  };

  /**
   * Mount the view onto a node
   *
   * @param {Element|String} node An element or CSS selector
   *
   * @return {View}
   */
  View.prototype.appendTo = function(node) {
    getNode(node).appendChild(this.el);
    this.emit('mounted');
    View.emit('mounted', this);
    return this;
  };

  /**
   * Replace an element in the DOM with this view
   *
   * @param {Element|String} node An element or CSS selector
   *
   * @return {View}
   */
  View.prototype.replace = function(node) {
    node.parentNode.replaceChild(this.el, getNode(node));
    this.emit('mounted');
    View.emit('mounted', this);
    return this;
  };

  /**
   * Insert the view before a node
   *
   * @param {Element|String} node
   *
   * @return {View}
   */
  View.prototype.before = function(node) {
    node.parentNode.insertBefore(this.el, getNode(node));
    this.emit('mounted');
    View.emit('mounted', this);
    return this;
  };

  /**
   * Insert the view after a node
   *
   * @param {Element|String} node
   *
   * @return {View}
   */
  View.prototype.after = function(node) {
    var target = getNode(node);
    if (target.nextSibling) {
      node.parentNode.insertBefore(this.el, target.nextSibling);
      this.emit('mounted');
      View.emit('mounted', this);
    }
    else {
      this.mount(node.parentNode);
    }
    return this;
  };

  /**
   * Remove the view from the DOM
   *
   * @return {View}
   */
  View.prototype.remove = function() {
    if (this.isMounted() === false) return this;
    this.el.parentNode.removeChild(this.el);
    this.emit('unmounted');
    View.emit('unmounted', this);
    return this;
  };

  /**
   * Interpolate a string
   *
   * @param {String} str
   */
  View.prototype.interpolate = function(str) {
    var self = this;
    var data = {};
    var props = this.bindings.interpolator.props(str);
    props.forEach(function(prop){
      data[prop] = self.get(prop);
    });
    return this.bindings.interpolator.value(str, {
      context: this.scope || this,
      scope: data
    });
  };

  /**
   * Watch a property for changes
   *
   * @param {Strign} prop
   * @param {Function} callback
   */
  View.prototype.watch = function(prop, callback) {
    var self = this;
    if (Array.isArray(prop)) {
      return prop.forEach(function(name){
        self.watch(name, callback);
      });
    }
    var value = this.model.get(prop);
    if (value === undefined && this.scope) {
      this.scope.watch(prop, callback);
      if (!this.scopeWatchers[prop]) {
        this.scopeWatchers[prop] = [];
      }
      this.scopeWatchers[prop].push(callback);
      return;
    }
    return this.model.watch(prop, callback);
  };

  /**
   * Stop watching a property
   *
   * @param {Strign} prop
   * @param {Function} callback
   */
  View.prototype.unwatch = function(prop, callback) {
    var self = this;
    if (Array.isArray(prop)) {
      return prop.forEach(function(name){
        self.unwatch(name, callback);
      });
    }
    var value = this.model.get(prop);
    if (value === undefined && this.scope) {
      this.scope.unwatch(prop, callback);
      if (!this.scopeWatchers[prop]) return;
      var index = this.scopeWatchers[prop].indexOf(callback);
      this.scopeWatchers[prop].splice(index, 1);
      return;
    }
    return this.model.unwatch(prop, callback);
  };

  return View;
};<commit_msg>Fixed bug when replacing using a CSS selector<commit_after>var emitter = require('emitter');
var model = require('./model');
var Bindings = require('./bindings');
var each = require('each');

/**
 * Each of the events that are called on the view
 * and have helper methods created for them.
 */

var lifecycleEvents = [
  'construct',
  'created',
  'ready',
  'mounted',
  'unmounted',
  'destroying',
  'destroyed'
];

/**
 * Get a node using element the element itself
 * or a CSS selector
 *
 * @param {Element|String} node
 *
 * @return {Element}
 */

function getNode(node) {
  if (typeof node === 'string') {
    node = document.querySelector(node);
    if (!node) throw new Error('DOM node doesn\'t exist');
  }
  return node;
}

module.exports = function(template) {

  /**
   * Stores all of the directives, views,
   * filters etc. that we might want to share
   * between views.
   *
   * @type {Bindings}
   */
  var bindings = new Bindings();

  /**
   * Stores the state of the view.
   *
   * @type {Function}
   */
  var Model = model();

  /**
   * The view controls the lifecycle of the
   * element that it creates from a template.
   * Each element can only have one view and
   * each view can only have one element.
   */
  function View(options) {
    options = options || {};
    View.emit('construct', this, [options]);
    this.options = options;
    this.children = [];
    this.template = options.template || template;
    this.owner = options.owner;
    this.bindings = options.bindings || bindings;
    this.root = this;
    if (this.owner) {
      this.owner.children.push(this);
      this.root = this.owner.root;
    }
    this.scope = options.scope;
    this.scopeWatchers = {};
    this.model = new Model(View.parse(options));
    this.data = this.model.props;
    View.emit('created', this);
    this.el = this.render();
    View.emit('ready', this);
  }

  /**
   * Mixins
   */
  emitter(View);
  emitter(View.prototype);

  /**
   * Add a directive
   *
   * @param {String|Regex} match
   * @param {Function} fn
   *
   * @return {View}
   */
  View.directive = function(match, fn) {
    bindings.directive(match, fn);
    return this;
  };

  /**
   * Add a component
   *
   * @param {String} match
   * @param {Function} fn
   *
   * @return {View}
   */
  View.compose = function(name, Child) {
    bindings.component(name, Child);
    return this;
  };

  /**
   * Add interpolation filter
   *
   * @param {String} name
   * @param {Function} fn
   *
   * @return {View}
   */
  View.filter = function(name, fn) {
    if (typeof name !== 'string') {
      for(var key in name) {
        View.filter(key, name[key]);
      }
      return;
    }
    bindings.filter(name, fn);
    return this;
  };

  /**
   * Use a plugin
   *
   * @return {View}
   */
  View.use = function(fn, options) {
    fn(View, options);
    return this;
  };

  /**
   * Create helper methods for binding to events
   */
  lifecycleEvents.forEach(function(name) {
    View[name] = function(fn){
      View.on(name, function(view, args){
        fn.apply(view, args);
      });
    };
  });

  /**
   * Parse the options for the initial data
   */
  View.parse = function(options) {
    return options.data;
  };

  /**
   * Set the state off the view. This will trigger
   * refreshes to the UI. If we were previously
   * watching the parent scope for changes to this
   * property, we will remove all of those watchers
   * and then bind them to our model instead.
   *
   * @param {Object} obj
   */
  View.prototype.set = function(key, value) {
    if ( typeof key !== 'string' ) {
      for(var name in key) this.set(name, key[name]);
      return this;
    }
    if (this.scope && this.scopeWatchers[key]) {
      var self = this;
      this.scopeWatchers[key].forEach(function(callback){
        self.scope.unwatch(key, callback);
        self.model.watch(key, callback);
      });
      delete this.scopeWatchers[key];
    }
    this.model.set(key, value);
    return this;
  };

  /**
   * Get some data
   *
   * @param {String} key
   */
  View.prototype.get = function(key) {
    var value = this.model.get(key);
    if (value === undefined && this.scope) {
      return this.scope.get(key);
    }
    return value;
  };

  /**
   * Remove the element from the DOM
   */
  View.prototype.destroy = function() {
    var self = this;
    this.emit('destroying');
    View.emit('destroying', this);
    this.remove();
    this.model.destroy();
    this.off();
    this.children.forEach(function(child){
      child.destroy();
    });
    if (this.owner) {
      var index = this.owner.children.indexOf(this);
      this.owner.children.splice(index, 1);
    }
    each(this.scopeWatchers, function(key, callbacks){
      callbacks.forEach(function(callback){
        self.scope.unwatch(key, callback);
      });
    });
    this.scopeWatchers = null;
    this.scope = null;
    this.el = null;
    this.owner = null;
    this.root = null;
    this.data = null;
    this.emit('destroyed');
    View.emit('destroyed', this);
  };

  /**
   * Is the view mounted in the DOM
   *
   * @return {Boolean}
   */
  View.prototype.isMounted = function() {
    return this.el != null && this.el.parentNode != null;
  };

  /**
   * Render the view to an element. This should
   * only ever render the element once.
   */
  View.prototype.render = function() {
    return this.bindings.bind(this);
  };

  /**
   * Mount the view onto a node
   *
   * @param {Element|String} node An element or CSS selector
   *
   * @return {View}
   */
  View.prototype.appendTo = function(node) {
    getNode(node).appendChild(this.el);
    this.emit('mounted');
    View.emit('mounted', this);
    return this;
  };

  /**
   * Replace an element in the DOM with this view
   *
   * @param {Element|String} node An element or CSS selector
   *
   * @return {View}
   */
  View.prototype.replace = function(node) {
    var target = getNode(node);
    target.parentNode.replaceChild(this.el, target);
    this.emit('mounted');
    View.emit('mounted', this);
    return this;
  };

  /**
   * Insert the view before a node
   *
   * @param {Element|String} node
   *
   * @return {View}
   */
  View.prototype.before = function(node) {
    node.parentNode.insertBefore(this.el, getNode(node));
    this.emit('mounted');
    View.emit('mounted', this);
    return this;
  };

  /**
   * Insert the view after a node
   *
   * @param {Element|String} node
   *
   * @return {View}
   */
  View.prototype.after = function(node) {
    var target = getNode(node);
    if (target.nextSibling) {
      node.parentNode.insertBefore(this.el, target.nextSibling);
      this.emit('mounted');
      View.emit('mounted', this);
    }
    else {
      this.mount(node.parentNode);
    }
    return this;
  };

  /**
   * Remove the view from the DOM
   *
   * @return {View}
   */
  View.prototype.remove = function() {
    if (this.isMounted() === false) return this;
    this.el.parentNode.removeChild(this.el);
    this.emit('unmounted');
    View.emit('unmounted', this);
    return this;
  };

  /**
   * Interpolate a string
   *
   * @param {String} str
   */
  View.prototype.interpolate = function(str) {
    var self = this;
    var data = {};
    var props = this.bindings.interpolator.props(str);
    props.forEach(function(prop){
      data[prop] = self.get(prop);
    });
    return this.bindings.interpolator.value(str, {
      context: this.scope || this,
      scope: data
    });
  };

  /**
   * Watch a property for changes
   *
   * @param {Strign} prop
   * @param {Function} callback
   */
  View.prototype.watch = function(prop, callback) {
    var self = this;
    if (Array.isArray(prop)) {
      return prop.forEach(function(name){
        self.watch(name, callback);
      });
    }
    var value = this.model.get(prop);
    if (value === undefined && this.scope) {
      this.scope.watch(prop, callback);
      if (!this.scopeWatchers[prop]) {
        this.scopeWatchers[prop] = [];
      }
      this.scopeWatchers[prop].push(callback);
      return;
    }
    return this.model.watch(prop, callback);
  };

  /**
   * Stop watching a property
   *
   * @param {Strign} prop
   * @param {Function} callback
   */
  View.prototype.unwatch = function(prop, callback) {
    var self = this;
    if (Array.isArray(prop)) {
      return prop.forEach(function(name){
        self.unwatch(name, callback);
      });
    }
    var value = this.model.get(prop);
    if (value === undefined && this.scope) {
      this.scope.unwatch(prop, callback);
      if (!this.scopeWatchers[prop]) return;
      var index = this.scopeWatchers[prop].indexOf(callback);
      this.scopeWatchers[prop].splice(index, 1);
      return;
    }
    return this.model.unwatch(prop, callback);
  };

  return View;
};<|endoftext|>


<commit_before>'use strict';

/**
 * Kalabox module for cheking internet access.
 * @module kbox.util.internet
 */

// NPM modules
var Promise = require('../promise.js');
var _ = require('lodash');
var http = require('http');
var VError = require('verror');
var log = require('../core.js').log.make('INTERNET');

/**
 * Resolve a DNS query against a given URL to validate Internet access.
 * @arg {function} callback - Callback function.
 * @arg {error} callback.err - Possible error if there is no Internet access.
 * @example
 * kbox.util.internet.check(function(err) {
 *   if (err) {
 *    throw err;
 *   }
 *   console.log('We have internet access! Download all TeH things!!!!');
 * });
 */
exports.check = function(callback) {

  // List of URLS to get, so that if none of them responded it would either
  // be the end of the world or you have no Internet connection.
  var urls = [
    'http://www.google.com',
    'http://www.w3.org',
    'http://www.amazon.com'
  ];

  // Retry options.
  var retryOpts = {
    max: 7,
    backoff: 1000
  };

  // Try to reach the Internet inside of a promise retry.
  return Promise.retry(function(counter) {

    // Let the user know what we are doing.
    log.info('Checking connection to the Internet: ' + counter);

    // Build an array of request promises.
    var requests = _.map(urls, function(url) {
      return Promise.fromNode(function(cb) {
        // Get URL.
        http.get(url, function(res) {
          if (res.statusCode === 200) {
            // Status code 200 tells us everything is ok!
            cb();
          } else {
            cb(new Error('Status code: ' + res.statusCode));
          }
        })
        .on('error', function(err) {
          cb(err);
        });
      })
      // Make sure we have a timeout.
      .timeout(5 * 1000);
    });

    // Wait until one url get request fulfills.
    return Promise.any(requests);

  }, retryOpts)
  // Wrap errors.
  .catch(function() {
    throw new Error('The Internet is unreachable!');
  })
  // Return.
  .nodeify(callback);

};
<commit_msg>Fixing Internet check.<commit_after>'use strict';

/**
 * Kalabox module for cheking internet access.
 * @module kbox.util.internet
 */

// NPM modules
var Promise = require('../promise.js');
var _ = require('lodash');
var http = require('http');
var VError = require('verror');
var log = require('../core.js').log.make('INTERNET');

/**
 * Resolve a DNS query against a given URL to validate Internet access.
 * @arg {function} callback - Callback function.
 * @arg {error} callback.err - Possible error if there is no Internet access.
 * @example
 * kbox.util.internet.check(function(err) {
 *   if (err) {
 *    throw err;
 *   }
 *   console.log('We have internet access! Download all TeH things!!!!');
 * });
 */
exports.check = function(callback) {

  // List of URLS to get, so that if none of them responded it would either
  // be the end of the world or you have no Internet connection.
  var urls = [
    'http://www.google.com',
    'http://www.w3.org',
    'http://pingtest.net'
  ];

  // Retry options.
  var retryOpts = {
    max: 7,
    backoff: 1000
  };

  // Regular expression for checking status codes.
  var regex = new RegExp(/^[23][0-9][0-9]$/);

  // Try to reach the Internet inside of a promise retry.
  return Promise.retry(function(counter) {

    // Let the user know what we are doing.
    log.info('Checking connection to the Internet: ' + counter);

    // List of requests for us to cancel later.
    var requests = [];

    // Build an array of request promises.
    var checks = _.map(urls, function(url) {
      return Promise.fromNode(function(cb) {
        // Get URL.
        var req = http.get(url, function(res) {
          if (regex.test(res.statusCode.toString())) {
            cb();
          } else {
            cb(new Error('Status code: ' + res.statusCode));
          }
        })
        .on('error', function(err) {
          cb(err);
        });
        // Add to list of requests so we can cancel the losers.
        requests.push(req);
      });
    });

    // Wait until one url get request fulfills.
    return Promise.any(checks)
    // Abort the remaining requests.
    .then(function() {
      return Promise.map(requests, function(req) {
        req.abort();
      });
    });

  }, retryOpts)
  // Log success.
  .then(function() {
    log.info('OK');
  })
  // Wrap errors.
  .catch(function() {
    throw new Error('The Internet is unreachable!');
  })
  // Return.
  .nodeify(callback);

};
<|endoftext|>