<commit_before>JSJaCHBC_NKEYS = 5; // number of keys to generate

function JSJaCHttpBindingConnection(oDbg) {
	this.base = JSJaCConnection;
	this.base(oDbg);

	this.keys  = null;
	this.setup = false;

	this.connect = JSJaCHBCConnect;
	this.disconnect = JSJaCHBCDisconnect;
	this._prepareResponse = JSJaCHBCPrepareResponse;
	this._getRequestString = JSJaCHBCGetRequestString;
	this._setupRequest = JSJaCHBCSetupRequest;
	this._getStreamID = JSJaCHBCGetStreamID;
}

function JSJaCHBCKeys(oDbg) {
	var seed = Math.random();

	this.k = new Array();
	this.k[0] = seed.toString();
	this.oDbg = oDbg;

	for (var i=1; i<JSJaCHBC_NKEYS; i++) {
		this.k[i] = b64_sha1(this.k[i-1]);
		oDbg.log(i+": "+this.k[i],4);
	}

	this.indexAt = JSJaCHBC_NKEYS-1;
	this.getKey = function() {
		return this.k[this.indexAt--];
	};
	this.lastKey = function() { return (this.indexAt == 0); };
}

function JSJaCHBCSetupRequest(async) {

	var req = XmlHttp.create();

	try {
		req.open("POST",this.http_base,async);
		req.setRequestHeader('Content-Type','text/xml');
	} catch(e) { this.oDbg.log(e,1); }

	return req;
}

function JSJaCHBCGetRequestString(xml) {
	this.rid++;

	var reqstr = "" + xml + "";
	} else {
		reqstr += "/>";
	}

	return reqstr;
}

function JSJaCHBCPrepareResponse(req) {
	if (!this.connected())
		return null;

	/* handle error */

	if (req.status != 200) {
		this.oDbg.log("invalid response:\n" + req.responseText,1);
		clearTimeout(this.timeout); // remove timer
		this._connected = false;
		this.oDbg.log("Disconnected.",1);
		this.handleEvent('ondisconnect');
		if (req.status < 500)
  		this.handleEvent('onerror',JSJaCError('500','cancel','service-unavailable'));
		else
	  	this.handleEvent('onerror',JSJaCError('503','cancel','service-unavailable'));
		return null;
	}

	var response = XmlDocument.create();
	response.loadXML(req.responseText);

	// Check for errors from the server
  var body = response.firstChild;
	if (body.getAttribute("type") == "terminate") {
		this.oDbg.log("invalid response:\n" + req.responseText,1);
		clearTimeout(this.timeout); // remove timer
		this._connected = false;
		this.oDbg.log("Disconnected.",1);
		this.handleEvent('ondisconnect');
		this.handleEvent('onerror',JSJaCError('500','cancel','service-unavailable'));
		return null;
	}

	return response;
}

