<commit_before>/**
 *
 * A document as represented here looks like this
 *
 * var document = {
 *   id: <document id>,
 *   resourcelist: <list of resources in load order>
 *   resourcemap: <dict of resource id -> resource object>
 * }
 *
 * A resource looks like this:
 *
 * {
 *   documentID: doc id,
 *   resourceID: res id,
 *   // maps to resman events:
 *   urlload:
 *   request: requeststart object
 *   requestheader
 *   response
 *   responseheader
 *   urldone
 *
 */

cls.ResourceManagerService = function(view, data)
{
  if (cls.ResourceManagerService.instance)
  {
    return cls.ResourceManagerService.instance;
  }
  cls.ResourceManagerService.instance = this;

  this._seen_doc_ids = [];
  this._document_contexts = {}; // mapping document id -> list of requests
  this._current_document = null;

  this._enable_content_tracking = function()
  {
    this._res_service.requestSetResponseMode(null, [[3, 1]]);
  }

  this._on_abouttoloaddocument_bound = function(msg)
  {

    var data = new cls.DocumentManager["1.0"].AboutToLoadDocument(msg);

    this._seen_doc_ids = [];
    this._current_document = {
      id: null,
      topresource: data.resourceID,
      resourcelist: [],
      resourcemap: {},
      firsttime: data.time,
      lasttime: null
    };

  }.bind(this);

  this._on_urlload_bound = function(msg)
  {
    if (!this._current_document) { return; }
    var data = new cls.ResourceManager["1.0"].UrlLoad(msg);

    this._current_document.resourcelist.push(data.resourceID);
    this._current_document.resourcemap[data.resourceID] = {urlload: data};
  }.bind(this);

  this._on_request_bound = function(msg)
  {
    if (!this._current_document) { return; }
    var data = new cls.ResourceManager["1.0"].Request(msg);

    var resource = this._current_document.resourcemap[data.resourceID];
    if (!resource) {
      opera.postError("No exist! " + JSON.stringify(data));
    }
    resource.request = data;

  }.bind(this);

  this._on_requestheader_bound = function(msg)
  {
    if (!this._current_document) { return; }
    var data = new cls.ResourceManager["1.0"].RequestHeader(msg);
    var resource = this._current_document.resourcemap[data.resourceID];
    if (resource) {
      resource.requestheader = data;
    }
  }.bind(this);

  this._on_requestfinished_bound = function(msg)
  {
    if (!this._current_document) { return; }
    var data = new cls.ResourceManager["1.0"].RequestFinished(msg);
    var resource = this._current_document.resourcemap[data.resourceID];
    if (resource) {
      resource.requestfinished = data;
    }
  }.bind(this);

  this._on_response_bound = function(msg)
  {
    if (!this._current_document) { return; }
    var data = new cls.ResourceManager["1.0"].Response(msg);

    var resource = this._current_document.resourcemap[data.resourceID];
    if (resource) {
      resource.response = data;
    }
  }.bind(this);

  this._on_responseheader_bound = function(msg)
  {
    if (!this._current_document) { return; }
    var data = new cls.ResourceManager["1.0"].ResponseHeader(msg);
    var resource = this._current_document.resourcemap[data.resourceID];
    if (resource) {
      resource.responseheader = data;
    }
  }.bind(this);

  this._on_responsefinished_bound = function(msg)
  {
    if (!this._current_document) { return; }
    var data = new cls.ResourceManager["1.0"].ResponseFinished(msg);
    var resource = this._current_document.resourcemap[data.resourceID];
    if (resource) {
      resource.responsefinished = data;
    }
  }.bind(this);

  this._on_urlfinished_bound = function(msg)
  {
    if (!this._current_document) { return; }
    var data = new cls.ResourceManager["1.0"].UrlFinished(msg);
    var resource = this._current_document.resourcemap[data.resourceID];
    if (resource) {
      resource.urlfinished = data;
      this._current_document.lasttime = data.time;
    }
  }.bind(this);

  this.init = function()
  {
    this._res_service = window.services['resource-manager'];
    this._res_service.addListener("urlload", this._on_urlload_bound);
    this._res_service.addListener("request", this._on_request_bound);
    this._res_service.addListener("requestheader", this._on_requestheader_bound);
    this._res_service.addListener("requestfinished", this._on_requestfinished_bound);
    this._res_service.addListener("response", this._on_response_bound);
    this._res_service.addListener("responseheader", this._on_responseheader_bound);
    this._res_service.addListener("responsefinished", this._on_responsefinished_bound);
    this._res_service.addListener("urlfinished", this._on_urlfinished_bound);

    this._doc_service = window.services['document-manager'];
    this._doc_service.addListener("abouttoloaddocument", this._on_abouttoloaddocument_bound);
  };

  this.get_request_context = function()
  {
    if (this._current_document &&
        this._current_document.resourcelist &&
        this._current_document.resourcelist.length)
    {
      return new cls.RequestContext(this.get_resource_list());
    }
    return null;
  };

  /**
   * Returns an array of resource objects. The internal representation is to
   * keep separate lists of seen resources and a map of id/resource.
   */
  this.get_resource_list = function()
  {
    if (! this._current_document) { return []; }
    var mapfun = function(e)
    {
      return this._current_document.resourcemap[e];
    };

    return this._current_document.resourcelist.map(mapfun, this);
  };

  this.get_resource_for_id = function(id)
  {
    if (this._current_document && id in this._current_document.resourcemap)
    {
        return this._current_document.resourcemap[id];
    }
    return null;
  };

  this.init();
  };

cls.RequestContext = function(reslist)
{
  this.resources = reslist;

  this.get_resource = function(id)
  {
    return this.resources.filter(function(e) { return e.urlload.resourceID == id; })[0];
  };

  /**
   * Grab stuff based on mime type, ignoring subtype
   */
  this.get_resources_for_type = function()
  {
    // FIXME: this will change, as it will be using urlfinished not responsefinished
    var types = Array.prototype.slice.call(arguments, 0);

    var filterfun = function(e)
    {
      if (! e.responsefinished) { return false; }
      var type = e.responsefinished.data.mimeType.split("/")[0];
      return types.indexOf(type) > -1;
    };

    return this.resources.filter(filterfun);
  };
  // alias with plural name
  this.get_resources_for_types = this.get_resources_for_type;

  this.get_resources_for_mime = function()
  {
    // FIXME: this will change, as it will be using urlfinished not responsefinished
    var mimes = Array.prototype.slice.call(arguments, 0);

    var filterfun = function(e)
    {
      return e.responsefinished && mimes.indexOf(e.responsefinished.data.mimeType) > -1;
    };

    return this.resources.filter(filterfun);
  };

  this.get_resource_groups = function()
  {
    var imgs = this.get_resources_for_type("image");
    var stylesheets = this.get_resources_for_mime("text/css");
    var markup = this.get_resources_for_mime("text/html",
                                             "application/xhtml+xml");
    var scripts = this.get_resources_for_mime("application/javascript",
                                              "text/javascript");

    var known = [].concat(imgs, stylesheets, markup, scripts);
    var other = this.resources.filter(function(e) {
      return known.indexOf(e) == -1;
    });
    return {
      images: imgs, stylesheets: stylesheets, markup: markup,
      scripts: scripts, other: other
    }
  }

  this.get_resource_sizes = function()
  {
    var groups = this.get_resource_groups();
    var ret = {};
    var sizefun = function(e) { return e.urlfinished ? e.urlfinished.contentLength : 0; };
    var total = 0;
    for (var key in groups)
    {
      var current = groups[key].sum(sizefun);
      total += current;
      ret[key] = current;
    }
    ret.total = total;
    return ret;
  }

  this.get_resource_times = function()
  {
    var groups = this.get_resource_groups();
    var ret = {};
    // don't count stuff in progress!
    var timefun = function(e) { return e.urlfinished ? e.urlfinished.time - e.urlload.time : 0; };

    var total = 0;
    for (var key in groups)
    {
      var current = groups[key].sum(timefun);
      total += current;
      ret[key] = current;
    }
    ret.total = total;

    return ret;
  };

  this.get_start_time = function()
  {
    return this.resources[0].urlload.time;
  }

  this.get_end_time = function()
  {
    return this.resources[this.resources.length-1].urlfinished.time;
  }

  this.get_duration = function()
  {
    var last = 0;
    this.resources.forEach(function(e) { if (e.urlfinished) { last = e.urlfinished.time; }});
    return last - this.resources[0].urlload.time;
  };

}<commit_msg>Added sane handling of redirects and skeleton handling of retries<commit_after>/**
 *
 * A document as represented here looks like this
 *
 * var document = {
 *   id: <document id>,
 *   resourcelist: <list of resources in load order>
 *   resourcemap: <dict of resource id -> resource object>
 * }
 *
 * A resource looks like this:
 *
 * {
 *   documentID: doc id,
 *   resourceID: res id,
 *   // maps to resman events:
 *   urlload:
 *   request: requeststart object
 *   requestheader
 *   response
 *   responseheader
 *   urldone
 *
 */

