<commit_before>/*
 * Vibe
 * http://vibe-project.github.io/projects/vibe-protocol/
 *
 * Copyright 2014 The Vibe Project
 * Licensed under the Apache License, Version 2.0
 * http://www.apache.org/licenses/LICENSE-2.0
 */
var events = require("events");
var url = require("url");
var http = require("http");
var createBaseTransport = require("./transport-base-transport");

http.globalAgent.maxSockets = Infinity;

// It creates a base transport which provides common functionalities of HTTP
// transport.
module.exports = function(uri, options) {
    // A transport object.
    var self = createBaseTransport(uri, options);
    // A flag to check if this transport is opened.
    var opened = false;
    self.on("open", function() {
        opened = true;
    });
    self.on("close", function() {
        opened = false;
    });
    // For the client to send message to the server,
    self.send = function(data) {
        var reqOpts = url.parse(uri);
        // Prepares for the request headers.
        reqOpts.headers = {};
        // `id` param should be added to query. As it has already `transport`
        // param, `&` can be preceded safely.
        reqOpts.path += "&id=" + encodeURIComponent(self.id);
        // The request's method should be `POST`.
        reqOpts.method = "POST";
        // If any error occurs when performing a request,
        function onerror(error) {
            // Try again as long as the connection is opened if sending event to
            // the server fails for some reason.
            if (opened) {
                self.send(data);
            }
        }
        // `data` should be either a `Buffer` or a string.
        if (typeof data === "string") {
            // The content type header should be `text/plain; charset=utf-8` for
            // text message.
            reqOpts.headers["content-type"] = "text/plain; charset=utf-8";
            // The final body should be prefixed with `data=` and encoded in
            // `utf-8`.
            http.request(reqOpts).on("error", onerror).end("data=" + data, "utf-8");
        } else {
            // The content type header should be `application/octet-stream` for
            // binary message.
            reqOpts.headers["content-type"] = "application/octet-stream";
            http.request(reqOpts).on("error", onerror).end(data);
        }
        return this;
    };
    self.close = function() {
        // Aborts the real connection. `abort` should be implemented by others
        // and ensure that `close` event is fired.
        self.abort();
        // Server may not detect disconnection for some reason. Notifies the
        // server of disconnection of this connection to make sure. In this
        // request, `id` param should be added to query and `when` param should
        // be set to `abort`.
        http.get(uri + "&when=abort&id=" + encodeURIComponent(self.id));
        return this;
    };
    return self;
};<commit_msg>Fixes #58 emit an error and close the transport if sending a message fails<commit_after>/*
 * Vibe
 * http://vibe-project.github.io/projects/vibe-protocol/
 *
 * Copyright 2014 The Vibe Project
 * Licensed under the Apache License, Version 2.0
 * http://www.apache.org/licenses/LICENSE-2.0
 */
var events = require("events");
var url = require("url");
var http = require("http");
var createBaseTransport = require("./transport-base-transport");

http.globalAgent.maxSockets = Infinity;

// It creates a base transport which provides common functionalities of HTTP
// transport.
module.exports = function(uri, options) {
    // A transport object.
    var self = createBaseTransport(uri, options);
    // For the client to send message to the server,
    self.send = function(data) {
        var reqOpts = url.parse(uri);
        // Prepares for the request headers.
        reqOpts.headers = {};
        // `id` param should be added to query. As it has already `transport`
        // param, `&` can be preceded safely.
        reqOpts.path += "&id=" + encodeURIComponent(self.id);
        // The request's method should be `POST`.
        reqOpts.method = "POST";
        // Any error occurred when performing a request should propagate to
        // transport.
        function onerror(error) {
            self.emit("error", error);
        }
        // If it fails to perform a request, the connection should be closed.
        function onclose() {
            // It will fire `close` event finally.
            self.close();
        }

        // `data` should be either a `Buffer` or a string.
        if (typeof data === "string") {
            // The content type header should be `text/plain; charset=utf-8` for
            // text message.
            reqOpts.headers["content-type"] = "text/plain; charset=utf-8";
            // The final body should be prefixed with `data=` and encoded in
            // `utf-8`.
            http.request(reqOpts).on("error", onerror).on("close", onclose).end("data=" + data, "utf-8");
        } else {
            // The content type header should be `application/octet-stream` for
            // binary message.
            reqOpts.headers["content-type"] = "application/octet-stream";
            http.request(reqOpts).on("error", onerror).on("close", onclose).end(data);
        }
        return this;
    };
    self.close = function() {
        // Aborts the real connection. `abort` should be implemented by others
        // and ensure that `close` event is fired.
        self.abort();
        // Server may not detect disconnection for some reason. Notifies the
        // server of disconnection of this connection to make sure. In this
        // request, `id` param should be added to query and `when` param should
        // be set to `abort`.
        http.get(uri + "&when=abort&id=" + encodeURIComponent(self.id));
        return this;
    };
    return self;
};<|endoftext|>


<commit_before>/**
 * Basic Finder Queries
 */

var usageError = require('../../utils/usageError'),
    utils = require('../../utils/helpers'),
    normalize = require('../../utils/normalize'),
    Deferred = require('../deferred'),
    Joins = require('./joins'),
    Operations = require('./operations'),
    Integrator = require('../integrator'),
    _ = require('lodash'),
    async = require('async');

module.exports = {

  /**
   * Find a single record that meets criteria
   *
   * @param {Object} criteria to search
   * @param {Function} callback
   * @return Deferred object if no callback
   */

  findOne: function(criteria, cb) {
    var self = this;

    if(typeof criteria === 'function') {
      cb = criteria;
      criteria = null;
    }

    // Check if criteria is an integer or string and normalize criteria
    // to object, using the specified primary key field.
    if(_.isNumber(criteria) || _.isString(criteria)) {

      // Default to id as primary key
      var pk = 'id';

      // If autoPK is not used, attempt to find a primary key
      if (!self.autoPK) {
        // Check which attribute is used as primary key
        for(var key in self.attributes) {
          if(!self.attributes[key].hasOwnProperty('primaryKey')) continue;

          // Check if custom primaryKey value is falsy
          if(!self.attributes[key].primaryKey) continue;

          // If a custom primary key is defined, use it
          pk = key;
          break;
        }
      }

      // Temporary store the given criteria
      var pkCriteria = _.clone(criteria);

      // Make the criteria object, with the primary key
      criteria = {};
      criteria[pk] = pkCriteria;
    }

    // Normalize criteria
    criteria = normalize.criteria(criteria);

    // Return Deferred or pass to adapter
    if(typeof cb !== 'function') {
      return new Deferred(this, this.findOne, criteria);
    }

    // Transform Search Criteria
    criteria = self._transformer.serialize(criteria);

    // If there was something defined in the criteria that would return no results, don't even
    // run the query and just return an empty result set.
    if(criteria === false) {
      return cb(null, null);
    }

    // Build up an operations set
    var operations = new Operations(self, criteria, 'findOne');

    // Run the operations
    operations.run(function(err, values) {
      if(err) return cb(err);
      if(!values) return cb();

      // If no joins are used grab the only item from the cache and pass to the returnResults
      // function.
      if(!criteria.joins) {
        values = values[Object.keys(values)[0]];
        return returnResults(values);
      }

      // Find the primaryKey of the current model so it can be passed down to the integrator.
      // Use 'id' as a good general default;
      var primaryKey = 'id';

      Object.keys(self._schema.schema).forEach(function(key) {
        if(self._schema.schema[key].hasOwnProperty('primaryKey')) primaryKey = key;
      });

      // Perform in-memory joins
      Integrator(values, criteria.joins, primaryKey, function(err, results) {
        if(err) return cb(err);
        if(!results) return cb();

        // Serialize values coming from an in-memory join before modelizing
        var _results = [];
        results.forEach(function(res) {
          var val = self._transformer.serialize(res);
          _results.push(val);
        });

        returnResults(_results);
      });

      function returnResults(results) {
        var models = [];
        var joins = criteria.joins ? criteria.joins : [];
        var data = new Joins(joins, results, self._schema.schema, self.waterline.collections);

        // If `data.models` is invalid (not an array) return early to avoid getting into trouble.
        if (!data || !data.models || !data.models.forEach) {
          return cb(new Error('Values returned from operations set are not an array...'));
        }

        data.models.forEach(function(model) {

          // Unserialize result value
          var value = self._transformer.unserialize(model);

          // Create a model for the top level values
          models.push(new self._model(value, data.options));
        });

        cb(null, models[0]);
      }
    });
  },

  /**
   * Find All Records that meet criteria
   *
   * @param {Object} search criteria
   * @param {Object} options
   * @param {Function} callback
   * @return Deferred object if no callback
   */

  find: function(criteria, options, cb) {
    var self = this;

    var usage = utils.capitalize(this.identity) + '.find([criteria],[options]).exec(callback|switchback)';

    if(typeof criteria === 'function') {
      cb = criteria;
      criteria = null;
      options = null;
    }

    if(typeof options === 'function') {
      cb = options;
      options = null;
    }

    // Normalize criteria
    criteria = normalize.criteria(criteria);

    // Fold in options
    if(options === Object(options) && criteria === Object(criteria)) {
      criteria = _.extend({}, criteria, options);
    }

    // Validate Arguments
    if(typeof criteria === 'function' || typeof options === 'criteria') {
      return usageError('Invalid options specified!', usage, cb);
    }

    // Return Deferred or pass to adapter
    if(typeof cb !== 'function') {
      // console.log('DEFERED!', cb);
      return new Deferred(this, this.find, criteria);
    }

    // If there was something defined in the criteria that would return no results, don't even
    // run the query and just return an empty result set.
    if(criteria === false) {
      return cb(null, []);
    }

    // Transform Search Criteria
    if (!self._transformer) {
      throw new Error('Waterline can not access transformer-- maybe the context of the method is being overridden?');
    }
    criteria = self._transformer.serialize(criteria);

    // Build up an operations set
    var operations = new Operations(self, criteria, 'find');

    // Run the operations
    operations.run(function(err, values) {
      if(err) return cb(err);
      if(!values) return cb();

      // If no joins are used grab current collection's item from the cache and pass to the returnResults
      // function.
      if(!criteria.joins) {
        values = values[self.identity];
        return returnResults(values);
      }

      // Find the primaryKey of the current model so it can be passed down to the integrator.
      // Use 'id' as a good general default;
      var primaryKey = 'id';

      Object.keys(self._schema.schema).forEach(function(key) {
        if(self._schema.schema[key].hasOwnProperty('primaryKey')) primaryKey = key;
      });

      // Perform in-memory joins
      Integrator(values, criteria.joins, primaryKey, function(err, results) {
        if(err) return cb(err);
        if(!results) return cb();

        // Serialize values coming from an in-memory join before modelizing
        var _results = [];
        results.forEach(function(res) {
          var val = self._transformer.serialize(res);
          _results.push(val);
        });

        returnResults(_results);
      });

      function returnResults(results) {

        var models = [];
        var joins = criteria.joins ? criteria.joins : [];
        var data = new Joins(joins, results, self._schema.schema, self.waterline.collections);

        // If `data.models` is invalid (not an array) return early to avoid getting into trouble.
        if (!data || !data.models || !data.models.forEach) {
          return cb(new Error('Values returned from operations set are not an array...'));
        }

        data.models.forEach(function(model) {

          // Unserialize result value
          var value = self._transformer.unserialize(model);

          // Create a model for the top level values
          models.push(new self._model(value, data.options));
        });

        cb(null, models);
      }

    });
  },

  where: function() {
    this.find.apply(this, Array.prototype.slice.call(arguments));
  },

  select: function() {
    this.find.apply(this, Array.prototype.slice.call(arguments));
  },

  /**
   * findAll
   * [[ Deprecated! ]]
   *
   * @param  {Object}   criteria
   * @param  {Object}   options
   * @param  {Function} cb
   */
  findAll: function(criteria, options, cb) {
    if(typeof criteria === 'function') {
      cb = criteria;
      criteria = null;
      options = null;
    }

    if(typeof options === 'function') {
      cb = options;
      options = null;
    }

    // Return Deferred or pass to adapter
    if(typeof cb !== 'function') {
      return new Deferred(this, this.findAll, criteria);
    }

    cb(new Error('In Waterline >= 0.9, findAll() has been deprecated in favor of find().' +
                '\nPlease visit the migration guide at http://sailsjs.org for help upgrading.'));
  }

};
<commit_msg>Apply same fix to findOne<commit_after>/**
 * Basic Finder Queries
 */

