<commit_before>'use strict';
var expect = require( 'chai' ).expect;

var PostsRequest = require( '../../lib/posts' );
var CollectionRequest = require( '../../lib/shared/collection-request' );
var WPRequest = require( '../../lib/shared/wp-request' );

describe( 'wp.posts', function() {

	describe( 'constructor', function() {

		var posts;

		beforeEach(function() {
			posts = new PostsRequest();
		});

		it( 'should create a PostsRequest instance', function() {
			expect( posts instanceof PostsRequest ).to.be.true;
		});

		it( 'should set any passed-in options', function() {
			posts = new PostsRequest({
				booleanProp: true,
				strProp: 'Some string'
			});
			expect( posts._options.booleanProp ).to.be.true;
			expect( posts._options.strProp ).to.equal( 'Some string' );
		});

		it( 'should default _options to {}', function() {
			expect( posts._options ).to.deep.equal( {} );
		});

		it( 'should intitialize instance properties', function() {
			expect( posts._filters ).to.deep.equal( {} );
			expect( posts._taxonomyFilters ).to.deep.equal( {} );
			expect( posts._path ).to.deep.equal( {} );
			expect( posts._template ).to.equal( 'posts(/:id)(/:action)(/:actionId)' );
			var _supportedMethods = posts._supportedMethods.sort().join( '|' );
			expect( _supportedMethods ).to.equal( 'get|head|post' );
		});

		it( 'should inherit PostsRequest from CollectionRequest', function() {
			expect( posts instanceof CollectionRequest ).to.be.true;
			expect( posts instanceof WPRequest ).to.be.true;
		});

		it( 'should inherit prototype methods from both ancestors', function() {
			// Spot-check from CollectionRequest:
			expect( posts ).to.have.property( 'filter' );
			expect( posts.filter ).to.be.a( 'function' );
			expect( posts ).to.have.property( 'param' );
			expect( posts.param ).to.be.a( 'function' );
			// From WPRequest:
			expect( posts ).to.have.property( 'get' );
			expect( posts.get ).to.be.a( 'function' );
			expect( posts ).to.have.property( '_renderURI' );
			expect( posts._renderURI ).to.be.a( 'function' );
		});

	});

	describe( '_pathValidators', function() {

		it( 'defines validators for id and action', function() {
			var posts = new PostsRequest();
			expect( posts._pathValidators ).to.deep.equal({
				id: /^\d+$/,
				action: /(meta|comments|revisions)/
			});
		});

	});

	describe( 'query methods', function() {

		var posts;

		beforeEach(function() {
			posts = new PostsRequest();
			posts._options = {
				endpoint: '/wp-json/'
			};
		});

		it( 'provides a method to set the ID', function() {
			expect( posts ).to.have.property( 'id' );
			expect( posts.id ).to.be.a( 'function' );
			posts.id( 314159 );
			expect( posts._path ).to.have.property( 'id' );
			expect( posts._path.id ).to.equal( 314159 );
		});

		it( 'parses ID parameters into integers', function() {
			expect( posts ).to.have.property( 'id' );
			expect( posts.id ).to.be.a( 'function' );
			posts.id( '8' );
			expect( posts._path ).to.have.property( 'id' );
			expect( posts._path.id ).to.equal( 8 );
			posts.id( 4.019 );
			expect( posts._path.id ).to.equal( 4 );
		});

		it( 'should update the supported methods when setting ID', function() {
			posts.id( 8 );
			var _supportedMethods = posts._supportedMethods.sort().join( '|' );
			expect( _supportedMethods ).to.equal( 'delete|get|head|post|put' );
		});

		it( 'provides a method to query for comments', function() {
			expect( posts ).to.have.property( 'comments' );
			expect( posts.comments ).to.be.a( 'function' );
			posts.comments();
			expect( posts._path ).to.have.property( 'action' );
			expect( posts._path.action ).to.equal( 'comments' );
		});

		it( 'provides a method to query by type', function() {
			expect( posts ).to.have.property( 'type' );
			expect( posts.type ).to.be.a( 'function' );
			posts.type( 'some_cpt' );
			expect( posts._params ).to.have.property( 'type' );
			expect( posts._params.type ).to.deep.equal('some_cpt');

			var uri = posts._renderURI();
			expect( uri ).to.equal( '/wp-json/posts?type=some_cpt' );
		});

		it( 'merges the values provided in successive calls to type', function() {
			posts.type( 'cpt1' ).type( 'cpt2' );
			expect( posts._params.type ).to.deep.equal([
				'cpt1',
				'cpt2'
			]);
			posts.type([ 'page' ]);
			expect( posts._params.type ).to.deep.equal([
				'cpt1',
				'cpt2',
				'page'
			]);

			var uri = '/wp-json/posts?type%5B0%5D=cpt1&type%5B1%5D=cpt2&type%5B2%5D=page';
			expect( posts._renderURI() ).to.equal( uri );
		});

	});

	describe( 'URL Generation', function() {

		var posts;

		beforeEach(function() {
			posts = new PostsRequest();
			posts._options = {
				endpoint: '/wp-json/'
			};
		});

		it( 'should create the URL for retrieving all posts', function() {
			var path = posts._renderURI();
			expect( path ).to.equal( '/wp-json/posts' );
		});

		it( 'should create the URL for retrieving a specific post', function() {
			var path = posts.id( 1337 )._renderURI();
			expect( path ).to.equal( '/wp-json/posts/1337' );
		});

		it( 'throws an error if an invalid ID is specified', function() {
			expect(function numberPassesValidation() {
				posts._path = { id: 8 };
				posts._renderPath();
			}).not.to.throw();

			expect(function stringFailsValidation() {
				posts._path = { id: 'wombat' };
				posts._renderPath();
			}).to.throw();
		});

		it( 'should create the URL for retrieving all comments for a specific post', function() {
			var path = posts.id( 1337 ).comments()._renderURI();
			expect( path ).to.equal( '/wp-json/posts/1337/comments' );
		});

		it( 'should create the URL for retrieving a specific comment', function() {
			var path = posts.id( 1337 ).comments().comment( 9001 )._renderURI();
			expect( path ).to.equal( '/wp-json/posts/1337/comments/9001' );
		});

		it( 'should force the "comments" action when comment() is called', function() {
			var path = posts.id( 2501 ).comment( 9 )._renderURI();
			expect( path ).to.equal( '/wp-json/posts/2501/comments/9' );
		});

		it( 'should create the URL for retrieving the revisions for a specific post', function() {
			var path = posts.id( 1337 ).revisions()._renderURI();
			expect( path ).to.equal( '/wp-json/posts/1337/revisions' );
		});

		it( 'should force authentication when querying posts/id/revisions', function() {
			posts.id( 1337 ).revisions();
			expect( posts._options ).to.have.property( 'auth' );
			expect( posts._options.auth ).to.be.true;
		});

		it( 'should restrict template changes to a single instance', function() {
			posts._template = 'path/with/post/nr/:id';
			var newPosts = new PostsRequest();
			newPosts._options.endpoint = 'endpoint/url/';
			var path = newPosts.id( 3 )._renderURI();
			expect( path ).to.equal( 'endpoint/url/posts/3' );
		});

	});

});
<commit_msg>added tests for meta function<commit_after>'use strict';
var expect = require( 'chai' ).expect;

var PostsRequest = require( '../../lib/posts' );
var CollectionRequest = require( '../../lib/shared/collection-request' );
var WPRequest = require( '../../lib/shared/wp-request' );

