<commit_before>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * All rights reserved.
 */

/**
 * @name Raster
 *
 * @class The Raster item represents an image in a Paper.js project.
 *
 * @extends PlacedItem
 */
var Raster = this.Raster = PlacedItem.extend(/** @lends Raster# */{
	// Raster doesn't make the distinction between the different bounds,
	// so use the same name for all of them
	_boundsType: 'bounds',

	// TODO: Implement url / type, width, height.
	// TODO: Have PlacedSymbol & Raster inherit from a shared class?
	// DOCS: Document Raster constructor.
	/**
	 * Creates a new raster item and places it in the active layer.
	 *
	 * @param {HTMLImageElement|Canvas|string} [object]
	 */
	initialize: function(object, pointOrMatrix) {
		this.base(pointOrMatrix);
		if (object.getContext) {
			this.setCanvas(object);
		} else {
/*#*/ if (options.browser) {
			// If it's a string, get the element with this id first.
			if (typeof object === 'string')
				object = document.getElementById(object);
/*#*/ } else if (options.server) {
			// If we're running on the server and it's a string,
			// load it from disk:
			if (typeof object === 'string') {
				// TODO: load images async
				var data = fs.readFileSync(object);
				object = new Image();
				object.src = data;
			}
/*#*/ } // options.server
			this.setImage(object);
		}
	},

	clone: function() {
		var image = this._image;
		if (!image) {
			// If the Raster contains a Canvas object, we need to create
			// a new one and draw this raster's canvas on it.
			image = CanvasProvider.getCanvas(this._size);
			image.getContext('2d').drawImage(this._canvas, 0, 0);
		}
		var copy = new Raster(image);
		return this._clone(copy);
	},

	/**
	 * The size of the raster in pixels.
	 *
	 * @type Size
	 * @bean
	 */
	getSize: function() {
		return this._size;
	},

	setSize: function() {
		var size = Size.read(arguments),
			// Get reference to image before changing canvas
			image = this.getImage();
		// Setting canvas internally sets _size
		this.setCanvas(CanvasProvider.getCanvas(size));
		// Draw image back onto new canvas
		this.getContext(true).drawImage(image, 0, 0, size.width, size.height);
	},

	/**
	 * The width of the raster in pixels.
	 *
	 * @type Number
	 * @bean
	 */
	getWidth: function() {
		return this._size.width;
	},

	/**
	 * The height of the raster in pixels.
	 *
	 * @type Number
	 * @bean
	 */
	getHeight: function() {
		return this._size.height;
	},

	/**
	 * Pixels per inch of the raster at its current size.
	 *
	 * @type Size
	 * @bean
	 */
	getPpi: function() {
		var matrix = this._matrix,
			orig = new Point(0, 0).transform(matrix),
			u = new Point(1, 0).transform(matrix).subtract(orig),
			v = new Point(0, 1).transform(matrix).subtract(orig);
		return Size.create(
			72 / u.getLength(),
			72 / v.getLength()
		);
	},

	/**
	 * The Canvas 2d drawing context of the raster.
	 *
	 * @type Context
	 * @bean
	 */
	getContext: function(/* notifyChange */) {
		if (!this._context)
			this._context = this.getCanvas().getContext('2d');
		// Support a hidden parameter that indicates if the context will be used
		// to modify the Raster object. We can notify such changes ahead since
		// they are only used afterwards for redrawing.
		if (arguments[0])
			this._changed(Change.PIXELS);
		return this._context;
	},

	setContext: function(context) {
		this._context = context;
	},

	getCanvas: function() {
		if (!this._canvas) {
			this._canvas = CanvasProvider.getCanvas(this._size);
			if (this._image)
				this.getContext(true).drawImage(this._image, 0, 0);
		}
		return this._canvas;
	},

	setCanvas: function(canvas) {
		if (this._canvas)
			CanvasProvider.returnCanvas(this._canvas);
		this._canvas = canvas;
		this._size = Size.create(canvas.width, canvas.height);
		this._image = null;
		this._context = null;
		this._changed(Change.GEOMETRY | Change.PIXELS);
	},

	/**
	 * The HTMLImageElement or Canvas of the raster.
	 *
	 * @type HTMLImageElement|Canvas
	 * @bean
	 */
	getImage: function() {
		return this._image || this.getCanvas();
	},

	// TODO: Support string id of image element.
	setImage: function(image) {
		if (this._canvas)
			CanvasProvider.returnCanvas(this._canvas);
		this._image = image;
/*#*/ if (options.browser) {
		this._size = Size.create(image.naturalWidth, image.naturalHeight);
/*#*/ } else if (options.server) {
		this._size = Size.create(image.width, image.height);
/*#*/ } // options.server
		this._canvas = null;
		this._context = null;
		this._changed(Change.GEOMETRY);
	},

	// DOCS: document Raster#getSubImage
	/**
	 * @param {Rectangle} rect the boundaries of the sub image in pixel
	 * coordinates
	 *
	 * @return {Canvas}
	 */
	getSubImage: function(rect) {
		rect = Rectangle.read(arguments);
		var canvas = CanvasProvider.getCanvas(rect.getSize());
		canvas.getContext('2d').drawImage(this.getCanvas(), rect.x, rect.y,
				canvas.width, canvas.height, 0, 0, canvas.width, canvas.height);
		return canvas;
	},

	/**
	 * Draws an image on the raster.
	 *
	 * @param {HTMLImageELement|Canvas} image
	 * @param {Point} point the offset of the image as a point in pixel
	 * coordinates
	 */
	drawImage: function(image, point) {
		point = Point.read(arguments, 1);
		this.getContext(true).drawImage(image, point.x, point.y);
	},

	/**
	 * Calculates the average color of the image within the given path,
	 * rectangle or point. This can be used for creating raster image
	 * effects.
	 *
	 * @param {Path|Rectangle|Point} object
	 * @return {RgbColor} the average color contained in the area covered by the
	 * specified path, rectangle or point.
	 */
	getAverageColor: function(object) {
		if (!object)
			object = this.getBounds();
		var bounds, path;
		if (object instanceof PathItem) {
			// TODO: What if the path is smaller than 1 px?
			// TODO: How about rounding of bounds.size?
			path = object;
			bounds = object.getBounds();
		} else if (object.width) {
			bounds = new Rectangle(object);
		} else if (object.x) {
			// Create a rectangle of 1px size around the specified coordinates
			bounds = Rectangle.create(object.x - 0.5, object.y - 0.5, 1, 1);
		}
		// Use a sample size of max 32 x 32 pixels, into which the path is
		// scaled as a clipping path, and then the actual image is drawn in and
		// sampled.
		var sampleSize = 32,
			width = Math.min(bounds.width, sampleSize),
			height = Math.min(bounds.height, sampleSize);
		// Reuse the same sample context for speed. Memory consumption is low
		// since it's only 32 x 32 pixels.
		var ctx = Raster._sampleContext;
		if (!ctx) {
			ctx = Raster._sampleContext = CanvasProvider.getCanvas(
					sampleSize.clone()).getContext('2d');
		} else {
			// Clear the sample canvas:
			ctx.clearRect(0, 0, sampleSize, sampleSize);
		}
		ctx.save();
		// Scale the context so that the bounds ends up at the given sample size
		ctx.scale(width / bounds.width, height / bounds.height);
		ctx.translate(-bounds.x, -bounds.y);
		// If a path was passed, draw it as a clipping mask:
		if (path)
			path.draw(ctx, { clip: true });
		// Now draw the image clipped into it.
		this._matrix.applyToContext(ctx);
		ctx.drawImage(this._canvas || this._image,
				-this._size.width / 2, -this._size.height / 2);
		ctx.restore();
		// Get pixel data from the context and calculate the average color value
		// from it, taking alpha into account.
		var pixels = ctx.getImageData(0.5, 0.5, Math.ceil(width),
				Math.ceil(height)).data,
			channels = [0, 0, 0],
			total = 0;
		for (var i = 0, l = pixels.length; i < l; i += 4) {
			var alpha = pixels[i + 3];
			total += alpha;
			alpha /= 255;
			channels[0] += pixels[i] * alpha;
			channels[1] += pixels[i + 1] * alpha;
			channels[2] += pixels[i + 2] * alpha;
		}
		for (var i = 0; i < 3; i++)
			channels[i] /= total;
		return total ? Color.read(channels) : null;
	},

	/**
	 * {@grouptitle Pixels}
	 * Gets the color of a pixel in the raster.
	 *
	 * @name Raster#getPixel
	 * @function
	 * @param x the x offset of the pixel in pixel coordinates
	 * @param y the y offset of the pixel in pixel coordinates
	 * @return {RgbColor} the color of the pixel
	 */
	/**
	 * Gets the color of a pixel in the raster.
	 *
	 * @name Raster#getPixel
	 * @function
	 * @param point the offset of the pixel as a point in pixel coordinates
	 * @return {RgbColor} the color of the pixel
	 */
	getPixel: function(point) {
		point = Point.read(arguments);
		var pixels = this.getContext().getImageData(point.x, point.y, 1, 1).data,
			channels = new Array(4);
		for (var i = 0; i < 4; i++)
			channels[i] = pixels[i] / 255;
		return RgbColor.read(channels);
	},

	/**
	 * Sets the color of the specified pixel to the specified color.
	 *
	 * @name Raster#setPixel
	 * @function
	 * @param x the x offset of the pixel in pixel coordinates
	 * @param y the y offset of the pixel in pixel coordinates
	 * @param color the color that the pixel will be set to
	 */
	/**
	 * Sets the color of the specified pixel to the specified color.
	 *
	 * @name Raster#setPixel
	 * @function
	 * @param point the offset of the pixel as a point in pixel coordinates
	 * @param color the color that the pixel will be set to
	 */
	setPixel: function(point, color) {
		var hasPoint = arguments.length == 2;
		point = Point.read(arguments, 0, hasPoint ? 1 : 2);
		color = Color.read(arguments, hasPoint ? 1 : 2);
		var ctx = this.getContext(true),
			imageData = ctx.createImageData(1, 1),
			alpha = color.getAlpha();
		imageData.data[0] = color.getRed() * 255;
		imageData.data[1] = color.getGreen() * 255;
		imageData.data[2] = color.getBlue() * 255;
		imageData.data[3] = alpha != null ? alpha * 255 : 255;
		ctx.putImageData(imageData, point.x, point.y);
	},

	// DOCS: document Raster#createData
	/**
	 * {@grouptitle Image Data}
	 * @param {Size} size
	 * @return {ImageData}
	 */
	createData: function(size) {
		size = Size.read(arguments);
		return this.getContext().createImageData(size.width, size.height);
	},

	// TODO: Rename to #get/setImageData, as it will conflict with Item#getData
	// DOCS: document Raster#getData
	/**
	 * @param {Rectangle} rect
	 * @return {ImageData}
	 */
	getData: function(rect) {
		rect = Rectangle.read(arguments);
		if (rect.isEmpty())
			rect = new Rectangle(this.getSize());
		return this.getContext().getImageData(rect.x, rect.y,
				rect.width, rect.height);
	},

	// DOCS: document Raster#setData
	/**
	 * @param {ImageData} data
	 * @param {Point} point
	 * @return {ImageData}
	 */
	setData: function(data, point) {
		point = Point.read(arguments, 1);
		this.getContext(true).putImageData(data, point.x, point.y);
	},

	_getBounds: function(type, matrix) {
		var rect = new Rectangle(this._size).setCenter(0, 0);
		return matrix ? matrix._transformBounds(rect) : rect;
	},

	_hitTest: function(point, options) {
		if (point.isInside(this._getBounds())) {
			var that = this;
			return new HitResult('pixel', that, {
				offset: point.add(that._size.divide(2)).round(),
				// Becomes HitResult#color
				getColor: function() {
					return that.getPixel(this.offset);
				}
			});
		}
	},

	draw: function(ctx, param) {
		ctx.drawImage(this._canvas || this._image,
				-this._size.width / 2, -this._size.height / 2);
	},

	drawSelected: function(ctx, matrix) {
		Item.drawSelectedBounds(new Rectangle(this._size).setCenter(0, 0), ctx,
				matrix);
	}
});
<commit_msg>Fix bug in Raster#getAverageColor().<commit_after>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * All rights reserved.
 */