var usageError = require('../../utils/usageError'),
    utils = require('../../utils/helpers'),
    normalize = require('../../utils/normalize'),
    Deferred = require('../deferred'),
    Joins = require('./joins'),
    Operations = require('./operations'),
    Integrator = require('../integrator'),
    _ = require('lodash'),
    async = require('async');

module.exports = {

  /**
   * Find a single record that meets criteria
   *
   * @param {Object} criteria to search
   * @param {Function} callback
   * @return Deferred object if no callback
   */

  findOne: function(criteria, cb) {
    var self = this;

    if(typeof criteria === 'function') {
      cb = criteria;
      criteria = null;
    }

    // Check if criteria is an integer or string and normalize criteria
    // to object, using the specified primary key field.
    if(_.isNumber(criteria) || _.isString(criteria)) {

      // Default to id as primary key
      var pk = 'id';

      // If autoPK is not used, attempt to find a primary key
      if (!self.autoPK) {
        // Check which attribute is used as primary key
        for(var key in self.attributes) {
          if(!self.attributes[key].hasOwnProperty('primaryKey')) continue;

          // Check if custom primaryKey value is falsy
          if(!self.attributes[key].primaryKey) continue;

          // If a custom primary key is defined, use it
          pk = key;
          break;
        }
      }

      // Temporary store the given criteria
      var pkCriteria = _.clone(criteria);

      // Make the criteria object, with the primary key
      criteria = {};
      criteria[pk] = pkCriteria;
    }

    // Normalize criteria
    criteria = normalize.criteria(criteria);

    // Return Deferred or pass to adapter
    if(typeof cb !== 'function') {
      return new Deferred(this, this.findOne, criteria);
    }

    // Transform Search Criteria
    criteria = self._transformer.serialize(criteria);

    // If there was something defined in the criteria that would return no results, don't even
    // run the query and just return an empty result set.
    if(criteria === false) {
      return cb(null, null);
    }

    // Build up an operations set
    var operations = new Operations(self, criteria, 'findOne');

    // Run the operations
    operations.run(function(err, values) {
      if(err) return cb(err);
      if(!values) return cb();

      // If no joins are used grab the only item from the cache and pass to the returnResults
      // function.
      if(!criteria.joins) {
        values = values[self.identity];
        return returnResults(values);
      }

      // Find the primaryKey of the current model so it can be passed down to the integrator.
      // Use 'id' as a good general default;
      var primaryKey = 'id';

      Object.keys(self._schema.schema).forEach(function(key) {
        if(self._schema.schema[key].hasOwnProperty('primaryKey')) primaryKey = key;
      });

      // Perform in-memory joins
      Integrator(values, criteria.joins, primaryKey, function(err, results) {
        if(err) return cb(err);
        if(!results) return cb();

        // Serialize values coming from an in-memory join before modelizing
        var _results = [];
        results.forEach(function(res) {
          var val = self._transformer.serialize(res);
          _results.push(val);
        });

        returnResults(_results);
      });

      function returnResults(results) {
        var models = [];
        var joins = criteria.joins ? criteria.joins : [];
        var data = new Joins(joins, results, self._schema.schema, self.waterline.collections);

        // If `data.models` is invalid (not an array) return early to avoid getting into trouble.
        if (!data || !data.models || !data.models.forEach) {
          return cb(new Error('Values returned from operations set are not an array...'));
        }

        data.models.forEach(function(model) {

          // Unserialize result value
          var value = self._transformer.unserialize(model);

          // Create a model for the top level values
          models.push(new self._model(value, data.options));
        });

        cb(null, models[0]);
      }
    });
  },

  /**
   * Find All Records that meet criteria
   *
   * @param {Object} search criteria
   * @param {Object} options
   * @param {Function} callback
   * @return Deferred object if no callback
   */

  find: function(criteria, options, cb) {
    var self = this;

    var usage = utils.capitalize(this.identity) + '.find([criteria],[options]).exec(callback|switchback)';

    if(typeof criteria === 'function') {
      cb = criteria;
      criteria = null;
      options = null;
    }

    if(typeof options === 'function') {
      cb = options;
      options = null;
    }

    // Normalize criteria
    criteria = normalize.criteria(criteria);

    // Fold in options
    if(options === Object(options) && criteria === Object(criteria)) {
      criteria = _.extend({}, criteria, options);
    }

    // Validate Arguments
    if(typeof criteria === 'function' || typeof options === 'criteria') {
      return usageError('Invalid options specified!', usage, cb);
    }

    // Return Deferred or pass to adapter
    if(typeof cb !== 'function') {
      // console.log('DEFERED!', cb);
      return new Deferred(this, this.find, criteria);
    }

    // If there was something defined in the criteria that would return no results, don't even
    // run the query and just return an empty result set.
    if(criteria === false) {
      return cb(null, []);
    }

    // Transform Search Criteria
    if (!self._transformer) {
      throw new Error('Waterline can not access transformer-- maybe the context of the method is being overridden?');
    }
    criteria = self._transformer.serialize(criteria);

    // Build up an operations set
    var operations = new Operations(self, criteria, 'find');

    // Run the operations
    operations.run(function(err, values) {
      if(err) return cb(err);
      if(!values) return cb();

      // If no joins are used grab current collection's item from the cache and pass to the returnResults
      // function.
      if(!criteria.joins) {
        values = values[self.identity];
        return returnResults(values);
      }

      // Find the primaryKey of the current model so it can be passed down to the integrator.
      // Use 'id' as a good general default;
      var primaryKey = 'id';

      Object.keys(self._schema.schema).forEach(function(key) {
        if(self._schema.schema[key].hasOwnProperty('primaryKey')) primaryKey = key;
      });

      // Perform in-memory joins
      Integrator(values, criteria.joins, primaryKey, function(err, results) {
        if(err) return cb(err);
        if(!results) return cb();

        // Serialize values coming from an in-memory join before modelizing
        var _results = [];
        results.forEach(function(res) {
          var val = self._transformer.serialize(res);
          _results.push(val);
        });

        returnResults(_results);
      });

      function returnResults(results) {

        var models = [];
        var joins = criteria.joins ? criteria.joins : [];
        var data = new Joins(joins, results, self._schema.schema, self.waterline.collections);

        // If `data.models` is invalid (not an array) return early to avoid getting into trouble.
        if (!data || !data.models || !data.models.forEach) {
          return cb(new Error('Values returned from operations set are not an array...'));
        }

        data.models.forEach(function(model) {

          // Unserialize result value
          var value = self._transformer.unserialize(model);

          // Create a model for the top level values
          models.push(new self._model(value, data.options));
        });

        cb(null, models);
      }

    });
  },

  where: function() {
    this.find.apply(this, Array.prototype.slice.call(arguments));
  },

  select: function() {
    this.find.apply(this, Array.prototype.slice.call(arguments));
  },

  /**
   * findAll
   * [[ Deprecated! ]]
   *
   * @param  {Object}   criteria
   * @param  {Object}   options
   * @param  {Function} cb
   */
  findAll: function(criteria, options, cb) {
    if(typeof criteria === 'function') {
      cb = criteria;
      criteria = null;
      options = null;
    }

    if(typeof options === 'function') {
      cb = options;
      options = null;
    }

    // Return Deferred or pass to adapter
    if(typeof cb !== 'function') {
      return new Deferred(this, this.findAll, criteria);
    }

    cb(new Error('In Waterline >= 0.9, findAll() has been deprecated in favor of find().' +
                '\nPlease visit the migration guide at http://sailsjs.org for help upgrading.'));
  }

};
<|endoftext|>


<commit_before>/**
 * Basic Finder Queries
 */

var usageError = require('../../utils/usageError'),
    utils = require('../../utils/helpers'),
    normalize = require('../../utils/normalize'),
    Deferred = require('../deferred'),
    Joins = require('./joins'),
    _ = require('lodash');

