function Layer(settings) {
	this.id = settings.id;
	this.name = settings.name;
	this.type = settings.type;
	this.order = settings.order;
	this.src = settings.src;
	this.uploadedsrc = settings.uploadedsrc;
	this.width = settings.width;
	this.height = settings.height;
	this.left = settings.left;
	this.top = settings.top;
	this.move = settings.move;
	this.bounded = settings.bounded;
	this.resize = settings.resize;
	this.keepratio = settings.keepratio;
	this.meta = settings.meta;
	this.selected = false;

	this.node = document.createElement("div");
	this.node.id = "CanvasLayer_" + this.id;
	this.node.style.width = this.width + "px";
	this.node.style.height = this.height + "px";
	this.node.style.left = this.left + "px";
	this.node.style.top = this.top + "px";
	dojo.addClass(this.node, "layer");
	dojo.byId("Canvas").appendChild(this.node);
	this.updateCoords();
	this.image = document.createElement("img");
	this.image.src = this.src;
	this.image.style.width = "100%";
	this.image.style.height = "100%";
	this.node.appendChild(this.image);
	
	if (this.move) {
		this.mover = new dojo.dnd.Moveable(this.node.id);
		this.mover.layer = this;
		
		dojo.connect(this.mover, "onMoved", this, "updateCoords");
	
		dojo.connect(this.node, "onmousedown", this, function (e) {
			user.user_layers.selectLayer(this.id);
		});
		
		if (this.bounded) {
			this.mover.onMove = movableBoundedOnMove;
		}
		
		if (this.resize) {
			this.handle = document.createElement("div");
			this.node.appendChild(this.handle);
			
			this.resizer = new dojox.layout.ResizeHandle({
				targetId: this.node.id,
				activeResize: false,
				animateSizing: true,
				animateMethod: "combine",
				minWidth: 25,
				minHeight: 25,
				activeResizeClass: "CanvasActiveResize",
				ratio: (this.width / this.height)
			}, this.handle);
			
			if (this.keepratio) {
				this.resizer._getNewCoords = resizeHandleConstrainedCoords;
			}
			this.resizer._changeSizing = resizeHandleChangeSizing;
	
			this.resizer.startup();
			this.resizer.handleToggler = new dojo.fx.Toggler({
				node: this.resizer.resizeHandle,
				hideDuration: user.user_layers.selector.toggler.hideDuration,
				showDuration: user.user_layers.selector.toggler.showDuration
			});
			this.resizer.handleToggler.hide();
			
			dojo.connect(this.resizer.resizeHandle, "onmousedown", function (e) {
				e.stopPropagation();
			});
			dojo.connect(this.resizer, "onResize", this, "updateCoords");
			dojo.connect(this.resizer, "_changeSizing", this, function () {
				// The animation method only exists until _changeSizing is called, so connect the connection.
				dojo.connect(this.resizer.anim, "onEnd", this, "updateCoords");
			});
		}
	}
	
	this.setOrder(this.order);
	return this;
}

Layer.prototype.setOrder = function (newOrder) {

	if (!newOrder) {
		// Find a good order number to use from layers.
		var max = 0;
		for (var i in user.user_layers.layers) {
			if (user.user_layers.layers[i].order > max) {
				max = user.user_layers.layers[i].order;
			}
		}
		newOrder = max + 1;
		// (If no layers yet, max will remain 0, and the new order will be 1. Perfect!)
	}
	this.order = newOrder;
	this.z = 10 + this.order * 2;
	this.node.style.zIndex = this.z;
}

Layer.prototype.updateCoords = function () {
	this.coords = new Object();

	this.coords.l = 0;
	this.coords.t = 0;
	this.coords.r = 0;
	this.coords.b = 0;
	this.coords.w = this.node.offsetWidth;
	this.coords.h = this.node.offsetHeight;

	var el = this.node;
	do {
		this.coords.l += el.offsetLeft;
		this.coords.t += el.offsetTop;
	} while ((el = el.offsetParent) != null);

	// The loop above gets the absolute coordinates so now 
	// get them relative to the viewport...
	user.user_layers.updateCoords();
	this.coords.l -= user.user_layers.x;
	this.coords.t -= user.user_layers.y;

	this.coords.r = this.coords.l + this.coords.w;
	this.coords.b = this.coords.t + this.coords.h;
	
	if (this.resize) {
		this.coords.handle = {
			l : this.coords.r - 15,
			t : this.coords.b - 15,
			r : this.coords.r - 2,
			b : this.coords.b - 2,
			w : 13,
			h : 13
		}
	}
	user.user_layers.selector.node.style.left = this.coords.l - user.user_layers.selector.border + "px";
	user.user_layers.selector.node.style.top = this.coords.t - user.user_layers.selector.border + "px";
	user.user_layers.selector.node.style.width = this.coords.w + "px";
	user.user_layers.selector.node.style.height = this.coords.h + "px";
	
	if (this.resize && this.coords.w > 2 * this.width) {
		if (!this.resampleAlert) {
			this.resampleAlert = true;
			user.oversampledAlert();
		}
		dojo.query("#Canvas .selector").addClass("selectorOversampled");
	} else {
		this.resampleAlert = false;
		dojo.query("#Canvas .selector").removeClass("selectorOversampled");
	}
}

Layer.prototype.onSelect = function () {
	this.selected = true;
	if (this.resize) {
		this.resizer.handleToggler.show();
	}
	this.updateCoords();
}

Layer.prototype.onUnselect = function () {
	this.selected = false;
	if (this.resize) {
		this.resizer.handleToggler.hide();
	}
}