/**
 * @name Raster
 *
 * @class The Raster item represents an image in a Paper.js project.
 *
 * @extends PlacedItem
 */
var Raster = this.Raster = PlacedItem.extend(/** @lends Raster# */{
	// Raster doesn't make the distinction between the different bounds,
	// so use the same name for all of them
	_boundsType: 'bounds',

	// TODO: Implement url / type, width, height.
	// TODO: Have PlacedSymbol & Raster inherit from a shared class?
	// DOCS: Document Raster constructor.
	/**
	 * Creates a new raster item and places it in the active layer.
	 *
	 * @param {HTMLImageElement|Canvas|string} [object]
	 */
	initialize: function(object, pointOrMatrix) {
		this.base(pointOrMatrix);
		if (object.getContext) {
			this.setCanvas(object);
		} else {
/*#*/ if (options.browser) {
			// If it's a string, get the element with this id first.
			if (typeof object === 'string')
				object = document.getElementById(object);
/*#*/ } else if (options.server) {
			// If we're running on the server and it's a string,
			// load it from disk:
			if (typeof object === 'string') {
				// TODO: load images async
				var data = fs.readFileSync(object);
				object = new Image();
				object.src = data;
			}
/*#*/ } // options.server
			this.setImage(object);
		}
	},

	clone: function() {
		var image = this._image;
		if (!image) {
			// If the Raster contains a Canvas object, we need to create
			// a new one and draw this raster's canvas on it.
			image = CanvasProvider.getCanvas(this._size);
			image.getContext('2d').drawImage(this._canvas, 0, 0);
		}
		var copy = new Raster(image);
		return this._clone(copy);
	},

	/**
	 * The size of the raster in pixels.
	 *
	 * @type Size
	 * @bean
	 */
	getSize: function() {
		return this._size;
	},

	setSize: function() {
		var size = Size.read(arguments),
			// Get reference to image before changing canvas
			image = this.getImage();
		// Setting canvas internally sets _size
		this.setCanvas(CanvasProvider.getCanvas(size));
		// Draw image back onto new canvas
		this.getContext(true).drawImage(image, 0, 0, size.width, size.height);
	},

	/**
	 * The width of the raster in pixels.
	 *
	 * @type Number
	 * @bean
	 */
	getWidth: function() {
		return this._size.width;
	},

	/**
	 * The height of the raster in pixels.
	 *
	 * @type Number
	 * @bean
	 */
	getHeight: function() {
		return this._size.height;
	},

	/**
	 * Pixels per inch of the raster at its current size.
	 *
	 * @type Size
	 * @bean
	 */
	getPpi: function() {
		var matrix = this._matrix,
			orig = new Point(0, 0).transform(matrix),
			u = new Point(1, 0).transform(matrix).subtract(orig),
			v = new Point(0, 1).transform(matrix).subtract(orig);
		return Size.create(
			72 / u.getLength(),
			72 / v.getLength()
		);
	},

	/**
	 * The Canvas 2d drawing context of the raster.
	 *
	 * @type Context
	 * @bean
	 */
	getContext: function(/* notifyChange */) {
		if (!this._context)
			this._context = this.getCanvas().getContext('2d');
		// Support a hidden parameter that indicates if the context will be used
		// to modify the Raster object. We can notify such changes ahead since
		// they are only used afterwards for redrawing.
		if (arguments[0])
			this._changed(Change.PIXELS);
		return this._context;
	},

	setContext: function(context) {
		this._context = context;
	},

	getCanvas: function() {
		if (!this._canvas) {
			this._canvas = CanvasProvider.getCanvas(this._size);
			if (this._image)
				this.getContext(true).drawImage(this._image, 0, 0);
		}
		return this._canvas;
	},

	setCanvas: function(canvas) {
		if (this._canvas)
			CanvasProvider.returnCanvas(this._canvas);
		this._canvas = canvas;
		this._size = Size.create(canvas.width, canvas.height);
		this._image = null;
		this._context = null;
		this._changed(Change.GEOMETRY | Change.PIXELS);
	},

	/**
	 * The HTMLImageElement or Canvas of the raster.
	 *
	 * @type HTMLImageElement|Canvas
	 * @bean
	 */
	getImage: function() {
		return this._image || this.getCanvas();
	},

	// TODO: Support string id of image element.
	setImage: function(image) {
		if (this._canvas)
			CanvasProvider.returnCanvas(this._canvas);
		this._image = image;
/*#*/ if (options.browser) {
		this._size = Size.create(image.naturalWidth, image.naturalHeight);
/*#*/ } else if (options.server) {
		this._size = Size.create(image.width, image.height);
/*#*/ } // options.server
		this._canvas = null;
		this._context = null;
		this._changed(Change.GEOMETRY);
	},

	// DOCS: document Raster#getSubImage
	/**
	 * @param {Rectangle} rect the boundaries of the sub image in pixel
	 * coordinates
	 *
	 * @return {Canvas}
	 */
	getSubImage: function(rect) {
		rect = Rectangle.read(arguments);
		var canvas = CanvasProvider.getCanvas(rect.getSize());
		canvas.getContext('2d').drawImage(this.getCanvas(), rect.x, rect.y,
				canvas.width, canvas.height, 0, 0, canvas.width, canvas.height);
		return canvas;
	},

	/**
	 * Draws an image on the raster.
	 *
	 * @param {HTMLImageELement|Canvas} image
	 * @param {Point} point the offset of the image as a point in pixel
	 * coordinates
	 */
	drawImage: function(image, point) {
		point = Point.read(arguments, 1);
		this.getContext(true).drawImage(image, point.x, point.y);
	},

	/**
	 * Calculates the average color of the image within the given path,
	 * rectangle or point. This can be used for creating raster image
	 * effects.
	 *
	 * @param {Path|Rectangle|Point} object
	 * @return {RgbColor} the average color contained in the area covered by the
	 * specified path, rectangle or point.
	 */
	getAverageColor: function(object) {
		if (!object)
			object = this.getBounds();
		var bounds, path;
		if (object instanceof PathItem) {
			// TODO: What if the path is smaller than 1 px?
			// TODO: How about rounding of bounds.size?
			path = object;
			bounds = object.getBounds();
		} else if (object.width) {
			bounds = new Rectangle(object);
		} else if (object.x) {
			// Create a rectangle of 1px size around the specified coordinates
			bounds = Rectangle.create(object.x - 0.5, object.y - 0.5, 1, 1);
		}
		// Use a sample size of max 32 x 32 pixels, into which the path is
		// scaled as a clipping path, and then the actual image is drawn in and
		// sampled.
		var sampleSize = 32,
			width = Math.min(bounds.width, sampleSize),
			height = Math.min(bounds.height, sampleSize);
		// Reuse the same sample context for speed. Memory consumption is low
		// since it's only 32 x 32 pixels.
		var ctx = Raster._sampleContext;
		if (!ctx) {
			ctx = Raster._sampleContext = CanvasProvider.getCanvas(
					new Size(sampleSize)).getContext('2d');
		} else {
			// Clear the sample canvas:
			ctx.clearRect(0, 0, sampleSize, sampleSize);
		}
		ctx.save();
		// Scale the context so that the bounds ends up at the given sample size
		ctx.scale(width / bounds.width, height / bounds.height);
		ctx.translate(-bounds.x, -bounds.y);
		// If a path was passed, draw it as a clipping mask:
		if (path)
			path.draw(ctx, { clip: true });
		// Now draw the image clipped into it.
		this._matrix.applyToContext(ctx);
		ctx.drawImage(this._canvas || this._image,
				-this._size.width / 2, -this._size.height / 2);
		ctx.restore();
		// Get pixel data from the context and calculate the average color value
		// from it, taking alpha into account.
		var pixels = ctx.getImageData(0.5, 0.5, Math.ceil(width),
				Math.ceil(height)).data,
			channels = [0, 0, 0],
			total = 0;
		for (var i = 0, l = pixels.length; i < l; i += 4) {
			var alpha = pixels[i + 3];
			total += alpha;
			alpha /= 255;
			channels[0] += pixels[i] * alpha;
			channels[1] += pixels[i + 1] * alpha;
			channels[2] += pixels[i + 2] * alpha;
		}
		for (var i = 0; i < 3; i++)
			channels[i] /= total;
		return total ? Color.read(channels) : null;
	},

	/**
	 * {@grouptitle Pixels}
	 * Gets the color of a pixel in the raster.
	 *
	 * @name Raster#getPixel
	 * @function
	 * @param x the x offset of the pixel in pixel coordinates
	 * @param y the y offset of the pixel in pixel coordinates
	 * @return {RgbColor} the color of the pixel
	 */
	/**
	 * Gets the color of a pixel in the raster.
	 *
	 * @name Raster#getPixel
	 * @function
	 * @param point the offset of the pixel as a point in pixel coordinates
	 * @return {RgbColor} the color of the pixel
	 */
	getPixel: function(point) {
		point = Point.read(arguments);
		var pixels = this.getContext().getImageData(point.x, point.y, 1, 1).data,
			channels = new Array(4);
		for (var i = 0; i < 4; i++)
			channels[i] = pixels[i] / 255;
		return RgbColor.read(channels);
	},

	/**
	 * Sets the color of the specified pixel to the specified color.
	 *
	 * @name Raster#setPixel
	 * @function
	 * @param x the x offset of the pixel in pixel coordinates
	 * @param y the y offset of the pixel in pixel coordinates
	 * @param color the color that the pixel will be set to
	 */
	/**
	 * Sets the color of the specified pixel to the specified color.
	 *
	 * @name Raster#setPixel
	 * @function
	 * @param point the offset of the pixel as a point in pixel coordinates
	 * @param color the color that the pixel will be set to
	 */
	setPixel: function(point, color) {
		var hasPoint = arguments.length == 2;
		point = Point.read(arguments, 0, hasPoint ? 1 : 2);
		color = Color.read(arguments, hasPoint ? 1 : 2);
		var ctx = this.getContext(true),
			imageData = ctx.createImageData(1, 1),
			alpha = color.getAlpha();
		imageData.data[0] = color.getRed() * 255;
		imageData.data[1] = color.getGreen() * 255;
		imageData.data[2] = color.getBlue() * 255;
		imageData.data[3] = alpha != null ? alpha * 255 : 255;
		ctx.putImageData(imageData, point.x, point.y);
	},

	// DOCS: document Raster#createData
	/**
	 * {@grouptitle Image Data}
	 * @param {Size} size
	 * @return {ImageData}
	 */
	createData: function(size) {
		size = Size.read(arguments);
		return this.getContext().createImageData(size.width, size.height);
	},

	// TODO: Rename to #get/setImageData, as it will conflict with Item#getData
	// DOCS: document Raster#getData
	/**
	 * @param {Rectangle} rect
	 * @return {ImageData}
	 */
	getData: function(rect) {
		rect = Rectangle.read(arguments);
		if (rect.isEmpty())
			rect = new Rectangle(this.getSize());
		return this.getContext().getImageData(rect.x, rect.y,
				rect.width, rect.height);
	},

	// DOCS: document Raster#setData
	/**
	 * @param {ImageData} data
	 * @param {Point} point
	 * @return {ImageData}
	 */
	setData: function(data, point) {
		point = Point.read(arguments, 1);
		this.getContext(true).putImageData(data, point.x, point.y);
	},

	_getBounds: function(type, matrix) {
		var rect = new Rectangle(this._size).setCenter(0, 0);
		return matrix ? matrix._transformBounds(rect) : rect;
	},

	_hitTest: function(point, options) {
		if (point.isInside(this._getBounds())) {
			var that = this;
			return new HitResult('pixel', that, {
				offset: point.add(that._size.divide(2)).round(),
				// Becomes HitResult#color
				getColor: function() {
					return that.getPixel(this.offset);
				}
			});
		}
	},

	draw: function(ctx, param) {
		ctx.drawImage(this._canvas || this._image,
				-this._size.width / 2, -this._size.height / 2);
	},

	drawSelected: function(ctx, matrix) {
		Item.drawSelectedBounds(new Rectangle(this._size).setCenter(0, 0), ctx,
				matrix);
	}
});
<|endoftext|>