describe( 'wp.posts', function() {

	describe( 'constructor', function() {

		var posts;

		beforeEach(function() {
			posts = new PostsRequest();
		});

		it( 'should create a PostsRequest instance', function() {
			expect( posts instanceof PostsRequest ).to.be.true;
		});

		it( 'should set any passed-in options', function() {
			posts = new PostsRequest({
				booleanProp: true,
				strProp: 'Some string'
			});
			expect( posts._options.booleanProp ).to.be.true;
			expect( posts._options.strProp ).to.equal( 'Some string' );
		});

		it( 'should default _options to {}', function() {
			expect( posts._options ).to.deep.equal( {} );
		});

		it( 'should intitialize instance properties', function() {
			expect( posts._filters ).to.deep.equal( {} );
			expect( posts._taxonomyFilters ).to.deep.equal( {} );
			expect( posts._path ).to.deep.equal( {} );
			expect( posts._template ).to.equal( 'posts(/:id)(/:action)(/:actionId)' );
			var _supportedMethods = posts._supportedMethods.sort().join( '|' );
			expect( _supportedMethods ).to.equal( 'get|head|post' );
		});

		it( 'should inherit PostsRequest from CollectionRequest', function() {
			expect( posts instanceof CollectionRequest ).to.be.true;
			expect( posts instanceof WPRequest ).to.be.true;
		});

		it( 'should inherit prototype methods from both ancestors', function() {
			// Spot-check from CollectionRequest:
			expect( posts ).to.have.property( 'filter' );
			expect( posts.filter ).to.be.a( 'function' );
			expect( posts ).to.have.property( 'param' );
			expect( posts.param ).to.be.a( 'function' );
			// From WPRequest:
			expect( posts ).to.have.property( 'get' );
			expect( posts.get ).to.be.a( 'function' );
			expect( posts ).to.have.property( '_renderURI' );
			expect( posts._renderURI ).to.be.a( 'function' );
		});

	});

	describe( '_pathValidators', function() {

		it( 'defines validators for id and action', function() {
			var posts = new PostsRequest();
			expect( posts._pathValidators ).to.deep.equal({
				id: /^\d+$/,
				action: /(meta|comments|revisions)/
			});
		});

	});

	describe( 'query methods', function() {

		var posts;

		beforeEach(function() {
			posts = new PostsRequest();
			posts._options = {
				endpoint: '/wp-json/'
			};
		});

		it( 'provides a method to set the ID', function() {
			expect( posts ).to.have.property( 'id' );
			expect( posts.id ).to.be.a( 'function' );
			posts.id( 314159 );
			expect( posts._path ).to.have.property( 'id' );
			expect( posts._path.id ).to.equal( 314159 );
		});

		it( 'provides a method to get all meta for given post', function() {
			expect( posts).to.have.property( 'meta' );
			expect( posts.meta).to.be.a( 'function' );
			posts.id( 3 ).meta();
			expect( posts._path ).to.have.property( 'action' );
			expect( posts._path.action ).to.equal( 'meta' );
		});

		it( 'provides a method to get specific post meta', function() {
			expect( posts).to.have.property( 'meta' );
			expect( posts.meta).to.be.a( 'function' );
			posts.id( 3 ).meta( 5 );
			expect( posts._path ).to.have.property( 'actionId' );
			expect( posts._path.actionId ).to.equal( 5 );
		});

		it( 'parses ID parameters into integers', function() {
			expect( posts ).to.have.property( 'id' );
			expect( posts.id ).to.be.a( 'function' );
			posts.id( '8' );
			expect( posts._path ).to.have.property( 'id' );
			expect( posts._path.id ).to.equal( 8 );
			posts.id( 4.019 );
			expect( posts._path.id ).to.equal( 4 );
		});

		it( 'should update the supported methods when setting ID', function() {
			posts.id( 8 );
			var _supportedMethods = posts._supportedMethods.sort().join( '|' );
			expect( _supportedMethods ).to.equal( 'delete|get|head|post|put' );
		});

		it( 'provides a method to query for comments', function() {
			expect( posts ).to.have.property( 'comments' );
			expect( posts.comments ).to.be.a( 'function' );
			posts.comments();
			expect( posts._path ).to.have.property( 'action' );
			expect( posts._path.action ).to.equal( 'comments' );
		});

		it( 'provides a method to query by type', function() {
			expect( posts ).to.have.property( 'type' );
			expect( posts.type ).to.be.a( 'function' );
			posts.type( 'some_cpt' );
			expect( posts._params ).to.have.property( 'type' );
			expect( posts._params.type ).to.deep.equal('some_cpt');

			var uri = posts._renderURI();
			expect( uri ).to.equal( '/wp-json/posts?type=some_cpt' );
		});

		it( 'merges the values provided in successive calls to type', function() {
			posts.type( 'cpt1' ).type( 'cpt2' );
			expect( posts._params.type ).to.deep.equal([
				'cpt1',
				'cpt2'
			]);
			posts.type([ 'page' ]);
			expect( posts._params.type ).to.deep.equal([
				'cpt1',
				'cpt2',
				'page'
			]);

			var uri = '/wp-json/posts?type%5B0%5D=cpt1&type%5B1%5D=cpt2&type%5B2%5D=page';
			expect( posts._renderURI() ).to.equal( uri );
		});

	});

	describe( 'URL Generation', function() {

		var posts;

		beforeEach(function() {
			posts = new PostsRequest();
			posts._options = {
				endpoint: '/wp-json/'
			};
		});

		it( 'should create the URL for retrieving all posts', function() {
			var path = posts._renderURI();
			expect( path ).to.equal( '/wp-json/posts' );
		});

		it( 'should create the URL for retrieving a specific post', function() {
			var path = posts.id( 1337 )._renderURI();
			expect( path ).to.equal( '/wp-json/posts/1337' );
		});

		it( 'throws an error if an invalid ID is specified', function() {
			expect(function numberPassesValidation() {
				posts._path = { id: 8 };
				posts._renderPath();
			}).not.to.throw();

			expect(function stringFailsValidation() {
				posts._path = { id: 'wombat' };
				posts._renderPath();
			}).to.throw();
		});

		it( 'should create the URL for retrieving all comments for a specific post', function() {
			var path = posts.id( 1337 ).comments()._renderURI();
			expect( path ).to.equal( '/wp-json/posts/1337/comments' );
		});

		it( 'should create the URL for retrieving a specific comment', function() {
			var path = posts.id( 1337 ).comments().comment( 9001 )._renderURI();
			expect( path ).to.equal( '/wp-json/posts/1337/comments/9001' );
		});

		it( 'should force the "comments" action when comment() is called', function() {
			var path = posts.id( 2501 ).comment( 9 )._renderURI();
			expect( path ).to.equal( '/wp-json/posts/2501/comments/9' );
		});

		it( 'should create the URL for retrieving the revisions for a specific post', function() {
			var path = posts.id( 1337 ).revisions()._renderURI();
			expect( path ).to.equal( '/wp-json/posts/1337/revisions' );
		});

		it( 'should force authentication when querying posts/id/revisions', function() {
			posts.id( 1337 ).revisions();
			expect( posts._options ).to.have.property( 'auth' );
			expect( posts._options.auth ).to.be.true;
		});

		it( 'should restrict template changes to a single instance', function() {
			posts._template = 'path/with/post/nr/:id';
			var newPosts = new PostsRequest();
			newPosts._options.endpoint = 'endpoint/url/';
			var path = newPosts.id( 3 )._renderURI();
			expect( path ).to.equal( 'endpoint/url/posts/3' );
		});

	});

});
<|endoftext|>


<commit_before>define(['goo/math/Vector3', 'goo/math/MathUtils'],
/** @lends */
function (Vector3, MathUtils) {
	"use strict";

	/**
	 * @class Constructs a new ray with an origin at (0,0,0) and a direction of (0,0,1).
	 */
	function Ray (origin, direction) {
		this.origin = origin || new Vector3();
		this.direction = direction || new Vector3().copy(Vector3.UNIT_Z);

		// TODO: Put on the prototype and make sure that none of them interfere
		this.calcVec1 = new Vector3();
		this.calcVec2 = new Vector3();
		this.calcVec3 = new Vector3();
		this.calcVec4 = new Vector3();
	}

	/**
	 * Check for intersection of this ray and and a quad or triangle, either just inside the shape or for the plane defined by the shape (doPlanar ==
	 * true)
	 *
	 * @param polygonVertices 3 or 4 vector3s defining a triangle or quad
	 * @param [doPlanar]
	 * @param locationStore Vector3 to store our intersection point in.
	 * @return true if this ray intersects a polygon described by the given vertices.
	 */
	Ray.prototype.intersects = function (polygonVertices, doPlanar, locationStore) {
		if (polygonVertices.length === 3) {
			return this.intersectsTriangle(polygonVertices[0], polygonVertices[1], polygonVertices[2], doPlanar, locationStore);
		} else if (polygonVertices.length === 4) {
			return this.intersectsTriangle(polygonVertices[0], polygonVertices[1], polygonVertices[2], doPlanar, locationStore)
				|| this.intersectsTriangle(polygonVertices[0], polygonVertices[2], polygonVertices[3], doPlanar, locationStore);
		}
		return false;
	};

	/**
	 * Ray vs triangle implementation.
	 *
	 * @param pointA First
	 * @param pointB
	 * @param pointC
	 * @param [doPlanar]
	 * @param [locationStore]
	 * @return true if this ray intersects a triangle formed by the given three points.
	 */
	Ray.prototype.intersectsTriangle = function (pointA, pointB, pointC, doPlanar, locationStore) {
		var diff = this.calcVec1.set(this.origin).sub(pointA);
		var edge1 = this.calcVec2.set(pointB).sub(pointA);
		var edge2 = this.calcVec3.set(pointC).sub(pointA);
		var norm = this.calcVec4.set(edge1).cross(edge2);

		var dirDotNorm = this.direction.dot(norm);
		var sign;
		if (dirDotNorm > MathUtils.EPSILON) {
			sign = 1.0;
		} else if (dirDotNorm < -MathUtils.EPSILON) {
			sign = -1.0;
			dirDotNorm = -dirDotNorm;
		} else {
			// ray and triangle/quad are parallel
			return false;
		}

		var dirDotDiffxEdge2 = sign * this.direction.dot(Vector3.cross(diff, edge2, edge2));
		var result = false;
		if (dirDotDiffxEdge2 >= 0.0) {
			var dirDotEdge1xDiff = sign * this.direction.dot(edge1.cross(diff));
			if (dirDotEdge1xDiff >= 0.0) {
				if (dirDotDiffxEdge2 + dirDotEdge1xDiff <= dirDotNorm) {
					var diffDotNorm = -sign * diff.dot(norm);
					if (diffDotNorm >= 0.0) {
						// ray intersects triangle
						// if storage vector is null, just return true,
						if (!locationStore) {
							return true;
						}
						// else fill in.
						var inv = 1.0 / dirDotNorm;
						var t = diffDotNorm * inv;
						if (!doPlanar) {
							locationStore.setv(this.origin).add_d(this.direction.x * t, this.direction.y * t, this.direction.z * t);
						} else {
							// these weights can be used to determine
							// interpolated values, such as texture coord.
							// eg. texcoord s,t at intersection point:
							// s = w0*s0 + w1*s1 + w2*s2;
							// t = w0*t0 + w1*t1 + w2*t2;
							var w1 = dirDotDiffxEdge2 * inv;
							var w2 = dirDotEdge1xDiff * inv;
							// float w0 = 1.0 - w1 - w2;
							locationStore.setd(t, w1, w2);
						}
						result = true;
					}
				}
			}
		}
		return result;
	};

	/**
	 * @param worldVertices an array (size 3 or 4) of vectors describing a polygon
	 * @return the distance from our origin to the primitive or Infinity if we do not intersect.
	 */
	Ray.prototype.getDistanceToPrimitive = function (worldVertices) {
		// Intersection test
		var intersect = this.calcVec1;
		if (this.intersects(worldVertices, false, intersect)) {
			return this.origin.distance(intersect.x, intersect.y, intersect.z);
		}
		return Infinity;
	};

	/**
	 * @param plane
	 * @param locationStore if not null, and this ray intersects the plane, the world location of the point of intersection is stored in this vector.
	 * @return true if the ray collides with the given Plane
	 */
	Ray.prototype.intersectsPlane = function (plane, locationStore) {
		var normal = plane.normal;
		var denominator = normal.dot(this.direction);

		if (Math.abs(denominator) < 0.00001) {
			return false; // coplanar
		}

		var numerator = -normal.dot(this.origin) + plane.constant;
		var ratio = numerator / denominator;

		if (ratio < 0.00001) {
			return false; // intersects behind origin
		}

		if (locationStore) {
			locationStore.setv(this.direction).scale(ratio).addv(this.origin);
		}

		return true;
	};

	/**
	 * @param {Vector3} point
	 * @param {Vecotr3} [store] if not null, the closest point is stored in this param
	 * @return the squared distance from this ray to the given point.
	 */
	Ray.prototype.distanceSquared = function (point, store) {
		var vectorA = this.calcVec1;

		vectorA.setv(point).subv(this.origin);
		var t0 = this.direction.dot(vectorA);
		if (t0 > 0) {
			// d = |P - (O + t*D)|
			vectorA.setv(this.direction).scale(t0);
			vectorA.addv(this.origin);
		} else {
			// ray is closest to origin point
			vectorA.setv(this.origin);
		}

		// Save away the closest point if requested.
		if (store) {
			store.setv(vectorA);
		}

		vectorA.subv(point);
		return vectorA.lengthSquared();
	};

	return Ray;
});<commit_msg>minor jshint fix<commit_after>define(['goo/math/Vector3', 'goo/math/MathUtils'],
/** @lends */
function (Vector3, MathUtils) {
	"use strict";

	/**
	 * @class Constructs a new ray with an origin at (0,0,0) and a direction of (0,0,1).
	 */
	function Ray(origin, direction) {
		this.origin = origin || new Vector3();
		this.direction = direction || new Vector3().copy(Vector3.UNIT_Z);

		// TODO: Put on the prototype and make sure that none of them interfere
		this.calcVec1 = new Vector3();
		this.calcVec2 = new Vector3();
		this.calcVec3 = new Vector3();
		this.calcVec4 = new Vector3();
	}

	/**
	 * Check for intersection of this ray and and a quad or triangle, either just inside the shape or for the plane defined by the shape (doPlanar ==
	 * true)
	 *
	 * @param polygonVertices 3 or 4 vector3s defining a triangle or quad
	 * @param [doPlanar]
	 * @param locationStore Vector3 to store our intersection point in.
	 * @return true if this ray intersects a polygon described by the given vertices.
	 */
	Ray.prototype.intersects = function (polygonVertices, doPlanar, locationStore) {
		if (polygonVertices.length === 3) {
			return this.intersectsTriangle(polygonVertices[0], polygonVertices[1], polygonVertices[2], doPlanar, locationStore);
		} else if (polygonVertices.length === 4) {
			return this.intersectsTriangle(polygonVertices[0], polygonVertices[1], polygonVertices[2], doPlanar, locationStore)
				|| this.intersectsTriangle(polygonVertices[0], polygonVertices[2], polygonVertices[3], doPlanar, locationStore);
		}
		return false;
	};

	/**
	 * Ray vs triangle implementation.
	 *
	 * @param pointA First
	 * @param pointB
	 * @param pointC
	 * @param [doPlanar]
	 * @param [locationStore]
	 * @return true if this ray intersects a triangle formed by the given three points.
	 */
	Ray.prototype.intersectsTriangle = function (pointA, pointB, pointC, doPlanar, locationStore) {
		var diff = this.calcVec1.set(this.origin).sub(pointA);
		var edge1 = this.calcVec2.set(pointB).sub(pointA);
		var edge2 = this.calcVec3.set(pointC).sub(pointA);
		var norm = this.calcVec4.set(edge1).cross(edge2);

		var dirDotNorm = this.direction.dot(norm);
		var sign;
		if (dirDotNorm > MathUtils.EPSILON) {
			sign = 1.0;
		} else if (dirDotNorm < -MathUtils.EPSILON) {
			sign = -1.0;
			dirDotNorm = -dirDotNorm;
		} else {
			// ray and triangle/quad are parallel
			return false;
		}

		var dirDotDiffxEdge2 = sign * this.direction.dot(Vector3.cross(diff, edge2, edge2));
		var result = false;
		if (dirDotDiffxEdge2 >= 0.0) {
			var dirDotEdge1xDiff = sign * this.direction.dot(edge1.cross(diff));
			if (dirDotEdge1xDiff >= 0.0) {
				if (dirDotDiffxEdge2 + dirDotEdge1xDiff <= dirDotNorm) {
					var diffDotNorm = -sign * diff.dot(norm);
					if (diffDotNorm >= 0.0) {
						// ray intersects triangle
						// if storage vector is null, just return true,
						if (!locationStore) {
							return true;
						}
						// else fill in.
						var inv = 1.0 / dirDotNorm;
						var t = diffDotNorm * inv;
						if (!doPlanar) {
							locationStore.setv(this.origin).add_d(this.direction.x * t, this.direction.y * t, this.direction.z * t);
						} else {
							// these weights can be used to determine
							// interpolated values, such as texture coord.
							// eg. texcoord s,t at intersection point:
							// s = w0*s0 + w1*s1 + w2*s2;
							// t = w0*t0 + w1*t1 + w2*t2;
							var w1 = dirDotDiffxEdge2 * inv;
							var w2 = dirDotEdge1xDiff * inv;
							// float w0 = 1.0 - w1 - w2;
							locationStore.setd(t, w1, w2);
						}
						result = true;
					}
				}
			}
		}
		return result;
	};

	/**
	 * @param worldVertices an array (size 3 or 4) of vectors describing a polygon
	 * @return the distance from our origin to the primitive or Infinity if we do not intersect.
	 */
	Ray.prototype.getDistanceToPrimitive = function (worldVertices) {
		// Intersection test
		var intersect = this.calcVec1;
		if (this.intersects(worldVertices, false, intersect)) {
			return this.origin.distance(intersect.x, intersect.y, intersect.z);
		}
		return Infinity;
	};

	/**
	 * @param plane
	 * @param locationStore if not null, and this ray intersects the plane, the world location of the point of intersection is stored in this vector.
	 * @return true if the ray collides with the given Plane
	 */
	Ray.prototype.intersectsPlane = function (plane, locationStore) {
		var normal = plane.normal;
		var denominator = normal.dot(this.direction);

		if (Math.abs(denominator) < 0.00001) {
			return false; // coplanar
		}

		var numerator = -normal.dot(this.origin) + plane.constant;
		var ratio = numerator / denominator;

		if (ratio < 0.00001) {
			return false; // intersects behind origin
		}

		if (locationStore) {
			locationStore.setv(this.direction).scale(ratio).addv(this.origin);
		}

		return true;
	};

	/**
	 * @param {Vector3} point
	 * @param {Vecotr3} [store] if not null, the closest point is stored in this param
	 * @return the squared distance from this ray to the given point.
	 */
	Ray.prototype.distanceSquared = function (point, store) {
		var vectorA = this.calcVec1;

		vectorA.setv(point).subv(this.origin);
		var t0 = this.direction.dot(vectorA);
		if (t0 > 0) {
			// d = |P - (O + t*D)|
			vectorA.setv(this.direction).scale(t0);
			vectorA.addv(this.origin);
		} else {
			// ray is closest to origin point
			vectorA.setv(this.origin);
		}

		// Save away the closest point if requested.
		if (store) {
			store.setv(vectorA);
		}

		vectorA.subv(point);
		return vectorA.lengthSquared();
	};

	return Ray;
});<|endoftext|>