module.exports = {

  /**
   * Find a single record that meets criteria
   *
   * @param {Object} criteria to search
   * @param {Function} callback
   * @return Deferred object if no callback
   */

  findOne: function(criteria, cb) {
    var self = this;

    if(typeof criteria === 'function') {
      cb = criteria;
      criteria = null;
    }

    // Check if criteria is an integer or string and normalize criteria
    // to object, using the specified primary key field.
    if(_.isNumber(criteria) || _.isString(criteria)) {

      // Default to id as primary key
      var pk = 'id';

      // If autoPK is not used, attempt to find a primary key
      if (!self.autoPK) {
        // Check which attribute is used as primary key
        for(var key in self.attributes) {
          if(!self.attributes[key].hasOwnProperty('primaryKey')) continue;

          // Check if custom primaryKey value is falsy
          if(!self.attributes[key].primaryKey) continue;

          // If a custom primary key is defined, use it
          pk = key;
          break;
        }
      }

      // Temporary store the given criteria
      var pkCriteria = _.clone(criteria);

      // Make the criteria object, with the primary key
      criteria = {};
      criteria[pk] = pkCriteria;
    }

    // Normalize criteria
    criteria = normalize.criteria(criteria);

    // Return Deferred or pass to adapter
    if(typeof cb !== 'function') {
      return new Deferred(this, this.findOne, criteria);
    }

    // Transform Search Criteria
    criteria = self._transformer.serialize(criteria);

    // If there was something defined in the criteria that would return no results, don't even
    // run the query and just return an empty result set.
    if(criteria === false) {
      return cb(null, null);
    }

    // Pass criteria to adapter definition
    this._adapter.findOne(criteria, function(err, values) {
      if(err) return cb(err);
      if(!values) return cb();

      var models = [];
      var data = new Joins(criteria.joins, values, self._schema.schema, self.waterline.collections);

      data.models.forEach(function(model) {

        // Unserialize result value
        var value = self._transformer.unserialize(model);

        // Create a model for the top level values
        models.push(new self._model(value, data.options));
      });

      cb(null, models[0]);
    });
  },

  /**
   * Find All Records that meet criteria
   *
   * @param {Object} search criteria
   * @param {Object} options
   * @param {Function} callback
   * @return Deferred object if no callback
   */

  find: function(criteria, options, cb) {
    var self = this;

    var usage = utils.capitalize(this.identity) + '.find([criteria],[options],callback)';

    if(typeof criteria === 'function') {
      cb = criteria;
      criteria = null;
      options = null;
    }

    if(typeof options === 'function') {
      cb = options;
      options = null;
    }

    // Normalize criteria and fold in options
    criteria = normalize.criteria(criteria);

    if(options === Object(options) && criteria === Object(criteria)) {
      criteria = _.extend({}, criteria, options);
    }

    // Validate Arguments
    if(typeof criteria === 'function' || typeof options === 'criteria') {
      return usageError('Invalid options specified!', usage, cb);
    }

    // Return Deferred or pass to adapter
    if(typeof cb !== 'function') {
      return new Deferred(this, this.find, criteria);
    }

    // If there was something defined in the criteria that would return no results, don't even
    // run the query and just return an empty result set.
    if(criteria === false) {
      return cb(null, []);
    }

    // Transform Search Criteria
    criteria = self._transformer.serialize(criteria);

    // Pass criteria to adapter definition
    this._adapter.find(criteria, function(err, values) {
      if(err) return cb(err);

      var models = [];
      var data = new Joins(criteria.joins, values, self._schema.schema, self.waterline.collections);

      data.models.forEach(function(model) {

        // Unserialize result value
        var value = self._transformer.unserialize(model);

        // Create a model for the top level values
        models.push(new self._model(value, data.options));
      });

      cb(null, models);
    });
  },

  where: function() {
    this.find.apply(this, Array.prototype.slice.call(arguments));
  },

  select: function() {
    this.find.apply(this, Array.prototype.slice.call(arguments));
  },

  /**
   * findAll
   * [[ Deprecated! ]]
   *
   * @param  {Object}   criteria
   * @param  {Object}   options
   * @param  {Function} cb
   */
  findAll: function(criteria, options, cb) {
    if(typeof criteria === 'function') {
      cb = criteria;
      criteria = null;
      options = null;
    }

    if(typeof options === 'function') {
      cb = options;
      options = null;
    }

    // Return Deferred or pass to adapter
    if(typeof cb !== 'function') {
      return new Deferred(this, this.findAll, criteria);
    }

    cb(new Error('In Waterline 0.9, findAll() has been deprecated in favor of find().' +
                '\nPlease visit the migration guide at http://sailsjs.org for help upgrading.'));
  }

};
<commit_msg>better lift-time error for core context issues in collections.<commit_after>/**
 * Basic Finder Queries
 */

var usageError = require('../../utils/usageError'),
    utils = require('../../utils/helpers'),
    normalize = require('../../utils/normalize'),
    Deferred = require('../deferred'),
    Joins = require('./joins'),
    _ = require('lodash');

module.exports = {

  /**
   * Find a single record that meets criteria
   *
   * @param {Object} criteria to search
   * @param {Function} callback
   * @return Deferred object if no callback
   */

  findOne: function(criteria, cb) {
    var self = this;

    if(typeof criteria === 'function') {
      cb = criteria;
      criteria = null;
    }

    // Check if criteria is an integer or string and normalize criteria
    // to object, using the specified primary key field.
    if(_.isNumber(criteria) || _.isString(criteria)) {

      // Default to id as primary key
      var pk = 'id';

      // If autoPK is not used, attempt to find a primary key
      if (!self.autoPK) {
        // Check which attribute is used as primary key
        for(var key in self.attributes) {
          if(!self.attributes[key].hasOwnProperty('primaryKey')) continue;

          // Check if custom primaryKey value is falsy
          if(!self.attributes[key].primaryKey) continue;

          // If a custom primary key is defined, use it
          pk = key;
          break;
        }
      }

      // Temporary store the given criteria
      var pkCriteria = _.clone(criteria);

      // Make the criteria object, with the primary key
      criteria = {};
      criteria[pk] = pkCriteria;
    }

    // Normalize criteria
    criteria = normalize.criteria(criteria);

    // Return Deferred or pass to adapter
    if(typeof cb !== 'function') {
      return new Deferred(this, this.findOne, criteria);
    }

    // Transform Search Criteria
    criteria = self._transformer.serialize(criteria);

    // If there was something defined in the criteria that would return no results, don't even
    // run the query and just return an empty result set.
    if(criteria === false) {
      return cb(null, null);
    }

    // Pass criteria to adapter definition
    this._adapter.findOne(criteria, function(err, values) {
      if(err) return cb(err);
      if(!values) return cb();

      var models = [];
      var data = new Joins(criteria.joins, values, self._schema.schema, self.waterline.collections);

      data.models.forEach(function(model) {

        // Unserialize result value
        var value = self._transformer.unserialize(model);

        // Create a model for the top level values
        models.push(new self._model(value, data.options));
      });

      cb(null, models[0]);
    });
  },

  /**
   * Find All Records that meet criteria
   *
   * @param {Object} search criteria
   * @param {Object} options
   * @param {Function} callback
   * @return Deferred object if no callback
   */

  find: function(criteria, options, cb) {
    var self = this;

    var usage = utils.capitalize(this.identity) + '.find([criteria],[options],callback)';

    if(typeof criteria === 'function') {
      cb = criteria;
      criteria = null;
      options = null;
    }

    if(typeof options === 'function') {
      cb = options;
      options = null;
    }

    // Normalize criteria and fold in options
    criteria = normalize.criteria(criteria);

    if(options === Object(options) && criteria === Object(criteria)) {
      criteria = _.extend({}, criteria, options);
    }

    // Validate Arguments
    if(typeof criteria === 'function' || typeof options === 'criteria') {
      return usageError('Invalid options specified!', usage, cb);
    }

    // Return Deferred or pass to adapter
    if(typeof cb !== 'function') {
      return new Deferred(this, this.find, criteria);
    }

    // If there was something defined in the criteria that would return no results, don't even
    // run the query and just return an empty result set.
    if(criteria === false) {
      return cb(null, []);
    }

    // Transform Search Criteria
    if (!self._transformer) throw new Error('Waterline can not access transformer-- maybe the context is wrong?');
    criteria = self._transformer.serialize(criteria);

    // Pass criteria to adapter definition
    this._adapter.find(criteria, function(err, values) {
      if(err) return cb(err);

      var models = [];
      var data = new Joins(criteria.joins, values, self._schema.schema, self.waterline.collections);

      data.models.forEach(function(model) {

        // Unserialize result value
        var value = self._transformer.unserialize(model);

        // Create a model for the top level values
        models.push(new self._model(value, data.options));
      });

      cb(null, models);
    });
  },

  where: function() {
    this.find.apply(this, Array.prototype.slice.call(arguments));
  },

  select: function() {
    this.find.apply(this, Array.prototype.slice.call(arguments));
  },

  /**
   * findAll
   * [[ Deprecated! ]]
   *
   * @param  {Object}   criteria
   * @param  {Object}   options
   * @param  {Function} cb
   */
  findAll: function(criteria, options, cb) {
    if(typeof criteria === 'function') {
      cb = criteria;
      criteria = null;
      options = null;
    }

    if(typeof options === 'function') {
      cb = options;
      options = null;
    }

    // Return Deferred or pass to adapter
    if(typeof cb !== 'function') {
      return new Deferred(this, this.findAll, criteria);
    }

    cb(new Error('In Waterline 0.9, findAll() has been deprecated in favor of find().' +
                '\nPlease visit the migration guide at http://sailsjs.org for help upgrading.'));
  }

};
<|endoftext|>


<commit_before>/**
 * Basic Finder Queries
 */

var usageError = require('../../utils/usageError'),
    utils = require('../../utils/helpers'),
    normalize = require('../../utils/normalize'),
    sorter = require('../../utils/sorter'),
    Deferred = require('../deferred'),
    Joins = require('./joins'),
    Operations = require('./operations'),
    Integrator = require('../integrator'),
    waterlineCriteria = require('waterline-criteria'),
    _ = require('lodash'),
    async = require('async'),
    hasOwnProperty = utils.object.hasOwnProperty;