<commit_before>/**
 * jquery.combostars.js: Turn select widgets into star rating widgets.
 *
 * Copyright (c) 2015 John Parsons.
 *
 * This software may be modified and distributed under the terms of the MIT license. See the LICENSE file for details.
 */

$(function () {

	/**
	 * Determines the needed background position for a star with the specified height and fill state.
	 *
	 * @param starHeight the height of a star, in px.
	 * @param isFilled whether or not the star image is filled in.
	 */
	var getBackgroundPosition = function (starHeight, isFilled) {
		return '0 ' + (isFilled ? '0' : '-' + (starHeight) + 'px');
	};

	$.fn.combostars = function (config) {
		'use strict';

		var options = {
			starUrl: '../src/img/stars.png',
			starWidth: 16,
			starHeight: 15
		};

		$.extend(options, config);

		var numStars = this.children().length, // Number of stars = number of options in the combo box
			wrapper = $('<span />').addClass('combostar-wrapper'), // Wrapper for the stars
			i, // Counter variable
			newStar, // Newly created star
			select = this; // The select that combostars() is called on

		/** Handles a click on a star. Updates the value of the combo box and sets view state. */
		var starClickHandler = function () {
			var clickedStar = $(this).data('star-index'),
				newValue = select.find('option:nth-child(' + clickedStar + ')').attr('value');

			select.val(newValue);
			select.change();

			// Update star backgrounds
			wrapper.find('.combostar-star').each(function () {
				$(this).css('background-position', getBackgroundPosition(options.starHeight, $(this).data('star-index') <= clickedStar));
			});
		};

		wrapper = this.wrap(wrapper).parent(); // Wrap the widget with a star container
		this.hide(); // Hide the combobox so we can show the star widget

		// Add all the stars to the container
		for (i = 1; i <= numStars; i++) {
			newStar = $('<div />').css({
				display: 'inline',
				'padding-left' : options.starWidth + 'px', // Use padding to set the width and height
				'padding-top' : options.starHeight + 'px',
				'padding-right': 0,
				'padding-bottom' : 0,
				'background-image' : 'url(' + options.starUrl + ')', // Image to display as the star
				'background-position' : getBackgroundPosition(options.starHeight, false),
				'font-size' : 0 // Setting font size to 0 allows us to set the height exclusively with padding
			}).addClass('combostar-star')
			.data('star-index', i)
			.on('click', starClickHandler);

			wrapper.append(newStar);
		}
	};
});<commit_msg>Make plugin work when called on multiple selects<commit_after>/**
 * jquery.combostars.js: Turn select widgets into star rating widgets.
 *
 * Copyright (c) 2015 John Parsons.
 *
 * This software may be modified and distributed under the terms of the MIT license. See the LICENSE file for details.
 */

$(function () {

	/**
	 * Determines the needed background position for a star with the specified height and fill state.
	 *
	 * @param starHeight the height of a star, in px.
	 * @param isFilled whether or not the star image is filled in.
	 */
	var getBackgroundPosition = function (starHeight, isFilled) {
		return '0 ' + (isFilled ? '0' : '-' + (starHeight) + 'px');
	};

	$.fn.combostars = function (config) {
		'use strict';

		// Deal with multiple selects by recursively applying combostars to each one
		if (this.length > 1) {
			this.each(function () {
				$(this).combostars(config);
			});
			return this;
		}

		var options = {
			starUrl: '../src/img/stars.png',
			starWidth: 16,
			starHeight: 15
		};

		$.extend(options, config);

		var numStars = this.children().length, // Number of stars = number of options in the combo box
			wrapper = $('<span />').addClass('combostar-wrapper'), // Wrapper for the stars
			i, // Counter variable
			newStar, // Newly created star
			select = this; // The select that combostars() is called on

		/** Handles a click on a star. Updates the value of the combo box and sets view state. */
		var starClickHandler = function () {
			var clickedStar = $(this).data('star-index'),
				newValue = select.find('option:nth-child(' + clickedStar + ')').attr('value');

			select.val(newValue);
			select.change();

			// Update star backgrounds
			wrapper.find('.combostar-star').each(function () {
				$(this).css('background-position', getBackgroundPosition(options.starHeight, $(this).data('star-index') <= clickedStar));
			});
		};

		wrapper = this.wrap(wrapper).parent(); // Wrap the widget with a star container
		this.hide(); // Hide the combobox so we can show the star widget

		// Add all the stars to the container
		for (i = 1; i <= numStars; i++) {
			newStar = $('<div />').css({
				display: 'inline',
				'padding-left' : options.starWidth + 'px', // Use padding to set the width and height
				'padding-top' : options.starHeight + 'px',
				'padding-right': 0,
				'padding-bottom' : 0,
				'background-image' : 'url(' + options.starUrl + ')', // Image to display as the star
				'background-position' : getBackgroundPosition(options.starHeight, false),
				'font-size' : 0 // Setting font size to 0 allows us to set the height exclusively with padding
			}).addClass('combostar-star')
			.data('star-index', i)
			.on('click', starClickHandler);

			wrapper.append(newStar);
		}

		return this;
	};
});<|endoftext|>


<commit_before>'use strict';

var angular = require('angular');

var app = angular.module('formulate');

/**
 * @description Generate unique form names
 */
var genFormName = (function () {
    var counter = 0;

    return function () {
        counter += 1;

        return 'form' + counter;
    };
}());