<commit_before>/* This file is part of Indico.
 * Copyright (C) 2002 - 2015 European Organization for Nuclear Research (CERN).
 *
 * Indico is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 3 of the
 * License, or (at your option) any later version.
 *
 * Indico is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Indico; if not, see <http://www.gnu.org/licenses/>.
 */

(function(global, $) {
    'use strict';

    // Ajaxifies a link to open the target page (which needs to be using WPJinjaMixin.render_template) in a
    // modal dialog. Any forms on the page are ajaxified and should be using redirect_or_jsonify() in case
    // of success (or just return a JSON response containing success=true and possibly flashedmessages).
    // The link target MUST point to a page which is also valid when loaded directly in the browser since the
    // link could still be opened in a new tab manually. If you don't have a non-AJAX version, use ajaxDialog().
    $.fn.ajaxDialog = function jqAjaxDialog(options) {
        return this.on('click', function(e) {
            e.preventDefault();
            ajaxDialog($.extend({}, options, {
                url: $(this).attr('href')
            }));
        });
    };

    // See the documentation of $.fn.ajaxDialog - use this function if you want to open an ajax-based dialog
    // manually instead of triggering it from a link using its href.
    global.ajaxDialog = function ajaxDialog(options) {
        options = $.extend({
            title: null, // title of the dialog
            url: null, // url to GET the form from
            backSelector: '[data-button-back]', // elements in the form which will close the form
            onClose: null // callback to invoke after closing the dialog. first argument is null if closed manually,
                          // otherwise the JSON returned by the server
        }, options);

        var popup = null;

        $.ajax({
            type: 'GET',
            url: options.url,
            error: handleAjaxError,
            success: function(data) {
                if (handleAjaxError(data)) {
                    return;
                }
                showDialog(data);
            }
        });

        function showDialog(dialogData) {
            popup = new ExclusivePopup(options.title, function() {
                if (options.onClose) {
                    options.onClose(null);
                }
                return true;
            }, false, false);
            popup.draw = function() {
                this.ExclusivePopup.prototype.draw.call(this, dialogData.html);
            };
            popup.postDraw = ajaxifyForms;
            popup.open();
        }

        function closeDialog(callbackData) {
            if (options.onClose) {
                options.onClose(callbackData);
            }
            popup.close();
        }

        function ajaxifyForms() {
            var killProgress = null;
            var forms = popup.contentContainer.find('form');
            showFormErrors(popup.resultContainer);
            forms.on('click', options.backSelector, function(e) {
                e.preventDefault();
                closeDialog();
            }).each(function() {
                // We often use forms with an empty action; those need to go to
                // their page and not the page that loaded the dialog!
                var action = $(this).attr('action') || options.url;
                $(this).ajaxForm({
                    url: action,
                    dataType: 'json',
                    beforeSubmit: function() {
                        killProgress = IndicoUI.Dialogs.Util.progress();
                    },
                    error: function(xhr) {
                        killProgress();
                        closeDialog();
                        handleAjaxError(xhr);
                    },
                    success: function(data) {
                        killProgress();
                        if (handleAjaxError(data)) {
                            closeDialog();
                            return;
                        }

                        if (data.flashed_messages) {
                            var flashed = $(data.flashed_messages.trim()).children();
                            $('#flashed-messages').append(flashed);
                        }

                        if (data.close_dialog || data.success) {
                            closeDialog(data);
                        } else if (data.html) {
                            popup.contentContainer.html(data.html);
                            ajaxifyForms();
                        }
                    }
                });
            });
        }
    };
})(window, jQuery);
<commit_msg>Show throbber when loading ajax dialog<commit_after>/* This file is part of Indico.
 * Copyright (C) 2002 - 2015 European Organization for Nuclear Research (CERN).
 *
 * Indico is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 3 of the
 * License, or (at your option) any later version.
 *
 * Indico is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Indico; if not, see <http://www.gnu.org/licenses/>.
 */

(function(global, $) {
    'use strict';

    // Ajaxifies a link to open the target page (which needs to be using WPJinjaMixin.render_template) in a
    // modal dialog. Any forms on the page are ajaxified and should be using redirect_or_jsonify() in case
    // of success (or just return a JSON response containing success=true and possibly flashedmessages).
    // The link target MUST point to a page which is also valid when loaded directly in the browser since the
    // link could still be opened in a new tab manually. If you don't have a non-AJAX version, use ajaxDialog().
    $.fn.ajaxDialog = function jqAjaxDialog(options) {
        return this.on('click', function(e) {
            e.preventDefault();
            ajaxDialog($.extend({}, options, {
                url: $(this).attr('href')
            }));
        });
    };

    // See the documentation of $.fn.ajaxDialog - use this function if you want to open an ajax-based dialog
    // manually instead of triggering it from a link using its href.
    global.ajaxDialog = function ajaxDialog(options) {
        options = $.extend({
            title: null, // title of the dialog
            url: null, // url to GET the form from
            backSelector: '[data-button-back]', // elements in the form which will close the form
            onClose: null // callback to invoke after closing the dialog. first argument is null if closed manually,
                          // otherwise the JSON returned by the server
        }, options);

        var popup = null;

        $.ajax({
            type: 'GET',
            url: options.url,
            complete: IndicoUI.Dialogs.Util.progress(),
            error: handleAjaxError,
            success: function(data) {
                if (handleAjaxError(data)) {
                    return;
                }
                showDialog(data);
            }
        });

        function showDialog(dialogData) {
            popup = new ExclusivePopup(options.title, function() {
                if (options.onClose) {
                    options.onClose(null);
                }
                return true;
            }, false, false);
            popup.draw = function() {
                this.ExclusivePopup.prototype.draw.call(this, dialogData.html);
            };
            popup.postDraw = ajaxifyForms;
            popup.open();
        }

        function closeDialog(callbackData) {
            if (options.onClose) {
                options.onClose(callbackData);
            }
            popup.close();
        }

        function ajaxifyForms() {
            var killProgress = null;
            var forms = popup.contentContainer.find('form');
            showFormErrors(popup.resultContainer);
            forms.on('click', options.backSelector, function(e) {
                e.preventDefault();
                closeDialog();
            }).each(function() {
                // We often use forms with an empty action; those need to go to
                // their page and not the page that loaded the dialog!
                var action = $(this).attr('action') || options.url;
                $(this).ajaxForm({
                    url: action,
                    dataType: 'json',
                    beforeSubmit: function() {
                        killProgress = IndicoUI.Dialogs.Util.progress();
                    },
                    error: function(xhr) {
                        killProgress();
                        closeDialog();
                        handleAjaxError(xhr);
                    },
                    success: function(data) {
                        killProgress();
                        if (handleAjaxError(data)) {
                            closeDialog();
                            return;
                        }

                        if (data.flashed_messages) {
                            var flashed = $(data.flashed_messages.trim()).children();
                            $('#flashed-messages').append(flashed);
                        }

                        if (data.close_dialog || data.success) {
                            closeDialog(data);
                        } else if (data.html) {
                            popup.contentContainer.html(data.html);
                            ajaxifyForms();
                        }
                    }
                });
            });
        }
    };
})(window, jQuery);
<|endoftext|>