module.exports = {

  /**
   * Find a single record that meets criteria
   *
   * @param {Object} criteria to search
   * @param {Function} callback
   * @return Deferred object if no callback
   */

  findOne: function(criteria, cb) {
    var self = this;

    if(typeof criteria === 'function') {
      cb = criteria;
      criteria = null;
    }

    // Check if criteria is an integer or string and normalize criteria
    // to object, using the specified primary key field.
    criteria = normalize.expandPK(self, criteria);

    // Normalize criteria
    criteria = normalize.criteria(criteria);

    // Return Deferred or pass to adapter
    if(typeof cb !== 'function') {
      return new Deferred(this, this.findOne, criteria);
    }

    // Transform Search Criteria
    criteria = self._transformer.serialize(criteria);

    // If there was something defined in the criteria that would return no results, don't even
    // run the query and just return an empty result set.
    if(criteria === false) {
      return cb(null, null);
    }

    // Build up an operations set
    var operations = new Operations(self, criteria, 'findOne');

    // Run the operations
    operations.run(function(err, values) {
      if(err) return cb(err);
      if(!values.cache) return cb();

      // If no joins are used grab the only item from the cache and pass to the returnResults
      // function.
      if(!criteria.joins) {
        values = values.cache[self.identity];
        return returnResults(values);
      }

      // If the values are already combined, return the results
      if(values.combined) {
        return returnResults(values.cache[self.identity]);
      }

      // Find the primaryKey of the current model so it can be passed down to the integrator.
      // Use 'id' as a good general default;
      var primaryKey = 'id';

      Object.keys(self._schema.schema).forEach(function(key) {
        if(self._schema.schema[key].hasOwnProperty('primaryKey')) primaryKey = key;
      });

      // Perform in-memory joins
      Integrator(values.cache, criteria.joins, primaryKey, function(err, results) {
        if(err) return cb(err);
        if(!results) return cb();

        // We need to run one last check on the results using the criteria. This allows a self
        // association where we end up with two records in the cache both having each other as
        // embedded objects and we only want one result. However we need to filter any join criteria
        // out of the top level where query so that searchs by primary key still work.
        var tmpCriteria = _.cloneDeep(criteria.where);
        if(!tmpCriteria) tmpCriteria = {};

        criteria.joins.forEach(function(join) {
          if(!hasOwnProperty(join, 'alias')) return;
          if(!hasOwnProperty(tmpCriteria, join.alias)) return;
          delete tmpCriteria[join.alias];
        });

        // Pass results into Waterline-Criteria
        var _criteria = { where: tmpCriteria };
        results = waterlineCriteria('parent', { parent: results }, _criteria).results;

        // Serialize values coming from an in-memory join before modelizing
        var _results = [];
        results.forEach(function(res) {

          // Go Ahead and perform any sorts on the associated data
          criteria.joins.forEach(function(join) {
            if(!join.criteria) return;
            var c = normalize.criteria(join.criteria);
            if(!c.sort) return;

            var alias = join.alias;
            res[alias] = sorter(res[alias], c.sort);
          });

          var val = self._transformer.serialize(res);
          _results.push(val);
        });

        returnResults(_results);
      });

      function returnResults(results) {

        if(!results) return cb();

        // Normalize results to an array
        if(!Array.isArray(results) && results) results = [results];

        // Unserialize each of the results before attempting any join logic on them
        var unserializedModels = [];
        results.forEach(function(result) {
          unserializedModels.push(self._transformer.unserialize(result));
        });

        var models = [];
        var joins = criteria.joins ? criteria.joins : [];
        var data = new Joins(joins, unserializedModels, self._schema.schema, self.waterline.collections);

        // If `data.models` is invalid (not an array) return early to avoid getting into trouble.
        if (!data || !data.models || !data.models.forEach) {
          return cb(new Error('Values returned from operations set are not an array...'));
        }

        // Create a model for the top level values
        data.models.forEach(function(model) {
          models.push(new self._model(model, data.options));
        });

        cb(null, models[0]);
      }
    });
  },

  /**
   * Find All Records that meet criteria
   *
   * @param {Object} search criteria
   * @param {Object} options
   * @param {Function} callback
   * @return Deferred object if no callback
   */

  find: function(criteria, options, cb) {
    var self = this;

    var usage = utils.capitalize(this.identity) + '.find([criteria],[options]).exec(callback|switchback)';

    if(typeof criteria === 'function') {
      cb = criteria;
      criteria = null;
      options = null;
    }

    if(typeof options === 'function') {
      cb = options;
      options = null;
    }

    // Check if criteria is an integer or string and normalize criteria
    // to object, using the specified primary key field.
    criteria = normalize.expandPK(self, criteria);

    // Normalize criteria
    criteria = normalize.criteria(criteria);

    // Fold in options
    if(options === Object(options) && criteria === Object(criteria)) {
      criteria = _.extend({}, criteria, options);
    }

    // Validate Arguments
    if(typeof criteria === 'function' || typeof options === 'criteria') {
      return usageError('Invalid options specified!', usage, cb);
    }

    // Return Deferred or pass to adapter
    if(typeof cb !== 'function') {
      return new Deferred(this, this.find, criteria);
    }

    // If there was something defined in the criteria that would return no results, don't even
    // run the query and just return an empty result set.
    if(criteria === false) {
      return cb(null, []);
    }

    // Transform Search Criteria
    if (!self._transformer) {
      throw new Error('Waterline can not access transformer-- maybe the context of the method is being overridden?');
    }

    criteria = self._transformer.serialize(criteria);

    // Build up an operations set
    var operations = new Operations(self, criteria, 'find');

    // Run the operations
    operations.run(function(err, values) {
      if(err) return cb(err);
      if(!values.cache) return cb();

      // If no joins are used grab current collection's item from the cache and pass to the returnResults
      // function.
      if(!criteria.joins) {
        values = values.cache[self.identity];
        return returnResults(values);
      }

      // If the values are already combined, return the results
      if(values.combined) {
        return returnResults(values.cache[self.identity]);
      }

      // Find the primaryKey of the current model so it can be passed down to the integrator.
      // Use 'id' as a good general default;
      var primaryKey = 'id';

      Object.keys(self._schema.schema).forEach(function(key) {
        if(self._schema.schema[key].hasOwnProperty('primaryKey')) primaryKey = key;
      });

      // Perform in-memory joins
      Integrator(values.cache, criteria.joins, primaryKey, function(err, results) {
        if(err) return cb(err);
        if(!results) return cb();

        // We need to run one last check on the results using the criteria. This allows a self
        // association where we end up with two records in the cache both having each other as
        // embedded objects and we only want one result. However we need to filter any join criteria
        // out of the top level where query so that searchs by primary key still work.
        var tmpCriteria = _.cloneDeep(criteria.where);
        if(!tmpCriteria) tmpCriteria = {};

        criteria.joins.forEach(function(join) {
          if(!hasOwnProperty(join, 'alias')) return;
          if(!hasOwnProperty(tmpCriteria, join.alias)) return;
          delete tmpCriteria[join.alias];
        });

        // Pass results into Waterline-Criteria
        var _criteria = { where: tmpCriteria };
        results = waterlineCriteria('parent', { parent: results }, _criteria).results;

        // Serialize values coming from an in-memory join before modelizing
        var _results = [];
        results.forEach(function(res) {

          // Go Ahead and perform any sorts on the associated data
          criteria.joins.forEach(function(join) {
            if(!join.criteria) return;
            var c = normalize.criteria(join.criteria);
            if(!c.sort) return;

            var alias = join.alias;
            res[alias] = sorter(res[alias], c.sort);
          });

          var val = self._transformer.serialize(res);
          _results.push(val);
        });

        returnResults(_results);
      });

      function returnResults(results) {

        if(!results) return cb(null, []);

        // Normalize results to an array
        if(!Array.isArray(results) && results) results = [results];

        // Unserialize each of the results before attempting any join logic on them
        var unserializedModels = [];

        if(results) {
          results.forEach(function(result) {
            unserializedModels.push(self._transformer.unserialize(result));
          });
        }

        var models = [];
        var joins = criteria.joins ? criteria.joins : [];
        var data = new Joins(joins, unserializedModels, self._schema.schema, self.waterline.collections);

        // If `data.models` is invalid (not an array) return early to avoid getting into trouble.
        if (!data || !data.models || !data.models.forEach) {
          return cb(new Error('Values returned from operations set are not an array...'));
        }

        // Create a model for the top level values
        data.models.forEach(function(model) {
          models.push(new self._model(model, data.options));
        });

        cb(null, models);
      }

    });
  },

  where: function() {
    this.find.apply(this, Array.prototype.slice.call(arguments));
  },

  select: function() {
    this.find.apply(this, Array.prototype.slice.call(arguments));
  },

  /**
   * findAll
   * [[ Deprecated! ]]
   *
   * @param  {Object}   criteria
   * @param  {Object}   options
   * @param  {Function} cb
   */
  findAll: function(criteria, options, cb) {
    if(typeof criteria === 'function') {
      cb = criteria;
      criteria = null;
      options = null;
    }

    if(typeof options === 'function') {
      cb = options;
      options = null;
    }

    // Return Deferred or pass to adapter
    if(typeof cb !== 'function') {
      return new Deferred(this, this.findAll, criteria);
    }

    cb(new Error('In Waterline >= 0.9, findAll() has been deprecated in favor of find().' +
                '\nPlease visit the migration guide at http://sailsjs.org for help upgrading.'));
  }

};
<commit_msg>parse OR criteria in final criteria pass<commit_after>/**
 * Basic Finder Queries
 */

var usageError = require('../../utils/usageError'),
    utils = require('../../utils/helpers'),
    normalize = require('../../utils/normalize'),
    sorter = require('../../utils/sorter'),
    Deferred = require('../deferred'),
    Joins = require('./joins'),
    Operations = require('./operations'),
    Integrator = require('../integrator'),
    waterlineCriteria = require('waterline-criteria'),
    _ = require('lodash'),
    async = require('async'),
    hasOwnProperty = utils.object.hasOwnProperty;