function FormulateController($scope, $element, $http, $q) {
    var self = this;

    // Set reference to injected object to be used in prototype functions
    this.injected = {
        $q: $q,
        $http: $http,
        $scope: $scope,
        $element: $element
    };

    // set unique form name
    this.formName = genFormName();

    // Map Fields for faster access
    this.fieldMap = {};
    this.formData.fields.forEach(function (field) {
        self.fieldMap[field.id] = field;
    });

    // ng-model of fields
    this.fieldModels = {};
}

FormulateController.prototype.getFieldById = function (id) {
    return this.fieldMap[id];
};
FormulateController.prototype.submit = function () {
    var self = this;
    var formCtrl = this
        .injected
        .$element
        .find('form')
        .controller('form');

    function parseResponse(response) {
        var deferred = self.injected.$q.defer();

        if (response.data && response.data.Success === true) {
            deferred.resolve(response.data);
        } else {
            deferred.reject(response.message);
        }

        return deferred.promise;
    }

    function submitPost() {
        var data = angular.extend({}, self.formData.payload, self.fieldModels);

        return self
            .injected
            .$http({
                method: 'POST',
                url: self.formData.url,
                data: data,
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded'
                },
                transformRequest: function (obj) {
                    return Object
                        .keys(obj)
                        .map(function (key) {
                            return encodeURIComponent(key) + "=" + encodeURIComponent(obj[key]);
                        })
                        .join('&');
                }
            })
            .then(parseResponse);
    }

    function postSuccess(data) {
        self.injected.$scope.$emit('Formulate.formSubmit.OK', {
            fields: self.fieldModels,
            formName: self.formName,
            data: data
        });
    }

    function postFailed(message) {
        self.injected.$scope.$emit('Formulate.formSubmit.Failed', {
            fields: self.fieldModels,
            formName: self.formName,
            message: message
        });
    }

    if (formCtrl.$valid) {
        submitPost().then(postSuccess, postFailed);
    }
};

function formulate() {
    return {
        restrict: "E",
        replace: true,
        template: '
' +
            '<form data-ng-submit="ctrl.submit(ctrl.formName)" class="form" name="{{ctrl.formName}}">' +
            '<formulate-rows rows="ctrl.formData.rows"></formulate-rows>' +
            '</form>' +
            '
',

        controller: FormulateController,
        controllerAs: "ctrl",
        bindToController: true,

        scope: {
            formData: '='
        }
    };
}
app.directive('formulateResponsiveForm', formulate);<commit_msg>Emit the correct form name from submission event.<commit_after>'use strict';

var angular = require('angular');

var app = angular.module('formulate');

/**
 * @description Generate unique form names
 */
var genFormName = (function () {
    var counter = 0;

    return function () {
        counter += 1;

        return 'form' + counter;
    };
}());

function FormulateController($scope, $element, $http, $q) {
    var self = this;

    // Set reference to injected object to be used in prototype functions
    this.injected = {
        $q: $q,
        $http: $http,
        $scope: $scope,
        $element: $element
    };

    // set unique form name
    this.generatedName = genFormName();

    // Map Fields for faster access
    this.fieldMap = {};
    this.formData.fields.forEach(function (field) {
        self.fieldMap[field.id] = field;
    });

    // ng-model of fields
    this.fieldModels = {};
}

FormulateController.prototype.getFieldById = function (id) {
    return this.fieldMap[id];
};
FormulateController.prototype.submit = function () {
    var self = this;
    var formCtrl = this
        .injected
        .$element
        .find('form')
        .controller('form');

    function parseResponse(response) {
        var deferred = self.injected.$q.defer();

        if (response.data && response.data.Success === true) {
            deferred.resolve(response.data);
        } else {
            deferred.reject(response.message);
        }

        return deferred.promise;
    }

    function submitPost() {
        var data = angular.extend({}, self.formData.payload, self.fieldModels);

        return self
            .injected
            .$http({
                method: 'POST',
                url: self.formData.url,
                data: data,
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded'
                },
                transformRequest: function (obj) {
                    return Object
                        .keys(obj)
                        .map(function (key) {
                            return encodeURIComponent(key) + "=" + encodeURIComponent(obj[key]);
                        })
                        .join('&');
                }
            })
            .then(parseResponse);
    }

    function postSuccess(data) {
        self.injected.$scope.$emit('Formulate.formSubmit.OK', {
            fields: self.fieldModels,
            name: self.formData.name,
            response: data
        });
    }

    function postFailed(message) {
        self.injected.$scope.$emit('Formulate.formSubmit.Failed', {
            fields: self.fieldModels,
            name: self.formData.name,
            message: message
        });
    }

    if (formCtrl.$valid) {
        submitPost().then(postSuccess, postFailed);
    }
};

function formulate() {
    return {
        restrict: "E",
        replace: true,
        template: '
' +
            '<form data-ng-submit="ctrl.submit(ctrl.generatedName)" class="form" name="{{ctrl.generatedName}}">' +
            '<formulate-rows rows="ctrl.formData.rows"></formulate-rows>' +
            '</form>' +
            '
',

        controller: FormulateController,
        controllerAs: "ctrl",
        bindToController: true,

        scope: {
            formData: '='
        }
    };
}
app.directive('formulateResponsiveForm', formulate);<|endoftext|>


<commit_before>/*
 *
 * (c) Copyright Ascensio System SIA 2010-2019
 *
 * This program is a free software product. You can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License (AGPL)
 * version 3 as published by the Free Software Foundation. In accordance with
 * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
 * that Ascensio System SIA expressly excludes the warranty of non-infringement
 * of any third-party rights.
 *
 * This program is distributed WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR  PURPOSE. For
 * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
 *
 * You can contact Ascensio System SIA at 20A-12 Ernesta Birznieka-Upisha
 * street, Riga, Latvia, EU, LV-1050.
 *
 * The  interactive user interfaces in modified source and object code versions
 * of the Program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU AGPL version 3.
 *
 * Pursuant to Section 7(b) of the License you must retain the original Product
 * logo when distributing the program. Pursuant to Section 7(e) we decline to
 * grant you any rights under trademark law for use of our trademarks.
 *
 * All the Product's GUI elements, including illustrations and icon sets, as
 * well as technical writing content are licensed under the terms of the
 * Creative Commons Attribution-ShareAlike 4.0 International. See the License
 * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
 *
*/
/**
 *  TreeView.js
 *
 *  Created by Julia Radzhabova on 12/14/17
 *
 */

if (Common === undefined)
    var Common = {};