cls.ResourceManagerService = function(view, data)
{
  if (cls.ResourceManagerService.instance)
  {
    return cls.ResourceManagerService.instance;
  }
  cls.ResourceManagerService.instance = this;

  this._seen_doc_ids = [];
  this._document_contexts = {}; // mapping document id -> list of requests
  this._current_document = null;

  this._enable_content_tracking = function()
  {
    this._res_service.requestSetResponseMode(null, [[3, 1]]);
  }

  this._on_abouttoloaddocument_bound = function(msg)
  {
    var data = new cls.DocumentManager["1.0"].AboutToLoadDocument(msg);

    // if not a top resource, just ignore. This usually means it's an iframe
    if (data.parentDocumentID) { return; }

    this._seen_doc_ids = [];
    this._current_document = {
      id: null,
      topresource: data.resourceID,
      resourcelist: [],
      resourcemap: {},
      redirects: {},
      firsttime: data.time,
      lasttime: null
    };

  }.bind(this);

  this._on_urlload_bound = function(msg)
  {
    if (!this._current_document) { return; }
    var data = new cls.ResourceManager["1.0"].UrlLoad(msg);
    this._current_document.resourcelist.push(data.resourceID);
    var resource = {urlload: data,
                    redirected_from: this._current_document.redirects[data.resourceID]};
    this._current_document.resourcemap[data.resourceID] = resource;
  }.bind(this);

  this._on_request_bound = function(msg)
  {
    if (!this._current_document) { return; }
    var data = new cls.ResourceManager["1.0"].Request(msg);

    var resource = this._current_document.resourcemap[data.resourceID];
    if (!resource) {
      opera.postError("No exist! " + JSON.stringify(data));
    }
    resource.request = data;

  }.bind(this);

  this._on_requestheader_bound = function(msg)
  {
    if (!this._current_document) { return; }
    var data = new cls.ResourceManager["1.0"].RequestHeader(msg);
    var resource = this._current_document.resourcemap[data.resourceID];
    if (resource) {
      resource.requestheader = data;
    }
  }.bind(this);

  this._on_requestfinished_bound = function(msg)
  {
    if (!this._current_document) { return; }
    var data = new cls.ResourceManager["1.0"].RequestFinished(msg);
    var resource = this._current_document.resourcemap[data.resourceID];
    if (resource) {
      resource.requestfinished = data;
    }
  }.bind(this);

  this._on_response_bound = function(msg)
  {
    if (!this._current_document) { return; }
    var data = new cls.ResourceManager["1.0"].Response(msg);

    var resource = this._current_document.resourcemap[data.resourceID];
    if (resource) {
      resource.response = data;
    }
  }.bind(this);

  this._on_responseheader_bound = function(msg)
  {
    if (!this._current_document) { return; }
    var data = new cls.ResourceManager["1.0"].ResponseHeader(msg);
    var resource = this._current_document.resourcemap[data.resourceID];
    if (resource) {
      resource.responseheader = data;
    }
  }.bind(this);

  this._on_responsefinished_bound = function(msg)
  {
    if (!this._current_document) { return; }
    var data = new cls.ResourceManager["1.0"].ResponseFinished(msg);
    var resource = this._current_document.resourcemap[data.resourceID];
    if (resource) {
      resource.responsefinished = data;
    }
  }.bind(this);

  this._on_urlfinished_bound = function(msg)
  {
    if (!this._current_document) { return; }
    var data = new cls.ResourceManager["1.0"].UrlFinished(msg);
    var resource = this._current_document.resourcemap[data.resourceID];
    if (resource) {
      resource.urlfinished = data;
      this._current_document.lasttime = data.time;
    }
  }.bind(this);

  this._on_urlredirect_bound = function(msg)
  {
    var data = new cls.ResourceManager["1.0"].UrlRedirect(msg)
    var old_res = this._current_document.resourcemap[data.fromResourceID];
    var new_res = {urlload: data, redirect_from: old_res}
    delete this._current_document.resourcemap[data.fromResourceID];
    this._current_document.resourcelist.splice(this._current_document.resourcelist.indexOf(data.fromResourceID), 1);
    this._current_document.redirects[new_res.resourceID] = old_res;
  }.bind(this);

  this._on_requestretry_bound = function(msg)
  {
    opera.postError("got a retry " + JSON.stringify(msg, null, "    "));
  }.bind(this);

  this.init = function()
  {
    this._res_service = window.services['resource-manager'];
    this._res_service.addListener("urlload", this._on_urlload_bound);
    this._res_service.addListener("request", this._on_request_bound);
    this._res_service.addListener("requestretry", this._on_requestretry_bound);
    this._res_service.addListener("requestheader", this._on_requestheader_bound);
    this._res_service.addListener("requestfinished", this._on_requestfinished_bound);
    this._res_service.addListener("response", this._on_response_bound);
    this._res_service.addListener("responseheader", this._on_responseheader_bound);
    this._res_service.addListener("responsefinished", this._on_responsefinished_bound);
    this._res_service.addListener("urlredirect", this._on_urlredirect_bound);
    this._res_service.addListener("urlfinished", this._on_urlfinished_bound);

    this._doc_service = window.services['document-manager'];
    this._doc_service.addListener("abouttoloaddocument", this._on_abouttoloaddocument_bound);
  };

  this.get_request_context = function()
  {
    if (this._current_document &&
        this._current_document.resourcelist &&
        this._current_document.resourcelist.length)
    {
      return new cls.RequestContext(this.get_resource_list());
    }
    return null;
  };

  /**
   * Returns an array of resource objects. The internal representation is to
   * keep separate lists of seen resources and a map of id/resource.
   */
  this.get_resource_list = function()
  {
    if (! this._current_document) { return []; }
    var mapfun = function(e)
    {
      return this._current_document.resourcemap[e];
    };

    return this._current_document.resourcelist.map(mapfun, this);
  };

  this.get_resource_for_id = function(id)
  {
    if (this._current_document && id in this._current_document.resourcemap)
    {
        return this._current_document.resourcemap[id];
    }
    return null;
  };

  this.init();
  };

cls.RequestContext = function(reslist)
{
  this.resources = reslist;

  this.get_resource = function(id)
  {
    return this.resources.filter(function(e) { return e.urlload.resourceID == id; })[0];
  };

  /**
   * Grab stuff based on mime type, ignoring subtype
   */
  this.get_resources_for_type = function()
  {
    // FIXME: this will change, as it will be using urlfinished not responsefinished
    var types = Array.prototype.slice.call(arguments, 0);

    var filterfun = function(e)
    {
      if (! e.responsefinished) { return false; }
      var type = e.responsefinished.data.mimeType.split("/")[0];
      return types.indexOf(type) > -1;
    };

    return this.resources.filter(filterfun);
  };
  // alias with plural name
  this.get_resources_for_types = this.get_resources_for_type;

  this.get_resources_for_mime = function()
  {
    // FIXME: this will change, as it will be using urlfinished not responsefinished
    var mimes = Array.prototype.slice.call(arguments, 0);

    var filterfun = function(e)
    {
      return e.responsefinished && mimes.indexOf(e.responsefinished.data.mimeType) > -1;
    };

    return this.resources.filter(filterfun);
  };

  this.get_resource_groups = function()
  {
    var imgs = this.get_resources_for_type("image");
    var stylesheets = this.get_resources_for_mime("text/css");
    var markup = this.get_resources_for_mime("text/html",
                                             "application/xhtml+xml");
    var scripts = this.get_resources_for_mime("application/javascript",
                                              "text/javascript");

    var known = [].concat(imgs, stylesheets, markup, scripts);
    var other = this.resources.filter(function(e) {
      return known.indexOf(e) == -1;
    });
    return {
      images: imgs, stylesheets: stylesheets, markup: markup,
      scripts: scripts, other: other
    }
  }

  this.get_resource_sizes = function()
  {
    var groups = this.get_resource_groups();
    var ret = {};
    var sizefun = function(e) { return e.urlfinished ? e.urlfinished.contentLength : 0; };
    var total = 0;
    for (var key in groups)
    {
      var current = groups[key].sum(sizefun);
      total += current;
      ret[key] = current;
    }
    ret.total = total;
    return ret;
  }

  this.get_resource_times = function()
  {
    var groups = this.get_resource_groups();
    var ret = {};
    // don't count stuff in progress!
    var timefun = function(e) { return e.urlfinished ? e.urlfinished.time - e.urlload.time : 0; };

    var total = 0;
    for (var key in groups)
    {
      var current = groups[key].sum(timefun);
      total += current;
      ret[key] = current;
    }
    ret.total = total;

    return ret;
  };

  this.get_start_time = function()
  {
    return this.resources[0].urlload.time;
  }

  this.get_end_time = function()
  {
    return this.resources[this.resources.length-1].urlfinished.time;
  }

  this.get_duration = function()
  {
    var last = 0;
    this.resources.forEach(function(e) { if (e.urlfinished) { last = e.urlfinished.time; }});
    return last - this.resources[0].urlload.time;
  };

}<|endoftext|>


<commit_before>import Utilities from './Utilities.js';

class ContentMeter {
	constructor( barSelector, contentSelector, options ) {
		this.barSelector = barSelector;
		this.contentSelector = contentSelector;

		this.init();
	}

  readContentDimensions() {
    this.content = {
      height        : this.contentContainer.scrollHeight,
      visibleHeight : this.contentContainer.clientHeight,
      offset        : this.contentContainer.offsetTop,
      selfScrolled  : false
    }

    if ( this.content.visibleHeight > window.innerHeight ) {
      this.content.visibleHeight = window.innerHeight;
    } else {
      this.content.selfScrolled = true;
    }
  }

	createMeter() {
    const bar = document.createElement( "div" );
    this.bar = bar;

    bar.classList.add( "baza-contentmeter__bar" );
    bar.style.width = this.setBarWidth( this.content.selfScrolled );

    this.barContainer.style.overflow = "hidden";
    this.barContainer.appendChild( bar );

    this.updateClasses();
		this.bindUIEvents();
	}

	bindUIEvents() {
    const selfScrolled = this.content.selfScrolled;

    const scrollTarget = selfScrolled ? this.contentContainer : window;
		scrollTarget.addEventListener( "scroll", () => this.setBarWidth( selfScrolled ) );

    window.addEventListener( "scroll", () => this.updateClasses() );
		window.addEventListener( "resize", () => {
			this.readContentDimensions();
			this.setBarWidth( selfScrolled )
			this.updateClasses();
		} );
	}

  setBarWidth( ofScrollable = false ) {
    this.bar.style.width = this.getBarWidth( ofScrollable ) + "%";
  }

	getBarWidth( ofScrollable ) {
		let barW = 0;

		if ( !this.content ) this.readContentDimensions();

    barW = ofScrollable ? this.contentContainer.scrollTop : (Utilities.getDocScrolltop() - this.content.offset);
    barW = (barW + this.content.visibleHeight) / this.content.height * 100;
		barW = Utilities.limitTheNumber( barW, 0, 100 );

		return barW;
	}