module.exports = {

  /**
   * Find a single record that meets criteria
   *
   * @param {Object} criteria to search
   * @param {Function} callback
   * @return Deferred object if no callback
   */

  findOne: function(criteria, cb) {
    var self = this;

    if(typeof criteria === 'function') {
      cb = criteria;
      criteria = null;
    }

    // Check if criteria is an integer or string and normalize criteria
    // to object, using the specified primary key field.
    criteria = normalize.expandPK(self, criteria);

    // Normalize criteria
    criteria = normalize.criteria(criteria);

    // Return Deferred or pass to adapter
    if(typeof cb !== 'function') {
      return new Deferred(this, this.findOne, criteria);
    }

    // Transform Search Criteria
    criteria = self._transformer.serialize(criteria);

    // If there was something defined in the criteria that would return no results, don't even
    // run the query and just return an empty result set.
    if(criteria === false) {
      return cb(null, null);
    }

    // Build up an operations set
    var operations = new Operations(self, criteria, 'findOne');

    // Run the operations
    operations.run(function(err, values) {
      if(err) return cb(err);
      if(!values.cache) return cb();

      // If no joins are used grab the only item from the cache and pass to the returnResults
      // function.
      if(!criteria.joins) {
        values = values.cache[self.identity];
        return returnResults(values);
      }

      // If the values are already combined, return the results
      if(values.combined) {
        return returnResults(values.cache[self.identity]);
      }

      // Find the primaryKey of the current model so it can be passed down to the integrator.
      // Use 'id' as a good general default;
      var primaryKey = 'id';

      Object.keys(self._schema.schema).forEach(function(key) {
        if(self._schema.schema[key].hasOwnProperty('primaryKey')) primaryKey = key;
      });

      // Perform in-memory joins
      Integrator(values.cache, criteria.joins, primaryKey, function(err, results) {
        if(err) return cb(err);
        if(!results) return cb();

        // We need to run one last check on the results using the criteria. This allows a self
        // association where we end up with two records in the cache both having each other as
        // embedded objects and we only want one result. However we need to filter any join criteria
        // out of the top level where query so that searchs by primary key still work.
        var tmpCriteria = _.cloneDeep(criteria.where);
        if(!tmpCriteria) tmpCriteria = {};

        criteria.joins.forEach(function(join) {
          if(!hasOwnProperty(join, 'alias')) return;

          // Check for `OR` criteria
          if(hasOwnProperty(tmpCriteria, 'or')) {
            tmpCriteria.or.forEach(function(search) {
              if(!hasOwnProperty(search, join.alias)) return;
              delete search[join.alias];
            });
            return;
          }

          if(!hasOwnProperty(tmpCriteria, join.alias)) return;
          delete tmpCriteria[join.alias];
        });

        // Pass results into Waterline-Criteria
        var _criteria = { where: tmpCriteria };
        results = waterlineCriteria('parent', { parent: results }, _criteria).results;

        // Serialize values coming from an in-memory join before modelizing
        var _results = [];
        results.forEach(function(res) {

          // Go Ahead and perform any sorts on the associated data
          criteria.joins.forEach(function(join) {
            if(!join.criteria) return;
            var c = normalize.criteria(join.criteria);
            if(!c.sort) return;

            var alias = join.alias;
            res[alias] = sorter(res[alias], c.sort);
          });

          var val = self._transformer.serialize(res);
          _results.push(val);
        });

        returnResults(_results);
      });

      function returnResults(results) {

        if(!results) return cb();

        // Normalize results to an array
        if(!Array.isArray(results) && results) results = [results];

        // Unserialize each of the results before attempting any join logic on them
        var unserializedModels = [];
        results.forEach(function(result) {
          unserializedModels.push(self._transformer.unserialize(result));
        });

        var models = [];
        var joins = criteria.joins ? criteria.joins : [];
        var data = new Joins(joins, unserializedModels, self._schema.schema, self.waterline.collections);

        // If `data.models` is invalid (not an array) return early to avoid getting into trouble.
        if (!data || !data.models || !data.models.forEach) {
          return cb(new Error('Values returned from operations set are not an array...'));
        }

        // Create a model for the top level values
        data.models.forEach(function(model) {
          models.push(new self._model(model, data.options));
        });

        cb(null, models[0]);
      }
    });
  },

  /**
   * Find All Records that meet criteria
   *
   * @param {Object} search criteria
   * @param {Object} options
   * @param {Function} callback
   * @return Deferred object if no callback
   */

  find: function(criteria, options, cb) {
    var self = this;

    var usage = utils.capitalize(this.identity) + '.find([criteria],[options]).exec(callback|switchback)';

    if(typeof criteria === 'function') {
      cb = criteria;
      criteria = null;
      options = null;
    }

    if(typeof options === 'function') {
      cb = options;
      options = null;
    }

    // Check if criteria is an integer or string and normalize criteria
    // to object, using the specified primary key field.
    criteria = normalize.expandPK(self, criteria);

    // Normalize criteria
    criteria = normalize.criteria(criteria);

    // Fold in options
    if(options === Object(options) && criteria === Object(criteria)) {
      criteria = _.extend({}, criteria, options);
    }

    // Validate Arguments
    if(typeof criteria === 'function' || typeof options === 'criteria') {
      return usageError('Invalid options specified!', usage, cb);
    }

    // Return Deferred or pass to adapter
    if(typeof cb !== 'function') {
      return new Deferred(this, this.find, criteria);
    }

    // If there was something defined in the criteria that would return no results, don't even
    // run the query and just return an empty result set.
    if(criteria === false) {
      return cb(null, []);
    }

    // Transform Search Criteria
    if (!self._transformer) {
      throw new Error('Waterline can not access transformer-- maybe the context of the method is being overridden?');
    }

    criteria = self._transformer.serialize(criteria);

    // Build up an operations set
    var operations = new Operations(self, criteria, 'find');

    // Run the operations
    operations.run(function(err, values) {
      if(err) return cb(err);
      if(!values.cache) return cb();

      // If no joins are used grab current collection's item from the cache and pass to the returnResults
      // function.
      if(!criteria.joins) {
        values = values.cache[self.identity];
        return returnResults(values);
      }

      // If the values are already combined, return the results
      if(values.combined) {
        return returnResults(values.cache[self.identity]);
      }

      // Find the primaryKey of the current model so it can be passed down to the integrator.
      // Use 'id' as a good general default;
      var primaryKey = 'id';

      Object.keys(self._schema.schema).forEach(function(key) {
        if(self._schema.schema[key].hasOwnProperty('primaryKey')) primaryKey = key;
      });

      // Perform in-memory joins
      Integrator(values.cache, criteria.joins, primaryKey, function(err, results) {
        if(err) return cb(err);
        if(!results) return cb();

        // We need to run one last check on the results using the criteria. This allows a self
        // association where we end up with two records in the cache both having each other as
        // embedded objects and we only want one result. However we need to filter any join criteria
        // out of the top level where query so that searchs by primary key still work.
        var tmpCriteria = _.cloneDeep(criteria.where);
        if(!tmpCriteria) tmpCriteria = {};

        criteria.joins.forEach(function(join) {
          if(!hasOwnProperty(join, 'alias')) return;

          // Check for `OR` criteria
          if(hasOwnProperty(tmpCriteria, 'or')) {
            tmpCriteria.or.forEach(function(search) {
              if(!hasOwnProperty(search, join.alias)) return;
              delete search[join.alias];
            });
            return;
          }

          if(!hasOwnProperty(tmpCriteria, join.alias)) return;
          delete tmpCriteria[join.alias];
        });

        // Pass results into Waterline-Criteria
        var _criteria = { where: tmpCriteria };
        results = waterlineCriteria('parent', { parent: results }, _criteria).results;

        // Serialize values coming from an in-memory join before modelizing
        var _results = [];
        results.forEach(function(res) {

          // Go Ahead and perform any sorts on the associated data
          criteria.joins.forEach(function(join) {
            if(!join.criteria) return;
            var c = normalize.criteria(join.criteria);
            if(!c.sort) return;

            var alias = join.alias;
            res[alias] = sorter(res[alias], c.sort);
          });

          var val = self._transformer.serialize(res);
          _results.push(val);
        });

        returnResults(_results);
      });

      function returnResults(results) {

        if(!results) return cb(null, []);

        // Normalize results to an array
        if(!Array.isArray(results) && results) results = [results];

        // Unserialize each of the results before attempting any join logic on them
        var unserializedModels = [];

        if(results) {
          results.forEach(function(result) {
            unserializedModels.push(self._transformer.unserialize(result));
          });
        }

        var models = [];
        var joins = criteria.joins ? criteria.joins : [];
        var data = new Joins(joins, unserializedModels, self._schema.schema, self.waterline.collections);

        // If `data.models` is invalid (not an array) return early to avoid getting into trouble.
        if (!data || !data.models || !data.models.forEach) {
          return cb(new Error('Values returned from operations set are not an array...'));
        }

        // Create a model for the top level values
        data.models.forEach(function(model) {
          models.push(new self._model(model, data.options));
        });

        cb(null, models);
      }

    });
  },

  where: function() {
    this.find.apply(this, Array.prototype.slice.call(arguments));
  },

  select: function() {
    this.find.apply(this, Array.prototype.slice.call(arguments));
  },

  /**
   * findAll
   * [[ Deprecated! ]]
   *
   * @param  {Object}   criteria
   * @param  {Object}   options
   * @param  {Function} cb
   */
  findAll: function(criteria, options, cb) {
    if(typeof criteria === 'function') {
      cb = criteria;
      criteria = null;
      options = null;
    }

    if(typeof options === 'function') {
      cb = options;
      options = null;
    }

    // Return Deferred or pass to adapter
    if(typeof cb !== 'function') {
      return new Deferred(this, this.findAll, criteria);
    }

    cb(new Error('In Waterline >= 0.9, findAll() has been deprecated in favor of find().' +
                '\nPlease visit the migration guide at http://sailsjs.org for help upgrading.'));
  }

};
<|endoftext|>


<commit_before>/*! @license ©2014 Miel Vander Sande - Multimedia Lab / iMinds / Ghent University */
/* Writer that serializes a SPARQL query result application/sparql+xml */

var SparqlResultWriter = require('./SparqlResultWriter'),
    _ = require('lodash'),
    util = require('../util/RdfUtil'),
    xml = require('xml');

function SparqlXMLResultWriter(sparqlIterator) {
  SparqlResultWriter.call(this, true, sparqlIterator);
}
SparqlResultWriter.inherits(SparqlXMLResultWriter);

SparqlXMLResultWriter.prototype._writeHead = function (variableNames) {
  var root = this._root = xml.element({ _attr: { xlmns: 'http://www.w3.org/2005/sparql-results#' } }),
      results = this._results = xml.element({}), self = this;
  xml({ sparql: root }, { stream: true, indent: '  ', declaration: true })
     .on('data', function (chunk) { self._push(chunk + '\n'); });
  if (variableNames.length)
    root.push({ head: variableNames.map(function (v) { return { variable: { _attr: { name: v } } }; }) });
  root.push({ results: results });
};

SparqlXMLResultWriter.prototype._writeBindings = function (result) {
  this._results.push({
    result: _.map(result, function (value, variable) {
      var xmlValue, lang, type;
      if (!util.isLiteral(value))
        xmlValue = util.isBlank(value) ? { bnode: value } : { uri: value };
      else {
        xmlValue = { literal: util.getLiteralValue(value) };
        if (lang = util.getLiteralLanguage(value))
          xmlValue.literal = [{ _attr: { 'xml:lang': lang } }, xmlValue.literal];
        else if (type = util.getLiteralType(value))
          xmlValue.literal = [{ _attr: {   datatype: type } }, xmlValue.literal];
      }
      return { binding: [{ _attr: { name: variable.substring(1) } }, xmlValue] };
    })
  });
};

SparqlXMLResultWriter.prototype._writeBoolean = function (result) {
  this._root.push({ boolean: result });
};