define([
    'common/main/lib/component/DataView'
], function () {
    'use strict';

    Common.UI.TreeViewModel = Common.UI.DataViewModel.extend({
        defaults: function() {
            return {
                id: Common.UI.getId(),
                name: '',
                isNotHeader: false,
                hasSubItems: false,
                hasParent: false,
                isEmptyItem: false,
                isExpanded: true,
                isVisible: true,
                selected: false,
                allowSelected: true,
                disabled: false,
                level: 0,
                index: 0
            }
        }
    });

    Common.UI.TreeViewStore = Backbone.Collection.extend({
        model: Common.UI.TreeViewModel,

        expandSubItems: function(record) {
            var me = this;
            var _expand_sub_items = function(idx, expanded, level) {
                for (var i=idx+1; i<me.length; i++) {
                    var item = me.at(i);
                    var item_level = item.get('level');
                    if (item_level>level) {
                        if (expanded)
                            item.set('isVisible', true);
                        if (item.get('hasSubItems'))
                            i = _expand_sub_items(i, item.get('isExpanded'), item_level );
                    } else {
                        return (i-1);
                    }
                }
            };

            record.set('isExpanded', true);
            _expand_sub_items(record.get('index'), true, record.get('level'));
        },

        collapseSubItems: function(record) {
            var start_level = record.get('level'),
                index = record.get('index');
            for (var i=index+1; i<this.length; i++) {
                var item = this.at(i);
                var item_level = item.get('level');
                if (item_level>start_level) {
                    item.set('isVisible', false);
                } else {
                    break;
                }
            }
            return i-1;
        },

        expandAll: function() {
            this.each(function(item) {
                item.set('isExpanded', true);
                item.set('isVisible', true);
            });
        },

        collapseAll: function() {
            for (var i=0; i<this.length; i++) {
                var item = this.at(i);
                if (!item.get('isNotHeader')) {
                    item.set('isExpanded', false);
                    i = this.collapseSubItems(item);
                }
            }
        },

        expandToLevel: function(expandLevel) {
            var me = this;
            var _expand_sub_items = function(idx, level) {
                var parent = me.at(idx);
                parent.set('isExpanded', false);
                for (var i=idx+1; i<me.length; i++) {
                    var item = me.at(i);
                    var item_level = item.get('level');
                    if (item_level>level) {
                        if (item_level<=expandLevel)
                            parent.set('isExpanded', true);
                        item.set('isVisible', item_level<=expandLevel);
                        if (item.get('hasSubItems'))
                            i = _expand_sub_items(i, item_level );
                    } else {
                        return (i-1);
                    }
                }
            };

            for (var j=0; j<this.length; j++) {
                var item = this.at(j);
                if (item.get('level')<=expandLevel || !item.get('hasParent')) {
                    item.set('isVisible', true);
                    if (!item.get('isNotHeader'))
                        j = _expand_sub_items(j, item.get('level'));
                }
            }
        }
    });

    Common.UI.TreeView = Common.UI.DataView.extend((function() {
        return {
            options: {
                handleSelect: true,
                showLast: true,
                allowScrollbar: true,
                scrollAlwaysVisible: true,
                emptyItemText: ''
            },

            template: _.template([
                '

'
            ].join('')),

            initialize : function(options) {
                options.store = options.store || new Common.UI.TreeViewStore();
                options.emptyItemText = options.emptyItemText || '';
                options.itemTemplate = options.itemTemplate || _.template([
                    '<div id="<%= id %>" class="tree-item <% if (!isVisible) { %>' + 'hidden' + '<% } %>" style="display: block;padding-left: <%= level*16 + 24 %>px;">',
                    '<% if (hasSubItems) { %>',
                        '<div class="tree-caret img-commonctrl ' + '<% if (!isExpanded) { %>' + 'up' + '<% } %>' + '" style="margin-left: <%= level*16 %>px;">
',
                    '<% } %>',
                    '<% if (isNotHeader) { %>',
                        '
<%= name %>
',
                    '<% } else if (isEmptyItem) { %>',
                        '
' + options.emptyItemText + '
',
                    '<% } else { %>',
                        '
<%= name %>
',
                    '<% } %>',
                    '
'
                ].join(''));
                Common.UI.DataView.prototype.initialize.call(this, options);
            },

            onAddItem: function(record, store, opts) {
                var view = new Common.UI.DataViewItem({
                    template: this.itemTemplate,
                    model: record
                });

                if (view) {
                    var innerEl = (this.$el || $(this.el)).find('.inner').addBack().filter('.inner');
                    if (innerEl) {
                        (this.dataViewItems.length<1) && innerEl.find('.empty-text').remove();

                        if (opts && opts.at!==undefined) {
                            var idx = opts.at;
                            var innerDivs = innerEl.find('> div');
                            if (idx > 0)
                                $(innerDivs.get(idx - 1)).after(view.render().el);
                            else {
                                (innerDivs.length > 0) ? $(innerDivs[idx]).before(view.render().el) : innerEl.append(view.render().el);
                            }
                            this.dataViewItems = this.dataViewItems.slice(0, idx).concat(view).concat(this.dataViewItems.slice(idx));
                        } else {
                            innerEl.append(view.render().el);
                            this.dataViewItems.push(view);
                        }

                        var name = record.get('name');
                        if (name.length > 37 - record.get('level')*2)
                            record.set('tip', name);
                        if (record.get('tip')) {
                            var view_el = $(view.el);
                            view_el.attr('data-toggle', 'tooltip');
                            view_el.tooltip({
                                title       : record.get('tip'),
                                placement   : 'cursor',
                                zIndex : this.tipZIndex
                            });
                        }

                        this.listenTo(view, 'change',      this.onChangeItem);
                        this.listenTo(view, 'remove',      this.onRemoveItem);
                        this.listenTo(view, 'click',       this.onClickItem);
                        this.listenTo(view, 'dblclick',    this.onDblClickItem);
                        this.listenTo(view, 'select',      this.onSelectItem);
                        this.listenTo(view, 'contextmenu', this.onContextMenuItem);

                        if (!this.isSuspendEvents)
                            this.trigger('item:add', this, view, record);
                    }
                }
            },

            onClickItem: function(view, record, e) {
                var btn = $(e.target);
                if (btn && btn.hasClass('tree-caret')) {
                    var tip = view.$el.data('bs.tooltip');
                    if (tip) (tip.tip()).remove();

                    var isExpanded = !record.get('isExpanded');
                    record.set('isExpanded', isExpanded);
                    this.store[(isExpanded) ? 'expandSubItems' : 'collapseSubItems'](record);
                    this.scroller.update({minScrollbarLength: 40, alwaysVisibleY: this.scrollAlwaysVisible});
                } else
                    Common.UI.DataView.prototype.onClickItem.call(this, view, record, e);
            },

            expandAll: function() {
                this.store.expandAll();
                this.scroller.update({minScrollbarLength: 40, alwaysVisibleY: this.scrollAlwaysVisible});
            },

            collapseAll: function() {
                this.store.collapseAll();
                this.scroller.update({minScrollbarLength: 40, alwaysVisibleY: this.scrollAlwaysVisible});
            },

            expandToLevel: function(expandLevel) {
                this.store.expandToLevel(expandLevel);
                this.scroller.update({minScrollbarLength: 40, alwaysVisibleY: this.scrollAlwaysVisible});
            },

            expandRecord: function(record) {
                if (record) {
                    record.set('isExpanded', true);
                    this.store.expandSubItems(record);
                    this.scroller.update({minScrollbarLength: 40, alwaysVisibleY: this.scrollAlwaysVisible});
                }
            },

            collapseRecord: function(record) {
                if (record) {
                    record.set('isExpanded', false);
                    this.store.collapseSubItems(record);
                    this.scroller.update({minScrollbarLength: 40, alwaysVisibleY: this.scrollAlwaysVisible});
                }
            },

            focus: function() {
                this.cmpEl && this.cmpEl.find('.treeview').focus();
            }
        }
    })());
});<commit_msg>[Common] Treeview: add key support for expand/collapse<commit_after>/*
 *
 * (c) Copyright Ascensio System SIA 2010-2019
 *
 * This program is a free software product. You can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License (AGPL)
 * version 3 as published by the Free Software Foundation. In accordance with
 * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
 * that Ascensio System SIA expressly excludes the warranty of non-infringement
 * of any third-party rights.
 *
 * This program is distributed WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR  PURPOSE. For
 * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
 *
 * You can contact Ascensio System SIA at 20A-12 Ernesta Birznieka-Upisha
 * street, Riga, Latvia, EU, LV-1050.
 *
 * The  interactive user interfaces in modified source and object code versions
 * of the Program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU AGPL version 3.
 *
 * Pursuant to Section 7(b) of the License you must retain the original Product
 * logo when distributing the program. Pursuant to Section 7(e) we decline to
 * grant you any rights under trademark law for use of our trademarks.
 *
 * All the Product's GUI elements, including illustrations and icon sets, as
 * well as technical writing content are licensed under the terms of the
 * Creative Commons Attribution-ShareAlike 4.0 International. See the License
 * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
 *
*/
/**
 *  TreeView.js
 *
 *  Created by Julia Radzhabova on 12/14/17
 *
 */

if (Common === undefined)
    var Common = {};