	updateClasses() {
		if ( Utilities.getDocScrolltop() > ( this.content.height + this.content.offset )
		  || ( Utilities.getDocScrolltop() + this.content.visibleHeight ) < this.content.offset ) {

			this.barContainer.classList.add( "js-invisible" );
		} else {
			this.barContainer.classList.remove( "js-invisible" );
		}
	}

  init() {
    this.barContainer = Utilities.getDOMElement( this.barSelector );
    this.contentContainer = Utilities.getDOMElement( this.contentSelector );

    if ( this.barContainer !== null && this.contentContainer !== null ) {
      this.readContentDimensions();
      this.createMeter();
    } else {
      throw new Error( "Wrong selectors or given selectors match no elements." );
    }
  }
};

export default ContentMeter;
<commit_msg>Rearrange methods<commit_after>import Utilities from './Utilities.js';

class ContentMeter {
	constructor( barSelector, contentSelector, options ) {
		this.barSelector = barSelector;
		this.contentSelector = contentSelector;

		this.init();
	}

	updateClasses() {
		if ( Utilities.getDocScrolltop() > ( this.content.height + this.content.offset )
		  || ( Utilities.getDocScrolltop() + this.content.visibleHeight ) < this.content.offset ) {

			this.barContainer.classList.add( "js-invisible" );
		} else {
			this.barContainer.classList.remove( "js-invisible" );
		}
	}

  getBarWidth( ofScrollable ) {
    let barW = 0;

    if ( !this.content ) this.readContentDimensions();

    barW = ofScrollable ? this.contentContainer.scrollTop : (Utilities.getDocScrolltop() - this.content.offset);
    barW = (barW + this.content.visibleHeight) / this.content.height * 100;
    barW = Utilities.limitTheNumber( barW, 0, 100 );

    return barW;
  }

  setBarWidth( ofScrollable = false ) {
    this.bar.style.width = this.getBarWidth( ofScrollable ) + "%";
  }

  readContentDimensions() {
    this.content = {
      height        : this.contentContainer.scrollHeight,
      visibleHeight : this.contentContainer.clientHeight,
      offset        : this.contentContainer.offsetTop,
      selfScrolled  : false
    }

    if ( this.content.visibleHeight > window.innerHeight ) {
      this.content.visibleHeight = window.innerHeight;
    } else {
      this.content.selfScrolled = true;
    }
  }

  bindUIEvents() {
    const selfScrolled = this.content.selfScrolled;

    const scrollTarget = selfScrolled ? this.contentContainer : window;
    scrollTarget.addEventListener( "scroll", () => this.setBarWidth( selfScrolled ) );

    window.addEventListener( "scroll", () => this.updateClasses() );
    window.addEventListener( "resize", () => {
      this.readContentDimensions();
      this.setBarWidth( selfScrolled )
      this.updateClasses();
    } );
  }

  createMeter() {
    const bar = document.createElement( "div" );
    this.bar = bar;

    bar.classList.add( "baza-contentmeter__bar" );
    bar.style.width = this.setBarWidth( this.content.selfScrolled );

    this.barContainer.style.overflow = "hidden";
    this.barContainer.appendChild( bar );

    this.updateClasses();
    this.bindUIEvents();
  }

  init() {
    this.barContainer = Utilities.getDOMElement( this.barSelector );
    this.contentContainer = Utilities.getDOMElement( this.contentSelector );

    if ( this.barContainer !== null && this.contentContainer !== null ) {
      this.readContentDimensions();
      this.createMeter();
    } else {
      throw new Error( "Wrong selectors or given selectors match no elements." );
    }
  }
};

export default ContentMeter;
<|endoftext|>


<commit_before>Object.assign(pc, function () {

    var ammoVec1, ammoQuat, ammoTransform;

    /**
     * @private
     * @class
     * @name pc.Trigger
     * @classdesc Creates a trigger object used to create internal physics objects that interact with
     * rigid bodies and trigger collision events with no collision response.
     * @param {pc.Application} app - The running {pc.Application}.
     * @param {pc.Component} component - The component for which the trigger will be created.
     * @param {pc.ComponentData} data - The data for the component.
     */
    var Trigger = function Trigger(app, component, data) {
        this.entity = component.entity;
        this.component = component;
        this.app = app;

        if (typeof Ammo !== 'undefined' && ! ammoVec1) {
            ammoVec1 = new Ammo.btVector3();
            ammoQuat = new Ammo.btQuaternion();
            ammoTransform = new Ammo.btTransform();
        }

        this.initialize(data);
    };

    Object.assign(Trigger.prototype,  {
        initialize: function (data) {
            var entity = this.entity;
            var shape = data.shape;

            if (shape && typeof Ammo !== 'undefined') {
                if (entity.trigger) {
                    entity.trigger.destroy();
                }

                var mass = 1;

                var pos = entity.getPosition();
                var rot = entity.getRotation();

                ammoVec1.setValue(pos.x, pos.y, pos.z);
                ammoQuat.setValue(rot.x, rot.y, rot.z, rot.w);

                ammoTransform.setOrigin(ammoVec1);
                ammoTransform.setRotation(ammoQuat);

                var body = this.app.systems.rigidbody.createBody(mass, shape, ammoTransform);

                body.setRestitution(0);
                body.setFriction(0);
                body.setDamping(0, 0);
                ammoVec1.setValue(0, 0, 0);
                body.setLinearFactor(ammoVec1);
                body.setAngularFactor(ammoVec1);

                body.setCollisionFlags(body.getCollisionFlags() | pc.BODYFLAG_NORESPONSE_OBJECT);
                body.entity = entity;

                this.body = body;

                if (this.component.enabled && entity.enabled) {
                    this.enable();
                }
            }
        },

        destroy: function () {
            var body = this.body;
            if (!body) return;

            this.disable();

            this.app.systems.rigidbody.destroyBody(body);
        },

        updateTransform: function () {
            var wtm = this.entity.getWorldTransform();
            ammoTransform.setFromOpenGLMatrix(wtm.data);

            var body = this.body;
            body.setWorldTransform(ammoTransform);
            body.activate();
        },

        enable: function () {
            var body = this.body;
            if (!body) return;

            var systems = this.app.systems;
            systems.rigidbody.addBody(body, pc.BODYGROUP_TRIGGER, pc.BODYMASK_NOT_STATIC ^ pc.BODYGROUP_TRIGGER);
            systems.rigidbody._triggers.push(this);

            // set the body's activation state to active so that it is
            // simulated properly again
            body.forceActivationState(pc.BODYSTATE_ACTIVE_TAG);

            this.updateTransform();
        },

        disable: function () {
            var body = this.body;
            if (!body) return;

            var systems = this.app.systems;
            var idx = systems.rigidbody._triggers.indexOf(this);
            if (idx > -1) {
                systems.rigidbody._triggers.splice(idx, 1);
            }
            systems.rigidbody.removeBody(body);

            // set the body's activation state to disable simulation so
            // that it properly deactivates after we remove it from the physics world
            body.forceActivationState(pc.BODYSTATE_DISABLE_SIMULATION);
        }
    });

    return {
        Trigger: Trigger
    };
}());
<commit_msg>[FIX] Triggers on scaled entities now work again (#2140)<commit_after>Object.assign(pc, function () {

    var ammoVec1, ammoQuat, ammoTransform;

    /**
     * @private
     * @class
     * @name pc.Trigger
     * @classdesc Creates a trigger object used to create internal physics objects that interact with
     * rigid bodies and trigger collision events with no collision response.
     * @param {pc.Application} app - The running {pc.Application}.
     * @param {pc.Component} component - The component for which the trigger will be created.
     * @param {pc.ComponentData} data - The data for the component.
     */
    var Trigger = function Trigger(app, component, data) {
        this.entity = component.entity;
        this.component = component;
        this.app = app;

        if (typeof Ammo !== 'undefined' && ! ammoVec1) {
            ammoVec1 = new Ammo.btVector3();
            ammoQuat = new Ammo.btQuaternion();
            ammoTransform = new Ammo.btTransform();
        }

        this.initialize(data);
    };

    Object.assign(Trigger.prototype,  {
        initialize: function (data) {
            var entity = this.entity;
            var shape = data.shape;

            if (shape && typeof Ammo !== 'undefined') {
                if (entity.trigger) {
                    entity.trigger.destroy();
                }

                var mass = 1;

                var pos = entity.getPosition();
                var rot = entity.getRotation();

                ammoVec1.setValue(pos.x, pos.y, pos.z);
                ammoQuat.setValue(rot.x, rot.y, rot.z, rot.w);

                ammoTransform.setOrigin(ammoVec1);
                ammoTransform.setRotation(ammoQuat);

                var body = this.app.systems.rigidbody.createBody(mass, shape, ammoTransform);

                body.setRestitution(0);
                body.setFriction(0);
                body.setDamping(0, 0);
                ammoVec1.setValue(0, 0, 0);
                body.setLinearFactor(ammoVec1);
                body.setAngularFactor(ammoVec1);

                body.setCollisionFlags(body.getCollisionFlags() | pc.BODYFLAG_NORESPONSE_OBJECT);
                body.entity = entity;

                this.body = body;

                if (this.component.enabled && entity.enabled) {
                    this.enable();
                }
            }
        },

        destroy: function () {
            var body = this.body;
            if (!body) return;

            this.disable();

            this.app.systems.rigidbody.destroyBody(body);
        },

        _getEntityTransform: function (transform) {
            var pos = this.entity.getPosition();
            var rot = this.entity.getRotation();

            ammoVec1.setValue(pos.x, pos.y, pos.z);
            ammoQuat.setValue(rot.x, rot.y, rot.z, rot.w);

            transform.setOrigin(ammoVec1);
            transform.setRotation(ammoQuat);
        },

        updateTransform: function () {
            this._getEntityTransform(ammoTransform);

            var body = this.body;
            body.setWorldTransform(ammoTransform);
            body.activate();
        },

        enable: function () {
            var body = this.body;
            if (!body) return;

            var systems = this.app.systems;
            systems.rigidbody.addBody(body, pc.BODYGROUP_TRIGGER, pc.BODYMASK_NOT_STATIC ^ pc.BODYGROUP_TRIGGER);
            systems.rigidbody._triggers.push(this);

            // set the body's activation state to active so that it is
            // simulated properly again
            body.forceActivationState(pc.BODYSTATE_ACTIVE_TAG);

            this.updateTransform();
        },

        disable: function () {
            var body = this.body;
            if (!body) return;

            var systems = this.app.systems;
            var idx = systems.rigidbody._triggers.indexOf(this);
            if (idx > -1) {
                systems.rigidbody._triggers.splice(idx, 1);
            }
            systems.rigidbody.removeBody(body);

            // set the body's activation state to disable simulation so
            // that it properly deactivates after we remove it from the physics world
            body.forceActivationState(pc.BODYSTATE_DISABLE_SIMULATION);
        }
    });

    return {
        Trigger: Trigger
    };
}());
<|endoftext|>


