<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/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * Copyright (c) 2011, <NAME> & <NAME>
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * All rights reserved.
 */

var View = this.View = Base.extend({
	/** @lends View# */

	beans: true,

	// DOCS: View: there is alot left to document
	// TODO: Add bounds parameter that defines position within canvas?
	// Find a good name for these bounds, since #bounds is already the artboard
	// bounds of the visible area.
	/**
	 * Creates a view object
	 * @param {Canvas} canvas
	 * @constructs View
	 * 
	 * @class The View object..
	 */
	initialize: function(canvas) {
		// Associate this view with the active paper scope.
		this._scope = paper;
		// Push it onto project.views and set index:
		this._index = this._scope.views.push(this) - 1;
		// Handle canvas argument
		var size;
		if (canvas && canvas instanceof HTMLCanvasElement) {
			this._canvas = canvas;
			var offset = DomElement.getOffset(canvas);
			// If the canvas has the resize attribute, resize the it to fill the
			// window and resize it again whenever the user resizes the window.
			if (canvas.attributes.resize) {
				size = DomElement.getWindowSize().subtract(offset);
				canvas.width = size.width;
				canvas.height = size.height;
				var that = this;
				DomEvent.add(window, {
					resize: function(event) {
						// Only get canvas offset if it's not invisible (size is
						// 0, 0), as otherwise the offset would be wrong.
						if (!DomElement.getSize(canvas).equals([0, 0]))
							offset = DomElement.getOffset(canvas);
						// Set the size now, which internally calls onResize
						that.setViewSize(
								DomElement.getWindowSize().subtract(offset));
						// If there's a _onFrameCallback, call it staight away,
						// but without requesting another animation frame.
						if (that._onFrameCallback) {
							that._onFrameCallback(0, true);
						} else {
							that.draw();
						}
					}
				});
			} else {
				size = Size.create(canvas.offsetWidth, canvas.offsetHeight);
			}
			// TODO: Test this on IE:
			if (canvas.attributes.stats) {
				this._stats = new Stats();
				// Align top-left to the canvas
				var element = this._stats.domElement,
					style = element.style;
				style.position = 'absolute';
				style.left = offset.x + 'px';
				style.top = offset.y + 'px';
				document.body.appendChild(element);
			}
		} else {
			// 2nd argument onwards could be view size, otherwise use default:
			size = Size.read(arguments, 1);
			if (size.isZero())
				size = new Size(1024, 768);
			this._canvas = CanvasProvider.getCanvas(size);
		}
		this._viewBounds = LinkedRectangle.create(this, 'setViewBounds',
				0, 0, size.width, size.height);
		this._context = this._canvas.getContext('2d');
		this._matrix = new Matrix();
		this._zoom = 1;
		this._events = this._createEvents();
		DomEvent.add(this._canvas, this._events);
		// Make sure the first view is focused for keyboard input straight away
		if (!View.focused)
			View.focused = this;
	},

	/**
	 * The bounds of the view, i.e. the bounds of the part of the project which
	 * is visible in the window.
	 * 
	 * @type Rectangle
	 * @bean
	 */
	getViewBounds: function() {
		return this._viewBounds;
	},

	setViewBounds: function(bounds) {
		bounds = Rectangle.read(arguments);
		var size = bounds.getSize(),
			delta = size.subtract(this._viewBounds.getSize());
		// TODO: Take into acount bounds.x/y and decide on what grounds to
		// change canvas size. Also, if x/y is not 0, do we need to add that
		// to transform, or is that up to the user?
		this._canvas.width = size.width;
		this._canvas.height = size.height;
		// Call onResize handler on any size change
		if (this.onResize) {
			this.onResize({
				size: size,
				delta: delta
			});
		}
		// Force recalculation
		this._bounds = null;
	},

	/**
	 * @type Size
	 * @bean
	 */
	getViewSize: function() {
		return this._viewBounds.getSize();
	},

	setViewSize: function(size) {
		this._viewBounds.setSize.apply(this._viewBounds, arguments);
	},

	/**
	 * @type Size
	 * @bean
	 */
	getBounds: function() {
		if (!this._bounds)
			this._bounds = this._matrix._transformBounds(this._viewBounds);
		return this._bounds;
	},

	/**
	 * @type Size
	 * @bean
	 */
	getSize: function() {
		return this.getBounds().getSize();
	},

	/**
	 * @type Point
	 * @bean
	 */
	getCenter: function() {
		return this.getBounds().getCenter();
	},

	setCenter: function(center) {
		this.scrollBy(Point.read(arguments).subtract(this.getCenter()));
	},

	/**
	 * @type Number
	 * @bean
	 */
	getZoom: function() {
		return this._zoom;
	},

	setZoom: function(zoom) {
		// TODO: Clamp the view between 1/32 and 64, just like Illustrator?
		this._transform(new Matrix().scale(zoom / this._zoom, this.getCenter()));
		this._zoom = zoom;
	},

	/**
	 * @param {Point} point
	 */
	scrollBy: function(point) {
		this._transform(new Matrix().translate(Point.read(arguments).negate()));
	},

	_transform: function(matrix, flags) {
		this._matrix.preConcatenate(matrix);
		// Force recalculation of these values next time they are requested.
		this._bounds = null;
		this._inverse = null;
	},

	draw: function() {
		if (this._stats)
			this._stats.update();
		// Initial tests conclude that clearing the canvas using clearRect
		// is always faster than setting canvas.width = canvas.width
		// http://jsperf.com/clearrect-vs-setting-width/7
		var ctx =this._context,
			bounds = this._viewBounds;
		ctx.clearRect(bounds._x, bounds._y,
				// TODO: +1... what if we have multiple views in one canvas? 
				bounds._width + 1, bounds._height + 1);

		ctx.save();
		this._matrix.applyToContext(ctx);
		// Just draw the active project for now
		this._scope.project.draw(ctx);
		ctx.restore();
	},

	activate: function() {
		this._scope.view = this;
	},

	remove: function() {
		var res = Base.splice(this._scope.views, null, this._index, 1);
		// Uninstall event handlers again for this view.
		DomEvent.remove(this._canvas, this._events);
		// Clearing _onFrame makes the frame handler stop automatically.
		this._scope = this._canvas = this._events = this._onFrame = null;
		return !!res.length;
	},

	// TODO: getInvalidBounds
	// TODO: invalidate(rect)
	// TODO: style: artwork / preview / raster / opaque / ink
	// TODO: getShowGrid
	// TODO: getMousePoint
	// TODO: artworkToView(rect)

	// TODO: Consider naming these projectToView, viewToProject
	artworkToView: function(point) {
		return this._matrix._transformPoint(Point.read(arguments));
	},

	viewToArtwork: function(point) {
		return this._getInverse()._transformPoint(Point.read(arguments));
	},

	_getInverse: function() {
		if (!this._inverse)
			this._inverse = this._matrix.createInverse();
		return this._inverse;
	},

	/**
	 * Handler function that is called whenever a view gets resized.
	 * 
	 * @type function
	 */
	onResize: null,

	/**
	 * Handler function to be called on each frame of an animation.
	 * 
	 * @type function
	 * @bean
	 */
	getOnFrame: function() {
		return this._onFrame;
	},

	setOnFrame: function(onFrame) {
		this._onFrame = onFrame;
		if (!onFrame) {
			delete this._onFrameCallback;
			return;
		}
		var that = this,
			requested = false,
			before,
			time = 0,
			count = 0;
		this._onFrameCallback = function(param, dontRequest) {
			requested = false;
			if (!that._onFrame)
				return;
			// Set the global paper object to the current scope
			paper = that._scope;
			// Request next frame already
			requested = true;
			if (!dontRequest) {
				DomEvent.requestAnimationFrame(that._onFrameCallback,
						that._canvas);
			}
			var now = Date.now() / 1000,
			 	delta = before ? now - before : 0;
			that._onFrame({
				delta: delta, // Time elapsed since last redraw in seconds
				time: time += delta, // Time since first call of frame() in seconds
				count: count++
			});
			before = now;
			// Automatically draw view on each frame.
			that.draw();
		};
		// Call the onFrame handler straight away, initializing the sequence
		// of onFrame calls.
		if (!requested)
			this._onFrameCallback();
	},

	_createEvents: function() {
		var that = this,
			tool,
			timer,
			curPoint,
			dragging = false;
<commit_msg>View: Move onResize below onFrame and document both of them.<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/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * Copyright (c) 2011, <NAME> & <NAME>
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * All rights reserved.
 */

var View = this.View = Base.extend({
	/** @lends View# */

	beans: true,

	// DOCS: View: there is alot left to document
	// TODO: Add bounds parameter that defines position within canvas?
	// Find a good name for these bounds, since #bounds is already the artboard
	// bounds of the visible area.
	/**
	 * Creates a view object
	 * @param {Canvas} canvas
	 * @constructs View
	 * 
	 * @class The View object..
	 */
	initialize: function(canvas) {
		// Associate this view with the active paper scope.
		this._scope = paper;
		// Push it onto project.views and set index:
		this._index = this._scope.views.push(this) - 1;
		// Handle canvas argument
		var size;
		if (canvas && canvas instanceof HTMLCanvasElement) {
			this._canvas = canvas;
			var offset = DomElement.getOffset(canvas);
			// If the canvas has the resize attribute, resize the it to fill the
			// window and resize it again whenever the user resizes the window.
			if (canvas.attributes.resize) {
				size = DomElement.getWindowSize().subtract(offset);
				canvas.width = size.width;
				canvas.height = size.height;
				var that = this;
				DomEvent.add(window, {
					resize: function(event) {
						// Only get canvas offset if it's not invisible (size is
						// 0, 0), as otherwise the offset would be wrong.
						if (!DomElement.getSize(canvas).equals([0, 0]))
							offset = DomElement.getOffset(canvas);
						// Set the size now, which internally calls onResize
						that.setViewSize(
								DomElement.getWindowSize().subtract(offset));
						// If there's a _onFrameCallback, call it staight away,
						// but without requesting another animation frame.
						if (that._onFrameCallback) {
							that._onFrameCallback(0, true);
						} else {
							that.draw();
						}
					}
				});
			} else {
				size = Size.create(canvas.offsetWidth, canvas.offsetHeight);
			}
			// TODO: Test this on IE:
			if (canvas.attributes.stats) {
				this._stats = new Stats();
				// Align top-left to the canvas
				var element = this._stats.domElement,
					style = element.style;
				style.position = 'absolute';
				style.left = offset.x + 'px';
				style.top = offset.y + 'px';
				document.body.appendChild(element);
			}
		} else {
			// 2nd argument onwards could be view size, otherwise use default:
			size = Size.read(arguments, 1);
			if (size.isZero())
				size = new Size(1024, 768);
			this._canvas = CanvasProvider.getCanvas(size);
		}
		this._viewBounds = LinkedRectangle.create(this, 'setViewBounds',
				0, 0, size.width, size.height);
		this._context = this._canvas.getContext('2d');
		this._matrix = new Matrix();
		this._zoom = 1;
		this._events = this._createEvents();
		DomEvent.add(this._canvas, this._events);
		// Make sure the first view is focused for keyboard input straight away
		if (!View.focused)
			View.focused = this;
	},

	/**
	 * The bounds of the view, i.e. the bounds of the part of the project which
	 * is visible in the window.
	 * 
	 * @type Rectangle
	 * @bean
	 */
	getViewBounds: function() {
		return this._viewBounds;
	},

	setViewBounds: function(bounds) {
		bounds = Rectangle.read(arguments);
		var size = bounds.getSize(),
			delta = size.subtract(this._viewBounds.getSize());
		// TODO: Take into acount bounds.x/y and decide on what grounds to
		// change canvas size. Also, if x/y is not 0, do we need to add that
		// to transform, or is that up to the user?
		this._canvas.width = size.width;
		this._canvas.height = size.height;
		// Call onResize handler on any size change
		if (this.onResize) {
			this.onResize({
				size: size,
				delta: delta
			});
		}
		// Force recalculation
		this._bounds = null;
	},

	/**
	 * @type Size
	 * @bean
	 */
	getViewSize: function() {
		return this._viewBounds.getSize();
	},

	setViewSize: function(size) {
		this._viewBounds.setSize.apply(this._viewBounds, arguments);
	},

	/**
	 * @type Size
	 * @bean
	 */
	getBounds: function() {
		if (!this._bounds)
			this._bounds = this._matrix._transformBounds(this._viewBounds);
		return this._bounds;
	},

	/**
	 * @type Size
	 * @bean
	 */
	getSize: function() {
		return this.getBounds().getSize();
	},

	/**
	 * @type Point
	 * @bean
	 */
	getCenter: function() {
		return this.getBounds().getCenter();
	},

	setCenter: function(center) {
		this.scrollBy(Point.read(arguments).subtract(this.getCenter()));
	},

	/**
	 * @type Number
	 * @bean
	 */
	getZoom: function() {
		return this._zoom;
	},

	setZoom: function(zoom) {
		// TODO: Clamp the view between 1/32 and 64, just like Illustrator?
		this._transform(new Matrix().scale(zoom / this._zoom, this.getCenter()));
		this._zoom = zoom;
	},

	/**
	 * @param {Point} point
	 */
	scrollBy: function(point) {
		this._transform(new Matrix().translate(Point.read(arguments).negate()));
	},

	_transform: function(matrix, flags) {
		this._matrix.preConcatenate(matrix);
		// Force recalculation of these values next time they are requested.
		this._bounds = null;
		this._inverse = null;
	},

	draw: function() {
		if (this._stats)
			this._stats.update();
		// Initial tests conclude that clearing the canvas using clearRect
		// is always faster than setting canvas.width = canvas.width
		// http://jsperf.com/clearrect-vs-setting-width/7
		var ctx =this._context,
			bounds = this._viewBounds;
		ctx.clearRect(bounds._x, bounds._y,
				// TODO: +1... what if we have multiple views in one canvas? 
				bounds._width + 1, bounds._height + 1);

		ctx.save();
		this._matrix.applyToContext(ctx);
		// Just draw the active project for now
		this._scope.project.draw(ctx);
		ctx.restore();
	},

	activate: function() {
		this._scope.view = this;
	},

	remove: function() {
		var res = Base.splice(this._scope.views, null, this._index, 1);
		// Uninstall event handlers again for this view.
		DomEvent.remove(this._canvas, this._events);
		// Clearing _onFrame makes the frame handler stop automatically.
		this._scope = this._canvas = this._events = this._onFrame = null;
		return !!res.length;
	},

	// TODO: getInvalidBounds
	// TODO: invalidate(rect)
	// TODO: style: artwork / preview / raster / opaque / ink
	// TODO: getShowGrid
	// TODO: getMousePoint
	// TODO: artworkToView(rect)

	// TODO: Consider naming these projectToView, viewToProject
	artworkToView: function(point) {
		return this._matrix._transformPoint(Point.read(arguments));
	},

	viewToArtwork: function(point) {
		return this._getInverse()._transformPoint(Point.read(arguments));
	},

	_getInverse: function() {
		if (!this._inverse)
			this._inverse = this._matrix.createInverse();
		return this._inverse;
	},

	/**
	 * {@grouptitle Event Handlers}
	 * Handler function to be called on each frame of an animation.
	 * The function receives an event object which contains information about
	 * the frame event:
	 * 
	 * <b>event.count</b>: the number of times the frame event was fired.
	 * <b>event.time</b>: the total amount of time passed since the first frame
	 * event in seconds.
	 * <b>event.delta</b>: the time passed in seconds since the last frame
	 * event.
	 * 
	 * @example
	 * // Creating an animation:
	 * 
	 * // Create a rectangle shaped path between {x: 20, y: 20}
	 * // and {x: 50, y: 50}:
	 * var path = new Path.Rectangle([20, 20], [50, 50]);
	 * path.fillColor = 'black';
	 * 
	 * function onFrame(event) {
	 * 	// Every frame, rotate the path by 1 degree:
	 * 	path.rotate(1);
	 * }
	 * 
	 * @type function
	 * @bean
	 */
	getOnFrame: function() {
		return this._onFrame;
	},

	setOnFrame: function(onFrame) {
		this._onFrame = onFrame;
		if (!onFrame) {
			delete this._onFrameCallback;
			return;
		}
		var that = this,
			requested = false,
			before,
			time = 0,
			count = 0;
		this._onFrameCallback = function(param, dontRequest) {
			requested = false;
			if (!that._onFrame)
				return;
			// Set the global paper object to the current scope
			paper = that._scope;
			// Request next frame already
			requested = true;
			if (!dontRequest) {
				DomEvent.requestAnimationFrame(that._onFrameCallback,
						that._canvas);
			}
			var now = Date.now() / 1000,
			 	delta = before ? now - before : 0;
			that._onFrame({
				delta: delta, // Time elapsed since last redraw in seconds
				time: time += delta, // Time since first call of frame() in seconds
				count: count++
			});
			before = now;
			// Automatically draw view on each frame.
			that.draw();
		};
		// Call the onFrame handler straight away, initializing the sequence
		// of onFrame calls.
		if (!requested)
			this._onFrameCallback();
	},

	/**
	 * Handler function that is called whenever a view is resized.
	 * 
	 * @example
	 * // Repositioning items when a view is resized:
	 * 
	 * // Create a circle shaped path in the center of the view:
	 * var path = new Path.Circle(view.bounds.center, 30);
	 * path.fillColor = 'red';
	 * 
	 * function onResize(event) {
	 * 	// Whenever the view is resized, move the path to its center:
	 * 	path.position = view.center;
	 * }
	 * 
	 * @type function
	 */
	onResize: null,

	_createEvents: function() {
		var that = this,
			tool,
			timer,
			curPoint,
			dragging = false;

{'fraction_non_alphanumeric': 0.12882035294772587, 'fraction_numerical': 0.0056783388075488505, 'mean_word_length': 3.005351170568562, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '454534', 'n_tokens_mistral': 6531, 'n_tokens_neox': 5533, 'n_words': 1696}