define([
    'common/main/lib/component/DataView'
], function () {
    'use strict';

    Common.UI.TreeViewModel = Common.UI.DataViewModel.extend({
        defaults: function() {
            return {
                id: Common.UI.getId(),
                name: '',
                isNotHeader: false,
                hasSubItems: false,
                hasParent: false,
                isEmptyItem: false,
                isExpanded: true,
                isVisible: true,
                selected: false,
                allowSelected: true,
                disabled: false,
                level: 0,
                index: 0
            }
        }
    });

    Common.UI.TreeViewStore = Backbone.Collection.extend({
        model: Common.UI.TreeViewModel,

        expandSubItems: function(record) {
            var me = this;
            var _expand_sub_items = function(idx, expanded, level) {
                for (var i=idx+1; i<me.length; i++) {
                    var item = me.at(i);
                    var item_level = item.get('level');
                    if (item_level>level) {
                        if (expanded)
                            item.set('isVisible', true);
                        if (item.get('hasSubItems'))
                            i = _expand_sub_items(i, item.get('isExpanded'), item_level );
                    } else {
                        return (i-1);
                    }
                }
            };

            record.set('isExpanded', true);
            _expand_sub_items(record.get('index'), true, record.get('level'));
        },

        collapseSubItems: function(record) {
            var start_level = record.get('level'),
                index = record.get('index');
            for (var i=index+1; i<this.length; i++) {
                var item = this.at(i);
                var item_level = item.get('level');
                if (item_level>start_level) {
                    item.set('isVisible', false);
                } else {
                    break;
                }
            }
            return i-1;
        },

        expandAll: function() {
            this.each(function(item) {
                item.set('isExpanded', true);
                item.set('isVisible', true);
            });
        },

        collapseAll: function() {
            for (var i=0; i<this.length; i++) {
                var item = this.at(i);
                if (!item.get('isNotHeader')) {
                    item.set('isExpanded', false);
                    i = this.collapseSubItems(item);
                }
            }
        },

        expandToLevel: function(expandLevel) {
            var me = this;
            var _expand_sub_items = function(idx, level) {
                var parent = me.at(idx);
                parent.set('isExpanded', false);
                for (var i=idx+1; i<me.length; i++) {
                    var item = me.at(i);
                    var item_level = item.get('level');
                    if (item_level>level) {
                        if (item_level<=expandLevel)
                            parent.set('isExpanded', true);
                        item.set('isVisible', item_level<=expandLevel);
                        if (item.get('hasSubItems'))
                            i = _expand_sub_items(i, item_level );
                    } else {
                        return (i-1);
                    }
                }
            };

            for (var j=0; j<this.length; j++) {
                var item = this.at(j);
                if (item.get('level')<=expandLevel || !item.get('hasParent')) {
                    item.set('isVisible', true);
                    if (!item.get('isNotHeader'))
                        j = _expand_sub_items(j, item.get('level'));
                }
            }
        }
    });

    Common.UI.TreeView = Common.UI.DataView.extend((function() {
        return {
            options: {
                handleSelect: true,
                showLast: true,
                allowScrollbar: true,
                scrollAlwaysVisible: true,
                emptyItemText: '',
                keyMoveDirection: 'both'
            },

            template: _.template([
                '

'
            ].join('')),

            initialize : function(options) {
                options.store = options.store || new Common.UI.TreeViewStore();
                options.emptyItemText = options.emptyItemText || '';
                options.itemTemplate = options.itemTemplate || _.template([
                    '<div id="<%= id %>" class="tree-item <% if (!isVisible) { %>' + 'hidden' + '<% } %>" style="display: block;padding-left: <%= level*16 + 24 %>px;">',
                    '<% if (hasSubItems) { %>',
                        '<div class="tree-caret img-commonctrl ' + '<% if (!isExpanded) { %>' + 'up' + '<% } %>' + '" style="margin-left: <%= level*16 %>px;">
',
                    '<% } %>',
                    '<% if (isNotHeader) { %>',
                        '
<%= name %>
',
                    '<% } else if (isEmptyItem) { %>',
                        '
' + options.emptyItemText + '
',
                    '<% } else { %>',
                        '
<%= name %>
',
                    '<% } %>',
                    '
'
                ].join(''));
                Common.UI.DataView.prototype.initialize.call(this, options);
            },

            onAddItem: function(record, store, opts) {
                var view = new Common.UI.DataViewItem({
                    template: this.itemTemplate,
                    model: record
                });

                if (view) {
                    var innerEl = (this.$el || $(this.el)).find('.inner').addBack().filter('.inner');
                    if (innerEl) {
                        (this.dataViewItems.length<1) && innerEl.find('.empty-text').remove();

                        if (opts && opts.at!==undefined) {
                            var idx = opts.at;
                            var innerDivs = innerEl.find('> div');
                            if (idx > 0)
                                $(innerDivs.get(idx - 1)).after(view.render().el);
                            else {
                                (innerDivs.length > 0) ? $(innerDivs[idx]).before(view.render().el) : innerEl.append(view.render().el);
                            }
                            this.dataViewItems = this.dataViewItems.slice(0, idx).concat(view).concat(this.dataViewItems.slice(idx));
                        } else {
                            innerEl.append(view.render().el);
                            this.dataViewItems.push(view);
                        }

                        var name = record.get('name');
                        if (name.length > 37 - record.get('level')*2)
                            record.set('tip', name);
                        if (record.get('tip')) {
                            var view_el = $(view.el);
                            view_el.attr('data-toggle', 'tooltip');
                            view_el.tooltip({
                                title       : record.get('tip'),
                                placement   : 'cursor',
                                zIndex : this.tipZIndex
                            });
                        }

                        this.listenTo(view, 'change',      this.onChangeItem);
                        this.listenTo(view, 'remove',      this.onRemoveItem);
                        this.listenTo(view, 'click',       this.onClickItem);
                        this.listenTo(view, 'dblclick',    this.onDblClickItem);
                        this.listenTo(view, 'select',      this.onSelectItem);
                        this.listenTo(view, 'contextmenu', this.onContextMenuItem);

                        if (!this.isSuspendEvents)
                            this.trigger('item:add', this, view, record);
                    }
                }
            },

            onClickItem: function(view, record, e) {
                var btn = $(e.target);
                if (btn && btn.hasClass('tree-caret')) {
                    var tip = view.$el.data('bs.tooltip');
                    if (tip) (tip.tip()).remove();

                    var isExpanded = !record.get('isExpanded');
                    record.set('isExpanded', isExpanded);
                    this.store[(isExpanded) ? 'expandSubItems' : 'collapseSubItems'](record);
                    this.scroller.update({minScrollbarLength: 40, alwaysVisibleY: this.scrollAlwaysVisible});
                } else
                    Common.UI.DataView.prototype.onClickItem.call(this, view, record, e);
            },

            expandAll: function() {
                this.store.expandAll();
                this.scroller.update({minScrollbarLength: 40, alwaysVisibleY: this.scrollAlwaysVisible});
            },

            collapseAll: function() {
                this.store.collapseAll();
                this.scroller.update({minScrollbarLength: 40, alwaysVisibleY: this.scrollAlwaysVisible});
            },

            expandToLevel: function(expandLevel) {
                this.store.expandToLevel(expandLevel);
                this.scroller.update({minScrollbarLength: 40, alwaysVisibleY: this.scrollAlwaysVisible});
            },

            expandRecord: function(record) {
                if (record) {
                    record.set('isExpanded', true);
                    this.store.expandSubItems(record);
                    this.scroller.update({minScrollbarLength: 40, alwaysVisibleY: this.scrollAlwaysVisible});
                }
            },

            collapseRecord: function(record) {
                if (record) {
                    record.set('isExpanded', false);
                    this.store.collapseSubItems(record);
                    this.scroller.update({minScrollbarLength: 40, alwaysVisibleY: this.scrollAlwaysVisible});
                }
            },

            onKeyDown: function (e, data) {
                if ( this.disabled ) return;
                if (data===undefined) data = e;
                if (_.indexOf(this.moveKeys, data.keyCode)>-1 || data.keyCode==Common.UI.Keys.RETURN) {
                    data.preventDefault();
                    data.stopPropagation();
                    var rec = this.getSelectedRec();
                    if (data.keyCode==Common.UI.Keys.RETURN) {
                        if (this.selectedBeforeHideRec) // only for ComboDataView menuPicker
                            rec = this.selectedBeforeHideRec;
                        this.trigger('item:click', this, this, rec, e);
                        if (this.parentMenu)
                            this.parentMenu.hide();
                    } else {
                        var idx = _.indexOf(this.store.models, rec);
                        if (idx<0) {
                            if (data.keyCode==Common.UI.Keys.LEFT) {
                                var target = $(e.target).closest('.dropdown-submenu.over');
                                if (target.length>0) {
                                    target.removeClass('over');
                                    target.find('> a').focus();
                                } else
                                    idx = 0;
                            } else
                                idx = 0;
                        } else if (this.options.keyMoveDirection == 'both') {
                            var hasSubItems = rec.get('hasSubItems');
                            var hasParent = rec.get('hasParent');
                            var isExpanded = rec.get('isExpanded');
                            if (data.keyCode==Common.UI.Keys.LEFT) {
                                if (hasSubItems && isExpanded)
                                    this.collapseRecord(rec);
                            } else if (data.keyCode==Common.UI.Keys.RIGHT) {
                                if (hasSubItems && !isExpanded)
                                    this.expandRecord(rec);
                            } else {
                                if (data.keyCode==Common.UI.Keys.DOWN) {
                                    for (var i=idx+1; i<this.store.length; i++) {
                                        if (this.store.at(i).get('isVisible')) {
                                            idx=i;
                                            break;
                                        }
                                    }
                                } else if (data.keyCode==Common.UI.Keys.UP) {
                                    for (var i=idx-1; i>=0; i--) {
                                        if (this.store.at(i).get('isVisible')) {
                                            idx=i;
                                            break;
                                        }
                                    }
                                }
                            }
                        } else {
                            idx = (data.keyCode==Common.UI.Keys.UP || data.keyCode==Common.UI.Keys.LEFT)
                                ? Math.max(0, idx-1)
                                : Math.min(this.store.length - 1, idx + 1) ;
                        }

                        if (idx !== undefined && idx>=0) rec = this.store.at(idx);
                        if (rec) {
                            this._fromKeyDown = true;
                            this.selectRecord(rec);
                            this._fromKeyDown = false;
                            this.scrollToRecord(rec);
                        }
                    }
                } else {
                    this.trigger('item:keydown', this, rec, e);
                }
            },

            focus: function() {
                this.cmpEl && this.cmpEl.find('.treeview').focus();
            }
        }
    })());
});<|endoftext|>


<commit_before>"use strict";

//
// JSIL loader. Synchronously loads all core JSIL scripts, adds essential libraries to the content manifest,
//  and loads your manifest scripts.
// Load this at the top of your document (optionally after declaring a jsilConfig dict in a previous script tag).
// Asset loading (after page load) is provided by JSIL.Browser.js.
//

(function (globalNamespace) {
  if (typeof (globalNamespace.JSIL) !== "undefined")
    throw new Error("JSIL.js loaded twice");

  var JSIL = {
    __FullName__: "JSIL",
    __NativeModules__: {}
  };

  Object.defineProperty(
    globalNamespace, "JSIL",
    {
      value: JSIL,
      configurable: false,
      enumerable: true,
      writable: false
    }
  );

  JSIL.GlobalNamespace = globalNamespace;

  if (typeof (globalNamespace.jsilConfig) !== "object")
    globalNamespace.jsilConfig = {};

  if (typeof (globalNamespace.contentManifest) !== "object")
    globalNamespace.contentManifest = {};
})(this);

contentManifest["JSIL"] = [];

var $jsilloaderstate = {
  environment: null,
  loadFailures: []
};