<commit_before>var CPU = require("./cpu");
var Keyboard = require("./keyboard");
var PPU = require("./ppu");
var PAPU = require("./papu");
var ROM = require("./rom");

var NES = function(opts) {
  this.opts = {
    onFrame: function() {},
    onAudio: function() {},
    onStatusUpdate: function() {},

    preferredFrameRate: 60,

    emulateSound: true,
    sampleRate: 44100 // Sound sample rate in hz
  };
  if (typeof opts !== "undefined") {
    var key;
    for (key in this.opts) {
      if (typeof opts[key] !== "undefined") {
        this.opts[key] = opts[key];
      }
    }
  }

  this.frameTime = 1000 / this.opts.preferredFrameRate;

  this.ui = {
    writeFrame: this.opts.onFrame,
    writeAudio: this.opts.onAudio,
    updateStatus: this.opts.onStatusUpdate
  };
  this.cpu = new CPU(this);
  this.ppu = new PPU(this);
  this.papu = new PAPU(this);
  this.mmap = null; // set in loadROM()
  this.keyboard = new Keyboard();

  this.ui.updateStatus("Ready to load a ROM.");
};

NES.prototype = {
  fpsFrameCount: 0,
  romData: null,

  // Resets the system
  reset: function() {
    if (this.mmap !== null) {
      this.mmap.reset();
    }

    this.cpu.reset();
    this.ppu.reset();
    this.papu.reset();

    this.lastFpsTime = null;
    this.fpsFrameCount = 0;
  },

  frame: function() {
    this.ppu.startFrame();
    var cycles = 0;
    var emulateSound = this.opts.emulateSound;
    var cpu = this.cpu;
    var ppu = this.ppu;
    var papu = this.papu;
    FRAMELOOP: for (;;) {
      if (cpu.cyclesToHalt === 0) {
        // Execute a CPU instruction
        cycles = cpu.emulate();
        if (emulateSound) {
          papu.clockFrameCounter(cycles);
        }
        cycles *= 3;
      } else {
        if (cpu.cyclesToHalt > 8) {
          cycles = 24;
          if (emulateSound) {
            papu.clockFrameCounter(8);
          }
          cpu.cyclesToHalt -= 8;
        } else {
          cycles = cpu.cyclesToHalt * 3;
          if (emulateSound) {
            papu.clockFrameCounter(cpu.cyclesToHalt);
          }
          cpu.cyclesToHalt = 0;
        }
      }

      for (; cycles > 0; cycles--) {
        if (
          ppu.curX === ppu.spr0HitX &&
          ppu.f_spVisibility === 1 &&
          ppu.scanline - 21 === ppu.spr0HitY
        ) {
          // Set sprite 0 hit flag:
          ppu.setStatusFlag(ppu.STATUS_SPRITE0HIT, true);
        }

        if (ppu.requestEndFrame) {
          ppu.nmiCounter--;
          if (ppu.nmiCounter === 0) {
            ppu.requestEndFrame = false;
            ppu.startVBlank();
            break FRAMELOOP;
          }
        }

        ppu.curX++;
        if (ppu.curX === 341) {
          ppu.curX = 0;
          ppu.endScanline();
        }
      }
    }
    this.fpsFrameCount++;
  },

  getFPS: function() {
    var now = +new Date();
    var fps = null;
    if (this.lastFpsTime) {
      fps = this.fpsFrameCount / ((now - this.lastFpsTime) / 1000);
    }
    this.fpsFrameCount = 0;
    this.lastFpsTime = now;
    return fps;
  },

  reloadROM: function() {
    if (this.romData !== null) {
      this.loadROM(this.romData);
    }
  },

  // Loads a ROM file into the CPU and PPU.
  // The ROM file is validated first.
  loadROM: function(data) {
    // Load ROM file:
    this.rom = new ROM(this);
    this.rom.load(data);

    this.reset();
    this.mmap = this.rom.createMapper();
    this.mmap.loadROM();
    this.ppu.setMirroring(this.rom.getMirroringType());
    this.romData = data;
  },

  setFramerate: function(rate) {
    this.opts.preferredFrameRate = rate;
    this.frameTime = 1000 / rate;
    this.papu.setSampleRate(this.opts.sampleRate, false);
  },

  toJSON: function() {
    return {
      romData: this.romData,
      cpu: this.cpu.toJSON(),
      mmap: this.mmap.toJSON(),
      ppu: this.ppu.toJSON()
    };
  },

  fromJSON: function(s) {
    this.loadROM(s.romData);
    this.cpu.fromJSON(s.cpu);
    this.mmap.fromJSON(s.mmap);
    this.ppu.fromJSON(s.ppu);
  }
};

module.exports = NES;
<commit_msg>Bind frame function so it can be used as callback<commit_after>var CPU = require("./cpu");
var Keyboard = require("./keyboard");
var PPU = require("./ppu");
var PAPU = require("./papu");
var ROM = require("./rom");

var NES = function(opts) {
  this.opts = {
    onFrame: function() {},
    onAudio: function() {},
    onStatusUpdate: function() {},

    preferredFrameRate: 60,

    emulateSound: true,
    sampleRate: 44100 // Sound sample rate in hz
  };
  if (typeof opts !== "undefined") {
    var key;
    for (key in this.opts) {
      if (typeof opts[key] !== "undefined") {
        this.opts[key] = opts[key];
      }
    }
  }

  this.frameTime = 1000 / this.opts.preferredFrameRate;

  this.ui = {
    writeFrame: this.opts.onFrame,
    writeAudio: this.opts.onAudio,
    updateStatus: this.opts.onStatusUpdate
  };
  this.cpu = new CPU(this);
  this.ppu = new PPU(this);
  this.papu = new PAPU(this);
  this.mmap = null; // set in loadROM()
  this.keyboard = new Keyboard();

  this.ui.updateStatus("Ready to load a ROM.");

  this.frame = this.frame.bind(this);
}

NES.prototype = {
  fpsFrameCount: 0,
  romData: null,

  // Resets the system
  reset: function() {
    if (this.mmap !== null) {
      this.mmap.reset();
    }

    this.cpu.reset();
    this.ppu.reset();
    this.papu.reset();

    this.lastFpsTime = null;
    this.fpsFrameCount = 0;
  },

  frame: function() {
    this.ppu.startFrame();
    var cycles = 0;
    var emulateSound = this.opts.emulateSound;
    var cpu = this.cpu;
    var ppu = this.ppu;
    var papu = this.papu;
    FRAMELOOP: for (;;) {
      if (cpu.cyclesToHalt === 0) {
        // Execute a CPU instruction
        cycles = cpu.emulate();
        if (emulateSound) {
          papu.clockFrameCounter(cycles);
        }
        cycles *= 3;
      } else {
        if (cpu.cyclesToHalt > 8) {
          cycles = 24;
          if (emulateSound) {
            papu.clockFrameCounter(8);
          }
          cpu.cyclesToHalt -= 8;
        } else {
          cycles = cpu.cyclesToHalt * 3;
          if (emulateSound) {
            papu.clockFrameCounter(cpu.cyclesToHalt);
          }
          cpu.cyclesToHalt = 0;
        }
      }

      for (; cycles > 0; cycles--) {
        if (
          ppu.curX === ppu.spr0HitX &&
          ppu.f_spVisibility === 1 &&
          ppu.scanline - 21 === ppu.spr0HitY
        ) {
          // Set sprite 0 hit flag:
          ppu.setStatusFlag(ppu.STATUS_SPRITE0HIT, true);
        }

        if (ppu.requestEndFrame) {
          ppu.nmiCounter--;
          if (ppu.nmiCounter === 0) {
            ppu.requestEndFrame = false;
            ppu.startVBlank();
            break FRAMELOOP;
          }
        }

        ppu.curX++;
        if (ppu.curX === 341) {
          ppu.curX = 0;
          ppu.endScanline();
        }
      }
    }
    this.fpsFrameCount++;
  },

  getFPS: function() {
    var now = +new Date();
    var fps = null;
    if (this.lastFpsTime) {
      fps = this.fpsFrameCount / ((now - this.lastFpsTime) / 1000);
    }
    this.fpsFrameCount = 0;
    this.lastFpsTime = now;
    return fps;
  },

  reloadROM: function() {
    if (this.romData !== null) {
      this.loadROM(this.romData);
    }
  },

  // Loads a ROM file into the CPU and PPU.
  // The ROM file is validated first.
  loadROM: function(data) {
    // Load ROM file:
    this.rom = new ROM(this);
    this.rom.load(data);

    this.reset();
    this.mmap = this.rom.createMapper();
    this.mmap.loadROM();
    this.ppu.setMirroring(this.rom.getMirroringType());
    this.romData = data;
  },

  setFramerate: function(rate) {
    this.opts.preferredFrameRate = rate;
    this.frameTime = 1000 / rate;
    this.papu.setSampleRate(this.opts.sampleRate, false);
  },

  toJSON: function() {
    return {
      romData: this.romData,
      cpu: this.cpu.toJSON(),
      mmap: this.mmap.toJSON(),
      ppu: this.ppu.toJSON()
    };
  },

  fromJSON: function(s) {
    this.loadROM(s.romData);
    this.cpu.fromJSON(s.cpu);
    this.mmap.fromJSON(s.mmap);
    this.ppu.fromJSON(s.ppu);
  }
};

module.exports = NES;
<|endoftext|>