<commit_before>/**
 * Remote
 * @author Tom
 */

var activeWebRequests = 0

/**
 * Ready state enumeration.
 */
var ReadyState = new Enum("Uninitialized", "Loading", "Loaded", "Interactive", "Complete");

//function jsonRpcStore(url, method, params) {
//      function commit() {
//
//      }
//
//      var store = {
//              list: function(handler) {
//
//              },
//              fetch: function(key, handler) {
//                      schedule()
//              }
//      };
//
//      return store;
//}

/**
 * Returns a JSON-RPC object source. Argument dontWatch specifies if the changes have not to be commited to the server.
 * @param {String} url
 * @param {String} method
 * @param {Object} [params]
 * @param {Boolean} [dontWatch]
 * @return {WatchObject, Source}
 */
function jsonRpcObject(url, method, params, dontWatch) {
        // not an object for url... curry
        var self = new Source();
        var sourceObject = mixWatchGetters(self, ["state", "error"]);
        var dataObject = new WatchObject();
        mixinInstance(self, dataObject, WatchObject);

        var updating = false;
        var requests = {};
        function commit(key, value) {
                var data = {};
                var empty = true;
                enumerate(requests, function(value, key) {
                        if (value) {
                                data[key] = dataObject.get(key);
                                empty = false;
                        }
                });
                requests = {};
                if (!empty) {
                        jsonRpcCommit(url, method, params, data, process, url);
                }
        }
        function commitKey(key) {
                if (!updating) {
                        requests[key] = true;
                        defer(commit);
                }
        }

        function process(result, error) {
                if (exists(error)) {
                        sourceObject.update({
                                state: SourceState.Error,
                                error: error
                        });
                } else {
                        if (exists(result)) {
                                updating = true;
                                if (!dontWatch) {
                                        enumerate(result, function(value, key) {
                                                dataObject.set(key, watch(watchize(value), curry(commitKey, key)));
                                        });
                                } else {
                                        enumerate(result, function(value, key) {
                                                dataObject.set(key, value);
                                        });
                                }
                                updating = false;
                        }
                        sourceObject.update({
                                state: SourceState.Loaded,
                                error: null
                        });
                }
        }

        dataObject.observe(function(value, key) {
                commitKey(key);
        });
        self.refresh = function() {
                sourceObject.update({
                        state: SourceState.Loading,
                        error: null
                });
                return jsonRpc(url, method, params, process);
        };
        self.refresh();
        return self;
}

/**
 * Returns a JSON-RPC value source.
 * @param {String} url
 * @param {String} method
 * @param {Object} [params]
 * @param {Object} [def]
 * @return {WatchAccessor} value
 */
function jsonRpcValue(url, method, params, def, dontStart, callback) {
        // not an object for url... curry
        var self = new Source();
        var object = mixWatchGetters(self, ["state", "error"]);
        object.set("data", def);
        mixinInstance(self, object.accessor("data"), WatchAccessor);

        function process(result, error) {
                if (exists(error)) {
                        object.update({
                                state: SourceState.Error,
                                error: error
                        });
                } else {
                        object.update({
                                state: SourceState.Loaded,
                                data: result,
                                error: null
                        });

                        if(callback) {
                            callback();
                        }
                }
        }

        self.set = function(value) {
            var old = self.get();
            object.update({
                        state: SourceState.Committing,
                        error: null
                });
            jsonRpcCommit(url, method, params, value, process);
            return old;
        };
        self.refresh = function() {
                object.update({
                        state: SourceState.Loading,
                        error: null
                });
                return jsonRpc(url, method, params, process);
        };
        if (!dontStart) {
            self.refresh();
        }
        return self;
}

/**
 * Commits the value over JSON-RPC and passes a result to the handler. Returns XMLHttpRequest object.
 * @param {String} url
 * @param {String} method
 * @param {Object} params
 * @param {Object} value
 * @param {Function} handler
 * @return {XMLHttpRequest} request
 */
function jsonRpcCommit(url, method, params, value, handler) {
        var args;
        if (!exists(params)) {
                args = value;
        } else if (isArray(params)) {
                args = $A(params);
                args.push(value);
        } else if (isObject(params)) {
                args = clone(params);
                args.value = value;
        } else {
                args = value;
        }
        return jsonRpc(url, method, args, handler);
}

/**
 * Invokes the method with the params over JSON-RPC and passes a result to the handler. Returns XMLHttpRequest object.
 * @param {String} url
 * @param {String} method
 * @param {Object} params
 * @param {Function} handler
 * @return {XMLHttpRequest} request
 */
function jsonRpc(url, method, params, handler) {
        return jsonRequest(url, {
                version: "1.1",
                origin: location.href,
                method: method,
                params: params
        }, function(response, error) {
                if (exists(error)) {
                        handler(response, "SERVER: " + error);
                } else {
                        handler(response.result, response.error);
                }
        });
}

/**
 * Sends the value as JSON and passes a result to the handler. Returns XMLHttpRequest object.
 * @param {String} url
 * @param {Object} value
 * @param {Function} handler
 * @return {XMLHttpRequest} request
 */
function jsonRequest(url, value, handler) {
        return webRequest(url, "application/json", Json.write(value), function(result, error) {
                handler(exists(result) ? Json.read(result) : result, error);
        });
}

/**
 * Sends the body to the url and passes a result to the handler. Returns XMLHttpRequest object.
 * @param {String} url
 * @param {String} contentType
 * @param {String} body
 * @param {Function} handler
 * @return {XMLHttpRequest} request
 */
function webRequest(url, contentType, body, handler) {
        var transport = Web.transport();
        try {
                transport.open("POST", url, true);
                transport.setRequestHeader("Accept", "text/javascript, text/html, application/xml, text/xml, application/json, */*");
                transport.setRequestHeader("Content-Type", contentType);
                transport.setRequestHeader("X-CSRF-Token", $('#csrf-token').attr('content'));
                transport.onreadystatechange = function() {
                        if (transport.readyState != ReadyState.Complete) {
                                return;
                        }
                        activeWebRequests--;
                        delete transport.onreadystatechange;
                        var status = transport.status;
                        if (status >= 200 && status < 300) {
                                handler(transport.responseText, null);
                        } else {
                                handler(transport.responseText, [status, transport.statusText]);
                        }
                }
                transport.send(body);
                activeWebRequests++;
        } catch (e) {
                handler(null, e);
        }
    return function() { return transport.abort(); };
}

/**
 * Requests static resource via HTTP GET
 * @param {String} url
 * @param {Function} handler
 * @return {XMLHttpRequest} request
 */
function webGet(url, handler) {
        var transport = Web.transport();
        try {
                transport.open("GET", url, true);
                transport.setRequestHeader("Accept", "text/javascript, text/html, application/xml, text/xml, application/json, */*");
                transport.setRequestHeader("Cache-Control", "max-age=1000000000");
                transport.onreadystatechange = function() {
                        if (transport.readyState != ReadyState.Complete) {
                                return;
                        }
                        transport.onreadystatechange = null;
                        var status = transport.status;
                        if (status >= 200 && status < 300) {
                                handler(transport.responseText, null);
                        } else {
                                handler(transport.responseText, [status, transport.statusText]);
                        }
                };
                transport.send(null);
        } catch (e) {
                handler(null, e);
        }
    return function() { return transport.abort(); };
}

var Web = {};

/**
 * Returns a new XMLHttpRequest.
 * @return {XMLHttpRequest}
 */
if (exists(global.XMLHttpRequest)) {
        Web.transport = function(){
                return new XMLHttpRequest();
        };
} else {
        Web.transport = function(){
        return tryAny(
                function() { return new ActiveXObject('Msxml2.XMLHTTP'); },
                function() { return new ActiveXObject('Microsoft.XMLHTTP'); }
        );
        };
}

function apiRequest(path, payload, opts) {
    // We always use cookie authentication and copy the payload object
    payload = $.extend({
        cookieauth: true
    }, payload || {});

    // Additional options
    opts = $.extend({
        method: 'GET',
        silentErrors: false // when true, no error dialog is shown for HTTPAPIErrors
    }, opts);

    // For convenience we map booleans to yes/no
    $.each(payload, function(key, val) {
        if(val === true || val === false) {
            payload[key] = val ? 'yes' : 'no';
        }
    });

    var dfd = $.Deferred();
    $.ajax({
        url: Indico.Urls.APIBase + path + '.json',
        data: payload,
        type: opts.method,
        cache: false, // we really don't want caching for AJAX requests!
        dataType: 'json',
        headers: {
            'X-CSRF-Token': $('#csrf-token').attr('content')
        }
    }).fail(function(xhr, status, error) {
        var errorMessage = 'Unknown Error';
        var resp;

        try {
            // Maybe we have JSON
            resp = $.parseJSON(xhr.responseText);
        }
        catch(e) {
            // We got HTML. Probably 404 or something like that.
            if(~xhr.responseText.indexOf('errorBoxContent')) {
                errorMessage = $(xhr.responseText).find('#errorBoxContent h1').text();
            }
            else {
                errorMessage = error;
            }
            dfd.reject(errorMessage);
        }

        // Handle JSON error
        if(resp) {
            dfd.reject(resp.message);
            if(resp._type == 'HTTPAPIError' && opts.silentErrors) {
                return;
            }
            if(resp.message) {
                errorMessage = resp.message;
            }
        }

        IndicoUtil.errorReport({
            title: $T('Error'),
            message: $T('API request failed:') + ' ' + errorMessage,
            type: 'noReport'
        });
    }).done(function(resp) {
        if (resp._type == 'HTTPAPIError') {
            dfd.reject(resp.message);
            if(!opts.silentErrors) {
                IndicoUtil.errorReport({
                    title: $T('Error'),
                    message: $T('API request failed:') + ' ' + resp.message,
                    type: 'noReport'
                });
            }
            return;
        }
        dfd.resolve(resp);
    });

    return dfd.promise();
}<commit_msg>[IMP] Make apiRequest errors reportable<commit_after>/**
 * Remote
 * @author Tom
 */