(function loadJSIL (config) {

  function Environment_Browser (config) {
    var self = this;
    this.config = config;
    this.scriptIndex = 0;
    this.scriptURIs = {};

    window.$scriptLoadFailed = function (i) {
      var uri = self.scriptURIs[i];

      if (window.JSIL && window.JSIL.Host && window.JSIL.Host.logWriteLine)
        JSIL.Host.logWriteLine("JSIL.js failed to load script '" + uri + "'");
      else if (window.console && window.console.log)
        console.error("JSIL.js failed to load script '" + uri + "'");

      $jsilloaderstate.loadFailures.push([uri]);

      if (config.onLoadFailure) {
        try {
          config.onLoadFailure(uri);
        } catch (exc) {
        }
      }
    }

    contentManifest["JSIL"].push(["Library", "JSIL.Storage.js"]);
    contentManifest["JSIL"].push(["Library", "JSIL.IO.js"]);
    contentManifest["JSIL"].push(["Library", "JSIL.JSON.js"]);
    contentManifest["JSIL"].push(["Library", "JSIL.XML.js"]);
  };

  Environment_Browser.prototype.getUserSetting = function (key) {
    key = key.toLowerCase();

    var query = window.location.search.substring(1);
    var vars = query.split('&');

    for (var i = 0; i < vars.length; i++) {
      var pair = vars[i].split('=');

      if (decodeURIComponent(pair[0]).toLowerCase() === key) {
        if (pair.length > 1)
          return decodeURIComponent(pair[1]);
        else
          return true;
      }
    }

    return false;
  };

  Environment_Browser.prototype.loadScript = function (uri) {
    if (window.console && window.console.log)
      window.console.log("Loading '" + uri + "'...");

    this.scriptIndex += 1;
    this.scriptURIs[this.scriptIndex] = uri;

    document.write(
      "<script type=\"text/javascript\" src=\"" + uri + "\" onerror=\"$scriptLoadFailed(" +
      this.scriptIndex +
      ")\"></script>"
    );
  };

  Environment_Browser.prototype.loadEnvironmentScripts = function () {
    var libraryRoot = this.config.libraryRoot;

    this.loadScript(libraryRoot + "JSIL.Browser.js");
    this.loadScript(libraryRoot + "JSIL.Browser.Audio.js");
    this.loadScript(libraryRoot + "JSIL.Browser.Loaders.js");
    this.loadScript(libraryRoot + "JSIL.Browser.Touch.js");
  };

  function Environment_SpidermonkeyShell (config) {
    var self = this;
    this.config = config;

    contentManifest["JSIL"].push(["Library", "JSIL.Storage.js"]);
    contentManifest["JSIL"].push(["Library", "JSIL.IO.js"]);
    contentManifest["JSIL"].push(["Library", "JSIL.XML.js"]);
  };

  Environment_SpidermonkeyShell.prototype.getUserSetting = function (key) {
    // FIXME
    return false;
  };

  Environment_SpidermonkeyShell.prototype.loadScript = function (uri) {
    load(uri);
  };

  Environment_SpidermonkeyShell.prototype.loadEnvironmentScripts = function () {
    this.loadScript(libraryRoot + "JSIL.Shell.js");
    this.loadScript(libraryRoot + "JSIL.Shell.Loaders.js");
  };

  var priorModule = JSIL.GlobalNamespace.Module;

  JSIL.BeginLoadNativeLibrary = function (name) {
    var priorModule = JSIL.GlobalNamespace.Module;
    JSIL.GlobalNamespace.Module = Object.create(null);
  };

  JSIL.EndLoadNativeLibrary = function (name) {
    var key = name;
    var lastIndex = Math.max(key.lastIndexOf("/"), key.lastIndexOf("\\"));
    if (lastIndex >= 0)
      key = key.substr(lastIndex + 1);

    // HACK
    key = key.replace(".emjs", ".dll").replace(".js", ".dll").toLowerCase();

    if (JSIL.__NativeModules__[key])
      throw new Error("A module named '" + key + "' is already loaded.");

    // HACK: FIXME: We need a global module to use as a fallback for scenarios
    //  where we don't know which module a call is interacting with
    if (!JSIL.__NativeModules__["__global__"])
      JSIL.__NativeModules__["__global__"] = JSIL.GlobalNamespace.Module;

    JSIL.__NativeModules__[key] = JSIL.GlobalNamespace.Module;

    JSIL.GlobalNamespace.Module = priorModule;
    priorModule = null;
  };

  var environments = {
    "browser": Environment_Browser,
    "spidermonkey_shell": Environment_SpidermonkeyShell
  }

  if (!config.environment) {
    if (typeof (window) !== "undefined")
      config.environment = "browser";
    else
      throw new Error("jsilConfig.environment not set and no default available");
  }

  var environment;

  if (typeof (config.environment) === "function") {
    environment = $jsilloaderstate.environment = new (config.environment)(config);
  } else if (typeof (config.environment) === "string") {
    var environmentType = environments[config.environment];
    if (!environmentType)
      throw new Error("No environment named '" + config.environment + "' available.");

    environment = $jsilloaderstate.environment = new (environmentType)(config);
  }

  if (typeof (config.libraryRoot) === "undefined")
    config.libraryRoot = "../Libraries/";

  var libraryRoot = config.libraryRoot;
  var manifestRoot = (config.manifestRoot = config.manifestRoot || "");
  config.scriptRoot = config.scriptRoot || "";
  config.fileRoot = config.fileRoot || "";
  config.assetRoot = config.assetRoot || "";

  if (typeof (config.contentRoot) === "undefined")
    config.contentRoot = "Content/";

  if (typeof (config.fileVirtualRoot) === "undefined")
    config.fileVirtualRoot = config.fileRoot || "";

  if (config.printStackTrace)
    environment.loadScript(libraryRoot + "printStackTrace.js");

  if (config.webgl2d)
    environment.loadScript(libraryRoot + "webgl-2d.js");

  if (config.gamepad)
    environment.loadScript(libraryRoot + "gamepad.js");

  environment.loadScript(libraryRoot + "Polyfills.js");
  environment.loadScript(libraryRoot + "mersenne.js");

  if (config.typedObjects || false) {
    environment.loadScript(libraryRoot + "typedobjects.js");
    environment.loadScript(libraryRoot + "JSIL.TypedObjects.js");
  }

  environment.loadScript(libraryRoot + "JSIL.Core.js");
  environment.loadScript(libraryRoot + "JSIL.Host.js");

  environment.loadEnvironmentScripts();

  environment.loadScript(libraryRoot + "JSIL.Core.Types.js");
  environment.loadScript(libraryRoot + "JSIL.Core.Reflection.js");
  environment.loadScript(libraryRoot + "JSIL.References.js");
  environment.loadScript(libraryRoot + "JSIL.Unsafe.js");
  environment.loadScript(libraryRoot + "JSIL.PInvoke.js");
  environment.loadScript(libraryRoot + "JSIL.Bootstrap.js");
  environment.loadScript(libraryRoot + "JSIL.Bootstrap.Int64.js");
  environment.loadScript(libraryRoot + "JSIL.Bootstrap.DateTime.js");
  environment.loadScript(libraryRoot + "JSIL.Bootstrap.Text.js");
  environment.loadScript(libraryRoot + "JSIL.Bootstrap.Resources.js");
  environment.loadScript(libraryRoot + "JSIL.Bootstrap.Linq.js");
  environment.loadScript(libraryRoot + "JSIL.Bootstrap.Async.js");

  if (config.interpreter || environment.getUserSetting("interpreter"))
    environment.loadScript(libraryRoot + "JSIL.ExpressionInterpreter.js");

  if (config.testFixture || environment.getUserSetting("testFixture"))
    environment.loadScript(libraryRoot + "JSIL.TestFixture.js");

  config.record |= Boolean(environment.getUserSetting("record"));
  config.replayURI = environment.getUserSetting("replayURI") || config.replayURI;
  config.replayName = environment.getUserSetting("replayName") || config.replayName;
  config.fastReplay = config.fastReplay || environment.getUserSetting("fastReplay") || false;
  config.autoPlay = config.autoPlay || environment.getUserSetting("autoPlay") || config.replayURI || config.replayName || false;

  if (
    config.record ||
    config.replayURI ||
    config.replayName
  ) {
    environment.loadScript(libraryRoot + "JSIL.Replay.js");
  }

  config.disableSound = config.disableSound || environment.getUserSetting("disableSound") || false;

  config.viewportScale = parseFloat((config.viewportScale || environment.getUserSetting("viewportScale") || 1.0).toString());

  config.disableFiltering = config.disableFiltering || environment.getUserSetting("disableFiltering") || false;

  config.enableFreezeAndSeal = config.enableFreezeAndSeal || environment.getUserSetting("enableFreezeAndSeal") || false;

  var manifests = config.manifests || [];

  for (var i = 0, l = manifests.length; i < l; i++)
    environment.loadScript(manifestRoot + manifests[i] + ".manifest.js");

  if (config.winForms || config.monogame) {
    contentManifest["JSIL"].push(["Library", "System.Drawing.js"]);
    contentManifest["JSIL"].push(["Library", "System.Windows.js"]);
  }

  if (config.xna) {
    contentManifest["JSIL"].push(["Library", "XNA/XNA4.js"]);

    switch (Number(config.xna)) {
      case 4:
        break;
      default:
        throw new Error("Unsupported XNA version");
    }

    contentManifest["JSIL"].push(["Library", "XNA/Content.js"]);
    contentManifest["JSIL"].push(["Library", "XNA/Graphics.js"]);
    contentManifest["JSIL"].push(["Library", "XNA/Input.js"]);
    contentManifest["JSIL"].push(["Library", "XNA/Audio.js"]);
    contentManifest["JSIL"].push(["Library", "XNA/Storage.js"]);
  }

  if (config.monogame) {
    contentManifest["JSIL"].push(["Library", "MonoGame/OpenTK.js"]);
    contentManifest["JSIL"].push(["Library", "MonoGame/OpenTK.GL.js"]);
    contentManifest["JSIL"].push(["Library", "MonoGame/OpenTK.AL.js"]);
    contentManifest["JSIL"].push(["Library", "MonoGame/SDL.js"]);
  }

  if (config.readOnlyStorage)
    contentManifest["JSIL"].push(["Library", "JSIL.ReadOnlyStorage.js"]);

  if (config.localStorage)
    contentManifest["JSIL"].push(["Library", "JSIL.LocalStorage.js"]);

})(jsilConfig);<commit_msg>Require native stuff to be built with MODULARIZE=1<commit_after>"use strict";

//
// JSIL loader. Synchronously loads all core JSIL scripts, adds essential libraries to the content manifest,
//  and loads your manifest scripts.
// Load this at the top of your document (optionally after declaring a jsilConfig dict in a previous script tag).
// Asset loading (after page load) is provided by JSIL.Browser.js.
//

(function (globalNamespace) {
  if (typeof (globalNamespace.JSIL) !== "undefined")
    throw new Error("JSIL.js loaded twice");

  var JSIL = {
    __FullName__: "JSIL",
    __NativeModules__: {}
  };

  Object.defineProperty(
    globalNamespace, "JSIL",
    {
      value: JSIL,
      configurable: false,
      enumerable: true,
      writable: false
    }
  );

  JSIL.GlobalNamespace = globalNamespace;

  if (typeof (globalNamespace.jsilConfig) !== "object")
    globalNamespace.jsilConfig = {};

  if (typeof (globalNamespace.contentManifest) !== "object")
    globalNamespace.contentManifest = {};
})(this);

contentManifest["JSIL"] = [];

var $jsilloaderstate = {
  environment: null,
  loadFailures: []
};