<commit_before>Doms.register('button', function(el){
  el.addClass('btn btn-default');
});
<commit_msg>Boostrap.js initial domready extensions<commit_after>Doms.register('button', function(el){
  el.addClass('btn btn-default');
});
Doms.register('.ks_form input', function(el){
  el.addClass('form-control');
});
<|endoftext|>


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

var Guacamole = Guacamole || {};

/**
 * Abstract audio recorder which streams arbitrary audio data to an underlying
 * Guacamole.OutputStream. It is up to implementations of this class to provide
 * some means of handling this Guacamole.OutputStream. Data produced by the
 * recorder is to be sent along the provided stream immediately.
 *
 * @constructor
 */
Guacamole.AudioRecorder = function AudioRecorder() {

    // AudioRecorder currently provides no functions

};

/**
 * Determines whether the given mimetype is supported by any built-in
 * implementation of Guacamole.AudioRecorder, and thus will be properly handled
 * by Guacamole.AudioRecorder.getInstance().
 *
 * @param {String} mimetype
 *     The mimetype to check.
 *
 * @returns {Boolean}
 *     true if the given mimetype is supported by any built-in
 *     Guacamole.AudioRecorder, false otherwise.
 */
Guacamole.AudioRecorder.isSupportedType = function isSupportedType(mimetype) {

    return Guacamole.RawAudioRecorder.isSupportedType(mimetype);

};

/**
 * Returns a list of all mimetypes supported by any built-in
 * Guacamole.AudioRecorder, in rough order of priority. Beware that only the
 * core mimetypes themselves will be listed. Any mimetype parameters, even
 * required ones, will not be included in the list. For example, "audio/L8" is
 * a supported raw audio mimetype that is supported, but it is invalid without
 * additional parameters. Something like "audio/L8;rate=44100" would be valid,
 * however (see https://tools.ietf.org/html/rfc4856).
 *
 * @returns {String[]}
 *     A list of all mimetypes supported by any built-in
 *     Guacamole.AudioRecorder, excluding any parameters.
 */
Guacamole.AudioRecorder.getSupportedTypes = function getSupportedTypes() {

    return Guacamole.RawAudioRecorder.getSupportedTypes();

};

/**
 * Returns an instance of Guacamole.AudioRecorder providing support for the
 * given audio format. If support for the given audio format is not available,
 * null is returned.
 *
 * @param {Guacamole.OutputStream} stream
 *     The Guacamole.OutputStream to send audio data through.
 *
 * @param {String} mimetype
 *     The mimetype of the audio data to be sent along the provided stream.
 *
 * @return {Guacamole.AudioRecorder}
 *     A Guacamole.AudioRecorder instance supporting the given mimetype and
 *     writing to the given stream, or null if support for the given mimetype
 *     is absent.
 */
Guacamole.AudioRecorder.getInstance = function getInstance(stream, mimetype) {

    // Use raw audio recorder if possible
    if (Guacamole.RawAudioRecorder.isSupportedType(mimetype))
        return new Guacamole.RawAudioRecorder(stream, mimetype);

    // No support for given mimetype
    return null;

};

/**
 * Implementation of Guacamole.AudioRecorder providing support for raw PCM
 * format audio. This recorder relies only on the Web Audio API and does not
 * require any browser-level support for its audio formats.
 *
 * @constructor
 * @augments Guacamole.AudioRecorder
 * @param {Guacamole.OutputStream} stream
 *     The Guacamole.OutputStream to write audio data to.
 *
 * @param {String} mimetype
 *     The mimetype of the audio data to send along the provided stream, which
 *     must be a "audio/L8" or "audio/L16" mimetype with necessary parameters,
 *     such as: "audio/L16;rate=44100,channels=2".
 */
Guacamole.RawAudioRecorder = function RawAudioRecorder(stream, mimetype) {

    /**
     * The format of audio this recorder will encode.
     *
     * @private
     * @type {Guacamole.RawAudioFormat}
     */
    var format = Guacamole.RawAudioFormat.parse(mimetype);

    /**
     * An instance of a Web Audio API AudioContext object, or null if the
     * Web Audio API is not supported.
     *
     * @private
     * @type {AudioContext}
     */
    var context = Guacamole.AudioContextFactory.getAudioContext();

    /**
     * A function which directly invokes the browser's implementation of
     * navigator.getUserMedia() with all provided parameters.
     *
     * @type Function
     */
    var getUserMedia = (navigator.getUserMedia
            || navigator.webkitGetUserMedia
            || navigator.mozGetUserMedia
            || navigator.msGetUserMedia).bind(navigator);

    /**
     * Guacamole.ArrayBufferWriter wrapped around the audio output stream
     * provided when this Guacamole.RawAudioRecorder was created.
     *
     * @private
     * @type {Guacamole.ArrayBufferWriter}
     */
    var writer = new Guacamole.ArrayBufferWriter(stream);

    /**
     * The type of typed array that will be used to represent each audio packet
     * internally. This will be either Int8Array or Int16Array, depending on
     * whether the raw audio format is 8-bit or 16-bit.
     *
     * @private
     * @constructor
     */
    var SampleArray = (format.bytesPerSample === 1) ? window.Int8Array : window.Int16Array;

    /**
     * The maximum absolute value of any sample within a raw audio packet sent
     * by this audio recorder. This depends only on the size of each sample,
     * and will be 128 for 8-bit audio and 32768 for 16-bit audio.
     *
     * @private
     * @type {Number}
     */
    var maxSampleValue = (format.bytesPerSample === 1) ? 128 : 32768;

    /**
     * The size of audio buffer to request from the Web Audio API when
     * recording audio. This must be a power of two between 256 and 16384
     * inclusive, as required by AudioContext.createScriptProcessor().
     *
     * @private
     * @type {Number}
     */
    var bufferSize = format.bytesPerSample * 256;

    /**
     * Converts the given AudioBuffer into an audio packet, ready for streaming
     * along the underlying output stream. Unlike the raw audio packets used by
     * this audio recorder, AudioBuffers require floating point samples and are
     * split into isolated planes of channel-specific data.
     *
     * @private
     * @param {AudioBuffer} audioBuffer
     *     The Web Audio API AudioBuffer that should be converted to a raw
     *     audio packet.
     *
     * @returns {SampleArray}
     *     A new raw audio packet containing the audio data from the provided
     *     AudioBuffer.
     */
    var toSampleArray = function toSampleArray(audioBuffer) {

        // Get array for raw PCM storage
        var data = new SampleArray(audioBuffer.length);

        // Convert each channel
        for (var channel = 0; channel < format.channels; channel++) {

            var audioData = audioBuffer.getChannelData(channel);

            // Fill array with data from audio buffer channel
            var offset = channel;
            for (var i = 0; i < audioData.length; i++) {
                data[offset] = audioData[i] * maxSampleValue;
                offset += format.channels;
            }

        }

        return data;

    };

    // Once audio stream is successfully open, request and begin reading audio
    writer.onack = function audioStreamAcknowledged(status) {

        // Abort stream if rejected
        if (status.code !== Guacamole.Status.Code.SUCCESS) {
            writer.sendEnd();
            return;
        }

        // Attempt to retrieve an audio input stream from the browser
        getUserMedia({ 'audio' : true }, function streamReceived(mediaStream) {

            // Create processing node which receives appropriately-sized audio buffers
            var processor = context.createScriptProcessor(bufferSize, format.channels, format.channels);
            processor.connect(context.destination);

            // Send blobs when audio buffers are received
            processor.onaudioprocess = function processAudio(e) {
                writer.sendData(toSampleArray(e.inputBuffer).buffer);
            };

            // Connect processing node to user's audio input source
            var source = context.createMediaStreamSource(mediaStream);
            source.connect(processor);

        }, function streamDenied() {

            // Simply end stream if audio access is not allowed
            writer.sendEnd();

        });

    };

};

Guacamole.RawAudioRecorder.prototype = new Guacamole.AudioRecorder();

/**
 * Determines whether the given mimetype is supported by
 * Guacamole.RawAudioRecorder.
 *
 * @param {String} mimetype
 *     The mimetype to check.
 *
 * @returns {Boolean}
 *     true if the given mimetype is supported by Guacamole.RawAudioRecorder,
 *     false otherwise.
 */
Guacamole.RawAudioRecorder.isSupportedType = function isSupportedType(mimetype) {

    // No supported types if no Web Audio API
    if (!Guacamole.AudioContextFactory.getAudioContext())
        return false;

    return Guacamole.RawAudioFormat.parse(mimetype) !== null;

};

/**
 * Returns a list of all mimetypes supported by Guacamole.RawAudioRecorder. Only
 * the core mimetypes themselves will be listed. Any mimetype parameters, even
 * required ones, will not be included in the list. For example, "audio/L8" is
 * a raw audio mimetype that may be supported, but it is invalid without
 * additional parameters. Something like "audio/L8;rate=44100" would be valid,
 * however (see https://tools.ietf.org/html/rfc4856).
 *
 * @returns {String[]}
 *     A list of all mimetypes supported by Guacamole.RawAudioRecorder,
 *     excluding any parameters. If the necessary JavaScript APIs for recording
 *     raw audio are absent, this list will be empty.
 */
Guacamole.RawAudioRecorder.getSupportedTypes = function getSupportedTypes() {

    // No supported types if no Web Audio API
    if (!Guacamole.AudioContextFactory.getAudioContext())
        return [];

    // We support 8-bit and 16-bit raw PCM
    return [
        'audio/L8',
        'audio/L16'
    ];

};
<commit_msg>GUAC-1511: Take number of channels into account when creating SampleArray.<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

var Guacamole = Guacamole || {};

/**
 * Abstract audio recorder which streams arbitrary audio data to an underlying
 * Guacamole.OutputStream. It is up to implementations of this class to provide
 * some means of handling this Guacamole.OutputStream. Data produced by the
 * recorder is to be sent along the provided stream immediately.
 *
 * @constructor
 */
Guacamole.AudioRecorder = function AudioRecorder() {

    // AudioRecorder currently provides no functions

};