var activeWebRequests = 0

/**
 * Ready state enumeration.
 */
var ReadyState = new Enum("Uninitialized", "Loading", "Loaded", "Interactive", "Complete");

//function jsonRpcStore(url, method, params) {
//      function commit() {
//
//      }
//
//      var store = {
//              list: function(handler) {
//
//              },
//              fetch: function(key, handler) {
//                      schedule()
//              }
//      };
//
//      return store;
//}

/**
 * Returns a JSON-RPC object source. Argument dontWatch specifies if the changes have not to be commited to the server.
 * @param {String} url
 * @param {String} method
 * @param {Object} [params]
 * @param {Boolean} [dontWatch]
 * @return {WatchObject, Source}
 */
function jsonRpcObject(url, method, params, dontWatch) {
        // not an object for url... curry
        var self = new Source();
        var sourceObject = mixWatchGetters(self, ["state", "error"]);
        var dataObject = new WatchObject();
        mixinInstance(self, dataObject, WatchObject);

        var updating = false;
        var requests = {};
        function commit(key, value) {
                var data = {};
                var empty = true;
                enumerate(requests, function(value, key) {
                        if (value) {
                                data[key] = dataObject.get(key);
                                empty = false;
                        }
                });
                requests = {};
                if (!empty) {
                        jsonRpcCommit(url, method, params, data, process, url);
                }
        }
        function commitKey(key) {
                if (!updating) {
                        requests[key] = true;
                        defer(commit);
                }
        }

        function process(result, error) {
                if (exists(error)) {
                        sourceObject.update({
                                state: SourceState.Error,
                                error: error
                        });
                } else {
                        if (exists(result)) {
                                updating = true;
                                if (!dontWatch) {
                                        enumerate(result, function(value, key) {
                                                dataObject.set(key, watch(watchize(value), curry(commitKey, key)));
                                        });
                                } else {
                                        enumerate(result, function(value, key) {
                                                dataObject.set(key, value);
                                        });
                                }
                                updating = false;
                        }
                        sourceObject.update({
                                state: SourceState.Loaded,
                                error: null
                        });
                }
        }

        dataObject.observe(function(value, key) {
                commitKey(key);
        });
        self.refresh = function() {
                sourceObject.update({
                        state: SourceState.Loading,
                        error: null
                });
                return jsonRpc(url, method, params, process);
        };
        self.refresh();
        return self;
}

/**
 * Returns a JSON-RPC value source.
 * @param {String} url
 * @param {String} method
 * @param {Object} [params]
 * @param {Object} [def]
 * @return {WatchAccessor} value
 */
function jsonRpcValue(url, method, params, def, dontStart, callback) {
        // not an object for url... curry
        var self = new Source();
        var object = mixWatchGetters(self, ["state", "error"]);
        object.set("data", def);
        mixinInstance(self, object.accessor("data"), WatchAccessor);

        function process(result, error) {
                if (exists(error)) {
                        object.update({
                                state: SourceState.Error,
                                error: error
                        });
                } else {
                        object.update({
                                state: SourceState.Loaded,
                                data: result,
                                error: null
                        });

                        if(callback) {
                            callback();
                        }
                }
        }

        self.set = function(value) {
            var old = self.get();
            object.update({
                        state: SourceState.Committing,
                        error: null
                });
            jsonRpcCommit(url, method, params, value, process);
            return old;
        };
        self.refresh = function() {
                object.update({
                        state: SourceState.Loading,
                        error: null
                });
                return jsonRpc(url, method, params, process);
        };
        if (!dontStart) {
            self.refresh();
        }
        return self;
}

/**
 * Commits the value over JSON-RPC and passes a result to the handler. Returns XMLHttpRequest object.
 * @param {String} url
 * @param {String} method
 * @param {Object} params
 * @param {Object} value
 * @param {Function} handler
 * @return {XMLHttpRequest} request
 */
function jsonRpcCommit(url, method, params, value, handler) {
        var args;
        if (!exists(params)) {
                args = value;
        } else if (isArray(params)) {
                args = $A(params);
                args.push(value);
        } else if (isObject(params)) {
                args = clone(params);
                args.value = value;
        } else {
                args = value;
        }
        return jsonRpc(url, method, args, handler);
}

/**
 * Invokes the method with the params over JSON-RPC and passes a result to the handler. Returns XMLHttpRequest object.
 * @param {String} url
 * @param {String} method
 * @param {Object} params
 * @param {Function} handler
 * @return {XMLHttpRequest} request
 */
function jsonRpc(url, method, params, handler) {
        return jsonRequest(url, {
                version: "1.1",
                origin: location.href,
                method: method,
                params: params
        }, function(response, error) {
                if (exists(error)) {
                        handler(response, "SERVER: " + error);
                } else {
                        handler(response.result, response.error);
                }
        });
}

/**
 * Sends the value as JSON and passes a result to the handler. Returns XMLHttpRequest object.
 * @param {String} url
 * @param {Object} value
 * @param {Function} handler
 * @return {XMLHttpRequest} request
 */
function jsonRequest(url, value, handler) {
        return webRequest(url, "application/json", Json.write(value), function(result, error) {
                handler(exists(result) ? Json.read(result) : result, error);
        });
}

/**
 * Sends the body to the url and passes a result to the handler. Returns XMLHttpRequest object.
 * @param {String} url
 * @param {String} contentType
 * @param {String} body
 * @param {Function} handler
 * @return {XMLHttpRequest} request
 */
function webRequest(url, contentType, body, handler) {
        var transport = Web.transport();
        try {
                transport.open("POST", url, true);
                transport.setRequestHeader("Accept", "text/javascript, text/html, application/xml, text/xml, application/json, */*");
                transport.setRequestHeader("Content-Type", contentType);
                transport.setRequestHeader("X-CSRF-Token", $('#csrf-token').attr('content'));
                transport.onreadystatechange = function() {
                        if (transport.readyState != ReadyState.Complete) {
                                return;
                        }
                        activeWebRequests--;
                        delete transport.onreadystatechange;
                        var status = transport.status;
                        if (status >= 200 && status < 300) {
                                handler(transport.responseText, null);
                        } else {
                                handler(transport.responseText, [status, transport.statusText]);
                        }
                }
                transport.send(body);
                activeWebRequests++;
        } catch (e) {
                handler(null, e);
        }
    return function() { return transport.abort(); };
}

/**
 * Requests static resource via HTTP GET
 * @param {String} url
 * @param {Function} handler
 * @return {XMLHttpRequest} request
 */
function webGet(url, handler) {
        var transport = Web.transport();
        try {
                transport.open("GET", url, true);
                transport.setRequestHeader("Accept", "text/javascript, text/html, application/xml, text/xml, application/json, */*");
                transport.setRequestHeader("Cache-Control", "max-age=1000000000");
                transport.onreadystatechange = function() {
                        if (transport.readyState != ReadyState.Complete) {
                                return;
                        }
                        transport.onreadystatechange = null;
                        var status = transport.status;
                        if (status >= 200 && status < 300) {
                                handler(transport.responseText, null);
                        } else {
                                handler(transport.responseText, [status, transport.statusText]);
                        }
                };
                transport.send(null);
        } catch (e) {
                handler(null, e);
        }
    return function() { return transport.abort(); };
}

var Web = {};

/**
 * Returns a new XMLHttpRequest.
 * @return {XMLHttpRequest}
 */
if (exists(global.XMLHttpRequest)) {
        Web.transport = function(){
                return new XMLHttpRequest();
        };
} else {
        Web.transport = function(){
        return tryAny(
                function() { return new ActiveXObject('Msxml2.XMLHTTP'); },
                function() { return new ActiveXObject('Microsoft.XMLHTTP'); }
        );
        };
}

function apiRequest(path, payload, opts) {
    // We always use cookie authentication and copy the payload object
    payload = $.extend({
        cookieauth: true
    }, payload || {});

    // Additional options
    opts = $.extend({
        method: 'GET',
        silentErrors: false // when true, no error dialog is shown for HTTPAPIErrors
    }, opts);

    // For convenience we map booleans to yes/no
    $.each(payload, function(key, val) {
        if(val === true || val === false) {
            payload[key] = val ? 'yes' : 'no';
        }
    });

    var dfd = $.Deferred();
    var requestInfo = {
        origin: 'unknown',
        params: payload,
        method: opts.method,
        path: path
    };

    $.ajax({
        url: Indico.Urls.APIBase + path + '.json',
        data: payload,
        type: opts.method,
        cache: false, // we really don't want caching for AJAX requests!
        dataType: 'json',
        headers: {
            'X-CSRF-Token': $('#csrf-token').attr('content')
        }
    }).fail(function(xhr, status, error) {
        var errorMessage = 'Unknown Error';
        var resp, code = '';

        try {
            // Maybe we have JSON
            resp = $.parseJSON(xhr.responseText);
        }
        catch(e) {
            // We got HTML. Probably 404 or something like that.
            if(~xhr.responseText.indexOf('errorBoxContent')) {
                errorMessage = $(xhr.responseText).find('#errorBoxContent h1').text();
            }
            else {
                errorMessage = error;
            }
            dfd.reject(errorMessage);
            code = 'HTTP-' + error;
        }

        // Handle JSON error
        if(resp) {
            dfd.reject(resp.message);
            if(resp._type == 'HTTPAPIError' && opts.silentErrors) {
                return;
            }
            if(resp.message) {
                errorMessage = resp.message;
            }
            code = 'APIError';
        }

        IndicoUtil.errorReport({
            title: $T('Error'),
            message: $T('API request failed:') + ' ' + errorMessage,
            inner: [],
            requestInfo: requestInfo,
            code: code
        });
    }).done(function(resp) {
        if (resp._type == 'HTTPAPIError') {
            dfd.reject(resp.message);
            if(!opts.silentErrors) {
                IndicoUtil.errorReport({
                    title: $T('Error'),
                    message: $T('API request failed:') + ' ' + resp.message,
                    inner: [],
                    requestInfo: requestInfo,
                    code: 'APIError'
                });
            }
            return;
        }
        dfd.resolve(resp);
    });

    return dfd.promise();
}<|endoftext|>


<commit_before>// get accurate timing.
// This file must be loaded after prettify.js for this to work.
PR_SHOULD_USE_CONTINUATION = false;

var attribToHtml, textToHtml;

var getInnerHtml;