(function loadJSIL (config) {

  function Environment_Browser (config) {
    var self = this;
    this.config = config;
    this.scriptIndex = 0;
    this.scriptURIs = {};

    window.$scriptLoadFailed = function (i) {
      var uri = self.scriptURIs[i];

      if (window.JSIL && window.JSIL.Host && window.JSIL.Host.logWriteLine)
        JSIL.Host.logWriteLine("JSIL.js failed to load script '" + uri + "'");
      else if (window.console && window.console.log)
        console.error("JSIL.js failed to load script '" + uri + "'");

      $jsilloaderstate.loadFailures.push([uri]);

      if (config.onLoadFailure) {
        try {
          config.onLoadFailure(uri);
        } catch (exc) {
        }
      }
    }

    contentManifest["JSIL"].push(["Library", "JSIL.Storage.js"]);
    contentManifest["JSIL"].push(["Library", "JSIL.IO.js"]);
    contentManifest["JSIL"].push(["Library", "JSIL.JSON.js"]);
    contentManifest["JSIL"].push(["Library", "JSIL.XML.js"]);
  };

  Environment_Browser.prototype.getUserSetting = function (key) {
    key = key.toLowerCase();

    var query = window.location.search.substring(1);
    var vars = query.split('&');

    for (var i = 0; i < vars.length; i++) {
      var pair = vars[i].split('=');

      if (decodeURIComponent(pair[0]).toLowerCase() === key) {
        if (pair.length > 1)
          return decodeURIComponent(pair[1]);
        else
          return true;
      }
    }

    return false;
  };

  Environment_Browser.prototype.loadScript = function (uri) {
    if (window.console && window.console.log)
      window.console.log("Loading '" + uri + "'...");

    this.scriptIndex += 1;
    this.scriptURIs[this.scriptIndex] = uri;

    document.write(
      "<script type=\"text/javascript\" src=\"" + uri + "\" onerror=\"$scriptLoadFailed(" +
      this.scriptIndex +
      ")\"></script>"
    );
  };

  Environment_Browser.prototype.loadEnvironmentScripts = function () {
    var libraryRoot = this.config.libraryRoot;

    this.loadScript(libraryRoot + "JSIL.Browser.js");
    this.loadScript(libraryRoot + "JSIL.Browser.Audio.js");
    this.loadScript(libraryRoot + "JSIL.Browser.Loaders.js");
    this.loadScript(libraryRoot + "JSIL.Browser.Touch.js");
  };

  function Environment_SpidermonkeyShell (config) {
    var self = this;
    this.config = config;

    contentManifest["JSIL"].push(["Library", "JSIL.Storage.js"]);
    contentManifest["JSIL"].push(["Library", "JSIL.IO.js"]);
    contentManifest["JSIL"].push(["Library", "JSIL.XML.js"]);
  };

  Environment_SpidermonkeyShell.prototype.getUserSetting = function (key) {
    // FIXME
    return false;
  };

  Environment_SpidermonkeyShell.prototype.loadScript = function (uri) {
    load(uri);
  };

  Environment_SpidermonkeyShell.prototype.loadEnvironmentScripts = function () {
    this.loadScript(libraryRoot + "JSIL.Shell.js");
    this.loadScript(libraryRoot + "JSIL.Shell.Loaders.js");
  };

  var priorModule = JSIL.GlobalNamespace.Module;

  JSIL.BeginLoadNativeLibrary = function (name) {
    var priorModule = JSIL.GlobalNamespace.Module;
    JSIL.GlobalNamespace.Module = Object.create(null);
  };

  JSIL.EndLoadNativeLibrary = function (name) {
    var key = name;
    var lastIndex = Math.max(key.lastIndexOf("/"), key.lastIndexOf("\\"));
    if (lastIndex >= 0)
      key = key.substr(lastIndex + 1);

    // HACK
    key = key.replace(".emjs", ".dll").replace(".js", ".dll").toLowerCase();

    if (JSIL.__NativeModules__[key])
      throw new Error("A module named '" + key + "' is already loaded.");

    if (typeof(JSIL.GlobalNamespace.Module) != "function") {
      throw new Error("The Module in " + name + " is not a function. Please pass '-s MODULARIZE=1' to emcc when compiling native libraries.");
    }

    var module = JSIL.GlobalNamespace.Module();

    // HACK: FIXME: We need a global module to use as a fallback for scenarios
    //  where we don't know which module a call is interacting with
    if (!JSIL.__NativeModules__["__global__"])
      JSIL.__NativeModules__["__global__"] = module;

    JSIL.__NativeModules__[key] = module;

    JSIL.GlobalNamespace.Module = priorModule;
    priorModule = null;
  };

  var environments = {
    "browser": Environment_Browser,
    "spidermonkey_shell": Environment_SpidermonkeyShell
  }

  if (!config.environment) {
    if (typeof (window) !== "undefined")
      config.environment = "browser";
    else
      throw new Error("jsilConfig.environment not set and no default available");
  }

  var environment;

  if (typeof (config.environment) === "function") {
    environment = $jsilloaderstate.environment = new (config.environment)(config);
  } else if (typeof (config.environment) === "string") {
    var environmentType = environments[config.environment];
    if (!environmentType)
      throw new Error("No environment named '" + config.environment + "' available.");

    environment = $jsilloaderstate.environment = new (environmentType)(config);
  }

  if (typeof (config.libraryRoot) === "undefined")
    config.libraryRoot = "../Libraries/";

  var libraryRoot = config.libraryRoot;
  var manifestRoot = (config.manifestRoot = config.manifestRoot || "");
  config.scriptRoot = config.scriptRoot || "";
  config.fileRoot = config.fileRoot || "";
  config.assetRoot = config.assetRoot || "";

  if (typeof (config.contentRoot) === "undefined")
    config.contentRoot = "Content/";

  if (typeof (config.fileVirtualRoot) === "undefined")
    config.fileVirtualRoot = config.fileRoot || "";

  if (config.printStackTrace)
    environment.loadScript(libraryRoot + "printStackTrace.js");

  if (config.webgl2d)
    environment.loadScript(libraryRoot + "webgl-2d.js");

  if (config.gamepad)
    environment.loadScript(libraryRoot + "gamepad.js");

  environment.loadScript(libraryRoot + "Polyfills.js");
  environment.loadScript(libraryRoot + "mersenne.js");

  if (config.typedObjects || false) {
    environment.loadScript(libraryRoot + "typedobjects.js");
    environment.loadScript(libraryRoot + "JSIL.TypedObjects.js");
  }

  environment.loadScript(libraryRoot + "JSIL.Core.js");
  environment.loadScript(libraryRoot + "JSIL.Host.js");

  environment.loadEnvironmentScripts();

  environment.loadScript(libraryRoot + "JSIL.Core.Types.js");
  environment.loadScript(libraryRoot + "JSIL.Core.Reflection.js");
  environment.loadScript(libraryRoot + "JSIL.References.js");
  environment.loadScript(libraryRoot + "JSIL.Unsafe.js");
  environment.loadScript(libraryRoot + "JSIL.PInvoke.js");
  environment.loadScript(libraryRoot + "JSIL.Bootstrap.js");
  environment.loadScript(libraryRoot + "JSIL.Bootstrap.Int64.js");
  environment.loadScript(libraryRoot + "JSIL.Bootstrap.DateTime.js");
  environment.loadScript(libraryRoot + "JSIL.Bootstrap.Text.js");
  environment.loadScript(libraryRoot + "JSIL.Bootstrap.Resources.js");
  environment.loadScript(libraryRoot + "JSIL.Bootstrap.Linq.js");
  environment.loadScript(libraryRoot + "JSIL.Bootstrap.Async.js");

  if (config.interpreter || environment.getUserSetting("interpreter"))
    environment.loadScript(libraryRoot + "JSIL.ExpressionInterpreter.js");

  if (config.testFixture || environment.getUserSetting("testFixture"))
    environment.loadScript(libraryRoot + "JSIL.TestFixture.js");

  config.record |= Boolean(environment.getUserSetting("record"));
  config.replayURI = environment.getUserSetting("replayURI") || config.replayURI;
  config.replayName = environment.getUserSetting("replayName") || config.replayName;
  config.fastReplay = config.fastReplay || environment.getUserSetting("fastReplay") || false;
  config.autoPlay = config.autoPlay || environment.getUserSetting("autoPlay") || config.replayURI || config.replayName || false;

  if (
    config.record ||
    config.replayURI ||
    config.replayName
  ) {
    environment.loadScript(libraryRoot + "JSIL.Replay.js");
  }

  config.disableSound = config.disableSound || environment.getUserSetting("disableSound") || false;

  config.viewportScale = parseFloat((config.viewportScale || environment.getUserSetting("viewportScale") || 1.0).toString());

  config.disableFiltering = config.disableFiltering || environment.getUserSetting("disableFiltering") || false;

  config.enableFreezeAndSeal = config.enableFreezeAndSeal || environment.getUserSetting("enableFreezeAndSeal") || false;

  var manifests = config.manifests || [];

  for (var i = 0, l = manifests.length; i < l; i++)
    environment.loadScript(manifestRoot + manifests[i] + ".manifest.js");

  if (config.winForms || config.monogame) {
    contentManifest["JSIL"].push(["Library", "System.Drawing.js"]);
    contentManifest["JSIL"].push(["Library", "System.Windows.js"]);
  }

  if (config.xna) {
    contentManifest["JSIL"].push(["Library", "XNA/XNA4.js"]);

    switch (Number(config.xna)) {
      case 4:
        break;
      default:
        throw new Error("Unsupported XNA version");
    }

    contentManifest["JSIL"].push(["Library", "XNA/Content.js"]);
    contentManifest["JSIL"].push(["Library", "XNA/Graphics.js"]);
    contentManifest["JSIL"].push(["Library", "XNA/Input.js"]);
    contentManifest["JSIL"].push(["Library", "XNA/Audio.js"]);
    contentManifest["JSIL"].push(["Library", "XNA/Storage.js"]);
  }

  if (config.monogame) {
    contentManifest["JSIL"].push(["Library", "MonoGame/OpenTK.js"]);
    contentManifest["JSIL"].push(["Library", "MonoGame/OpenTK.GL.js"]);
    contentManifest["JSIL"].push(["Library", "MonoGame/OpenTK.AL.js"]);
    contentManifest["JSIL"].push(["Library", "MonoGame/SDL.js"]);
  }

  if (config.readOnlyStorage)
    contentManifest["JSIL"].push(["Library", "JSIL.ReadOnlyStorage.js"]);

  if (config.localStorage)
    contentManifest["JSIL"].push(["Library", "JSIL.LocalStorage.js"]);

})(jsilConfig);<|endoftext|>