/**
 * Determines whether the given mimetype is supported by any built-in
 * implementation of Guacamole.AudioRecorder, and thus will be properly handled
 * by Guacamole.AudioRecorder.getInstance().
 *
 * @param {String} mimetype
 *     The mimetype to check.
 *
 * @returns {Boolean}
 *     true if the given mimetype is supported by any built-in
 *     Guacamole.AudioRecorder, false otherwise.
 */
Guacamole.AudioRecorder.isSupportedType = function isSupportedType(mimetype) {

    return Guacamole.RawAudioRecorder.isSupportedType(mimetype);

};

/**
 * Returns a list of all mimetypes supported by any built-in
 * Guacamole.AudioRecorder, in rough order of priority. Beware that only the
 * core mimetypes themselves will be listed. Any mimetype parameters, even
 * required ones, will not be included in the list. For example, "audio/L8" is
 * a supported raw audio mimetype that is supported, but it is invalid without
 * additional parameters. Something like "audio/L8;rate=44100" would be valid,
 * however (see https://tools.ietf.org/html/rfc4856).
 *
 * @returns {String[]}
 *     A list of all mimetypes supported by any built-in
 *     Guacamole.AudioRecorder, excluding any parameters.
 */
Guacamole.AudioRecorder.getSupportedTypes = function getSupportedTypes() {

    return Guacamole.RawAudioRecorder.getSupportedTypes();

};

/**
 * Returns an instance of Guacamole.AudioRecorder providing support for the
 * given audio format. If support for the given audio format is not available,
 * null is returned.
 *
 * @param {Guacamole.OutputStream} stream
 *     The Guacamole.OutputStream to send audio data through.
 *
 * @param {String} mimetype
 *     The mimetype of the audio data to be sent along the provided stream.
 *
 * @return {Guacamole.AudioRecorder}
 *     A Guacamole.AudioRecorder instance supporting the given mimetype and
 *     writing to the given stream, or null if support for the given mimetype
 *     is absent.
 */
Guacamole.AudioRecorder.getInstance = function getInstance(stream, mimetype) {

    // Use raw audio recorder if possible
    if (Guacamole.RawAudioRecorder.isSupportedType(mimetype))
        return new Guacamole.RawAudioRecorder(stream, mimetype);

    // No support for given mimetype
    return null;

};

/**
 * Implementation of Guacamole.AudioRecorder providing support for raw PCM
 * format audio. This recorder relies only on the Web Audio API and does not
 * require any browser-level support for its audio formats.
 *
 * @constructor
 * @augments Guacamole.AudioRecorder
 * @param {Guacamole.OutputStream} stream
 *     The Guacamole.OutputStream to write audio data to.
 *
 * @param {String} mimetype
 *     The mimetype of the audio data to send along the provided stream, which
 *     must be a "audio/L8" or "audio/L16" mimetype with necessary parameters,
 *     such as: "audio/L16;rate=44100,channels=2".
 */
Guacamole.RawAudioRecorder = function RawAudioRecorder(stream, mimetype) {

    /**
     * The format of audio this recorder will encode.
     *
     * @private
     * @type {Guacamole.RawAudioFormat}
     */
    var format = Guacamole.RawAudioFormat.parse(mimetype);

    /**
     * An instance of a Web Audio API AudioContext object, or null if the
     * Web Audio API is not supported.
     *
     * @private
     * @type {AudioContext}
     */
    var context = Guacamole.AudioContextFactory.getAudioContext();

    /**
     * A function which directly invokes the browser's implementation of
     * navigator.getUserMedia() with all provided parameters.
     *
     * @type Function
     */
    var getUserMedia = (navigator.getUserMedia
            || navigator.webkitGetUserMedia
            || navigator.mozGetUserMedia
            || navigator.msGetUserMedia).bind(navigator);

    /**
     * Guacamole.ArrayBufferWriter wrapped around the audio output stream
     * provided when this Guacamole.RawAudioRecorder was created.
     *
     * @private
     * @type {Guacamole.ArrayBufferWriter}
     */
    var writer = new Guacamole.ArrayBufferWriter(stream);

    /**
     * The type of typed array that will be used to represent each audio packet
     * internally. This will be either Int8Array or Int16Array, depending on
     * whether the raw audio format is 8-bit or 16-bit.
     *
     * @private
     * @constructor
     */
    var SampleArray = (format.bytesPerSample === 1) ? window.Int8Array : window.Int16Array;

    /**
     * The maximum absolute value of any sample within a raw audio packet sent
     * by this audio recorder. This depends only on the size of each sample,
     * and will be 128 for 8-bit audio and 32768 for 16-bit audio.
     *
     * @private
     * @type {Number}
     */
    var maxSampleValue = (format.bytesPerSample === 1) ? 128 : 32768;

    /**
     * The size of audio buffer to request from the Web Audio API when
     * recording audio. This must be a power of two between 256 and 16384
     * inclusive, as required by AudioContext.createScriptProcessor().
     *
     * @private
     * @type {Number}
     */
    var bufferSize = format.bytesPerSample * 256;

    /**
     * Converts the given AudioBuffer into an audio packet, ready for streaming
     * along the underlying output stream. Unlike the raw audio packets used by
     * this audio recorder, AudioBuffers require floating point samples and are
     * split into isolated planes of channel-specific data.
     *
     * @private
     * @param {AudioBuffer} audioBuffer
     *     The Web Audio API AudioBuffer that should be converted to a raw
     *     audio packet.
     *
     * @returns {SampleArray}
     *     A new raw audio packet containing the audio data from the provided
     *     AudioBuffer.
     */
    var toSampleArray = function toSampleArray(audioBuffer) {

        // Get array for raw PCM storage
        var data = new SampleArray(audioBuffer.length * format.channels);

        // Convert each channel
        for (var channel = 0; channel < format.channels; channel++) {

            var audioData = audioBuffer.getChannelData(channel);

            // Fill array with data from audio buffer channel
            var offset = channel;
            for (var i = 0; i < audioData.length; i++) {
                data[offset] = audioData[i] * maxSampleValue;
                offset += format.channels;
            }

        }

        return data;

    };

    // Once audio stream is successfully open, request and begin reading audio
    writer.onack = function audioStreamAcknowledged(status) {

        // Abort stream if rejected
        if (status.code !== Guacamole.Status.Code.SUCCESS) {
            writer.sendEnd();
            return;
        }

        // Attempt to retrieve an audio input stream from the browser
        getUserMedia({ 'audio' : true }, function streamReceived(mediaStream) {

            // Create processing node which receives appropriately-sized audio buffers
            var processor = context.createScriptProcessor(bufferSize, format.channels, format.channels);
            processor.connect(context.destination);

            // Send blobs when audio buffers are received
            processor.onaudioprocess = function processAudio(e) {
                writer.sendData(toSampleArray(e.inputBuffer).buffer);
            };

            // Connect processing node to user's audio input source
            var source = context.createMediaStreamSource(mediaStream);
            source.connect(processor);

        }, function streamDenied() {

            // Simply end stream if audio access is not allowed
            writer.sendEnd();

        });

    };

};

Guacamole.RawAudioRecorder.prototype = new Guacamole.AudioRecorder();

/**
 * Determines whether the given mimetype is supported by
 * Guacamole.RawAudioRecorder.
 *
 * @param {String} mimetype
 *     The mimetype to check.
 *
 * @returns {Boolean}
 *     true if the given mimetype is supported by Guacamole.RawAudioRecorder,
 *     false otherwise.
 */
Guacamole.RawAudioRecorder.isSupportedType = function isSupportedType(mimetype) {

    // No supported types if no Web Audio API
    if (!Guacamole.AudioContextFactory.getAudioContext())
        return false;

    return Guacamole.RawAudioFormat.parse(mimetype) !== null;

};

/**
 * Returns a list of all mimetypes supported by Guacamole.RawAudioRecorder. Only
 * the core mimetypes themselves will be listed. Any mimetype parameters, even
 * required ones, will not be included in the list. For example, "audio/L8" is
 * a raw audio mimetype that may be supported, but it is invalid without
 * additional parameters. Something like "audio/L8;rate=44100" would be valid,
 * however (see https://tools.ietf.org/html/rfc4856).
 *
 * @returns {String[]}
 *     A list of all mimetypes supported by Guacamole.RawAudioRecorder,
 *     excluding any parameters. If the necessary JavaScript APIs for recording
 *     raw audio are absent, this list will be empty.
 */
Guacamole.RawAudioRecorder.getSupportedTypes = function getSupportedTypes() {

    // No supported types if no Web Audio API
    if (!Guacamole.AudioContextFactory.getAudioContext())
        return [];

    // We support 8-bit and 16-bit raw PCM
    return [
        'audio/L8',
        'audio/L16'
    ];

};
<|endoftext|>


<commit_before>const Discord = require("discord.js");
const config = require("./../../config.json");
const os = require('os');

//  (x64) AMD Athlon (x3) Something @ 3ghz (clocked at ????MHz)
var hardwareinfo = "[CPU] (" + os.arch() + ") " + os.cpus()[0]["model"] + " (clocked at " + os.cpus()[0]["speed"] + "MHz)";
var softwareinfo = "[" + os.type() + "] " + os.release() + "\n[Hostname] " + os.hostname();

module.exports = function(message, client) {
	var pinginfo = client.ping + "ms";
	var shardinfo;
	if(!client.shard) {
		shardinfo = "The bot is not running in shard mode.";
	} else {
		shardinfo = client.shard.id;
	}

	var embed = new Discord.RichEmbed()
		.setFooter("MSS-Discord, " + config.MSS.version, "")
		.setTimestamp()
		.setURL("http://moustacheminer.com/")
		.addField("Average Ping", pinginfo)
		.addField("Shard ID", shardinfo, true)
		.addField("Hardware", hardwareinfo, true)
		.addField("Software", softwareinfo, true);

	message.channel.sendEmbed(embed, "", { disableEveryone: true });
}
<commit_msg>More inline testing<commit_after>const Discord = require("discord.js");
const config = require("./../../config.json");
const os = require('os');