function JSJaCHBCConnect(http_base,server,username,resource,pass,timerval,register) {
	// initial request to get sid and streamid

	this.http_base = http_base || '/';
	this.server = server || 'localhost';
	this.username = username;
	this.resource = resource;
	this.pass = pass;
	this.rid  = Math.round( 100000.5 + ( ( (900000.49999) - (100000.5) ) * Math.random() ) );
	this.wait = 60;
  this.hold = 1;
	this.oDbg.log("http_base: " + this.http_base + "\nserver:" + server,2);

	this.keys = new JSJaCHBCKeys(this.oDbg); // generate first set of keys
	key = this.keys.getKey();

	this.req = this._setupRequest(false);

	this.oDbg.log("send body",4);
        // Create a body class?
	this.req.send("");

	// extract session ID
	this.oDbg.log(this.req.getAllResponseHeaders(),4);

	this.oDbg.log(this.req.responseText,4);

	if (this.req.responseText.match(/sid=[\'"](.+?)[\'"]/))
			this.sid = RegExp.$1;
	this.oDbg.log("got sid: "+this.sid,2);

	this._connected = true;

	/* wait for initial stream response to extract streamid needed
	 * for digest auth
	 */
	this._getStreamID();
}

function JSJaCHBCGetStreamID() {

	this.oDbg.log(this.req.responseText,4);

	// extract stream id used for non-SASL authentication
	if (this.req.responseText.match(/authid=[\'"](.+?)[\'"]/)) {
			this.streamid = RegExp.$1;
			this.oDbg.log("got streamid: "+this.streamid,2);
	} else {
		oCon = this;
		setTimeout("oCon._sendEmpty()",1000);
		return;
	}

	this._process(timerval); // start polling

	if (register)
		this._doReg();
	else
		this._doAuth();
}

function JSJaCHBCDisconnect() {

	if (!this.connected())
		return;

	if (this.timeout)
		clearTimeout(this.timeout); // remove timer
	this.rid++;
	this.req = this._setupRequest(false);

	var body = XmlDocument.create();
	body.loadXML("<presence type='unavailable' xmlns='jabber:client'/>");
	this.req.send(body.xml);

	this.oDbg.log("Disconnected: "+this.req.responseText,2);
	this._connected = false;
	this.handleEvent('ondisconnect');
}
<commit_msg>JSJaCPrepareResponse: don't use supplied body element for parsing as it contains xmlns attribute which spoiles child nodes<commit_after>JSJaCHBC_NKEYS = 5; // number of keys to generate

var re = new RegExp(/]+>(.*)<\/body>/i);

function JSJaCHttpBindingConnection(oDbg) {
	this.base = JSJaCConnection;
	this.base(oDbg);

	this.keys  = null;
	this.setup = false;

	this.connect = JSJaCHBCConnect;
	this.disconnect = JSJaCHBCDisconnect;
	this._prepareResponse = JSJaCHBCPrepareResponse;
	this._getRequestString = JSJaCHBCGetRequestString;
	this._setupRequest = JSJaCHBCSetupRequest;
	this._getStreamID = JSJaCHBCGetStreamID;
}

function JSJaCHBCKeys(oDbg) {
	var seed = Math.random();

	this.k = new Array();
	this.k[0] = seed.toString();
	this.oDbg = oDbg;

	for (var i=1; i<JSJaCHBC_NKEYS; i++) {
		this.k[i] = b64_sha1(this.k[i-1]);
		oDbg.log(i+": "+this.k[i],4);
	}

	this.indexAt = JSJaCHBC_NKEYS-1;
	this.getKey = function() {
		return this.k[this.indexAt--];
	};
	this.lastKey = function() { return (this.indexAt == 0); };
}

function JSJaCHBCSetupRequest(async) {

	var req = XmlHttp.create();

	try {
		req.open("POST",this.http_base,async);
		req.setRequestHeader('Content-Type','text/xml');
	} catch(e) { this.oDbg.log(e,1); }

	return req;
}

function JSJaCHBCGetRequestString(xml) {
	this.rid++;

	var reqstr = "" + xml + "";
	} else {
		reqstr += "/>";
	}

	return reqstr;
}

function JSJaCHBCPrepareResponse(req) {
	if (!this.connected())
		return null;

	/* handle error */

	if (req.status != 200) {
		this.oDbg.log("invalid response:\n" + req.responseText,1);
		clearTimeout(this.timeout); // remove timer
		this._connected = false;
		this.oDbg.log("Disconnected.",1);
		this.handleEvent('ondisconnect');
		if (req.status < 500)
  		this.handleEvent('onerror',JSJaCError('500','cancel','service-unavailable'));
		else
	  	this.handleEvent('onerror',JSJaCError('503','cancel','service-unavailable'));
		return null;
	}

	// Check for errors from the server
  var body = req.responseXML.firstChild;
	if (body.getAttribute("type") == "terminate") {
		this.oDbg.log("invalid response:\n" + req.responseText,1);
		clearTimeout(this.timeout); // remove timer
		this._connected = false;
		this.oDbg.log("Disconnected.",1);
		this.handleEvent('ondisconnect');
		this.handleEvent('onerror',JSJaCError('500','cancel','service-unavailable'));
		return null;
	}

	/* it would be really nice if we could just use req.responseXML here.
	 * unfortunately given body element has a namespace attribute which
	 * causes that childNodes are prepended with some qualifying
	 * namespaceURI if treated seperately. if someone knows a more
	 * elegant solution to this, please let me know!
	 */

	re.exec(req.responseText);
	try {
		var response = XmlDocument.create();
		response.loadXML(""+RegExp.$1+"");
		return response;
	} catch (e) {
		return null;
	}
}

function JSJaCHBCConnect(http_base,server,username,resource,pass,timerval,register) {
	// initial request to get sid and streamid

	this.http_base = http_base || '/';
	this.server = server || 'localhost';
	this.username = username;
	this.resource = resource;
	this.pass = pass;
	this.rid  = Math.round( 100000.5 + ( ( (900000.49999) - (100000.5) ) * Math.random() ) );
	this.wait = 60;
  this.hold = 1;
	this.oDbg.log("http_base: " + this.http_base + "\nserver:" + server,2);

	this.keys = new JSJaCHBCKeys(this.oDbg); // generate first set of keys
	key = this.keys.getKey();

	this.req = this._setupRequest(false);

	this.oDbg.log("send body",4);
        // Create a body class?
	this.req.send("");

	// extract session ID
	this.oDbg.log(this.req.getAllResponseHeaders(),4);

	this.oDbg.log(this.req.responseText,4);

	if (this.req.responseText.match(/sid=[\'"](.+?)[\'"]/))
			this.sid = RegExp.$1;
	this.oDbg.log("got sid: "+this.sid,2);

	this._connected = true;

	/* wait for initial stream response to extract streamid needed
	 * for digest auth
	 */
	this._getStreamID();
}

function JSJaCHBCGetStreamID() {

	this.oDbg.log(this.req.responseText,4);

	// extract stream id used for non-SASL authentication
	if (this.req.responseText.match(/authid=[\'"](.+?)[\'"]/)) {
			this.streamid = RegExp.$1;
			this.oDbg.log("got streamid: "+this.streamid,2);
	} else {
		oCon = this;
		setTimeout("oCon._sendEmpty()",1000);
		return;
	}

	this._process(timerval); // start polling

	if (register)
		this._doReg();
	else
		this._doAuth();
}

function JSJaCHBCDisconnect() {

	if (!this.connected())
		return;

	if (this.timeout)
		clearTimeout(this.timeout); // remove timer
	this.rid++;
	this.req = this._setupRequest(false);

	var body = XmlDocument.create();
	body.loadXML("<presence type='unavailable' xmlns='jabber:client'/>");
	this.req.send(body.xml);

	this.oDbg.log("Disconnected: "+this.req.responseText,2);
	this._connected = false;
	this.handleEvent('ondisconnect');
}
<|endoftext|>


<commit_before>Ext.ns('Kwf.Utils');
/*
    By Osvaldas Valutis, www.osvaldas.info
    Available for use under the MIT License
*/
Kwf.Utils.DoubleTapToGo = function(el, params) {
    if (!('ontouchstart' in window) &&
        !navigator.msMaxTouchPoints &&
        !navigator.userAgent.toLowerCase().match( /windows phone os 7/i )
    ) return false;

    var applyOnEl = function(el, params) {
        Kwf.log(el);
        var curItem = false;
        $(el).on('click', function(e) {
            var item = $(this);
            if(item[0] != curItem[0]) {
                e.preventDefault();
                curItem = item;
            }
        });

        $(document).on('click touchstart MSPointerDown', function(e) {
            if (params && params.onopen && !el[0].doubleTapToGo) {
                el[0].doubleTapToGo = true;
                params.onopen.call(this, e, el[0]);
            }

            var resetItem = true;
            var parents = $( e.target ).parents();
            for (var i = 0; i < parents.length; i++) {
                if (parents[i] == curItem[0]) {
                    resetItem = false;
                }
            }
            if (resetItem) curItem = false;
        });
    };

    if (el instanceof jQuery) {
        applyOnEl(el, params);
    } else {
        Kwf.onJElementReady(el, Kwf.Utils.DoubleTapToGo);
    }
};
<commit_msg>Remove debug message<commit_after>Ext.ns('Kwf.Utils');
/*
    By Osvaldas Valutis, www.osvaldas.info
    Available for use under the MIT License
*/
Kwf.Utils.DoubleTapToGo = function(el, params) {
    if (!('ontouchstart' in window) &&
        !navigator.msMaxTouchPoints &&
        !navigator.userAgent.toLowerCase().match( /windows phone os 7/i )
    ) return false;

    var applyOnEl = function(el, params) {
        var curItem = false;
        $(el).on('click', function(e) {
            var item = $(this);
            if(item[0] != curItem[0]) {
                e.preventDefault();
                curItem = item;
            }
        });

        $(document).on('click touchstart MSPointerDown', function(e) {
            if (params && params.onopen && !el[0].doubleTapToGo) {
                el[0].doubleTapToGo = true;
                params.onopen.call(this, e, el[0]);
            }

            var resetItem = true;
            var parents = $( e.target ).parents();
            for (var i = 0; i < parents.length; i++) {
                if (parents[i] == curItem[0]) {
                    resetItem = false;
                }
            }
            if (resetItem) curItem = false;
        });
    };

    if (el instanceof jQuery) {
        applyOnEl(el, params);
    } else {
        Kwf.onJElementReady(el, Kwf.Utils.DoubleTapToGo);
    }
};
<|endoftext|>


<commit_before>var changePath = (function (Math, BezierCurve, Line, Vectors) {
    "use strict";

    function changePath(path, startX, startY, endX, endY, p1_x, p1_y, p2_x, p2_y) {

        var curve = path.curve;
        if (curve instanceof Line) {
            var vector = Vectors.get(startX, startY, endX, endY);
            var length = Vectors.magnitude(vector.x, vector.y);
            var unitVector = Vectors.normalize(vector.x, vector.y);

            curve.startX = startX;
            curve.startY = startY;
            curve.endX = endX;
            curve.endY = endY;
            curve.length = length;
            curve.unitVectorX = unitVector.x;
            curve.unitVectorY = unitVector.y;

        } else if (curve instanceof BezierCurve) {
            curve.pointA_x = startX;
            curve.pointA_y = startY;
            curve.pointB_x = p1_x;
            curve.pointB_y = p1_y;
            curve.pointC_x = p2_x;
            curve.pointC_y = p2_y;
            curve.pointD_x = endX;
            curve.pointD_y = endY;
        }
    }

    return changePath;
})(Math, BezierCurve, Line, Vectors);<commit_msg>fixes bug in resizing of paths refs #60 #core (cherry picked from commit 685971a)<commit_after>var changePath = (function (Math, BezierCurve, Line, Vectors) {
    "use strict";

    function changePath(path, startX, startY, endX, endY, p1_x, p1_y, p2_x, p2_y) {

        var curve = path.curve;
        if (curve instanceof Line) {
            var vector = Vectors.get(startX, startY, endX, endY);
            var length = Vectors.magnitude(vector.x, vector.y);
            var unitVector = Vectors.normalize(vector.x, vector.y);

            curve.startX = startX;
            curve.startY = startY;
            curve.endX = endX;
            curve.endY = endY;
            curve.length = length;
            curve.unitVectorX = unitVector.x;
            curve.unitVectorY = unitVector.y;
            curve.vectorX = vector.x;
            curve.vectorY = vector.y;

        } else if (curve instanceof BezierCurve) {
            curve.pointA_x = startX;
            curve.pointA_y = startY;
            curve.pointB_x = p1_x;
            curve.pointB_y = p1_y;
            curve.pointC_x = p2_x;
            curve.pointC_y = p2_y;
            curve.pointD_x = endX;
            curve.pointD_y = endY;
        }
    }

    return changePath;
})(Math, BezierCurve, Line, Vectors);<|endoftext|>


<commit_before>angular.module('pvta.controllers').controller('RouteMapController', function ($scope, $stateParams, $ionicLoading, Map, KML, Route) {
  ga('set', 'page', '/route-map.html');
  ga('send', 'pageview');
  var bounds = new google.maps.LatLngBounds();

  var mapOptions = {
    center: new google.maps.LatLng(42.386270, -72.525844),
    zoom: 15,
    mapTypeId: google.maps.MapTypeId.ROADMAP
  };

  $scope.map = new google.maps.Map(document.getElementById('map'), mapOptions);
  Map.init($scope.map, bounds);

  function placeVehicles () {
  //places every vehicle on said route on the map
    _.each($scope.vehicles, function (vehicle) {
      var message;
      var loc = new google.maps.LatLng(vehicle.Latitude, vehicle.Longitude);

      //if the vehicle is on time, make the text green. If it's late, make the text red and say late by how much
      if (vehicle.DisplayStatus === 'On Time') {
        message = '
Bus ' + vehicle.Name + ' - ' + vehicle.DisplayStatus + '
';
      }
      else {
        message = '
Bus ' + vehicle.Name + ' - ' + vehicle.DisplayStatus
          + ' by ' + vehicle.Deviation + ' minutes
';
      }

      //sets the content of the window to have a ton of information about the vehicle
      var content = '

'
      + $scope.route.ShortName + ': ' + vehicle.Destination + '
' + message + '
Last Stop: ' + vehicle.LastStop + '

';

      //add a listener for that vehicle with that content as part of the infobubble
      Map.addMapListener(Map.placeDesiredMarker(loc, 'http://www.google.com/mapfiles/kml/paddle/go.png'), content);
    });
  }

  $scope.$on('$ionicView.enter', function () {
    $ionicLoading.show({});
    var fileName = KML.pop();
    if (fileName) {
      //Map.addKML(fileName);
    }
    Map.plotCurrentLocation();
    $scope.route = Route.get({routeId: $stateParams.routeId}, function () {
      $scope.stops = $scope.route.Stops;
      Map.addKML($scope.route.RouteTraceFilename);
      $scope.vehicles = $scope.route.Vehicles;
      placeVehicles();
      $ionicLoading.hide();
    });
  });

});
<commit_msg>Undo unneeded changes to route-map<commit_after>angular.module('pvta.controllers').controller('RouteMapController', function ($scope, $stateParams, $ionicLoading, Map, KML, Route) {
  ga('set', 'page', '/route-map.html');
  ga('send', 'pageview');
  var bounds = new google.maps.LatLngBounds();

  var mapOptions = {
    center: new google.maps.LatLng(42.386270, -72.525844),
    zoom: 15,
    mapTypeId: google.maps.MapTypeId.ROADMAP
  };

  $scope.map = new google.maps.Map(document.getElementById('map'), mapOptions);
  Map.init($scope.map, bounds);

  function placeVehicles () {
  //places every vehicle on said route on the map
    _.each($scope.vehicles, function (vehicle) {
      var message;
      var loc = new google.maps.LatLng(vehicle.Latitude, vehicle.Longitude);

      //if the vehicle is on time, make the text green. If it's late, make the text red and say late by how much
      if (vehicle.DisplayStatus === 'On Time') {
        message = '
Bus ' + vehicle.Name + ' - ' + vehicle.DisplayStatus + '
';
      }
      else {
        message = '
Bus ' + vehicle.Name + ' - ' + vehicle.DisplayStatus
          + ' by ' + vehicle.Deviation + ' minutes
';
      }

      //sets the content of the window to have a ton of information about the vehicle
      var content = '

'
      + $scope.route.ShortName + ': ' + vehicle.Destination + '
' + message + '
Last Stop: ' + vehicle.LastStop + '

';

      //add a listener for that vehicle with that content as part of the infobubble
      Map.addMapListener(Map.placeDesiredMarker(loc, 'http://www.google.com/mapfiles/kml/paddle/go.png'), content);
    });
  }

  $scope.$on('$ionicView.enter', function () {
    $ionicLoading.show({});
    var fileName = KML.pop();
    if (fileName) {
      Map.addKML(fileName);
    }
    Map.plotCurrentLocation();
    $scope.route = Route.get({routeId: $stateParams.routeId}, function () {
      $scope.stops = $scope.route.Stops;
      $scope.vehicles = $scope.route.Vehicles;
      placeVehicles();
      $ionicLoading.hide();
    });
  });

});
<|endoftext|>


<commit_before>class UpdatingPopover extends React.Component {
  componentDidUpdate(prevProps) {
    if (prevProps.children !== this.props.children) {
      console.log('updating!');
      this.props.scheduleUpdate();
    }
  }

  render() {
    return <Popover {...this.props} />;
  }
}

const longContent = `
  Very long
  Multiline content
  that is engaging and what-not
`;
const shortContent = 'Short and sweet!';

function Example() {
  const [content, setContent] = useState(shortContent);

  useEffect(() => {
    const timerId = setInterval(() => {
      setContent(content === shortContent ? longContent : shortContent);
    }, 3000);

    return () => clearInterval(timerId);
  });

  return (
    <OverlayTrigger
      trigger="click"
      overlay={
        <UpdatingPopover id="popover-contained">{content}</UpdatingPopover>
      }
    >
      <Button>Holy guacamole!</Button>
    </OverlayTrigger>
  );
}

render(<Example />);
<commit_msg>Migrate UpdatingPopover component to functional<commit_after>const UpdatingPopover = React.forwardRef(
  ({ scheduleUpdate, children, ...props }, ref) => {
    useEffect(() => {
      console.log('updating!');
      scheduleUpdate();
    }, [children, scheduleUpdate]);
    return (
      <Popover ref={ref} {...props}>
        {children}
      </Popover>
    );
  },
);

const longContent = `
  Very long
  Multiline content
  that is engaging and what-not
`;
const shortContent = 'Short and sweet!';

function Example() {
  const [content, setContent] = useState(shortContent);

  useEffect(() => {
    const timerId = setInterval(() => {
      setContent(content === shortContent ? longContent : shortContent);
    }, 3000);

    return () => clearInterval(timerId);
  });

  return (
    <OverlayTrigger
      trigger="click"
      overlay={
        <UpdatingPopover id="popover-contained">{content}</UpdatingPopover>
      }
    >
      <Button>Holy guacamole!</Button>
    </OverlayTrigger>
  );
}

render(<Example />);
<|endoftext|>


<commit_before>describe("Jax.World", function() {
  var world;

  beforeEach(function() {
    SPEC_CONTEXT.prepare();
    world = SPEC_CONTEXT.world;
  });

  describe("adding a light", function() {
    var added, light;
    beforeEach(function() {
      added = false;
      light = new Jax.Light.Directional();
      world.on('lightAdded', function(light) { added = light; });
      world.addLight(light);
    });

    it("should fire event including the light object", function() {
      expect(added).toBe(light);
    });

    describe("removing a light", function() {
      var removed;
      beforeEach(function() {
        removed = false;
        world.on('lightRemoved', function(light) { removed = light; });
        world.removeLight(light);
      });

      it("should fire event including the light object", function() {
        expect(removed).toBe(light);
      });
    });

  });

  describe("disposal", function() {
    it("should remove objects from itself", function() {
      // it used to do a simple delete on objects, but now there
      // are event listeners that have to be unhooked, etc.
      var obj = world.addObject(new Jax.Model());
      spyOn(world, 'removeObject').andCallThrough();
      world.dispose();
      expect(world.removeObject).toHaveBeenCalledWith(obj);
    });

    it("should handle removal of multiple objects", function() {
      world.addObject(new Jax.Model({mesh: new Jax.Mesh.Quad()}));
      world.addObject(new Jax.Model({mesh: new Jax.Mesh.Quad()}));
      world.dispose();
    });

    it("should handle removal of multiple lights", function() {
      world.addLight(new Jax.Light.Directional);
      world.addLight(new Jax.Light.Point);
      world.dispose();
    });

    it("should not encounter errors disposing after render", function() {
      world.addObject(new Jax.Model({mesh: new Jax.Mesh.Quad()}));
      world.render();
      world.dispose();
    });

    it("twice in a row should have no effect", function() {
      world.addObject(new Jax.Model({mesh: new Jax.Mesh.Quad()}));
      world.render();
      world.dispose();
      world.dispose();
    });

    it("should not return any objects", function() {
      world.addObject(new Jax.Model({mesh: new Jax.Mesh.Quad()}));
      world.render();
      world.dispose();
      expect(world.getObjects()).toEqual([]);
    });
  });

  it("should remove objects from the octree", function() {
    // it used to do a simple delete on objects, but now there
    // are event listeners that have to be unhooked, etc.
    var obj = world.addObject(new Jax.Model({mesh: new Jax.Mesh.Quad()}));
    world.removeObject(obj);
    expect(world.octree.nestedObjectCount).toEqual(0);
  });

  it("should not add mesh-less objects to the octree", function() {
    // because such models may have their own rendering algo,
    // and by default they won't be sent down the pipe anyway.
    world.addObject(new Jax.Model());
    expect(world.octree.nestedObjectCount).toEqual(0);
  });

  it("should call 'render' on mesh-less objects", function() {
    // validates that the object does in fact get rendered if
    // not added to the octree
    var called = false;
    var obj = world.addObject(new Jax.Model({render: function(){ called = true; }}));
    world.render();
    expect(called).toBeTrue()
  });

  it("should add meshed objects to the octree", function() {
    // because such models may have their own rendering algo,
    // and by default they won't be sent down the pipe anyway.
    world.addObject(new Jax.Model({mesh: new Jax.Mesh.Quad}));
    expect(world.octree.nestedObjectCount).toEqual(1);
  });

  it("should render opaque objects in front-to-back order relative to the camera", function() {
    world.octree.splitThreshold = 1;
    var log = [];
    var logThis = function() { log.push(this); };
    var obj2 = world.addObject(new Jax.Model({render: logThis, position: [0,0,-4], mesh: new Jax.Mesh.Sphere}));
    var obj1 = world.addObject(new Jax.Model({render: logThis, position: [0,0,-1], mesh: new Jax.Mesh.Sphere}));
    world.render();
    expect(log).toEqual([obj1, obj2]);
  });

  it("should render transparent objects in back-to-front order relative to the camera", function() {
    world.octree.splitThreshold = 1;
    var log = [];
    var logThis = function() { log.push(this); };
    var obj1 = world.addObject(new Jax.Model({render: logThis, transparent: true, position: [0,0,-1], mesh: new Jax.Mesh.Sphere}));
    var obj2 = world.addObject(new Jax.Model({render: logThis, transparent: true, position: [0,0,-4], mesh: new Jax.Mesh.Sphere}));
    world.render();
    expect(log).toEqual([obj2, obj1]);
  });

  it("should render transparent objects after opaque objects", function() {
    world.octree.splitThreshold = 1;
    var log = [];
    var logThis = function() { log.push(this); };
    var obj1 = world.addObject(new Jax.Model({render: logThis, transparent: true, position: [0,0,-4], mesh: new Jax.Mesh.Sphere}));
    var obj2 = world.addObject(new Jax.Model({render: logThis, transparent: false, position: [0,0,-1], mesh: new Jax.Mesh.Sphere}));
    world.render();
    expect(log).toEqual([obj2, obj1]);
  });

  it("should notify shadow maps when added objects are modified", function() {
    var light = world.addLight(new Jax.Light.Directional());
    var obj = world.addObject(new Jax.Model());
    light.shadowmap.validate(SPEC_CONTEXT);
    expect(light.shadowmap).toBeValid(); // sanity check

    obj.camera.move(1);
    expect(light.shadowmap).not.toBeValid();
  });

  it("should stop notifying shadow maps after object has been removed", function() {
    var light = world.addLight(new Jax.Light.Directional());
    var obj = world.addObject(new Jax.Model());
    world.removeObject(obj);
    light.shadowmap.validate(SPEC_CONTEXT);
    expect(light.shadowmap).toBeValid(); // sanity check

    obj.camera.move(1);
    expect(light.shadowmap).toBeValid();
  });

  it("should render objects added to the world", function() {
    var mat = new Jax.Material();
    rendered_ids = [];
    mat.render = function(context, mesh, model) { rendered_ids.push(model.__unique_id); };
    var o1 = new Jax.Model({mesh: new Jax.Mesh.Quad(), position: [-2, 0, -5]});
    var o2 = new Jax.Model({mesh: new Jax.Mesh.Quad(), position: [2, 0, -5]});
    var o1id = o1.__unique_id, o2id = o2.__unique_id;
    world.addObject(o1);
    world.addObject(o2);
    world.render(mat);
    expect(rendered_ids).toEqual([o1id, o2id]);
  });

  it("should return objects added to world", function() {
    var obj = new (Jax.Model.create({one:1}))();
    expect(world.addObject(obj)).toBe(obj);
  });

  it("should return light sources added to world", function() {
    var lite = new Jax.Light();
    expect(world.addLight(lite)).toBe(lite);
  });

  describe("picking", function() {
    var at, ofront, otopleft, otopright, obottomleft, obottomright, mesh;

    beforeEach(function() {
      var width = SPEC_CONTEXT.canvas.width,
          height = SPEC_CONTEXT.canvas.height;
      at = { left: 0, right: width-1, top: 0, bottom: height-1,
             center_x: parseInt(width/2), center_y: parseInt(height/2) };

      // put some objects in the world for picking
      // function mesh() { return new Jax.Mesh.Sphere({size:1.0}); }

      mesh = new Jax.Mesh.Sphere({radius: 1.0});
      ofront       = world.addObject(new Jax.Model({position:[ 0.0, 0.0, -5],mesh:mesh}));
      otopleft     = world.addObject(new Jax.Model({position:[-2.5, 2.5, -5],mesh:mesh}));
      otopright    = world.addObject(new Jax.Model({position:[ 2.5, 2.5, -5],mesh:mesh}));
      obottomleft  = world.addObject(new Jax.Model({position:[-2.5,-2.5, -5],mesh:mesh}));
      obottomright = world.addObject(new Jax.Model({position:[ 2.5,-2.5, -5],mesh:mesh}));
    });

    it("center",       function() { expect(world.pick(at.center_x, at.center_y) === ofront).toBeTruthy(); });
    it("top left",     function() { expect(world.pick(at.left, at.top)          === otopleft).toBeTruthy(); });
    it("top right",    function() { expect(world.pick(at.right,at.top)          === otopright).toBeTruthy(); });
    it("bottom left",  function() { expect(world.pick(at.left, at.bottom)       === obottomleft).toBeTruthy(); });
    it("bottom right", function() { expect(world.pick(at.right,at.bottom)       === obottomright).toBeTruthy(); });

    it("object blocked by another larger object", function() {
      var front = world.addObject(new Jax.Model({position: [0.0, 0.0, -0.1], mesh: new Jax.Mesh.Quad({size: 10})}));
      expect(world.pick(at.center_x, at.center_y)).toBe(front);
    });

    it("object blocking another larger object", function() {
      var rear = world.addObject(new Jax.Model({position: [0.0, 0.0, -7], mesh: new Jax.Mesh.Quad({size: 10})}));
      expect(world.pick(at.center_x, at.center_y)).toBe(ofront);
    });

    it("region: everything", function() {
      var objects = world.pickRegion(at.left, at.top, at.right, at.bottom);
      expect(objects.indexOf(ofront)).not.toEqual(-1);
      expect(objects.indexOf(otopleft)).not.toEqual(-1);
      expect(objects.indexOf(otopright)).not.toEqual(-1);
      expect(objects.indexOf(obottomleft)).not.toEqual(-1);
      expect(objects.indexOf(obottomright)).not.toEqual(-1);
    });

    it("region: top-left quadrant", function() {
      var objects = world.pickRegion(at.left, at.top, at.center_x, at.center_y);
      expect(objects.indexOf(ofront)).not.toEqual(-1);
      expect(objects.indexOf(otopleft)).not.toEqual(-1);
      expect(objects.indexOf(otopright)).toEqual(-1);
      expect(objects.indexOf(obottomleft)).toEqual(-1);
      expect(objects.indexOf(obottomright)).toEqual(-1);
    });

    it("region: top-right quadrant", function() {
      var objects = world.pickRegion(at.right, at.top, at.center_x, at.center_y);
      expect(objects.indexOf(ofront)).not.toEqual(-1);
      expect(objects.indexOf(otopleft)).toEqual(-1);
      expect(objects.indexOf(otopright)).not.toEqual(-1);
      expect(objects.indexOf(obottomleft)).toEqual(-1);
      expect(objects.indexOf(obottomright)).toEqual(-1);
    });

    it("region: bottom-left quadrant", function() {
      var objects = world.pickRegion(at.left, at.bottom, at.center_x, at.center_y);
      expect(objects.indexOf(ofront)).not.toEqual(-1);
      expect(objects.indexOf(otopleft)).toEqual(-1);
      expect(objects.indexOf(otopright)).toEqual(-1);
      expect(objects.indexOf(obottomleft)).not.toEqual(-1);
      expect(objects.indexOf(obottomright)).toEqual(-1);
    });

    it("region: bottom-right quadrant", function() {
      var objects = world.pickRegion(at.right, at.bottom, at.center_x, at.center_y);
      expect(objects.indexOf(ofront)).not.toEqual(-1);
      expect(objects.indexOf(otopleft)).toEqual(-1);
      expect(objects.indexOf(otopright)).toEqual(-1);
      expect(objects.indexOf(obottomleft)).toEqual(-1);
      expect(objects.indexOf(obottomright)).not.toEqual(-1);
    });

    it("should be able to pick objects not explicitly added to the world", function() {
      var onested = new Jax.Model({position: [0.0, 0.0, -5], mesh: mesh});
      ofront.render = function(context, options) { onested.render(context, options); };
      expect(world.pick(at.center_x, at.center_y) === onested).toBeTruthy();
    });

    it("should be able to regionally pick objects not explicitly added to the world", function() {
      var onested = new Jax.Model({position: [0.0, 0.0, -5], mesh: mesh});
      ofront.render = function(context, options) { onested.render(context, options); };
      expect(world.pickRegion(at.center_x-1, at.center_y-1, at.center_x+1, at.center_y+1)[0] === onested).toBeTruthy();
    });
  });

  describe("with an object", function() {
    var model;
    beforeEach(function() { world.addObject(model = new Jax.Model()); });

    it("should remove objects by reference", function() {
      world.removeObject(model);
      expect(world.countObjects()).toEqual(0);
    });
  });

  describe("a model with `cull` set to `false`", function() {
    var model;
    beforeEach(function() { world.addObject(new Jax.Model({cull: false,mesh: new Jax.Mesh.Cube()})); });

    it("should not be added to the octree", function() {
      expect(world.octree.nestedObjectCount).toBe(0);
    });
  });

  describe("a mesh with `cull` set to `false`", function() {
    var model;
    beforeEach(function() { world.addObject(new Jax.Model({mesh: new Jax.Mesh.Cube({cull: false})})); });

    it("should not be added to the octree", function() {
      expect(world.octree.nestedObjectCount).toBe(0);
    });
  });

  describe("with lighting disabled", function() {
    beforeEach(function() { world.lighting.disable(); });

    // we no longer switch materials in Jax.World depending on lighting. "Basic" and "default" are the same, now.
    xit("should default to the 'basic' material", function() {
      var model = new Jax.Model();
      world.addObject(model);
      expect(model).toDefaultToMaterial("basic", world);
      world.render();
    });
  });

  describe("adding a light source from a string", function() {
    beforeEach(function() {
      Jax.Light.addResources({"test":{}});
      world.addLight("test");
    });

    it("should find the light source automatically", function() {
      expect(world.lights[0]).toBeInstanceOf(Jax.Light);
    });
  });
});
<commit_msg>remove redundant tests, make picking test less dependent upon canvas dimensions<commit_after>describe("Jax.World", function() {
  var world;

  beforeEach(function() {
    SPEC_CONTEXT.prepare();
    world = SPEC_CONTEXT.world;
  });

  describe("adding a light", function() {
    var added, light;
    beforeEach(function() {
      added = false;
      light = new Jax.Light.Directional();
      world.on('lightAdded', function(light) { added = light; });
      world.addLight(light);
    });

    it("should fire event including the light object", function() {
      expect(added).toBe(light);
    });

    describe("removing a light", function() {
      var removed;
      beforeEach(function() {
        removed = false;
        world.on('lightRemoved', function(light) { removed = light; });
        world.removeLight(light);
      });

      it("should fire event including the light object", function() {
        expect(removed).toBe(light);
      });
    });

  });

  describe("disposal", function() {
    it("should remove objects from itself", function() {
      // it used to do a simple delete on objects, but now there
      // are event listeners that have to be unhooked, etc.
      var obj = world.addObject(new Jax.Model());
      spyOn(world, 'removeObject').andCallThrough();
      world.dispose();
      expect(world.removeObject).toHaveBeenCalledWith(obj);
    });

    it("should handle removal of multiple objects", function() {
      world.addObject(new Jax.Model({mesh: new Jax.Mesh.Quad()}));
      world.addObject(new Jax.Model({mesh: new Jax.Mesh.Quad()}));
      world.dispose();
    });

    it("should handle removal of multiple lights", function() {
      world.addLight(new Jax.Light.Directional);
      world.addLight(new Jax.Light.Point);
      world.dispose();
    });

    it("should not encounter errors disposing after render", function() {
      world.addObject(new Jax.Model({mesh: new Jax.Mesh.Quad()}));
      world.render();
      world.dispose();
    });

    it("twice in a row should have no effect", function() {
      world.addObject(new Jax.Model({mesh: new Jax.Mesh.Quad()}));
      world.render();
      world.dispose();
      world.dispose();
    });

    it("should not return any objects", function() {
      world.addObject(new Jax.Model({mesh: new Jax.Mesh.Quad()}));
      world.render();
      world.dispose();
      expect(world.getObjects()).toEqual([]);
    });
  });

  it("should remove objects from the octree", function() {
    // it used to do a simple delete on objects, but now there
    // are event listeners that have to be unhooked, etc.
    var obj = world.addObject(new Jax.Model({mesh: new Jax.Mesh.Quad()}));
    world.removeObject(obj);
    expect(world.octree.nestedObjectCount).toEqual(0);
  });

  it("should not add mesh-less objects to the octree", function() {
    // because such models may have their own rendering algo,
    // and by default they won't be sent down the pipe anyway.
    world.addObject(new Jax.Model());
    expect(world.octree.nestedObjectCount).toEqual(0);
  });

  it("should call 'render' on mesh-less objects", function() {
    // validates that the object does in fact get rendered if
    // not added to the octree
    var called = false;
    var obj = world.addObject(new Jax.Model({render: function(){ called = true; }}));
    world.render();
    expect(called).toBeTrue()
  });

  it("should add meshed objects to the octree", function() {
    // because such models may have their own rendering algo,
    // and by default they won't be sent down the pipe anyway.
    world.addObject(new Jax.Model({mesh: new Jax.Mesh.Quad}));
    expect(world.octree.nestedObjectCount).toEqual(1);
  });

  it("should render opaque objects in front-to-back order relative to the camera", function() {
    world.octree.splitThreshold = 1;
    var log = [];
    var logThis = function() { log.push(this); };
    var obj2 = world.addObject(new Jax.Model({render: logThis, position: [0,0,-4], mesh: new Jax.Mesh.Sphere}));
    var obj1 = world.addObject(new Jax.Model({render: logThis, position: [0,0,-1], mesh: new Jax.Mesh.Sphere}));
    world.render();
    expect(log).toEqual([obj1, obj2]);
  });

  it("should render transparent objects in back-to-front order relative to the camera", function() {
    world.octree.splitThreshold = 1;
    var log = [];
    var logThis = function() { log.push(this); };
    var obj1 = world.addObject(new Jax.Model({render: logThis, transparent: true, position: [0,0,-1], mesh: new Jax.Mesh.Sphere}));
    var obj2 = world.addObject(new Jax.Model({render: logThis, transparent: true, position: [0,0,-4], mesh: new Jax.Mesh.Sphere}));
    world.render();
    expect(log).toEqual([obj2, obj1]);
  });

  it("should render transparent objects after opaque objects", function() {
    world.octree.splitThreshold = 1;
    var log = [];
    var logThis = function() { log.push(this); };
    var obj1 = world.addObject(new Jax.Model({render: logThis, transparent: true, position: [0,0,-4], mesh: new Jax.Mesh.Sphere}));
    var obj2 = world.addObject(new Jax.Model({render: logThis, transparent: false, position: [0,0,-1], mesh: new Jax.Mesh.Sphere}));
    world.render();
    expect(log).toEqual([obj2, obj1]);
  });

  it("should notify shadow maps when added objects are modified", function() {
    var light = world.addLight(new Jax.Light.Directional());
    var obj = world.addObject(new Jax.Model());
    light.shadowmap.validate(SPEC_CONTEXT);
    expect(light.shadowmap).toBeValid(); // sanity check

    obj.camera.move(1);
    expect(light.shadowmap).not.toBeValid();
  });

  it("should stop notifying shadow maps after object has been removed", function() {
    var light = world.addLight(new Jax.Light.Directional());
    var obj = world.addObject(new Jax.Model());
    world.removeObject(obj);
    light.shadowmap.validate(SPEC_CONTEXT);
    expect(light.shadowmap).toBeValid(); // sanity check

    obj.camera.move(1);
    expect(light.shadowmap).toBeValid();
  });

  it("should render objects added to the world", function() {
    var mat = new Jax.Material();
    rendered_ids = [];
    mat.render = function(context, mesh, model) { rendered_ids.push(model.__unique_id); };
    var o1 = new Jax.Model({mesh: new Jax.Mesh.Quad(), position: [-2, 0, -5]});
    var o2 = new Jax.Model({mesh: new Jax.Mesh.Quad(), position: [2, 0, -5]});
    var o1id = o1.__unique_id, o2id = o2.__unique_id;
    world.addObject(o1);
    world.addObject(o2);
    world.render(mat);
    expect(rendered_ids).toEqual([o1id, o2id]);
  });

  it("should return objects added to world", function() {
    var obj = new (Jax.Model.create({one:1}))();
    expect(world.addObject(obj)).toBe(obj);
  });

  it("should return light sources added to world", function() {
    var lite = new Jax.Light();
    expect(world.addLight(lite)).toBe(lite);
  });

  describe("picking", function() {
    var at, obj, otopleft, otopright, obottomleft, obottomright, mesh;

    beforeEach(function() {
      mesh = new Jax.Mesh.Sphere({radius: 1.0});
      obj  = world.addObject(new Jax.Model({position:[ 0.0, 0.0, -5],mesh:mesh}));
    });

    it("should find objects rendered by other objects", function() {
      var objNested = new Jax.Model({position: [0.0, 0.0, -5], mesh: mesh});
      obj.render = function(context, options) { objNested.render(context, options); };
      world.context.canvas.width = 1;
      world.context.canvas.height = 1;
      expect(world.pickRegion(0, 0, 1, 1)[0] === objNested).toBeTruthy();
    });
  });

  describe("with an object", function() {
    var model;
    beforeEach(function() { world.addObject(model = new Jax.Model()); });

    it("should remove objects by reference", function() {
      world.removeObject(model);
      expect(world.countObjects()).toEqual(0);
    });
  });

  describe("a model with `cull` set to `false`", function() {
    var model;
    beforeEach(function() { world.addObject(new Jax.Model({cull: false,mesh: new Jax.Mesh.Cube()})); });

    it("should not be added to the octree", function() {
      expect(world.octree.nestedObjectCount).toBe(0);
    });
  });

  describe("a mesh with `cull` set to `false`", function() {
    var model;
    beforeEach(function() { world.addObject(new Jax.Model({mesh: new Jax.Mesh.Cube({cull: false})})); });

    it("should not be added to the octree", function() {
      expect(world.octree.nestedObjectCount).toBe(0);
    });
  });

  describe("with lighting disabled", function() {
    beforeEach(function() { world.lighting.disable(); });

    // we no longer switch materials in Jax.World depending on lighting. "Basic" and "default" are the same, now.
    xit("should default to the 'basic' material", function() {
      var model = new Jax.Model();
      world.addObject(model);
      expect(model).toDefaultToMaterial("basic", world);
      world.render();
    });
  });

  describe("adding a light source from a string", function() {
    beforeEach(function() {
      Jax.Light.addResources({"test":{}});
      world.addLight("test");
    });

    it("should find the light source automatically", function() {
      expect(world.lights[0]).toBeInstanceOf(Jax.Light);
    });
  });
});
<|endoftext|>


<commit_before>#!/usr/bin/env node

var repl = require('repl')
  , ravendb = require('ravendb')
  , ArgumentParser = require('argparse').ArgumentParser

var version = '0.0.7'  // Keep in sync with package.json

var createDatastore = function(r, url, databaseName) {
  r.context.db = ravendb(url, databaseName)
  r.context.store = r.context.db.datastore
}

var useDatabase = function(r, databaseName) {
  if (!r.context.store) throw new Error('The datastore must first be set')

  r.context.db = ravendb(r.context.store.url, databaseName)
}

var currentDatabaseString = function(r) {
  return 'Using database "' + r.context.db.name + '" in datastore at: "' + r.context.store.url + '"'
}

var defineCommands = function(r) {
  r.defineCommand('store', {
    help: 'Use the RavenDB datastore at a url ".store <url>"',
    action: function(url) {
      if (!url) url = r.context.store.url
      else createDatastore(r, url, r.context.db.name) // TODO: IS THIS A GOOD IDEA, TO USE THE SAME DB NAME?

      console.log(currentDatabaseString(r))
      r.displayPrompt()
    }
  })

 r.defineCommand('use', {
    help: 'Use the database with the given name ".use <database-name>"',
    action: function(name) {
      if (!name) name = r.context.db.name
      else useDatabase(r, name)

      console.log(currentDatabaseString(r))
      r.displayPrompt()
    }
  })

  r.defineCommand('stats', {
    help: 'Show statistics for the current database',
    action: function() {
      try {
        r.context.db.getStats(function(error, stats) {
          if (error) {
            console.error(error)
            r.displayPrompt()
            return
          }

          console.log(stats)
          r.context._ = stats
          r.displayPrompt()
        })
      } catch (e) {
        console.error(e)
        r.displayPrompt()
      }
    }
  })

  r.defineCommand('collections', {
    help: 'Show all collections in the current database',
    action: function() {
      try {
        r.context.db.getCollections(function(error, collections) {
          if (error) {
            console.error(error)
            r.displayPrompt()
            return
          }

          if (!collections) console.log("No collections found.")
          else {
            for(var i=0; i < collections.length; i++) {
              console.log(collections[i])
            }
          }
          r.context._ = collections
          r.displayPrompt()
        })
      } catch (e) {
        console.error(e)
        r.displayPrompt()
      }
    }
  })

  r.defineCommand('create', {
    help: 'Save a document to a collection (e.g., .create CollectionName { id: "users/tony", firstName: "Tony" })',
    action: function(args) {
      try {
        var match = /(\w+)\s+(.*)/.exec(args)
        if (match.length != 3) throw Error('Wrong number of arguments; see .help for .savedoc usage')

        var collection = match[1]
        eval('var doc = ' + match[2])

        r.context.db.saveDocument(collection, doc, function(error, result) {
          if (error) console.error(error)

          if (result) console.log(result)
          r.context._ = result
          r.displayPrompt()
        })

      } catch (e) {
        console.error(e)
        r.displayPrompt()
      }
    }
  })

  r.defineCommand('read', {
    help: 'Get a document given its id (e.g., .read users/tony)',
    action: function(args) {
      try {
        if (!args) throw Error('Wrong number of arguments; see .help for more information')

        var id = args
        r.context.db.getDocument(id, function(error, result) {
          if (error) console.error(error)

          if (result) console.log(result)
          r.context._ = result
          r.displayPrompt()
        })

      } catch (e) {
        console.error(e)
        r.displayPrompt()
      }
    }
  })

  r.defineCommand('delete', {
    help: 'Delete a document given its id (e.g., .delete users/tony)',
    action: function(args) {
      try {
        if (!args) throw Error('Wrong number of arguments; see .help for more information')

        var id = args
        r.context.db.deleteDocument(id, function(error, result) {
          if (error) console.error(error)

          if (result) console.log(result)
          r.context._ = result
          r.displayPrompt()
        })

      } catch (e) {
        console.error(e)
        r.displayPrompt()
      }
    }
  })

  r.defineCommand('find', {
    help: 'Find documents (e.g., .find { firstName: "Tony" })',
    action: function(args) {
      try {
        if (!args) throw Error('Wrong number of arguments; see .help for .find usage')

        eval('var argsDoc = ' + args)

        r.context.db.find(argsDoc, function(error, result) {
          if (error) console.error(error)

          if (result) console.log(result)
          r.context._ = result
          r.displayPrompt()
        })

      } catch (e) {
        console.error(e)
        r.displayPrompt()
      }
    }
  })

  r.defineCommand('docs', {
    help: 'Retrieve documents in a collection (e.g., .doc Users)',
    action: function(args) {
      try {
        var match = /(\w+)/.exec(args)

        var collection = (match && match.length == 2) ? match[1] : null

        r.context.db.getDocsInCollection(collection, function(error, result) {
          if (error) console.error(error)

          if (result) console.log(result)
          r.context._ = result
          r.displayPrompt()
        })

      } catch (e) {
        console.error(e)
        r.displayPrompt()
      }
    }
  })

  r.defineCommand('count', {
    help: 'Show the count of documents in a collection or in the database if left blank (e.g., .count Users)',
    action: function(args) {
      try {
        r.context.db.getDocumentCount(args, function(error, result) {
          if (error) console.error(error)

          if (result) console.log(result)
          r.context._ = result
          r.displayPrompt()
        })
      } catch (e) {
        console.error(e)
        r.displayPrompt()
      }
    }
  })

  r.defineCommand('createIndex', {
    help: "Create an index with a name, map, and optional reduce: .create-index foobar { map: 'from doc in docs.Albums\rwhere doc.Genre != null\rselect new { Genre = doc.Genre.Id }'}",
    action: function(args) {
      try {
        var name, index = null
        var match = /^(\w+)\s+(.*)$/.exec(args)

        if (match.length != 3) {
          console.error('Unable to craete index: wrong number of arguments.  Type ".help" to see usage')
          return
        }

        name = match[1]
        index = match[2]

        eval ('var createIndexIndex = ' + index )

        r.context.db.createIndex(name,
                                 createIndexIndex['map'],
                                 createIndexIndex['reduce'],
                                 function(error, result) {
          if (error) console.error(error)

          if (result) console.log(result)
          r.context._ = result
          r.displayPrompt()
        })
      } catch(e) {
        console.error(e)
        r.displayPrompt()
      }
    }
  })

  r.defineCommand('deleteIndex', {
    help: 'Delete an given its name (e.g., .delete AlbumsByGenre)',
    action: function(args) {
      try {
        if (!args) throw Error('Wrong number of arguments; see .help for more information')

        var name = args
        r.context.db.deleteIndex(name, function(error, result) {
          if (error) console.error(error)

          if (result) console.log(result)
          r.context._ = result
          r.displayPrompt()
        })

      } catch (e) {
        console.error(e)
        r.displayPrompt()
      }
    }
  })

    r.defineCommand('createDatabase', {
    help: 'Create a database tenant (e.g., .createDatabase Foobar)',
    action: function(args) {
      try {
        if (!args) throw Error('Wrong number of arguments; see .help for more information')

        var name = args
        r.context.store.createDatabase(name, function(error, result) {
          if (error) console.error(error)

          if (result) console.log(result)
          r.context._ = result
          r.displayPrompt()
        })

      } catch (e) {
        console.error(e)
        r.displayPrompt()
      }
    }
  })

  r.defineCommand('deleteDatabase', {
    help: 'Delete a database given its name (e.g., .deleteDatabase Foobar)',
    action: function(args) {
      try {
        if (!args) throw Error('Wrong number of arguments; see .help for more information')

        var name = args
        r.context.store.deleteDatabase(name, function(error, result) {
          if (error) console.error(error)

          if (result) console.log(result)
          r.context._ = result
          r.displayPrompt()
        })

      } catch (e) {
        console.error(e)
        r.displayPrompt()
      }
    }
  })

}

var startREPL = function(store, databaseName) {
  console.log('RavenDB shell version ' + version)

  var r = repl.start("> ")
  createDatastore(r, store, databaseName)
  defineCommands(r)

  console.log(currentDatabaseString(r))
  r.displayPrompt()
  return r
}
var startInteractiveREPL = function(store, databaseName) {
  return startREPL(store, databaseName)
}

var startEvalREPL = function(string, store, databaseName) {
  var r = startREPL(store, databaseName)

  var lines = string.split('\n')

  lines.forEach(function(line) {
    if (line) {
      r.rli.write(line + '\n')
    }
  })
  return r
}

var startFileREPL = function(filename, store, databaseName) {
  var r = startREPL(store, databaseName)
  r.rli.write('.load ' + filename + '\n')
  return r
}

var parser = new ArgumentParser({
  'version': version,
  addHelp: true,
  description: 'RavenDB command line shell'
});
parser.addArgument(
  [ '-f', '--file' ],
  {
    help: 'load and execute a file of shell commands',
    dest: 'file'
  }
)
parser.addArgument(
  [ '-e', '--eval' ],
  {
    help: 'evaluate a single string of shell commands',
    dest: 'eval'
  }
)
parser.addArgument(
  [ '-s', '--store' ],
  {
    help: 'specify which data store to use (defaults to http://localhost:8080 if not specfied)',
    defaultValue: 'http://localhost:8080',
    dest: 'store'
  }
)
parser.addArgument(
  [ '-db', '--database'],
  {
    help: 'specify which database to use (defaults to "Default" if not specified)',
    defaultValue: 'Default',
    dest: 'database'
  }
)
parser.addArgument(
  [ '-ko', '--keep-open' ],
  {
    help: 'keep the shell open when the passed in file or eval is done executing',
    action: 'storeTrue',
    defaultValue: false,
    dest: 'keepOpen'
  }
)
var args = parser.parseArgs();

var shell
var keepOpen = args.keepOpen
var evalString = args.eval
var file = args.file
var store = args.store
var database = args.database

if (evalString) {
    shell = startEvalREPL(evalString, store, database)
} else if (file) {
    shell = startFileREPL(file, store, database)
}

if (!shell) {
  shell = startInteractiveREPL(store, database)
} else {
  if (!keepOpen) {
    shell.rli.close()
  }
}<commit_msg>add start and count optional params to .find method<commit_after>#!/usr/bin/env node

var repl = require('repl')
  , ravendb = require('ravendb')
  , ArgumentParser = require('argparse').ArgumentParser

var version = '0.0.7'  // Keep in sync with package.json

var createDatastore = function(r, url, databaseName) {
  r.context.db = ravendb(url, databaseName)
  r.context.store = r.context.db.datastore
}

var useDatabase = function(r, databaseName) {
  if (!r.context.store) throw new Error('The datastore must first be set')

  r.context.db = ravendb(r.context.store.url, databaseName)
}

var currentDatabaseString = function(r) {
  return 'Using database "' + r.context.db.name + '" in datastore at: "' + r.context.store.url + '"'
}

var defineCommands = function(r) {
  r.defineCommand('store', {
    help: 'Use the RavenDB datastore at a url ".store <url>"',
    action: function(url) {
      if (!url) url = r.context.store.url
      else createDatastore(r, url, r.context.db.name) // TODO: IS THIS A GOOD IDEA, TO USE THE SAME DB NAME?

      console.log(currentDatabaseString(r))
      r.displayPrompt()
    }
  })

 r.defineCommand('use', {
    help: 'Use the database with the given name ".use <database-name>"',
    action: function(name) {
      if (!name) name = r.context.db.name
      else useDatabase(r, name)

      console.log(currentDatabaseString(r))
      r.displayPrompt()
    }
  })

  r.defineCommand('stats', {
    help: 'Show statistics for the current database',
    action: function() {
      try {
        r.context.db.getStats(function(error, stats) {
          if (error) {
            console.error(error)
            r.displayPrompt()
            return
          }

          console.log(stats)
          r.context._ = stats
          r.displayPrompt()
        })
      } catch (e) {
        console.error(e)
        r.displayPrompt()
      }
    }
  })

  r.defineCommand('collections', {
    help: 'Show all collections in the current database',
    action: function() {
      try {
        r.context.db.getCollections(function(error, collections) {
          if (error) {
            console.error(error)
            r.displayPrompt()
            return
          }

          if (!collections) console.log("No collections found.")
          else {
            for(var i=0; i < collections.length; i++) {
              console.log(collections[i])
            }
          }
          r.context._ = collections
          r.displayPrompt()
        })
      } catch (e) {
        console.error(e)
        r.displayPrompt()
      }
    }
  })

  r.defineCommand('create', {
    help: 'Save a document to a collection (e.g., .create CollectionName { id: "users/tony", firstName: "Tony" })',
    action: function(args) {
      try {
        var match = /(\w+)\s+(.*)/.exec(args)
        if (match.length != 3) throw Error('Wrong number of arguments; see .help for .savedoc usage')

        var collection = match[1]
        eval('var doc = ' + match[2])

        r.context.db.saveDocument(collection, doc, function(error, result) {
          if (error) console.error(error)

          if (result) console.log(result)
          r.context._ = result
          r.displayPrompt()
        })

      } catch (e) {
        console.error(e)
        r.displayPrompt()
      }
    }
  })

  r.defineCommand('read', {
    help: 'Get a document given its id (e.g., .read users/tony)',
    action: function(args) {
      try {
        if (!args) throw Error('Wrong number of arguments; see .help for more information')

        var id = args
        r.context.db.getDocument(id, function(error, result) {
          if (error) console.error(error)

          if (result) console.log(result)
          r.context._ = result
          r.displayPrompt()
        })

      } catch (e) {
        console.error(e)
        r.displayPrompt()
      }
    }
  })

  r.defineCommand('delete', {
    help: 'Delete a document given its id (e.g., .delete users/tony)',
    action: function(args) {
      try {
        if (!args) throw Error('Wrong number of arguments; see .help for more information')

        var id = args
        r.context.db.deleteDocument(id, function(error, result) {
          if (error) console.error(error)

          if (result) console.log(result)
          r.context._ = result
          r.displayPrompt()
        })

      } catch (e) {
        console.error(e)
        r.displayPrompt()
      }
    }
  })

  r.defineCommand('find', {
    help: 'Find documents ".find <JSON object> [start count]" (e.g., .find { firstName: "Tony" } 20 100)',
    action: function(args) {
      try {
        if (!args) throw Error('Wrong number of arguments; see .help for .find usage')

        // Parse args: { attr: value } [start [count]]
        // .find { some: 'thing'} 20 100
        //       ^-------1------^^--2--^
        //                         3  5
        //                          ^4-^
        // match[0] = "{ some: 'thing'} 20 100"
        // match[1] = "{ some: 'thing'}"
        // match[2] = " 20 100"
        // match[3] = "20"
        // match[4] = " 100"
        // match[5] = "100"
        var match = /(\{.*\})(\s+(\d+)(\s+(\d+))?)?/.exec(args)
        eval('var doc = ' + match[1])
        var start = match[3] ? parseInt(match[3]) : null
        var count = match[5] ? parseInt(match[5]) : null

        r.context.db.find(doc, start, count, function(error, result) {
          if (error) console.error(error)

          if (result) console.log(result)
          r.context._ = result
          r.displayPrompt()
        })

      } catch (e) {
        console.error(e)
        r.displayPrompt()
      }
    }
  })

  r.defineCommand('docs', {
    help: 'Retrieve documents in a collection (e.g., .doc Users)',
    action: function(args) {
      try {
        var match = /(\w+)/.exec(args)

        var collection = (match && match.length == 2) ? match[1] : null

        r.context.db.getDocsInCollection(collection, function(error, result) {
          if (error) console.error(error)

          if (result) console.log(result)
          r.context._ = result
          r.displayPrompt()
        })

      } catch (e) {
        console.error(e)
        r.displayPrompt()
      }
    }
  })

  r.defineCommand('count', {
    help: 'Show the count of documents in a collection or in the database if left blank (e.g., .count Users)',
    action: function(args) {
      try {
        r.context.db.getDocumentCount(args, function(error, result) {
          if (error) console.error(error)

          if (result) console.log(result)
          r.context._ = result
          r.displayPrompt()
        })
      } catch (e) {
        console.error(e)
        r.displayPrompt()
      }
    }
  })

  r.defineCommand('createIndex', {
    help: "Create an index with a name, map, and optional reduce: .create-index foobar { map: 'from doc in docs.Albums\rwhere doc.Genre != null\rselect new { Genre = doc.Genre.Id }'}",
    action: function(args) {
      try {
        var name, index = null
        var match = /^(\w+)\s+(.*)$/.exec(args)

        if (match.length != 3) {
          console.error('Unable to craete index: wrong number of arguments.  Type ".help" to see usage')
          return
        }

        name = match[1]
        index = match[2]

        eval ('var createIndexIndex = ' + index )

        r.context.db.createIndex(name,
                                 createIndexIndex['map'],
                                 createIndexIndex['reduce'],
                                 function(error, result) {
          if (error) console.error(error)

          if (result) console.log(result)
          r.context._ = result
          r.displayPrompt()
        })
      } catch(e) {
        console.error(e)
        r.displayPrompt()
      }
    }
  })

  r.defineCommand('deleteIndex', {
    help: 'Delete an given its name (e.g., .delete AlbumsByGenre)',
    action: function(args) {
      try {
        if (!args) throw Error('Wrong number of arguments; see .help for more information')

        var name = args
        r.context.db.deleteIndex(name, function(error, result) {
          if (error) console.error(error)

          if (result) console.log(result)
          r.context._ = result
          r.displayPrompt()
        })

      } catch (e) {
        console.error(e)
        r.displayPrompt()
      }
    }
  })

    r.defineCommand('createDatabase', {
    help: 'Create a database tenant (e.g., .createDatabase Foobar)',
    action: function(args) {
      try {
        if (!args) throw Error('Wrong number of arguments; see .help for more information')

        var name = args
        r.context.store.createDatabase(name, function(error, result) {
          if (error) console.error(error)

          if (result) console.log(result)
          r.context._ = result
          r.displayPrompt()
        })

      } catch (e) {
        console.error(e)
        r.displayPrompt()
      }
    }
  })

  r.defineCommand('deleteDatabase', {
    help: 'Delete a database given its name (e.g., .deleteDatabase Foobar)',
    action: function(args) {
      try {
        if (!args) throw Error('Wrong number of arguments; see .help for more information')

        var name = args
        r.context.store.deleteDatabase(name, function(error, result) {
          if (error) console.error(error)

          if (result) console.log(result)
          r.context._ = result
          r.displayPrompt()
        })

      } catch (e) {
        console.error(e)
        r.displayPrompt()
      }
    }
  })

}

var startREPL = function(store, databaseName) {
  console.log('RavenDB shell version ' + version)

  var r = repl.start("> ")
  createDatastore(r, store, databaseName)
  defineCommands(r)

  console.log(currentDatabaseString(r))
  r.displayPrompt()
  return r
}
var startInteractiveREPL = function(store, databaseName) {
  return startREPL(store, databaseName)
}

var startEvalREPL = function(string, store, databaseName) {
  var r = startREPL(store, databaseName)

  var lines = string.split('\n')

  lines.forEach(function(line) {
    if (line) {
      r.rli.write(line + '\n')
    }
  })
  return r
}

var startFileREPL = function(filename, store, databaseName) {
  var r = startREPL(store, databaseName)
  r.rli.write('.load ' + filename + '\n')
  return r
}

var parser = new ArgumentParser({
  'version': version,
  addHelp: true,
  description: 'RavenDB command line shell'
});
parser.addArgument(
  [ '-f', '--file' ],
  {
    help: 'load and execute a file of shell commands',
    dest: 'file'
  }
)
parser.addArgument(
  [ '-e', '--eval' ],
  {
    help: 'evaluate a single string of shell commands',
    dest: 'eval'
  }
)
parser.addArgument(
  [ '-s', '--store' ],
  {
    help: 'specify which data store to use (defaults to http://localhost:8080 if not specfied)',
    defaultValue: 'http://localhost:8080',
    dest: 'store'
  }
)
parser.addArgument(
  [ '-db', '--database'],
  {
    help: 'specify which database to use (defaults to "Default" if not specified)',
    defaultValue: 'Default',
    dest: 'database'
  }
)
parser.addArgument(
  [ '-ko', '--keep-open' ],
  {
    help: 'keep the shell open when the passed in file or eval is done executing',
    action: 'storeTrue',
    defaultValue: false,
    dest: 'keepOpen'
  }
)
var args = parser.parseArgs();

var shell
var keepOpen = args.keepOpen
var evalString = args.eval
var file = args.file
var store = args.store
var database = args.database

if (evalString) {
    shell = startEvalREPL(evalString, store, database)
} else if (file) {
    shell = startFileREPL(file, store, database)
}

if (!shell) {
  shell = startInteractiveREPL(store, database)
} else {
  if (!keepOpen) {
    shell.rli.close()
  }
}<|endoftext|>


<commit_before>var http = require('http');
var phantom = require('node-phantom-simple');
var gm = require('gm');
var url = require('url');

var processing = false;
var requests = [];

var processImage = function(res, result, ph, imageWidth){
	var imageBuffer = new Buffer(result, 'base64');
	gm(imageBuffer, 'image.jpg').trim().resize(imageWidth || 240).toBuffer('JPG', function(err, newBuffer){
		res.writeHead(200, {
			'Content-Type': 'image/jpg',
			'Content-Length': newBuffer.length});
		res.end(newBuffer);
		ph.exit()
		processRequest.apply(null, requests.shift());
	});
}

// Request the current page and send back as png
// Then process next request in queue if there is one
var processRequest = function(req, res){
	if(req && res){
		var queryStrings = url.parse(req.url, true).query;
		processing = true;
		phantom.create(function(err,ph) {
			return ph.createPage(function(err, page) {
				page.set('viewportSize', {
					width: 1024,
					height: 1024
				});
				return page.open(req.url.slice(1), function(err, status) {
					page.renderBase64("JPEG", function(error, result){
						if (result) {
							processImage(res, result, ph, queryStrings.imageWidth);
						} else {
							console.log(error);
							res.send(500);
						}
					});

				});
			}, {parameters: {'ignore-ssl-errors': 'yes'}});
		});
	} else {
		processing = false;
	}
};

// Process the incoming request if not already processing
// Else put into queue
http.createServer(function (req, res) {
	if(!processing){
		processRequest(req, res);
	} else if (requests.length > 10) {
		requests = [];
		processRequest(req, res);
	} else {
		requests.push([req, res]);
	}
}).listen(process.env.PORT || 1337, function(){
	console.log('running');
});
<commit_msg>png support via querystring<commit_after>var http = require('http');
var phantom = require('node-phantom-simple');
var gm = require('gm');
var url = require('url');

var processing = false;
var requests = [];

var processImage = function(res, result, ph, imageWidth, imageFormat){
	var imageBuffer = new Buffer(result, 'base64');
	gm(imageBuffer, 'image.jpg').trim().resize(imageWidth || 240).toBuffer(imageFormat || 'JPG', function(err, newBuffer){
		res.writeHead(200, {
			'Content-Type': 'image/' + imageFormat || 'jpg',
			'Content-Length': newBuffer.length});
		res.end(newBuffer);
		ph.exit()
		processRequest.apply(null, requests.shift());
	});
}

// Request the current page and send back as png
// Then process next request in queue if there is one
var processRequest = function(req, res){
	if(req && res){
		var queryStrings = url.parse(req.url, true).query;
		processing = true;
		phantom.create(function(err,ph) {
			return ph.createPage(function(err, page) {
				page.set('viewportSize', {
					width: 1024,
					height: 1024
				});
				return page.open(req.url.slice(1), function(err, status) {
					page.renderBase64(queryStrings.imageFormat || "JPEG", function(error, result){
						if (result) {
							processImage(res, result, ph, queryStrings.imageWidth, queryStrings.imageFormat);
						} else {
							console.log(error);
							res.send(500);
						}
					});

				});
			}, {parameters: {'ignore-ssl-errors': 'yes'}});
		});
	} else {
		processing = false;
	}
};

// Process the incoming request if not already processing
// Else put into queue
http.createServer(function (req, res) {
	if(!processing){
		processRequest(req, res);
	} else if (requests.length > 10) {
		requests = [];
		processRequest(req, res);
	} else {
		requests.push([req, res]);
	}
}).listen(process.env.PORT || 1337, function(){
	console.log('running');
});
<|endoftext|>


<commit_before>(function(root, factory) {

    // Set up Sibyl appropriately for the environment

    if (typeof define === 'function' && define.amd) {
        // Start with AMD

        define(['underscore'], function(_) {
            // Export global even in AMD case in case this script is loaded with
            // others that may still expect a global Sibyl.
            root.Sibyl = factory(root, exports);
        });

    } else if (typeof exports !== 'undefined') {
        // Next for Node.js or CommonJS

        var _ = require('underscore');
        factory(root, exports, _);

    } else {
        // Finally, as a browser global
        root.Sibyl = factory(root, {}, root._);
    }

}(this, function(root, Sibyl, _) {

    /**
     * The Set object lets you store unique values of any type,
     * whether primitive values or object references.
     * @param {Array} initialKeys
     */

    function Set(initialValues) {

        this.size = 0;
        this.values = [];

        if (_.isArray(initialValues)) {
            for (var i = 0, l = initialValues.length; i < l; i++) {
                this.add(initialValues[i]);
            }
        }

        return this;

    }

    /**
     * Compute the intersection between
     * an infinite number of sets
     * @return {Set}
     */
    Set.intersection = function() {
        var set = new Set;
        var sets = arguments;
        sets[0].forEach(function(value) {
            var exists = true;
            for (var i = 1, l = sets.length; i < l; i++) {
                if (!sets[i].has(value)) {
                    exists = false;
                }
            }
            if (exists) {
                set.add(value);
            }
        });
        return set;
    };

    /**
     * Compute the union between
     * an infinite number of sets
     * @return {Set}
     */
    Set.union = function() {
        var set = new Set;
        var sets = arguments;
        for (var i = 1, l = sets.length; i < l; i++) {
            sets[i].forEach(function(value) {
                set.add(value);
            });
        }
        return set;
    };

    /**
     * Appends a new element with the given value
     * to the Set object.
     * @param {Object} value
     * @return {this}
     */
    Set.prototype.add = function(value) {
        if (!this.has(value)) {
            this.values.push(value);
            this.size++;
            this.sort();
        }
        return this;
    };

    /**
     * Removes all elements from the Set object.
     * @return {this}
     */
    Set.prototype.clear = function() {
        this.values = [];
        this.size = 0;
        return this;
    };

    /**
     * Removes the element associated to the value.
     * Set.prototype.has(value) will return false afterwards.
     * @param  {Object} value
     * @return {this}
     */
    Set.prototype.delete = function(value)  {
        var index = this.indexOf(value);
        if (index > -1) {
            this.values.splice(index, 1);
            this.size--;
            this.sort();
        }
        return this;
    };

    /**
     * Check if the key exists within the set
     * @param  {String}  key
     * @return {Boolean}
     */
    Set.prototype.has = function(value) {
        return this.indexOf(value) !== -1;
    };

    /**
     * Perform a fast and sorted lookup
     * for the given key
     * @param  {String} key
     * @return {Number}
     */
    Set.prototype.indexOf = function(value) {
        var index = -1;
        this.forEach(function(iValue, i) {
            if (this.comparator(iValue) >= this.comparator(value)) {
                if (_.isEqual(value, iValue)) {
                    index = i;
                }
                return false; // return false to break
            }
        }, this);
        return index;
    };

    /**
     * Perform a fast and sorted lookup
     * for the given key
     * @param  {String} key
     * @return {Number}
     */
    Set.prototype.forEach = function(callbackFn, thisArg) {
        for (var i = 0, l = this.values.length; i < l; i++) {
            if (callbackFn.call(thisArg, this.values[i], i) === false) {
                break;
            }
        }
    };

    /**
     * Sort the values of the set
     * to optimise querying
     * @return {this}
     */
    Set.prototype.sort = function() {
        var that = this;
        this.values.sort(function(a, b) {
            var ca = that.comparator(a);
            var cb = that.comparator(b);
            if (a < b) return -1;
            if (a > b) return 1;
            return 0;
        });
        return this;
    };

    /**
     * Comparator used to filter and sort
     * the values of the set
     * @param  {Object} value
     * @return {Object}
     */
    Set.prototype.comparator = function(value) {
        return value;
    };

    /**
     * Return an array of the values
     * making up the set
     * @return {Array}
     */
    Set.prototype.toArray = function() {
        return this.values;
    };

    /**
     * The Delphic Sibyl
     */
    Sibyl = function() {
        this.users = {};
        this.items = {};
    };

    Sibyl.prototype.recordLike = function(user, item) {

        if (!this.users[user]) {
            this.users[user] = {};
            this.users[user].likes = new Set;
            this.users[user].dislikes = new Set;
        }

        if (!this.items[item]) {
            this.items[item] = {};
            this.items[item].likedBy = new Set;
            this.items[item].dislikedBy = new Set;
        }

        this.users[user].likes.add(item);
        this.users[user].dislikes.delete(item);

        this.items[item].likedBy.add(user);
        this.items[item].dislikedBy.delete(user);

        return this;

    };

    Sibyl.prototype.recordDislike = function(user, item) {

        if (!this.users[user]) {
            this.users[user] = {};
            this.users[user].likes = new Set;
            this.users[user].dislikes = new Set;
        }

        if (!this.items[item]) {
            this.items[item] = {};
            this.items[item].likedBy = new Set;
            this.items[item].dislikedBy = new Set;
        }

        this.users[user].dislikes.add(item);
        this.users[user].likes.delete(item);

        this.items[item].dislikedBy.add(user);
        this.items[item].likedBy.delete(user);

        return this;

    };

    Sibyl.prototype.getSimilarityBetween = function(firstUserId, secondUserId) {

        var agreements,
            disagreements,
            total,
            firstUser,
            secondUser;

        firstUser = this.users[firstUserId];
        secondUser = this.users[secondUserId];

        // number of likes and dislikes in common
        agreements = Set.intersection(firstUser.likes, secondUser.likes).size;
        agreements += Set.intersection(firstUser.dislikes, secondUser.dislikes).size;

        // number of likes and dislikes not in common
        disagreements = Set.intersection(firstUser.likes, secondUser.dislikes).size;
        disagreements += Set.intersection(firstUser.dislikes, secondUser.likes).size;

        total = Set.union(firstUser.likes, firstUser.dislikes, secondUser.likes, secondUser.dislikes).size;

        return (agreements - disagreements) / total;

    };

    Sibyl.prototype.getPrediction = function(userId, itemId) {

        var hiveMindSum,
            ratedBy,
            user,
            item;

        user = this.users[userId];
        item = this.items[itemId];

        hiveMindSum = 0.0;
        ratedBy = item.likedBy.size + item.dislikedBy.size;

        item.likedBy.forEach(function(raterId) {
            hiveMindSum += this.getSimilarityBetween(userId, raterId);
        }, this);

        item.dislikedBy.forEach(function(raterId) {
            hiveMindSum -= this.getSimilarityBetween(userId, raterId);
        }, this);

        return hiveMindSum / ratedBy;

    };

    Sibyl.prototype.getSuggestion = function(userId, tolerance) {

        var set,
            user;

        tolerance = tolerance || 0;
        user = this.users[userId];

        set = new Set;
        set.comparator = function(item) {
            return item.prediction;
        }

        for (var itemId in this.items) {
            // filter out items the user has already rated
            if (!user.likes.has(itemId) && !user.dislikes.has(itemId)) {
                var prediction = this.getPrediction(userId, itemId);
                if (prediction > tolerance) {
                    set.add({
                        id: itemId,
                        prediction:
                    });
                }
            }
        }

        // return an ordered list of suggestions
        return _.flatten(_.pluck(set.toArray(), 'id'));

    };

    // Current version of the library. Keep in sync with `package.json`.
    Sibyl.VERSION = '0.1.0';

    Sibyl.Set = Set;

    return Sibyl;

}));
<commit_msg>Lake sure tolerance is lower or equal<commit_after>(function(root, factory) {

    // Set up Sibyl appropriately for the environment

    if (typeof define === 'function' && define.amd) {
        // Start with AMD

        define(['underscore'], function(_) {
            // Export global even in AMD case in case this script is loaded with
            // others that may still expect a global Sibyl.
            root.Sibyl = factory(root, exports);
        });

    } else if (typeof exports !== 'undefined') {
        // Next for Node.js or CommonJS

        var _ = require('underscore');
        factory(root, exports, _);

    } else {
        // Finally, as a browser global
        root.Sibyl = factory(root, {}, root._);
    }

}(this, function(root, Sibyl, _) {

    /**
     * The Set object lets you store unique values of any type,
     * whether primitive values or object references.
     * @param {Array} initialKeys
     */

    function Set(initialValues) {

        this.size = 0;
        this.values = [];

        if (_.isArray(initialValues)) {
            for (var i = 0, l = initialValues.length; i < l; i++) {
                this.add(initialValues[i]);
            }
        }

        return this;

    }

    /**
     * Compute the intersection between
     * an infinite number of sets
     * @return {Set}
     */
    Set.intersection = function() {
        var set = new Set;
        var sets = arguments;
        sets[0].forEach(function(value) {
            var exists = true;
            for (var i = 1, l = sets.length; i < l; i++) {
                if (!sets[i].has(value)) {
                    exists = false;
                }
            }
            if (exists) {
                set.add(value);
            }
        });
        return set;
    };

    /**
     * Compute the union between
     * an infinite number of sets
     * @return {Set}
     */
    Set.union = function() {
        var set = new Set;
        var sets = arguments;
        for (var i = 1, l = sets.length; i < l; i++) {
            sets[i].forEach(function(value) {
                set.add(value);
            });
        }
        return set;
    };

    /**
     * Appends a new element with the given value
     * to the Set object.
     * @param {Object} value
     * @return {this}
     */
    Set.prototype.add = function(value) {
        if (!this.has(value)) {
            this.values.push(value);
            this.size++;
            this.sort();
        }
        return this;
    };

    /**
     * Removes all elements from the Set object.
     * @return {this}
     */
    Set.prototype.clear = function() {
        this.values = [];
        this.size = 0;
        return this;
    };

    /**
     * Removes the element associated to the value.
     * Set.prototype.has(value) will return false afterwards.
     * @param  {Object} value
     * @return {this}
     */
    Set.prototype.delete = function(value)  {
        var index = this.indexOf(value);
        if (index > -1) {
            this.values.splice(index, 1);
            this.size--;
            this.sort();
        }
        return this;
    };

    /**
     * Check if the key exists within the set
     * @param  {String}  key
     * @return {Boolean}
     */
    Set.prototype.has = function(value) {
        return this.indexOf(value) !== -1;
    };

    /**
     * Perform a fast and sorted lookup
     * for the given key
     * @param  {String} key
     * @return {Number}
     */
    Set.prototype.indexOf = function(value) {
        var index = -1;
        this.forEach(function(iValue, i) {
            if (this.comparator(iValue) >= this.comparator(value)) {
                if (_.isEqual(value, iValue)) {
                    index = i;
                }
                return false; // return false to break
            }
        }, this);
        return index;
    };

    /**
     * Perform a fast and sorted lookup
     * for the given key
     * @param  {String} key
     * @return {Number}
     */
    Set.prototype.forEach = function(callbackFn, thisArg) {
        for (var i = 0, l = this.values.length; i < l; i++) {
            if (callbackFn.call(thisArg, this.values[i], i) === false) {
                break;
            }
        }
    };

    /**
     * Sort the values of the set
     * to optimise querying
     * @return {this}
     */
    Set.prototype.sort = function() {
        var that = this;
        this.values.sort(function(a, b) {
            var ca = that.comparator(a);
            var cb = that.comparator(b);
            if (a < b) return -1;
            if (a > b) return 1;
            return 0;
        });
        return this;
    };

    /**
     * Comparator used to filter and sort
     * the values of the set
     * @param  {Object} value
     * @return {Object}
     */
    Set.prototype.comparator = function(value) {
        return value;
    };

    /**
     * Return an array of the values
     * making up the set
     * @return {Array}
     */
    Set.prototype.toArray = function() {
        return this.values;
    };

    /**
     * The Delphic Sibyl
     */
    Sibyl = function() {
        this.users = {};
        this.items = {};
    };

    Sibyl.prototype.recordLike = function(user, item) {

        if (!this.users[user]) {
            this.users[user] = {};
            this.users[user].likes = new Set;
            this.users[user].dislikes = new Set;
        }

        if (!this.items[item]) {
            this.items[item] = {};
            this.items[item].likedBy = new Set;
            this.items[item].dislikedBy = new Set;
        }

        this.users[user].likes.add(item);
        this.users[user].dislikes.delete(item);

        this.items[item].likedBy.add(user);
        this.items[item].dislikedBy.delete(user);

        return this;

    };

    Sibyl.prototype.recordDislike = function(user, item) {

        if (!this.users[user]) {
            this.users[user] = {};
            this.users[user].likes = new Set;
            this.users[user].dislikes = new Set;
        }

        if (!this.items[item]) {
            this.items[item] = {};
            this.items[item].likedBy = new Set;
            this.items[item].dislikedBy = new Set;
        }

        this.users[user].dislikes.add(item);
        this.users[user].likes.delete(item);

        this.items[item].dislikedBy.add(user);
        this.items[item].likedBy.delete(user);

        return this;

    };

    Sibyl.prototype.getSimilarityBetween = function(firstUserId, secondUserId) {

        var agreements,
            disagreements,
            total,
            firstUser,
            secondUser;

        firstUser = this.users[firstUserId];
        secondUser = this.users[secondUserId];

        // number of likes and dislikes in common
        agreements = Set.intersection(firstUser.likes, secondUser.likes).size;
        agreements += Set.intersection(firstUser.dislikes, secondUser.dislikes).size;

        // number of likes and dislikes not in common
        disagreements = Set.intersection(firstUser.likes, secondUser.dislikes).size;
        disagreements += Set.intersection(firstUser.dislikes, secondUser.likes).size;

        total = Set.union(firstUser.likes, firstUser.dislikes, secondUser.likes, secondUser.dislikes).size;

        return (agreements - disagreements) / total;

    };

    Sibyl.prototype.getPrediction = function(userId, itemId) {

        var hiveMindSum,
            ratedBy,
            user,
            item;

        user = this.users[userId];
        item = this.items[itemId];

        hiveMindSum = 0.0;
        ratedBy = item.likedBy.size + item.dislikedBy.size;

        item.likedBy.forEach(function(raterId) {
            hiveMindSum += this.getSimilarityBetween(userId, raterId);
        }, this);

        item.dislikedBy.forEach(function(raterId) {
            hiveMindSum -= this.getSimilarityBetween(userId, raterId);
        }, this);

        return hiveMindSum / ratedBy;

    };

    Sibyl.prototype.getSuggestion = function(userId, tolerance) {

        var set,
            user;

        tolerance = tolerance || 0;
        user = this.users[userId];

        set = new Set;
        set.comparator = function(item) {
            return item.prediction;
        }

        for (var itemId in this.items) {
            // filter out items the user has already rated
            if (!user.likes.has(itemId) && !user.dislikes.has(itemId)) {
                var prediction = this.getPrediction(userId, itemId);
                if (prediction >= tolerance) {
                    set.add({
                        id: itemId,
                        prediction:
                    });
                }
            }
        }

        // return an ordered list of suggestions
        return _.flatten(_.pluck(set.toArray(), 'id'));

    };

    // Current version of the library. Keep in sync with `package.json`.
    Sibyl.VERSION = '0.1.0';

    Sibyl.Set = Set;

    return Sibyl;

}));
<|endoftext|>