(function () {
  /** is the given node's innerHTML normally unescaped? */
  function isRawContent(node) {
    return 'XMP' === node.tagName;
  }

  var newlineRe = /[\r\n]/g;
  /**
   * Are newlines and adjacent spaces significant in the given node's innerHTML?
   */
  function isPreformatted(node, content) {
    // PRE means preformatted, and is a very common case, so don't create
    // unnecessary computed style objects.
    if ('PRE' === node.tagName) { return true; }
    if (!newlineRe.test(content)) { return true; }  // Don't care
    var whitespace = '';
    // For disconnected nodes, IE has no currentStyle.
    if (node.currentStyle) {
      whitespace = node.currentStyle.whiteSpace;
    } else if (window.getComputedStyle) {
      // Firefox makes a best guess if node is disconnected whereas Safari
      // returns the empty string.
      whitespace = window.getComputedStyle(node, null).whiteSpace;
    }
    return !whitespace || whitespace === 'pre';
  }

  // Define regexps here so that the interpreter doesn't have to create an
  // object each time the function containing them is called.
  // The language spec requires a new object created even if you don't access
  // the $1 members.
  var pr_amp = /&/g;
  var pr_lt = /</g;
  var pr_gt = />/g;
  var pr_quot = /\"/g;

  /** escapest html special characters to html. */
  textToHtml = function (str) {
    return str.replace(pr_amp, '&')
        .replace(pr_lt, '<')
        .replace(pr_gt, '>');
  };

  /** like textToHtml but escapes double quotes to be attribute safe. */
  attribToHtml = function (str) {
    return str.replace(pr_amp, '&')
        .replace(pr_lt, '<')
        .replace(pr_gt, '>')
        .replace(pr_quot, '"');
  };

  var PR_innerHtmlWorks = null;
  getInnerHtml = function (node) {
    // inner html is hopelessly broken in Safari 2.0.4 when the content is
    // an html description of well formed XML and the containing tag is a PRE
    // tag, so we detect that case and emulate innerHTML.
    if (null === PR_innerHtmlWorks) {
      var testNode = document.createElement('PRE');
      testNode.appendChild(
          document.createTextNode('<!DOCTYPE foo PUBLIC "foo bar">\n<foo />'));
      PR_innerHtmlWorks = !/</.test(testNode.innerHTML);
    }

    if (PR_innerHtmlWorks) {
      var content = node.innerHTML;
      // XMP tags contain unescaped entities so require special handling.
      if (isRawContent(node)) {
        content = textToHtml(content);
      } else if (!isPreformatted(node, content)) {
        content = content.replace(/(
)[\r\n]+/g, '$1')
            .replace(/(?:[\r\n]+[ \t]*)+/g, ' ');
      }
      return content;
    }

    var out = [];
    for (var child = node.firstChild; child; child = child.nextSibling) {
      normalizedHtml(child, out);
    }
    return out.join('');
  };
})();

function normalizedHtml(node, out, opt_sortAttrs) {
  switch (node.nodeType) {
    case 1:  // an element
      var name = node.tagName.toLowerCase();

      out.push('<', name);
      var attrs = node.attributes;
      var n = attrs.length;
      if (n) {
        if (opt_sortAttrs) {
          var sortedAttrs = [];
          for (var i = n; --i >= 0;) { sortedAttrs[i] = attrs[i]; }
          sortedAttrs.sort(function (a, b) {
              return (a.name < b.name) ? -1 : a.name === b.name ? 0 : 1;
            });
          attrs = sortedAttrs;
        }
        for (var i = 0; i < n; ++i) {
          var attr = attrs[i];
          if (!attr.specified) { continue; }
          out.push(' ', attr.name.toLowerCase(),
                   '="', attribToHtml(attr.value), '"');
        }
      }
      out.push('>');
      for (var child = node.firstChild; child; child = child.nextSibling) {
        normalizedHtml(child, out, opt_sortAttrs);
      }
      if (node.firstChild || !/^(?:br|link|img)$/.test(name)) {
        out.push('<\/', name, '>');
      }
      break;
    case 3: case 4: // text
      out.push(textToHtml(node.nodeValue));
      break;
  }
}

/**
 * @param golden a mapping from IDs of prettyprinted chunks to an abbreviated
 *     form of the expected output.  See "var goldens" in prettify_test.html
 *     for an example.
 */
function go(goldens) {
  startClock();
  prettyPrint(function () { stopClock(); runTests(goldens); });
}