//  (x64) AMD Athlon (x3) Something @ 3ghz (clocked at ????MHz)
var hardwareinfo = "[CPU] (" + os.arch() + ") " + os.cpus()[0]["model"] + " (clocked at " + os.cpus()[0]["speed"] + "MHz)";
var softwareinfo = "[" + os.type() + "] " + os.release() + "\n[Hostname] " + os.hostname();

module.exports = function(message, client) {
	var pinginfo = client.ping + "ms";
	var shardinfo;
	if(!client.shard) {
		shardinfo = "The bot is not running in shard mode.";
	} else {
		shardinfo = client.shard.id;
	}

	var embed = new Discord.RichEmbed()
		.setFooter("MSS-Discord, " + config.MSS.version, "")
		.setTimestamp()
		.addField("Average Ping", pinginfo, true)
		.addField("Shard ID", shardinfo, true)
		.addField("Hardware", hardwareinfo, true)
		.addField("Software", softwareinfo, true);

	message.channel.sendEmbed(embed, "", { disableEveryone: true });
}
<|endoftext|>


<commit_before>/** @jsx REACT.DOM */
import React from 'react';
import cuid from 'cuid';

export default React.createClass({

  render: function () {
    return (

    );
  },

  componentDidMount: function() {

    let keyboardEl = this.refs.keyboard.getDOMNode();
    keyboardEl.id = cuid();
    this.hancock = new QwertyHancock({
      id: keyboardEl.id,
      width: 600,
      height: 150,
      octaves: 2,
      startNote: 'A3',
      whiteNotesColour: 'white',
      blackNotesColour: 'black',
      hoverColour: '#f3e939'
    });

    let currentlyPlaying = {};
    this.hancock.keyDown = (note, frequency) => {
      currentlyPlaying[note] = this.props.track.play(frequency);
    };
    this.hancock.keyUp = function(note) {
      currentlyPlaying[note].stop();
      delete currentlyPlaying[note];
    };

  },

});
<commit_msg>properly play different frequencies<commit_after>/** @jsx REACT.DOM */
import React from 'react';
import cuid from 'cuid';

export default React.createClass({

  render: function () {
    return (

    );
  },

  componentDidMount: function() {

    let keyboardEl = this.refs.keyboard.getDOMNode();
    keyboardEl.id = cuid();
    this.hancock = new QwertyHancock({
      id: keyboardEl.id,
      width: 600,
      height: 150,
      octaves: 2,
      startNote: 'A3',
      whiteNotesColour: 'white',
      blackNotesColour: 'black',
      hoverColour: '#f3e939'
    });

    let currentlyPlaying = {};
    this.hancock.keyDown = (note, frequency) => {
      currentlyPlaying[note] = this.props.track.play({ frequency });
    };
    this.hancock.keyUp = function(note) {
      currentlyPlaying[note].stop();
      delete currentlyPlaying[note];
    };

  },

});
<|endoftext|>


<commit_before>/*---------------------------------------------------------------
  :: sails-rest
  -> adapter
---------------------------------------------------------------*/

var Errors = require('waterline-errors').adapter,
  async = require('async'),
  restify = require('restify'),
  url = require('url'),
  _ = require('lodash'),
  inflections = require('underscore.inflections');

module.exports = (function() {
  "use strict";

  // Rest Custom Error Object
  function RestError(message, meta) {
    this.name = "RestError";
    this.message = message || "REST Error Message";
    this.meta = meta || {};
  }
  RestError.prototype = new Error();
  RestError.prototype.constructor = RestError;

  var connections = {};

  // Private functions
  /**
   * Format result object according to schema
   * @param result result object
   * @param collectionName name of collection the result object belongs to
   * @param config object representing the connection configuration
   * @param definition object representing the collection definition
   * @returns {*}
   */
  function formatResult(result, collectionName, config, definition) {
    if (_.isFunction(config.beforeFormatResult)) {
      result = config.beforeFormatResult(result);
    }

    _.each(definition, function(def, key) {
      if (def.type.match(/date/i)) {
        result[key] = new Date(result[key] ? result[key] : null);
      }
    });

    if (_.isFunction(config.afterFormatResult)) {
      result = config.afterFormatResult(result);
    }

    return result;
  }

  /**
   * Format results according to schema
   * @param results array of result objects (model instances)
   * @param collectionName name of collection the result object belongs to
   * @param config object representing the connection configuration
   * @param definition object representing the collection definition
   * @returns {*}
   */
  function formatResults(results, collectionName, config, definition) {
    if (_.isFunction(config.beforeFormatResults)) {
      results = config.beforeFormatResults(results);
    }

    results.forEach(function(result) {
      formatResult(result, collectionName, config, definition);
    });

    if (_.isFunction(config.afterFormatResults)) {
      results = config.afterFormatResults(results);
    }

    return results;
  }

  /**
   * Ensure results are contained in an array. Resolves variants in API responses such as `results` or `objects` instead of `[.....]`
   * @param data response data to format as results array
   * @param collectionName name of collection the result object belongs to
   * @param config object representing the connection configuration
   * @param definition object representing the collection definition
   * @returns {*}
   */
  function getResultsAsCollection(data, collectionName, config, definition) {
    var d = (data.objects || data.results || data),
      a = _.isArray(d) ? d : [d];

    return formatResults(a, collectionName, config, definition);
  }

  /**
   * Generate a pathname to use for a request
   * @param config object representing the connection configuration
   * @param restMethod string of the request method
   * @param values object representing the data being send (if any)
   * @param options object representing options passed from the calling method
   * @returns {*}
   */
  function getPathname(config, restMethod, values, options){
    return config.pathname + '/' + config.resource + (config.action ? '/' + config.action : '');
  }

  /**
   * Makes a REST request via restify
   * @param identity type of connection interface
   * @param collectionName name of collection the result object belongs to
   * @param methodName name of CRUD method being used
   * @param cb callback from method
   * @param options options from method
   * @param values values from method
   * @returns {*}
   */
  function makeRequest(identity, collectionName, methodName, cb, options, values) {
    var r = null,
      opt = null,
      cache = connections[identity].cache,
      config = _.cloneDeep(connections[identity].config),
      connection = connections[identity].connection,
      definition = connections[identity].definition,
      restMethod = config.methods[methodName],
      pathname;

    // Override config settings from options if available
    if (options && _.isPlainObject(options)) {
      _.each(config, function(val, key) {
        if (_.has(options, key)) {
          config[key] = options[key];
        }
      });
    }

    // if resource name not set in config,
    // try to get it from pluralized form of collectionName
    if (!config.resource) {
      config.resource = inflections.pluralize(collectionName);
    }

    pathname = config.getPathname(config, restMethod, values, options);

    if (options && options.where) {
      if (options.where.id) {
        // Add id to pathname if provided
        pathname += '/' + options.where.id;
        delete options.where.id;
      } else if (methodName === 'destroy' || methodName === 'update') {
        // Find all and make new request for each.
        makeRequest(identity, collectionName, 'find', function(error, results) {
          if (error) {
            cb(error);
          } else {
            _.each(results, function(result, i) {
              options = {
                where: {
                  id: result.id
                }
              };

              makeRequest(identity, collectionName, methodName, (i + 1) === results.length ? cb : function() {}, options, values);
            });
          }
        }, options);

        return;
      }

      // Add where statement as query parameters if requesting via GET
      if (restMethod === 'get') {
        _.extend(config.query, (options.where || {}));
        ['skip', 'limit', 'offset'].forEach(function(key){
          if(options[key] !== undefined){
            config.query[key] = options[key];
          }
        });
      }
      // Set opt if additional where statements are available
      else if (_.size(options.where)) {
        opt = options.where;
      } else {
        delete options.where;
      }
    }

    if (!opt && values) {
      opt = values;

      if (options) {
        opt = _.extend(options, opt);
      }
    }

    // Add pathname to connection
    _.extend(config, {
      pathname: pathname
    });

    // Format URI
    var uri = url.format(config);

    // Retrieve data from the cache
    if (methodName === 'find') {
      r = cache && cache.engine.get(uri);
    }

    if (r) {
      cb(null, r);
    } else if (_.isFunction(connection[restMethod])) {
      var path = uri.replace(connection.url.href, '/');

      var callback = function(err, req, res, data) {
        var restError,
            // check if response code is in 4xx or 5xx range
            responseErrorCode = res && /^(4|5)\d+$/.test(res.statusCode.toString());

        if (err && ( res === undefined || res === null || responseErrorCode ) ) {
          restError = new RestError(err.message, {req: req, res: res, data: data});
          cb(restError);
        } else {
          if (methodName === 'find') {
            r = getResultsAsCollection(data, collectionName, config, definition);
            if (cache) {
              cache.engine.set(uri, r);
            }
          } else {
            r = formatResult(data, collectionName, config, definition);
            if (cache) {
              cache.engine.del(uri);
            }
          }
          cb(null, r);
        }
      };

      // Make request via restify
      if (opt) {
        connection[restMethod](path, opt, callback);
      } else {
        connection[restMethod](path, callback);
      }
    } else {
      cb(new Error('Invalid REST method: ' + restMethod));
    }

    return false;
  }

  // Adapter
  var adapter = {

    syncable: false,

    defaults: {
      type: 'json',
      host: 'localhost',
      port: 80,
      protocol: 'http',
      pathname: '',
      resource: null,
      action: null,
      query: {},
      methods: {
        create: 'post',
        find: 'get',
        update: 'put',
        destroy: 'del'
      },
      getPathname: getPathname,
      beforeFormatResult: null,
      afterFormatResult: null,
      beforeFormatResults: null,
      afterFormatResults: null
    },

    registerConnection: function(connection, collections, cb) {
      if (!connection.identity) return cb(Errors.IdentityMissing);
      if (connections[connection.identity]) return cb(Errors.IdentityDuplicate);

      var config, clientMethod, instance;

      config = this.defaults ? _.extend({}, this.defaults, connection) : connection;
      config.methods = this.defaults ? _.extend({}, this.defaults.methods, connection.methods) : connection.methods;
      clientMethod = 'create' + config.type.substr(0, 1).toUpperCase() + config.type.substr(1).toLowerCase() + 'Client';

      if (!_.isFunction(restify[clientMethod])) {
        throw new Error('Invalid type provided');
      }

      instance = {
        config: config,
        connection: restify[clientMethod]({
          url: url.format({
            protocol: config.protocol,
            hostname: config.host,
            port: config.port
          }),
          headers: config.headers
        })
      };

      if (config.basicAuth) {
        instance.connection.basicAuth(config.basicAuth.username, config.basicAuth.password);
      }

      if (config.cache) {
        instance.cache = config.cache;
      }

      connections[connection.identity] = instance;

      cb();
    },

    create: function(connection, collectionName, values, cb) {
      makeRequest(connection, collectionName, 'create', cb, null, values);
    },

    find: function(connection, collectionName, options, cb) {
      makeRequest(connection, collectionName, 'find', cb, options);
    },

    update: function(connection, collectionName, options, values, cb) {
      makeRequest(connection, collectionName, 'update', cb, options, values);
    },

    destroy: function(connection, collectionName, options, cb) {
      makeRequest(connection, collectionName, 'destroy', cb, options);
    },

    drop: function(connection, collectionName, relations, cb) {
      cb();
    },

    define: function(connection, collectionName, definition, cb) {
      connections[connection].definition = definition;
      cb();
    },

    describe: function(connection, collectionName, cb) {
      cb(null, connections[connection].definition);
    }
  };

  return adapter;
}());
<commit_msg>JsDoc.<commit_after>/*---------------------------------------------------------------
  :: sails-rest
  -> adapter
---------------------------------------------------------------*/