SparqlXMLResultWriter.prototype._end = function () {
  this._results.close();
  this._root.close();
  SparqlResultWriter.prototype._end.call(this);
};

module.exports = SparqlXMLResultWriter;
<commit_msg>Fixed empty XML bindings on unbounded results (OPTIONAL)<commit_after>/*! @license ©2014 Miel Vander Sande - Multimedia Lab / iMinds / Ghent University */
/* Writer that serializes a SPARQL query result application/sparql+xml */

var SparqlResultWriter = require('./SparqlResultWriter'),
    _ = require('lodash'),
    util = require('../util/RdfUtil'),
    xml = require('xml');

function SparqlXMLResultWriter(sparqlIterator) {
  SparqlResultWriter.call(this, true, sparqlIterator);
}
SparqlResultWriter.inherits(SparqlXMLResultWriter);

SparqlXMLResultWriter.prototype._writeHead = function (variableNames) {
  var root = this._root = xml.element({ _attr: { xlmns: 'http://www.w3.org/2005/sparql-results#' } }),
      results = this._results = xml.element({}), self = this;
  xml({ sparql: root }, { stream: true, indent: '  ', declaration: true })
     .on('data', function (chunk) { self._push(chunk + '\n'); });
  if (variableNames.length)
    root.push({ head: variableNames.map(function (v) { return { variable: { _attr: { name: v } } }; }) });
  root.push({ results: results });
};

SparqlXMLResultWriter.prototype._writeBindings = function (result) {
  //Unbounded variables cannot be part of xml
  result = _.omit(result, function (value) {
    console.log(value);
    return value === undefined || value === null;
  });
  this._results.push({
    result: _.map(result, function (value, variable) {
      var xmlValue, lang, type;
      if (!util.isLiteral(value))
        xmlValue = util.isBlank(value) ? { bnode: value } : { uri: value };
      else {
        xmlValue = { literal: util.getLiteralValue(value) };
        if (lang = util.getLiteralLanguage(value))
          xmlValue.literal = [{ _attr: { 'xml:lang': lang } }, xmlValue.literal];
        else if (type = util.getLiteralType(value))
          xmlValue.literal = [{ _attr: {   datatype: type } }, xmlValue.literal];
      }
      return { binding: [{ _attr: { name: variable.substring(1) } }, xmlValue] };
    })
  });
};

SparqlXMLResultWriter.prototype._writeBoolean = function (result) {
  this._root.push({ boolean: result });
};

SparqlXMLResultWriter.prototype._end = function () {
  this._results.close();
  this._root.close();
  SparqlResultWriter.prototype._end.call(this);
};

module.exports = SparqlXMLResultWriter;
<|endoftext|>