function runTests(goldens) {
  /** number of characters in common at the end up to max. */
  function commonPrefix(a, b) {
    var n = Math.min(a.length, b.length);
    var i;
    for (i = 0; i < n; ++i) {
      if (a.charAt(i) !== b.charAt(i)) { break; }
    }
    return i;
  }

  /** number of characters in common at the end up to max. */
  function commonSuffix(a, b, max) {
    var n = Math.min(a.length - max, b.length - max);
    var i;
    for (i = 0; i < n; ++i) {
      if (a.charAt(a.length - i - 1) !== b.charAt(b.length - i - 1)) { break; }
    }
    return i;
  }

  /** convert a plain text string to html by escaping html special chars. */
  function html(plainText) {
    return attribToHtml(plainText).replace(/\xa0/g, ' ');
  }

  /**
   * get normalized markup.  innerHTML varies enough across browsers that we
   * can't use it.
   */
  function normalizedInnerHtml(node) {
    var out = [];
    for (var child = node.firstChild; child; child = child.nextSibling) {
      normalizedHtml(child, out, true);
    }
    out = out.join('');
    // more normalization to work around problems with non-ascii chars in
    // regexps in Safari
    for (var i = 0; (i = out.indexOf('\xa0')) >= 0;) {
      out = out.substring(0, i) + ' ' + out.substring(i + 1);
    }
    return out.replace(/\r\n?/g, '\n');
  }

  var htmlOut = [];
  var failures = 0;
  document.getElementById('errorReport').innerHTML =
      '
Running tests…<\/h1>';
  htmlOut.push('
Test results<\/h1>');
  for (var lang in goldens) {
    var container = document.getElementById(lang);
    // Convert abbreviations that start with `.
    var golden = goldens[lang].replace(/`([A-Z]{3})/g, function (_, lbl) {
        return (lbl == 'END'
            ? '<\/span>'
            : '');
      })
      // Line numbers
      .replace(/`#(?![0-9])/, '
')
      .replace(/`#([0-9])/g, '

');
    var actual = normalizedInnerHtml(container);
    if (golden !== actual) {  // test failed
      // write out
      var pre = commonPrefix(golden, actual);
      var post = commonSuffix(golden, actual, pre);

      ++failures;
      htmlOut.push(
          '
'
          + html(lang) + '<\/a> Failed<\/h2>');
      htmlOut.push(
          '<tt>' + html(golden.substring(0, pre)) +
          '»' +
          html(golden.substring(pre, golden.length - post)) +
          '<\/span>«' +
          html(golden.substring(golden.length - post)) +

          '
!==
' +

          html(actual.substring(0, pre)) +
          '»' +
          html(actual.substring(pre, actual.length - post)) +
          '<\/span>«' +
          html(actual.substring(actual.length - post)) + '<\/tt>');
    } else {
      htmlOut.push(
          '
' + html(lang) + '<\/a> OK<\/h2>');
    }
  }
  var summary = (failures ? (failures + ' test(s) failed') : 'Tests Passed');
  htmlOut.push('
' + summary + '<\/h2>');
  document.title += ' \u2014 ' + summary;
  document.getElementById('errorReport').innerHTML =
      htmlOut.join('').replace(/<br>/g, '<br>\n');
}

var startTime = null;
function startClock() {
  startTime = (new Date).getTime();
}

function stopClock() {
  var delta = (new Date).getTime() - startTime;
  startTime = null;
  document.getElementById('timing').innerHTML = 'Took ' + delta + ' ms';
}
<commit_msg>Made it more obvious when tests fail when I have a lot of tabs open<commit_after>// get accurate timing.
// This file must be loaded after prettify.js for this to work.
PR_SHOULD_USE_CONTINUATION = false;

var attribToHtml, textToHtml;

var getInnerHtml;

(function () {
  /** is the given node's innerHTML normally unescaped? */
  function isRawContent(node) {
    return 'XMP' === node.tagName;
  }

  var newlineRe = /[\r\n]/g;
  /**
   * Are newlines and adjacent spaces significant in the given node's innerHTML?
   */
  function isPreformatted(node, content) {
    // PRE means preformatted, and is a very common case, so don't create
    // unnecessary computed style objects.
    if ('PRE' === node.tagName) { return true; }
    if (!newlineRe.test(content)) { return true; }  // Don't care
    var whitespace = '';
    // For disconnected nodes, IE has no currentStyle.
    if (node.currentStyle) {
      whitespace = node.currentStyle.whiteSpace;
    } else if (window.getComputedStyle) {
      // Firefox makes a best guess if node is disconnected whereas Safari
      // returns the empty string.
      whitespace = window.getComputedStyle(node, null).whiteSpace;
    }
    return !whitespace || whitespace === 'pre';
  }

  // Define regexps here so that the interpreter doesn't have to create an
  // object each time the function containing them is called.
  // The language spec requires a new object created even if you don't access
  // the $1 members.
  var pr_amp = /&/g;
  var pr_lt = /</g;
  var pr_gt = />/g;
  var pr_quot = /\"/g;

  /** escapest html special characters to html. */
  textToHtml = function (str) {
    return str.replace(pr_amp, '&')
        .replace(pr_lt, '<')
        .replace(pr_gt, '>');
  };

  /** like textToHtml but escapes double quotes to be attribute safe. */
  attribToHtml = function (str) {
    return str.replace(pr_amp, '&')
        .replace(pr_lt, '<')
        .replace(pr_gt, '>')
        .replace(pr_quot, '"');
  };

  var PR_innerHtmlWorks = null;
  getInnerHtml = function (node) {
    // inner html is hopelessly broken in Safari 2.0.4 when the content is
    // an html description of well formed XML and the containing tag is a PRE
    // tag, so we detect that case and emulate innerHTML.
    if (null === PR_innerHtmlWorks) {
      var testNode = document.createElement('PRE');
      testNode.appendChild(
          document.createTextNode('<!DOCTYPE foo PUBLIC "foo bar">\n<foo />'));
      PR_innerHtmlWorks = !/</.test(testNode.innerHTML);
    }

    if (PR_innerHtmlWorks) {
      var content = node.innerHTML;
      // XMP tags contain unescaped entities so require special handling.
      if (isRawContent(node)) {
        content = textToHtml(content);
      } else if (!isPreformatted(node, content)) {
        content = content.replace(/(
)[\r\n]+/g, '$1')
            .replace(/(?:[\r\n]+[ \t]*)+/g, ' ');
      }
      return content;
    }

    var out = [];
    for (var child = node.firstChild; child; child = child.nextSibling) {
      normalizedHtml(child, out);
    }
    return out.join('');
  };
})();

function normalizedHtml(node, out, opt_sortAttrs) {
  switch (node.nodeType) {
    case 1:  // an element
      var name = node.tagName.toLowerCase();

      out.push('<', name);
      var attrs = node.attributes;
      var n = attrs.length;
      if (n) {
        if (opt_sortAttrs) {
          var sortedAttrs = [];
          for (var i = n; --i >= 0;) { sortedAttrs[i] = attrs[i]; }
          sortedAttrs.sort(function (a, b) {
              return (a.name < b.name) ? -1 : a.name === b.name ? 0 : 1;
            });
          attrs = sortedAttrs;
        }
        for (var i = 0; i < n; ++i) {
          var attr = attrs[i];
          if (!attr.specified) { continue; }
          out.push(' ', attr.name.toLowerCase(),
                   '="', attribToHtml(attr.value), '"');
        }
      }
      out.push('>');
      for (var child = node.firstChild; child; child = child.nextSibling) {
        normalizedHtml(child, out, opt_sortAttrs);
      }
      if (node.firstChild || !/^(?:br|link|img)$/.test(name)) {
        out.push('<\/', name, '>');
      }
      break;
    case 3: case 4: // text
      out.push(textToHtml(node.nodeValue));
      break;
  }
}

/**
 * @param golden a mapping from IDs of prettyprinted chunks to an abbreviated
 *     form of the expected output.  See "var goldens" in prettify_test.html
 *     for an example.
 */
function go(goldens) {
  startClock();
  prettyPrint(function () { stopClock(); runTests(goldens); });
}

function runTests(goldens) {
  /** number of characters in common at the end up to max. */
  function commonPrefix(a, b) {
    var n = Math.min(a.length, b.length);
    var i;
    for (i = 0; i < n; ++i) {
      if (a.charAt(i) !== b.charAt(i)) { break; }
    }
    return i;
  }

  /** number of characters in common at the end up to max. */
  function commonSuffix(a, b, max) {
    var n = Math.min(a.length - max, b.length - max);
    var i;
    for (i = 0; i < n; ++i) {
      if (a.charAt(a.length - i - 1) !== b.charAt(b.length - i - 1)) { break; }
    }
    return i;
  }

  /** convert a plain text string to html by escaping html special chars. */
  function html(plainText) {
    return attribToHtml(plainText).replace(/\xa0/g, ' ');
  }

  /**
   * get normalized markup.  innerHTML varies enough across browsers that we
   * can't use it.
   */
  function normalizedInnerHtml(node) {
    var out = [];
    for (var child = node.firstChild; child; child = child.nextSibling) {
      normalizedHtml(child, out, true);
    }
    out = out.join('');
    // more normalization to work around problems with non-ascii chars in
    // regexps in Safari
    for (var i = 0; (i = out.indexOf('\xa0')) >= 0;) {
      out = out.substring(0, i) + ' ' + out.substring(i + 1);
    }
    return out.replace(/\r\n?/g, '\n');
  }

  var htmlOut = [];
  var failures = 0;
  document.getElementById('errorReport').innerHTML =
      '
Running tests…<\/h1>';
  htmlOut.push('
Test results<\/h1>');
  for (var lang in goldens) {
    var container = document.getElementById(lang);
    // Convert abbreviations that start with `.
    var golden = goldens[lang].replace(/`([A-Z]{3})/g, function (_, lbl) {
        return (lbl == 'END'
            ? '<\/span>'
            : '');
      })
      // Line numbers
      .replace(/`#(?![0-9])/, '
')
      .replace(/`#([0-9])/g, '

');
    var actual = normalizedInnerHtml(container);
    if (golden !== actual) {  // test failed
      // write out
      var pre = commonPrefix(golden, actual);
      var post = commonSuffix(golden, actual, pre);

      ++failures;
      htmlOut.push(
          '
'
          + html(lang) + '<\/a> Failed<\/h2>');
      htmlOut.push(
          '<tt>' + html(golden.substring(0, pre)) +
          '»' +
          html(golden.substring(pre, golden.length - post)) +
          '<\/span>«' +
          html(golden.substring(golden.length - post)) +

          '
!==
' +

          html(actual.substring(0, pre)) +
          '»' +
          html(actual.substring(pre, actual.length - post)) +
          '<\/span>«' +
          html(actual.substring(actual.length - post)) + '<\/tt>');
    } else {
      htmlOut.push(
          '
' + html(lang) + '<\/a> OK<\/h2>');
    }
  }
  var summary = (
      failures
      ? (failures + ' test' + (failures === 1 ? '' : 's') + ' failed')
      : 'Tests Passed');
  var summaryStr = '
' + summary + '<\/h2>';
  htmlOut.push(summaryStr);
  htmlOut.splice(0, 0, summaryStr);
  document.title += ' \u2014 ' + summary;
  document.getElementById('errorReport').innerHTML =
      htmlOut.join('').replace(/<br>/g, '<br>\n');
}

var startTime = null;
function startClock() {
  startTime = (new Date).getTime();
}

function stopClock() {
  var delta = (new Date).getTime() - startTime;
  startTime = null;
  document.getElementById('timing').innerHTML = 'Took ' + delta + ' ms';
}
<|endoftext|>


<commit_before>(function () {
    'use strict';
    /*jslint nomen: true, browser: true*/
    /*global _,console,d3*/

    // Vertex: a process thread; Edge: the event of sending/recieving a tag
    function Vertex(pid, tid, time) {
        this.pid = pid;
        this.tid = tid;
        this.time = time;
    }

    function Node(vertex, children) {
        this.vertex = vertex;
        this.children = children;

        this.addChild = function addChild(childVertex) {
            var childNode = new Node(childVertex, []);
            children.push(childNode);
        };
    }

    function BucketNode(vertex, treeNum) {
        this.vertex = vertex;
        this.treeNum = treeNum;
    }

    var lifeline = [],
        util = {},
        treeLifeline = [],  // tree-like representation of the lifeline
        buckets = [],       // nodes per level
        timePeriod = 1;

    function loadLifeline() {
        util.ajaxget('/lifeline.json', function (response) {
            lifeline = JSON.parse(response);
        }, false);
    }

    // return tree, time
    // finding a parent - check all vertices on the level
    function findParent(vertex, bucket) {
        _.each(bucket, function (bNode) {
            if (vertex.pid === bNode.vertex.pid && vertex.tid === bNode.vertex.tid) {
                return bNode;
            }
        });
        return 0;
    }

    function addTreeChild(tree, curLevel, goalLevel, parentVertex, childVertex) {
        if (curLevel === goalLevel) {
            var vertex = tree.vertex;
            if (vertex.pid === parentVertex.pid && vertex.tid === parentVertex.tid && vertex.time === parentVertex.time) {
                tree.addChild(childVertex);
                return;
            }
        } else {
            _.each(tree.children, function (child) {
                addTreeChild(child, curLevel + 1, goalLevel, parentVertex, childVertex);
            });
        }
    }

    function addToBucket(level, vertex, treeNum) {
        var bucket = buckets[level] || [],
            bNode;
        if (bucket.length > 0) {
            _.each(bucket, function (bNode) {
                if (vertex.pid === bNode.vertex.pid && vertex.tid === bNode.vertex.tid) { // check if similar node is already in the bucket
                    bNode.treeNum = treeNum;
                    bNode.vertex.time = vertex.time;
                    return;
                }
            });
        } else {
            buckets[level] = [];
        }
        bNode = new BucketNode(vertex, treeNum);
        buckets[level].push(bNode);
    }

    function parseLifelineData() {
        _.each(lifeline, function (node) {
            var vert = new Vertex(node.dstProcessId, node.dstThreadId, node.time),
                parent = new Vertex(node.srcProcessId, node.srcThreadId, node.time),
                parentBucketNode,
                level = Math.floor(node.time / timePeriod),                           // actual tree level based on time
                tree,
                rootNode,
                bucket;

            // find parent process: look through each level down to roots
            while (level >= 0) {
                bucket = buckets[level] || [];
                parentBucketNode = findParent(parent, bucket);
                if (parentBucketNode) {
                    break;
                }
                level -= 1;
            }

            // have a parent, go to parent in identified tree and add the child -- update parent in bucket
            if (parentBucketNode) {
                tree = treeLifeline[parentBucketNode.treeNum];                 // a root Node
                addTreeChild(tree, 0, level, parentBucketNode.vertex, vert);
                addToBucket(level + 1, vert, parentBucketNode.treeNum);              // add child to the right bucket

                // do not have a parent in our current trees, create a new root == level 0
            } else {
                rootNode = new Node(parent, []);
                rootNode.addChild(vert);
                treeLifeline.push(rootNode);
                addToBucket(0, parent, treeLifeline.length - 1);
                addToBucket(1, vert, treeLifeline.length - 1);
            }
        });
    }

    function update(source, diagonal, tree, animationDuration, vis) {
        // Toggle children on click.
        function click(d) {
            if (d.children) {
                d._children = d.children;
                d.children = null;
            } else {
                d.children = d._children;
                d._children = null;
            }
            update(d, diagonal, tree, animationDuration, vis);
        }

        var nodes = tree.nodes(source).reverse(),
            nodeIdentifier,
            nodeEnter,
            link,
            node;

        // when we have more than 1 tree, needs more support in other parts of code
        /*for (var i=0; i<source.length; i++) {
            if (nodes) {
                _.each(tree.nodes(source[i]).reverse(), function(node) { nodes.push(node) });
            } else {
                nodes = tree.nodes(source[i]).reverse();
            }
        }*/

        // creates as many g.node as vertices in tree
        nodeIdentifier = 0;
        node = vis.selectAll("g.node")
            .data(nodes, function (d) {
                nodeIdentifier += 1;
                if (!d.id) {
                    d.id = nodeIdentifier;
                }
                return d.id;
            });

        // creating vertices in the tree (g with circle and text)
        nodeEnter = node.enter()
            .append("svg:g")
            .attr("class", "node");

        nodeEnter.append("svg:circle")
            .attr("r", 4.5)
            .style("fill", function (d) { return d._children ? "lightsteelblue" : "#fff"; })
            .on("click", click);

        nodeEnter.append("svg:text")
            .attr("x", -5)//function(d) { return d._children ? -8 : 8; }) --> used by original
            .attr("y", 18)
            .text(function (d) {
                return "process : " + d.pid + " thread : " + d.tid;
            });

        // Transition nodes to their new position (duration controls speed: higher == slower)
        nodeEnter.transition()
            .duration(animationDuration)
            .attr("transform", function (d) { return "translate(" + d.y + "," + d.x + ")"; })
            .style("opacity", 1)
            .select("circle")
            .style("fill", "lightsteelblue");

        node.transition()
            .duration(animationDuration)
            .attr("transform", function (d) { return "translate(" + d.y + "," + d.x + ")"; })
            .style("opacity", 1);

        node.exit().transition()
            .duration(animationDuration)
            .attr("transform", function () { return "translate(" + source.y + "," + source.x + ")"; })
            .style("opacity", 1e-6)
            .remove();

        // form the edges between the vertices
        link = vis.selectAll("path.link")
            .data(tree.links(nodes), function (d) { return d.target.id; });

        // Enter any new links at the parent's previous position.
        link.enter().insert("svg:path", "g")
            .attr("class", "link")
            .attr("d", function () {
                var o = {x: source.x0, y: source.y0};
                return diagonal({source: o, target: o});
            })
            .transition()
            .duration(animationDuration)
            .attr("d", diagonal);

        // Transition links to their new position.
        link.transition()
            .duration(animationDuration)
            .attr("d", diagonal);

        // Transition exiting nodes to the parent's new position.
        link.exit().transition()
            .duration(animationDuration)
            .attr("d", function () {
                var o = {x: source.x, y: source.y};
                return diagonal({source: o, target: o});
            })
            .remove();

        // Stash the old positions for transition.
        nodes.forEach(function (d) {
            d.x0 = d.x;
            d.y0 = d.y;
        });
    }

    function drawLifelineTree() {
        var w = 800,//960,  the width and height of the whole svg arrea
            h = 700,//2000;
            tree = d3.layout.tree().size([h, w - 160]),
            animationDuration = 500,

            diagonal = d3.svg.diagonal()
                .projection(function (d) { return [d.y, d.x]; }),

            // creates an SVG canvas
            vis = d3.select("#lifeline")
                .append("svg:svg")
                .attr("width", w)
                .attr("height", h)
                .append("svg:g")
                .attr("transform", "translate(20,0)");//moves the initial position of the svg:g element

        // want to draw from treeLifeline structure
        d3.json("tree_example.json", function (json) {
            json.x0 = 300;
            json.y0 = 0;
            /*for (var i=0; i<json.length; i++) {
                json[i].x0 = 300;//800;
                json[i].y0 = 0 + (i*200);
            }*/
            update(json, diagonal, tree, animationDuration, vis);

        });
    }

    function windowLoadHandler() {
        loadLifeline();
        parseLifelineData();
        drawLifelineTree();
    }

    window.addEventListener('load', windowLoadHandler, false);

    /* Utilities */

    util.ajaxget = function (url, callback, async) {
        var r = util.getAjaxRequest(callback);
        r.open("GET", url, async);
        r.send(null);
    };

    util.getAjaxRequest = function (callback) {
        var ajaxRequest;

        try {
            ajaxRequest = new XMLHttpRequest();
        } catch (e) {
            /*global ActiveXObject*/
            try {
                ajaxRequest = new ActiveXObject("Msxml2.XMLHTTP");
            } catch (e2) {
                try {
                    ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
                } catch (e3) {
                    return null;
                }
            }
        }

        ajaxRequest.onreadystatechange = function () {
            if (ajaxRequest.readyState === 4) {
                // TODO error response checking
                callback(ajaxRequest.responseText);
            }
        };

        return ajaxRequest;
    };

}());

<commit_msg>fix bug: clicking on a node collapses the whole tree<commit_after>(function () {
    'use strict';
    /*jslint nomen: true, browser: true*/
    /*global _,console,d3*/

    // Vertex: a process thread; Edge: the event of sending/recieving a tag
    function Vertex(pid, tid, time) {
        this.pid = pid;
        this.tid = tid;
        this.time = time;
    }

    function Node(vertex, children) {
        this.vertex = vertex;
        this.children = children;

        this.addChild = function addChild(childVertex) {
            var childNode = new Node(childVertex, []);
            children.push(childNode);
        };
    }

    function BucketNode(vertex, treeNum) {
        this.vertex = vertex;
        this.treeNum = treeNum;
    }

    var lifeline = [],
        util = {},
        treeLifeline = [],  // tree-like representation of the lifeline
        buckets = [],       // nodes per level
        timePeriod = 1;

    function loadLifeline() {
        util.ajaxget('/lifeline.json', function (response) {
            lifeline = JSON.parse(response);
        }, false);
    }

    // return tree, time
    // finding a parent - check all vertices on the level
    function findParent(vertex, bucket) {
        _.each(bucket, function (bNode) {
            if (vertex.pid === bNode.vertex.pid && vertex.tid === bNode.vertex.tid) {
                return bNode;
            }
        });
        return 0;
    }

    function addTreeChild(tree, curLevel, goalLevel, parentVertex, childVertex) {
        if (curLevel === goalLevel) {
            var vertex = tree.vertex;
            if (vertex.pid === parentVertex.pid && vertex.tid === parentVertex.tid && vertex.time === parentVertex.time) {
                tree.addChild(childVertex);
                return;
            }
        } else {
            _.each(tree.children, function (child) {
                addTreeChild(child, curLevel + 1, goalLevel, parentVertex, childVertex);
            });
        }
    }

    function addToBucket(level, vertex, treeNum) {
        var bucket = buckets[level] || [],
            bNode;
        if (bucket.length > 0) {
            _.each(bucket, function (bNode) {
                if (vertex.pid === bNode.vertex.pid && vertex.tid === bNode.vertex.tid) { // check if similar node is already in the bucket
                    bNode.treeNum = treeNum;
                    bNode.vertex.time = vertex.time;
                    return;
                }
            });
        } else {
            buckets[level] = [];
        }
        bNode = new BucketNode(vertex, treeNum);
        buckets[level].push(bNode);
    }

    function parseLifelineData() {
        _.each(lifeline, function (node) {
            var vert = new Vertex(node.dstProcessId, node.dstThreadId, node.time),
                parent = new Vertex(node.srcProcessId, node.srcThreadId, node.time),
                parentBucketNode,
                level = Math.floor(node.time / timePeriod),                           // actual tree level based on time
                tree,
                rootNode,
                bucket;

            // find parent process: look through each level down to roots
            while (level >= 0) {
                bucket = buckets[level] || [];
                parentBucketNode = findParent(parent, bucket);
                if (parentBucketNode) {
                    break;
                }
                level -= 1;
            }

            // have a parent, go to parent in identified tree and add the child -- update parent in bucket
            if (parentBucketNode) {
                tree = treeLifeline[parentBucketNode.treeNum];                 // a root Node
                addTreeChild(tree, 0, level, parentBucketNode.vertex, vert);
                addToBucket(level + 1, vert, parentBucketNode.treeNum);              // add child to the right bucket

                // do not have a parent in our current trees, create a new root == level 0
            } else {
                rootNode = new Node(parent, []);
                rootNode.addChild(vert);
                treeLifeline.push(rootNode);
                addToBucket(0, parent, treeLifeline.length - 1);
                addToBucket(1, vert, treeLifeline.length - 1);
            }
        });
    }

    /**
     * Updates the tree visualization
     * @param {Object} root - The root node of the tree
     * @param {Object} source - The source node of the update
    */
    function update(root, source, diagonal, tree, animationDuration, vis) {
        // Toggle children on click.
        function click(d) {
            if (d.children) {
                d._children = d.children;
                d.children = null;
            } else {
                d.children = d._children;
                d._children = null;
            }
            update(root, d, diagonal, tree, animationDuration, vis);
        }

        var nodes = tree.nodes(root).reverse(),
            nodeIdentifier,
            nodeEnter,
            link,
            node;

        // when we have more than 1 tree, needs more support in other parts of code
        /*for (var i=0; i<source.length; i++) {
            if (nodes) {
                _.each(tree.nodes(source[i]).reverse(), function(node) { nodes.push(node) });
            } else {
                nodes = tree.nodes(source[i]).reverse();
            }
        }*/

        // creates as many g.node as vertices in tree
        nodeIdentifier = 0;
        node = vis.selectAll("g.node")
            .data(nodes, function (d) {
                nodeIdentifier += 1;
                if (!d.id) {
                    d.id = nodeIdentifier;
                }
                return d.id;
            });

        // creating vertices in the tree (g with circle and text)
        nodeEnter = node.enter()
            .append("svg:g")
            .attr("class", "node");

        nodeEnter.append("svg:circle")
            .attr("r", 4.5)
            .style("fill", function (d) { return d._children ? "lightsteelblue" : "#fff"; })
            .on("click", click);

        nodeEnter.append("svg:text")
            .attr("x", -5)//function(d) { return d._children ? -8 : 8; }) --> used by original
            .attr("y", 18)
            .text(function (d) {
                return "process : " + d.pid + " thread : " + d.tid;
            });

        // Transition nodes to their new position (duration controls speed: higher == slower)
        nodeEnter.transition()
            .duration(animationDuration)
            .attr("transform", function (d) { return "translate(" + d.y + "," + d.x + ")"; })
            .style("opacity", 1)
            .select("circle")
            .style("fill", "lightsteelblue");

        node.transition()
            .duration(animationDuration)
            .attr("transform", function (d) { return "translate(" + d.y + "," + d.x + ")"; })
            .style("opacity", 1);

        node.exit().transition()
            .duration(animationDuration)
            .attr("transform", function () { return "translate(" + source.y + "," + source.x + ")"; })
            .style("opacity", 1e-6)
            .remove();

        // form the edges between the vertices
        link = vis.selectAll("path.link")
            .data(tree.links(nodes), function (d) { return d.target.id; });

        // Enter any new links at the parent's previous position.
        link.enter().insert("svg:path", "g")
            .attr("class", "link")
            .attr("d", function () {
                var o = {x: source.x0, y: source.y0};
                return diagonal({source: o, target: o});
            })
            .transition()
            .duration(animationDuration)
            .attr("d", diagonal);

        // Transition links to their new position.
        link.transition()
            .duration(animationDuration)
            .attr("d", diagonal);

        // Transition exiting nodes to the parent's new position.
        link.exit().transition()
            .duration(animationDuration)
            .attr("d", function () {
                var o = {x: source.x, y: source.y};
                return diagonal({source: o, target: o});
            })
            .remove();

        // Stash the old positions for transition.
        nodes.forEach(function (d) {
            d.x0 = d.x;
            d.y0 = d.y;
        });
    }

    function drawLifelineTree() {
        var w = 800,//960,  the width and height of the whole svg arrea
            h = 700,//2000;
            tree = d3.layout.tree().size([h, w - 160]),
            animationDuration = 500,

            diagonal = d3.svg.diagonal()
                .projection(function (d) { return [d.y, d.x]; }),

            // creates an SVG canvas
            vis = d3.select("#lifeline")
                .append("svg:svg")
                .attr("width", w)
                .attr("height", h)
                .append("svg:g")
                .attr("transform", "translate(20,0)");//moves the initial position of the svg:g element

        // want to draw from treeLifeline structure
        d3.json("tree_example.json", function (json) {
            json.x0 = 300;
            json.y0 = 0;
            /*for (var i=0; i<json.length; i++) {
                json[i].x0 = 300;//800;
                json[i].y0 = 0 + (i*200);
            }*/
            update(json, json, diagonal, tree, animationDuration, vis);
        });
    }

    function windowLoadHandler() {
        loadLifeline();
        parseLifelineData();
        drawLifelineTree();
    }

    window.addEventListener('load', windowLoadHandler, false);

    /* Utilities */

    util.ajaxget = function (url, callback, async) {
        var r = util.getAjaxRequest(callback);
        r.open("GET", url, async);
        r.send(null);
    };

    util.getAjaxRequest = function (callback) {
        var ajaxRequest;

        try {
            ajaxRequest = new XMLHttpRequest();
        } catch (e) {
            /*global ActiveXObject*/
            try {
                ajaxRequest = new ActiveXObject("Msxml2.XMLHTTP");
            } catch (e2) {
                try {
                    ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
                } catch (e3) {
                    return null;
                }
            }
        }

        ajaxRequest.onreadystatechange = function () {
            if (ajaxRequest.readyState === 4) {
                // TODO error response checking
                callback(ajaxRequest.responseText);
            }
        };

        return ajaxRequest;
    };

}());

<|endoftext|>