var Errors = require('waterline-errors').adapter,
  async = require('async'),
  restify = require('restify'),
  url = require('url'),
  _ = require('lodash'),
  inflections = require('underscore.inflections');

module.exports = (function() {
  "use strict";

  // Rest Custom Error Object
  function RestError(message, meta) {
    this.name = "RestError";
    this.message = message || "REST Error Message";
    this.meta = meta || {};
  }
  RestError.prototype = new Error();
  RestError.prototype.constructor = RestError;

  var connections = {};

  // Private functions
  /**
   * Format result object according to schema
   * @param {Object} result
   * @param {String} collectionName - collection the result object belongs to
   * @param {Object} config - connection configuration
   * @param {Object} definition - collection definition
   * @returns {Object}
   */
  function formatResult(result, collectionName, config, definition) {
    if (_.isFunction(config.beforeFormatResult)) {
      result = config.beforeFormatResult(result);
    }

    _.each(definition, function(def, key) {
      if (def.type.match(/date/i)) {
        result[key] = new Date(result[key] ? result[key] : null);
      }
    });

    if (_.isFunction(config.afterFormatResult)) {
      result = config.afterFormatResult(result);
    }

    return result;
  }

  /**
   * Format results according to schema
   * @param {Array} results - objects (model instances)
   * @param {String} collectionName - collection the result object belongs to
   * @param {Object} config - connection configuration
   * @param {Object} definition - collection definition
   * @returns {Array}
   */
  function formatResults(results, collectionName, config, definition) {
    if (_.isFunction(config.beforeFormatResults)) {
      results = config.beforeFormatResults(results);
    }

    results.forEach(function(result) {
      formatResult(result, collectionName, config, definition);
    });

    if (_.isFunction(config.afterFormatResults)) {
      results = config.afterFormatResults(results);
    }

    return results;
  }

  /**
   * Ensure results are contained in an array. Resolves variants in API responses such as `results` or `objects` instead of `[.....]`
   * @param {Object|Array} data - response data to format as results array
   * @param {String} collectionName - collection the result object belongs to
   * @param {Object} config - connection configuration
   * @param {Object} definition - collection definition
   * @returns {Object|Array}
   */
  function getResultsAsCollection(data, collectionName, config, definition) {
    var d = (data.objects || data.results || data),
      a = _.isArray(d) ? d : [d];

    return formatResults(a, collectionName, config, definition);
  }

  /**
   * Generate a pathname to use for a request
   * @param {Object} config - connection configuration
   * @param {String} method - request method
   * @param {Object} values - data being send (if any)
   * @param {Object} options - options passed from the calling method
   * @returns {Object}
   */
  function getPathname(config, method, values, options){
    return config.pathname + '/' + config.resource + (config.action ? '/' + config.action : '');
  }

  /**
   * Makes a REST request via restify
   * @param {String} identity - type of connection interface
   * @param {String} collectionName - collection the result object belongs to
   * @param {String} methodName - name of CRUD method being used
   * @param {Function} cb - callback from method
   * @param {Object} options - options from method
   * @param {Object|Array} [values] - values from method
   * @returns {Boolean}
   */
  function makeRequest(identity, collectionName, methodName, cb, options, values) {
    var r = null,
      opt = null,
      cache = connections[identity].cache,
      config = _.cloneDeep(connections[identity].config),
      connection = connections[identity].connection,
      definition = connections[identity].definition,
      restMethod = config.methods[methodName],
      pathname;

    // Override config settings from options if available
    if (options && _.isPlainObject(options)) {
      _.each(config, function(val, key) {
        if (_.has(options, key)) {
          config[key] = options[key];
        }
      });
    }

    // if resource name not set in config,
    // try to get it from pluralized form of collectionName
    if (!config.resource) {
      config.resource = inflections.pluralize(collectionName);
    }

    pathname = config.getPathname(config, restMethod, values, options);

    if (options && options.where) {
      if (options.where.id) {
        // Add id to pathname if provided
        pathname += '/' + options.where.id;
        delete options.where.id;
      } else if (methodName === 'destroy' || methodName === 'update') {
        // Find all and make new request for each.
        makeRequest(identity, collectionName, 'find', function(error, results) {
          if (error) {
            cb(error);
          } else {
            _.each(results, function(result, i) {
              options = {
                where: {
                  id: result.id
                }
              };

              makeRequest(identity, collectionName, methodName, (i + 1) === results.length ? cb : function() {}, options, values);
            });
          }
        }, options);

        return false;
      }

      // Add where statement as query parameters if requesting via GET
      if (restMethod === 'get') {
        _.extend(config.query, (options.where || {}));
        ['skip', 'limit', 'offset'].forEach(function(key){
          if(options[key] !== undefined){
            config.query[key] = options[key];
          }
        });
      }
      // Set opt if additional where statements are available
      else if (_.size(options.where)) {
        opt = options.where;
      } else {
        delete options.where;
      }
    }

    if (!opt && values) {
      opt = values;

      if (options) {
        opt = _.extend(options, opt);
      }
    }

    // Add pathname to connection
    config.pathname = pathname;

    // Format URI
    var uri = url.format(config);

    // Retrieve data from the cache
    if (methodName === 'find') {
      r = cache && cache.engine.get(uri);
    }

    if (r) {
      cb(null, r);
    } else if (_.isFunction(connection[restMethod])) {
      var path = uri.replace(connection.url.href, '/');

      var callback = function(err, req, res, data) {
        var restError,
            // check if response code is in 4xx or 5xx range
            responseErrorCode = res && /^(4|5)\d+$/.test(res.statusCode.toString());

        if (err && ( res === undefined || res === null || responseErrorCode ) ) {
          restError = new RestError(err.message, {req: req, res: res, data: data});
          cb(restError);
        } else {
          if (methodName === 'find') {
            r = getResultsAsCollection(data, collectionName, config, definition);
            if (cache) {
              cache.engine.set(uri, r);
            }
          } else {
            r = formatResult(data, collectionName, config, definition);
            if (cache) {
              cache.engine.del(uri);
            }
          }
          cb(null, r);
        }
      };

      // Make request via restify
      if (opt) {
        connection[restMethod](path, opt, callback);
      } else {
        connection[restMethod](path, callback);
      }
    } else {
      cb(new Error('Invalid REST method: ' + restMethod));
    }

    return false;
  }

  // Adapter
  var adapter = {

    syncable: false,

    defaults: {
      type: 'json',
      host: 'localhost',
      port: 80,
      protocol: 'http',
      pathname: '',
      resource: null,
      action: null,
      query: {},
      methods: {
        create: 'post',
        find: 'get',
        update: 'put',
        destroy: 'del'
      },
      getPathname: getPathname,
      beforeFormatResult: null,
      afterFormatResult: null,
      beforeFormatResults: null,
      afterFormatResults: null
    },

    registerConnection: function(connection, collections, cb) {
      if (!connection.identity) return cb(Errors.IdentityMissing);
      if (connections[connection.identity]) return cb(Errors.IdentityDuplicate);

      var config, clientMethod, instance;

      config = this.defaults ? _.extend({}, this.defaults, connection) : connection;
      config.methods = this.defaults ? _.extend({}, this.defaults.methods, connection.methods) : connection.methods;
      clientMethod = 'create' + config.type.substr(0, 1).toUpperCase() + config.type.substr(1).toLowerCase() + 'Client';

      if (!_.isFunction(restify[clientMethod])) {
        throw new Error('Invalid type provided');
      }

      instance = {
        config: config,
        connection: restify[clientMethod]({
          url: url.format({
            protocol: config.protocol,
            hostname: config.host,
            port: config.port
          }),
          headers: config.headers
        })
      };

      if (config.basicAuth) {
        instance.connection.basicAuth(config.basicAuth.username, config.basicAuth.password);
      }

      if (config.cache) {
        instance.cache = config.cache;
      }

      connections[connection.identity] = instance;

      cb();
    },

    create: function(connection, collectionName, values, cb) {
      makeRequest(connection, collectionName, 'create', cb, null, values);
    },

    find: function(connection, collectionName, options, cb) {
      makeRequest(connection, collectionName, 'find', cb, options);
    },

    update: function(connection, collectionName, options, values, cb) {
      makeRequest(connection, collectionName, 'update', cb, options, values);
    },

    destroy: function(connection, collectionName, options, cb) {
      makeRequest(connection, collectionName, 'destroy', cb, options);
    },

    drop: function(connection, collectionName, relations, cb) {
      cb();
    },

    define: function(connection, collectionName, definition, cb) {
      connections[connection].definition = definition;
      cb();
    },

    describe: function(connection, collectionName, cb) {
      cb(null, connections[connection].definition);
    }
  };

  return adapter;
}());
<|endoftext|>