<commit_before>var COMMONJS = typeof module == 'object' && module.exports;
if (COMMONJS) {
  var md5 = require('./md5');
  var sleep = require('./utils').sleep;
}
const bilibiliVideoProvider = async(cid, avid, page = 1, credentials = 'include', retries = 5, retryDelay = 500) => {
    let url={};
    url.low = 'https://www.biliplus.com/BPplayurl.php?cid=' + cid + '&otype=json&quality=1&type=mp4';
    url.mp4 = 'https://www.biliplus.com/BPplayurl.php?cid=' + cid + '&otype=json&type=mp4';
    url.flv = 'https://www.biliplus.com/BPplayurl.php?cid=' + cid + '&otype=json&type=flv';
    const parseJsonforFlvjs = (json) => {
        if (!json) return console.warn('parseJsonforFlvjs Failed: No JSON provided.');
        var mediaDataSource = {};
        mediaDataSource.type = 'flv';
        if (parseInt(json.timelength)) mediaDataSource.duration = parseInt(json.timelength);
        if (json.durl) mediaDataSource.segments = json.durl.map(obj => ({
            "duration": obj.length,
            "filesize": obj.size,
            "url": obj.url.match("ws.acgvideo.com") ? obj.url : obj.url.replace(/^http:\/\//, "https://")
        }));
        if (!json.durl) return console.warn('parseJsonforFlvjs Failed: Nothing to play.');
        return mediaDataSource;
    };
    const getVideoJson=async(url,retries) => {
      let json;
      try{
        json = await fetch(url,{credentials}).then(response => response.json());
      }catch(error){
        if (--retries>0) {
          await sleep(retryDelay);
          return await getVideoJson(type,retries);
        }
        else json = {
          'code': -1,
          'message': error
        };
      }
      return json;
    }
    let video={};
    for (let i of ['low','mp4','flv']) video[i]=await getVideoJson(url[i]);
    video.mediaDataSource = parseJsonforFlvjs(video.flv);
    return video;
};
if (COMMONJS) module.exports = bilibiliForeignVideoProvider;<commit_msg>bilibiliForeignVideoProvider: use es6 import/export<commit_after>import md5 from './md5';
import {sleep} from './utils';

const bilibiliVideoProvider = async(cid, avid, page = 1, credentials = 'include', retries = 5, retryDelay = 500) => {
    let url={};
    url.low = 'https://www.biliplus.com/BPplayurl.php?cid=' + cid + '&otype=json&quality=1&type=mp4';
    url.mp4 = 'https://www.biliplus.com/BPplayurl.php?cid=' + cid + '&otype=json&type=mp4';
    url.flv = 'https://www.biliplus.com/BPplayurl.php?cid=' + cid + '&otype=json&type=flv';
    const parseJsonforFlvjs = (json) => {
        if (!json) return console.warn('parseJsonforFlvjs Failed: No JSON provided.');
        var mediaDataSource = {};
        mediaDataSource.type = 'flv';
        if (parseInt(json.timelength)) mediaDataSource.duration = parseInt(json.timelength);
        if (json.durl) mediaDataSource.segments = json.durl.map(obj => ({
            "duration": obj.length,
            "filesize": obj.size,
            "url": obj.url.match("ws.acgvideo.com") ? obj.url : obj.url.replace(/^http:\/\//, "https://")
        }));
        if (!json.durl) return console.warn('parseJsonforFlvjs Failed: Nothing to play.');
        return mediaDataSource;
    };
    const getVideoJson=async(url,retries) => {
      let json;
      try{
        json = await fetch(url,{credentials}).then(response => response.json());
      }catch(error){
        if (--retries>0) {
          await sleep(retryDelay);
          return await getVideoJson(type,retries);
        }
        else json = {
          'code': -1,
          'message': error
        };
      }
      return json;
    }
    let video={};
    for (let i of ['low','mp4','flv']) video[i]=await getVideoJson(url[i]);
    video.mediaDataSource = parseJsonforFlvjs(video.flv);
    return video;
};
export default bilibiliVideoProvider;<|endoftext|>


<commit_before>/**
 * @module prop
 */

/**
 *
 * The default properties
 */
const defaultProp = {
  writable: true,
  enumerable: true,
  configurable: false,
  value: undefined
};

/**
 *
 * The prototype for the prop object
 * all methods are chainable
 */
const propProto = {
  _enum(v){
    this.enumerable=v;
    return this;
  },

  _writ(v){
    this.writable=v;
    return this;
  },

  _conf(v){
    this.configurable=v;
    return this;
  },

  get hidden() {
    return create(this)._enum(false);
  },

  get visible() {
    return create(this)._enum(true);
  },

  get constant() {
    return create(this)._writ(false);
  },

  get variable() {
    return create(this)._writ(true);
  },

  get frozen() {
    return create(this)._conf(false);
  },

  get thawed() {
    return create(this)._conf(true);
  }
};

function create(props) {
  const prop = makeTarget();
  Object.setPrototypeOf(prop, propProto);
  prop._enum(props.enumerable);
  prop._writ(props.writable);
  prop._conf(props.configurable);
  Object.defineProperty(prop, 'value',
    Object.assign(
      Object.getOwnPropertyDescriptor(props, 'value'),
      {configurable:true}
    )
  );
  return prop;
}

function makeTarget() {
  const prop = function (val, isMethod) {
    var rtn = create(prop);
    if (arguments.length > 0) {
      if (typeof val === 'function' && !isMethod) {
        Object.defineProperty(rtn, 'value', {get: val});
      } else {
        Object.defineProperty(rtn, 'value', {value: val});
      }
    }
    return rtn;
  };
  return prop;
}

module.exports = create(defaultProp);
<commit_msg>removed the use of assign (which is also ES6)<commit_after>/**
 * @module prop
 */

/**
 *
 * The default properties
 */
const defaultProp = {
  writable: true,
  enumerable: true,
  configurable: false,
  value: undefined
};

/**
 *
 * The prototype for the prop object
 * all methods are chainable
 */
const propProto = {
  _enum(v){
    this.enumerable=v;
    return this;
  },

  _writ(v){
    this.writable=v;
    return this;
  },

  _conf(v){
    this.configurable=v;
    return this;
  },

  get hidden() {
    return create(this)._enum(false);
  },

  get visible() {
    return create(this)._enum(true);
  },

  get constant() {
    return create(this)._writ(false);
  },

  get variable() {
    return create(this)._writ(true);
  },

  get frozen() {
    return create(this)._conf(false);
  },

  get thawed() {
    return create(this)._conf(true);
  }
};

function create(props) {
  const prop = makeTarget();
  Object.setPrototypeOf(prop, propProto);
  prop._enum(props.enumerable);
  prop._writ(props.writable);
  prop._conf(props.configurable);
  Object.defineProperty(prop, 'value',
    Object.getOwnPropertyDescriptor(props, 'value')
  );
  return prop;
}

function makeTarget() {
  const prop = function (val, isMethod) {
    var rtn = create(prop);
    if (arguments.length > 0) {
      if (typeof val === 'function' && !isMethod) {
        Object.defineProperty(rtn, 'value', {get: val});
      } else {
        Object.defineProperty(rtn, 'value', {value: val});
      }
    }
    return rtn;
  };
  return prop;
}

module.exports = create(defaultProp);
<|endoftext|>


<commit_before>$(document).ready(function(){
	//BEGIN GLOBAL VARIABLES
	var quesIDs;
	var curQues;
	var totalQues;
	var usrAns;
	var ans;
	//END
	init()

	function init() {
		quesIDs = [];
		curQues = 0;
		totalQues = 5;
		generateQuesIDs(totalQues);
		displayQues();
	}

	$('#submit').click(function(){
		if ($('input[name=selection]').is(':checked')) {
			checkAns()
		} else {
			alert("Select an answer!")
		}
	})

	function checkAns() {
		usrAns = $('input[name=selection]:checked').attr('id');
		ans = quDB.questions[quesIDs[curQues]].ans;
		if (usrAns == ans) {
			alert("Correct!");
		} else {
			alert("Incorrect!\nThe answer was " + ans);
		}
		alert("You've answered " + curQues + " questions.")
		displayQues()
	}

	function generateQuesIDs(amt) {
		for (var i=0;i<amt;i++) {
			quesIDs[i] = Math.floor(Math.random()*20);
			while (quesIDs[i] == quesIDs[i-1]) {
				quesIDs[i] = Math.floor(Math.random()*20);
			}
		}
	}

	function displayQues() {
		if (curQues == totalQues) {
			finish()
		} else {
			curQues++;
			var qu = quDB.questions[quesIDs[curQues]].qu;
			var c1 = quDB.questions[quesIDs[curQues]].c1;
			var c2 = quDB.questions[quesIDs[curQues]].c2;
			var c3 = quDB.questions[quesIDs[curQues]].c3;
			var c4 = quDB.questions[quesIDs[curQues]].c4;
			$('.question').html(qu);
			$('#labl_c1').html(c1);
			$('#labl_c2').html(c2);
			$('#labl_c3').html(c3);
			$('#labl_c4').html(c4);
		}
	}

	function finish() {
		alert("You've answered all the questions!");
	}
})
<commit_msg>Daily-ish update thingy<commit_after>$(document).ready(function(){
	//BEGIN GLOBAL VARIABLES
	var quesIDs;
	var curQues;
	var totalQues;
	var usrAns;
	var ans;
	//END
	init()

	function init() {
		//VAR DECS
		quesIDs = [];
		curQues = 0;
		totalQues = 5;
		//PAGE STYLING
		$('#submit').prop('disabled', false);
		$('#submit').val("Submit");
		//INIT
		generateQuesIDs(totalQues);
		displayQues();
	}

	$('input[name=debug]').click(function(){
		if ($('input[name=debug]').is(':checked')) {
			$('#submit').val("CurQues: " + curQues);
		} else {
			$('#submit').val("Submit");
		}
	});

	$('#submit').click(function(){
		if ($('input[name=selection]').is(':checked') && !($('input[name=debug]').is(':checked'))) {
			checkAns()
		} else if ($('input[name=debug]').is(':checked')) { //DEBUG
			displayQues(); //DEBUG
			$('#submit').val("CurQues: " + curQues); //DEBUG
		} else {
			alert("Select an answer!")
		}
	})

	function checkAns() {
		usrAns = $('input[name=selection]:checked').attr('id');
		ans = quDB.questions[quesIDs[curQues]].ans;
		if (usrAns == ans) {
			alert("Correct!"); //DEBUG
		} else {
			alert("Incorrect!\nThe answer was " + ans); //DEBUG
		}
		displayQues()
	}

	function generateQuesIDs(amt) {
		for (var i=0;i<=amt;i++) {
			quesIDs[i] = Math.floor(Math.random()*20);
			while (quesIDs[i] == quesIDs[i-1]) {
				quesIDs[i] = Math.floor(Math.random()*20);
			}
		}
	}

	function displayQues() {
		if (curQues == totalQues) {
			finish()
		} else {
			curQues++;
			var qu = quDB.questions[quesIDs[curQues]].qu;
			var c1 = quDB.questions[quesIDs[curQues]].c1;
			var c2 = quDB.questions[quesIDs[curQues]].c2;
			var c3 = quDB.questions[quesIDs[curQues]].c3;
			var c4 = quDB.questions[quesIDs[curQues]].c4;
			$('.question').html(qu);
			$('#labl_c1').html(c1);
			$('#labl_c2').html(c2);
			$('#labl_c3').html(c3);
			$('#labl_c4').html(c4);
		}
	}

	function finish() {
		$('#submit').prop('disabled', true);
		alert("You've answered all the questions!");
	}
})
<|endoftext|>


<commit_before>window.onload = function () {
    'use strict';

    // settings
    var numberOfRats = 10;
    var rerouteProbability = 0.02;
    var speedChangeProbability = 0.1;
    var sleepProbability = 0.005; // happens on average every 200 ticks

    var canvas = document.getElementById('canvas');
    var ctx = canvas.getContext('2d');

    // velocity sign for each direction
    var velocitySigns = {
        n: {x: 0, y: -1},
        e: {x: +1, y: 0},
        s: {x: 0, y: +1},
        w: {x: -1, y: 0}
    };

    // Static wall
    var wall = {
        update: function () {
        },
        // Render self
        draw: function () {
            ctx.fillStyle = '#000';
            ctx.strokeStyle = '#fff';
            ctx.lineWidth = 1;
            ctx.fillRect(this.x, this.y, this.width, this.height);
            ctx.strokeRect(this.x, this.y, this.width, this.height);
        },
        // Set object position and size
        init: function (x, y, width, height) {
            this.x = x;
            this.y = y;
            this.width = width;
            this.height = height;
        },
        // Check if coordinate is inside the wall. Accepts 2 numbers or an object {x, y}
        has: function (x, y) {
            if (typeof x === 'object') {
                y = x.y;
                x = x.x;
            }

            return x >= this.x &&
                x <= this.x + this.width &&
                y >= this.y &&
                y <= this.y + this.height;
        }
    };

    // Main actor
    var rat = {
        width: 15,
        height: 15,
        direction: 'n', // we need some value to begin looking around
        speed: 3, // scalar speed

        timers: {}, // any timers go here

        // Measure coordinates change
        getVelocity: function () {
            return {
                x: (velocitySigns[this.direction]).x * this.speed,
                y: (velocitySigns[this.direction]).y * this.speed
            };
        },

        // Where our new edges will be
        getNewEdges: function () {
            return this.getEdges(this.getVelocity());
        },

        // Where our new corners will be
        getNewCorners: function () {
            return this.getCorners({x: 0, y: 0});
        },

        // Where our corners are
        getCorners: function (velocity) {
            var edges = this.getEdges(velocity);

            return {
                nw: {x: edges.w, y: edges.n},
                ne: {x: edges.e, y: edges.n},
                sw: {x: edges.w, y: edges.s},
                se: {x: edges.e, y: edges.s}
            };
        },

        // Where our edges are
        getEdges: function (velocity) {
            return {
                e: this.x + this.width + velocity.x,
                s: this.y + this.height + velocity.y,
                n: this.y + velocity.y,
                w: this.x + velocity.x
            };
        },

        // Have a look around and see which directions are available
        look: function () {
            var self = this;
            var edges = this.getNewEdges();
            var corners = this.getNewCorners();

            // check with game borders
            var directions = {
                e: edges.e <= game.width,
                s: edges.s <= game.height,
                n: edges.n >= 0,
                w: edges.w >= 0
            };

            this.possibleDirections = [];

            // check with walls (will one of my front edges will be inside any wall?)
            game.walls.forEach(function (wall) {
                directions.e = directions.e && !wall.has(corners.ne) && !wall.has(corners.se);
                directions.s = directions.s && !wall.has(corners.se) && !wall.has(corners.sw);
                directions.n = directions.n && !wall.has(corners.ne) && !wall.has(corners.nw);
                directions.w = directions.w && !wall.has(corners.nw) && !wall.has(corners.sw);
            });

            // make an array of possible directions
            Object.keys(directions).forEach(function (direction) {
                if (directions[direction]) {
                    self.possibleDirections.push(direction);
                }
            });
        },

        /**
         * Choose movement direction.
         * If has parameter, tends to keep it. Yet has a small chance to change direction.
         * If no parameter is given, choose randomly.
         *
         * @param [direction] previous direction.
         */
        chooseDirection: function (direction) {
            // no argument or argument is impossible or chance
            if (!direction || this.possibleDirections.indexOf(direction) < 0 || prob(rerouteProbability)) {
                this.direction = this.possibleDirections[rnd(this.possibleDirections.length - 1)];
                //this.say('heading ' + this.direction + ', possible: ' + this.possibleDirections);
            } else {
                this.direction = direction;
            }

            if (!this.direction) {
                this.say('Could not find any direction');
            }
        },

        /**
         * Move in defined direction with variable speed
         */
        move: function () {
            var v;

            // speed fluctuates
            if (prob(speedChangeProbability)) {
                this.speed += (1 - rnd(2));
            }

            // calm down to speedy rat
            if (this.speed > 5) {
                this.speed -= 1;
            }
            // speed up to slow rat
            if (this.speed < 1) {
                this.speed += 1;
            }

            v = this.getVelocity();

            // actual moving
            this.x += v.x;
            this.y += v.y;
        },

        // Write a string into console
        say: function (what) {
            window.console.log(this.name + ' says: ' + what);
        },

        // Sleep for a number of ticks
        sleep: function (time) {
            this.timers.sleep = time;
            this.sleeping = true;
        },

        // Stop sleeping
        wake: function () {
            this.sleeping = false;
        },

        // Render self
        draw: function () {
            ctx.fillStyle = this.color;
            ctx.fillRect(this.x, this.y, this.width, this.height);
        },

        // Do this every tick
        update: function () {
            this.look();
            this.chooseDirection(this.direction);

            if (!this.sleeping) {
                // if me don't sleep, me move
                this.move();
                // but sometimes me may fall asleep
                if (prob(sleepProbability)) {
                    this.sleep(rnd(100) + 10);
                }
            } else {
                // if sleep timer exist, check it
                if (this.timers.sleep > 0) {
                    this.timers.sleep -= 1;
                } else {
                    this.wake();
                }
            }
        },

        init: function (x, y) {
            var i;
            var parts = 'pa,ma,ra,ta,ka,sa,la,va,na,ba,ra'.split(',');

            this.x = x;
            this.y = y;

            // make self a name
            this.name = '';
            for (i = 0; i < rnd(3) + 2; i++) {
                this.name += parts[rnd(parts.length - 1)];
            }

            this.gender = prob(0.5) ? 'MALE' : 'FEMALE';
            this.color = this.gender === 'MALE' ? '#69f' : '#f99';

        }
    };

    var game = {
        width: canvas.width,
        height: canvas.height,

        objects: [],
        rats: [],
        walls: [],

        draw: function () {
            ctx.clearRect(0, 0, canvas.width, canvas.height);

            for (var i = 0; i < this.objects.length; i++) {
                this.objects[i].draw();
            }
        },

        update: function () {
            for (var i = 0; i < this.objects.length; i++) {
                this.objects[i].update();
            }

        },

        // check if any wall contains the coordinate
        insideWall: function (coord) {
            var i, inside = false;

            for (i = 0; i < this.walls.length; i+=1) {
                if (this.walls[i].has(coord)) {
                    inside = true;
                }
            }

            return inside;
        },

        init: function () {
            var i, o;
            var coord = {};

            // make walls
            for (i = 0; i < 9; i += 1) {
                o = Object.create(wall);
                o.init(15 * (3 + 17 * (i % 3)), 15 * (3 + 12 * (Math.floor(i / 3))), 15 * 14, 15 * 9);
                this.walls.push(o);
            }

            // grow some rats
            for (i = 0; i < numberOfRats; i += 1) {
                o = Object.create(rat);
                this.objects.push(o);
                this.rats.push(o);

                // generate random coordinate until we find one that does not belong to a wall
                do {
                    coord = {
                        x: rnd(game.width - rat.width),
                        y: rnd(game.height - rat.height)
                    };
                } while (this.insideWall(coord));

                o.init(coord.x, coord.y);
            }

            this.objects = this.walls.concat(this.rats);
        }
    };

    /**
     * Main loop
     */
    function tick() {
        game.draw();
        game.update();
        requestAnimationFrame(tick);
    }

    /**
     * Returns a random integer from [0..n]
     * @param n
     * @returns {number}
     */
    function rnd(n) {
        return Math.floor(Math.random() * (n + 1));
    }

    /**
     * Returns true with probability of p. Otherwise, returns false
     * @param p
     * @returns {boolean}
     */
    function prob(p) {
        return Math.random() < p;
    }

    game.init();
    tick();

};<commit_msg>Actor as a base class<commit_after>/* global _ */
window.onload = function () {
    'use strict';

    // settings
    var numberOfRats = 10;
    var rerouteProbability = 0.02;
    var speedChangeProbability = 0.1;
    var sleepProbability = 0.005; // happens on average every 200 ticks

    var canvas = document.getElementById('canvas');
    var ctx = canvas.getContext('2d');

    // velocity sign for each direction
    var velocitySigns = {
        n: {x: 0, y: -1},
        e: {x: +1, y: 0},
        s: {x: 0, y: +1},
        w: {x: -1, y: 0}
    };

    var Actor = {
        // Check if coordinate is inside the actor. Accepts 2 numbers or a tuple {x, y}
        has: function (x, y) {

            if (typeof x === 'object') {
                y = x.y;
                x = x.x;
            }

            return x >= this.x &&
                x <= this.x + this.width &&
                y >= this.y &&
                y <= this.y + this.height;
        }
    };

    // Static wall
    var Wall = _.extend({}, Actor, {
        update: function () {
        },
        // Render self
        draw: function () {
            ctx.fillStyle = '#000';
            ctx.strokeStyle = '#fff';
            ctx.lineWidth = 1;
            ctx.fillRect(this.x, this.y, this.width, this.height);
            ctx.strokeRect(this.x, this.y, this.width, this.height);
        },
        // Set object position and size
        init: function (x, y, width, height) {
            this.x = x;
            this.y = y;
            this.width = width;
            this.height = height;
        }
    });

    // Main actor
    var Rat = _.extend({}, Actor, {
        width: 15,
        height: 15,
        direction: 'n', // we need some value to begin looking around
        speed: 3, // scalar speed

        timers: {}, // any timers go here

        // Measure coordinates change
        getVelocity: function () {
            return {
                x: (velocitySigns[this.direction]).x * this.speed,
                y: (velocitySigns[this.direction]).y * this.speed
            };
        },

        // Where our new edges will be
        getNewEdges: function () {
            return this.getEdges(this.getVelocity());
        },

        // Where our new corners will be
        getNewCorners: function () {
            return this.getCorners({x: 0, y: 0});
        },

        // Where our corners are
        getCorners: function (velocity) {
            var edges = this.getEdges(velocity);

            return {
                nw: {x: edges.w, y: edges.n},
                ne: {x: edges.e, y: edges.n},
                sw: {x: edges.w, y: edges.s},
                se: {x: edges.e, y: edges.s}
            };
        },

        // Where our edges are
        getEdges: function (velocity) {
            return {
                e: this.x + this.width + velocity.x,
                s: this.y + this.height + velocity.y,
                n: this.y + velocity.y,
                w: this.x + velocity.x
            };
        },

        // Have a look around and see which directions are available
        look: function () {
            var self = this;
            var edges = this.getNewEdges();
            var corners = this.getNewCorners();

            // check with game borders
            var directions = {
                e: edges.e <= game.width,
                s: edges.s <= game.height,
                n: edges.n >= 0,
                w: edges.w >= 0
            };

            this.possibleDirections = [];

            // check with walls (will one of my front edges will be inside any wall?)
            game.walls.forEach(function (wall) {
                directions.e = directions.e && !wall.has(corners.ne) && !wall.has(corners.se);
                directions.s = directions.s && !wall.has(corners.se) && !wall.has(corners.sw);
                directions.n = directions.n && !wall.has(corners.ne) && !wall.has(corners.nw);
                directions.w = directions.w && !wall.has(corners.nw) && !wall.has(corners.sw);
            });

            // make an array of possible directions
            Object.keys(directions).forEach(function (direction) {
                if (directions[direction]) {
                    self.possibleDirections.push(direction);
                }
            });
        },

        /**
         * Choose movement direction.
         * If has parameter, tends to keep it. Yet has a small chance to change direction.
         * If no parameter is given, choose randomly.
         *
         * @param [direction] previous direction.
         */
        chooseDirection: function (direction) {
            // no argument or argument is impossible or chance
            if (!direction || this.possibleDirections.indexOf(direction) < 0 || prob(rerouteProbability)) {
                this.direction = this.possibleDirections[rnd(this.possibleDirections.length - 1)];
                //this.say('heading ' + this.direction + ', possible: ' + this.possibleDirections);
            } else {
                this.direction = direction;
            }

            if (!this.direction) {
                this.say('Could not find any direction');
            }
        },

        /**
         * Move in defined direction with variable speed
         */
        move: function () {
            var v;

            // speed fluctuates
            if (prob(speedChangeProbability)) {
                this.speed += (1 - rnd(2));
            }

            // calm down to speedy rat
            if (this.speed > 5) {
                this.speed -= 1;
            }
            // speed up to slow rat
            if (this.speed < 1) {
                this.speed += 1;
            }

            v = this.getVelocity();

            // actual moving
            this.x += v.x;
            this.y += v.y;
        },

        // Write a string into console
        say: function (what) {
            window.console.log(this.name + ' says: ' + what);
        },

        // Sleep for a number of ticks
        sleep: function (time) {
            this.timers.sleep = time;
            this.sleeping = true;
        },

        // Stop sleeping
        wake: function () {
            this.sleeping = false;
        },

        // Render self
        draw: function () {
            ctx.fillStyle = this.color;
            ctx.fillRect(this.x, this.y, this.width, this.height);
        },

        // Do this every tick
        update: function () {
            this.look();
            this.chooseDirection(this.direction);

            if (!this.sleeping) {
                // if me don't sleep, me move
                this.move();
                // but sometimes me may fall asleep
                if (prob(sleepProbability)) {
                    this.sleep(rnd(100) + 10);
                }
            } else {
                // if sleep timer exist, check it
                if (this.timers.sleep > 0) {
                    this.timers.sleep -= 1;
                } else {
                    this.wake();
                }
            }
        },

        init: function (x, y) {
            var i;
            var parts = 'pa,ma,ra,ta,ka,sa,la,va,na,ba,ra'.split(',');

            this.x = x;
            this.y = y;

            // make self a name
            this.name = '';
            for (i = 0; i < rnd(3) + 2; i++) {
                this.name += parts[rnd(parts.length - 1)];
            }

            this.gender = prob(0.5) ? 'MALE' : 'FEMALE';
            this.color = this.gender === 'MALE' ? '#69f' : '#f99';

        }
    });

    var game = {
        width: canvas.width,
        height: canvas.height,

        objects: [],
        rats: [],
        walls: [],

        draw: function () {
            ctx.clearRect(0, 0, canvas.width, canvas.height);

            for (var i = 0; i < this.objects.length; i++) {
                this.objects[i].draw();
            }
        },

        update: function () {
            for (var i = 0; i < this.objects.length; i++) {
                this.objects[i].update();
            }

        },

        // check if any wall contains the coordinate
        insideWall: function (coord) {
            var i, inside = false;

            for (i = 0; i < this.walls.length; i += 1) {
                if (this.walls[i].has(coord)) {
                    inside = true;
                }
            }

            return inside;
        },

        init: function () {
            var i, o;
            var coord = {};

            // make walls
            for (i = 0; i < 9; i += 1) {
                o = Object.create(Wall);
                o.init(15 * (3 + 17 * (i % 3)), 15 * (3 + 12 * (Math.floor(i / 3))), 15 * 14, 15 * 9);
                this.walls.push(o);
            }

            // grow some rats
            for (i = 0; i < numberOfRats; i += 1) {
                o = Object.create(Rat);
                this.objects.push(o);
                this.rats.push(o);

                // generate random coordinate until we find one that does not belong to a wall
                do {
                    coord = {
                        x: rnd(game.width - Rat.width),
                        y: rnd(game.height - Rat.height)
                    };
                } while (this.insideWall(coord));

                o.init(coord.x, coord.y);
            }

            this.objects = this.walls.concat(this.rats);
        }
    };

    /**
     * Main loop
     */
    function tick() {
        game.draw();
        game.update();
        requestAnimationFrame(tick);
    }

    /**
     * Returns a random integer from [0..n]
     * @param n
     * @returns {number}
     */
    function rnd(n) {
        return Math.floor(Math.random() * (n + 1));
    }

    /**
     * Returns true with probability of p. Otherwise, returns false
     * @param p
     * @returns {boolean}
     */
    function prob(p) {
        return Math.random() < p;
    }

    game.init();
    tick();

};<|endoftext|>


<commit_before>//= require_self
//= require twitter/bootstrap
//= require raphael-min
//= require ./bubble/wheel
//= require ./bubble/jquery.infiniScroll
//= require jquery.scrollTo
//= require jquery.jeditable
//= require jquery.hoverIntent
//= require_directory ./bubble<commit_msg>Include jquery.prevent_scroll_propagation<commit_after>//= require_self
//= require twitter/bootstrap
//= require raphael-min
//= require ./bubble/wheel
//= require ./bubble/jquery.infiniScroll
//= require jquery.scrollTo
//= require jquery.jeditable
//= require jquery.hoverIntent
//= require jquery.prevent_scroll_propagation
//= require_directory ./bubble<|endoftext|>