Layer.prototype.onDelete = function () {
	if (this.move) {
		this.mover.destroy();
	}
	this.image.parentNode.removeChild(this.image);
	this.node.parentNode.removeChild(this.node);
}

Layer.prototype.moveNode = function (x, y) {
	if (this.move) {
		var l = (this.coords.l + x);
		var t = (this.coords.t + y);
		if (this.bounded) {
			var margin = 25; // How much of the layershould stay inside the viewport.
			var bounds = new Object();
			bounds.t = -1 * this.node.offsetHeight + margin;
			bounds.l = -1 * this.node.offsetWidth + margin;
			bounds.r = dojo.byId("Canvas").offsetWidth - margin;
			bounds.b = dojo.byId("Canvas").offsetHeight - margin;
			l = Math.max(Math.min(l, bounds.r), bounds.l);
			t = Math.max(Math.min(t, bounds.b), bounds.t);
		}
		this.node.style.left = l + "px";
		this.node.style.top = t + "px";
		this.updateCoords();
	}
}

Layer.prototype.alignNode = function (l, h, r, t, v, b) {
	if (this.move) {
		if (l) {
			this.node.style.left = 0 + "px";
		} else if (h) {
			this.node.style.left = ((dojo.byId("Canvas").offsetWidth - this.coords.w)/2) + "px";
		} else if (r) {
			this.node.style.left = (dojo.byId("Canvas").offsetWidth - this.coords.w) + "px";
		}
		if (t) {
			this.node.style.top = 0 + "px";
		} else if (v) {
			this.node.style.top = ((dojo.byId("Canvas").offsetHeight - this.coords.h)/2) + "px";
		} else if (b) {
			this.node.style.top = (dojo.byId("Canvas").offsetHeight - this.coords.h) + "px";
		}
		this.updateCoords();
	}
}

Layer.prototype.scaleNode = function (s) {
	if (this.resize & this.keepratio) {
		var w = (this.coords.w + s);
		var h = (w / this.resizer.ratio);
		if (w < this.resizer.minWidth) {
			w = this.resizer.minWidth;
			h = w / this.resizer.ratio;
		}
		if (h < this.resizer.minWidth) {
			h = this.resizer.minHeight;
			w = h * this.resizer.ratio;
		}
		this.node.style.width = w + "px";
		this.node.style.height = h + "px";
		this.updateCoords();
	}
}


movableBoundedOnMove = function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop) {
	// summary: called during every move notification,
	//	should actually move the node, can be overwritten.
	this.onMoving(mover, leftTop);
	
	var margin = 25; // How much of the layershould stay inside the viewport.
	var bounds = new Object();
	bounds.t = -1 * mover.node.offsetHeight + margin;
	bounds.l = -1 * mover.node.offsetWidth + margin;
	bounds.r = dojo.byId("Canvas").offsetWidth - margin;
	bounds.b = dojo.byId("Canvas").offsetHeight - margin;
	
	var s = mover.node.style;
	s.left = Math.max(Math.min(leftTop.l, bounds.r), bounds.l) + "px";
	s.top  = Math.max(Math.min(leftTop.t, bounds.b), bounds.t) + "px";
	this.onMoved(mover, leftTop);
}

resizeHandleConstrainedCoords = function (e) {
	// summary: Overridden coords function to constraint width/height to original aspect ratio.
	
	// On IE, if you move the mouse above/to the left of the object being resized,
	// sometimes clientX/Y aren't set, apparently.  Just ignore the event.
	try{
		if(!e.clientX  || !e.clientY){ return false; }
	}catch(e){
		// sometimes you get an exception accessing above fields...
		return false;
	}
	this._activeResizeLastEvent = e; 

	var dx = this.startPoint.x - e.clientX;
	var dy = this.startPoint.y - e.clientY;
	
	var newW = (this._resizeX) ? this.startSize.w - dx : this.startSize.w;
	var newH = (this._resizeY) ? this.startSize.h - dy : this.startSize.h;

	// constraint ratio
	if ((newW / newH) > this.ratio) {
		newW = newH * this.ratio;
	} else {
		newH = newW / this.ratio;
	}
	
	// minimum size check
	if(this.minSize){
		//var mb = dojo.marginBox(this.targetDomNode);
		if(newW < this.minSize.w){
			newW = this.minSize.w;
			// keep the ratio in check
			newH = newW / this.ratio;
		}
		if(newH < this.minSize.h){
			newH = this.minSize.h;
			// keep the ratio in check
			newW = newH * this.ratio;
		}
	}
	
	return {w:newW, h:newH};  // Object
}

resizeHandleChangeSizing = function(/*Event*/ e){
		// summary: apply sizing information based on information in (e) to attached node
		var tmp = this._getNewCoords(e);
		if(tmp===false){ return; }

		if(this.targetWidget && typeof this.targetWidget.resize == "function"){ 
			this.targetWidget.resize(tmp);
		}else{
			if(this.animateSizing){
				this.anim = dojo.fx[this.animateMethod]([
					dojo.animateProperty({
						node: this.targetDomNode,
						properties: { 
							width: { start: this.startSize.w, end: tmp.w, unit:'px' } 
						},	
						duration: this.animateDuration
					}),
					dojo.animateProperty({
						node: this.targetDomNode,
						properties: { 
							height: { start: this.startSize.h, end: tmp.h, unit:'px' }
						},
						duration: this.animateDuration
					})
				]);
				this.anim.play();
			}else{
				dojo.style(this.targetDomNode,"width",tmp.w+"px"); 
				dojo.style(this.targetDomNode,"height",tmp.h+"px");
			}
		}	
	}