80952// PEN  

zim.Pen = function(size, color, penType, damp, spread, borderColor, borderWidth, end, paper, nib, cache, ctrlKey, cropScale, undo, undoKeys, move, onTop, deleteable, doubleClickDelete, holdDelete, immediateStop, lineAlpha, lineBlendMode, frame, dashed, pullColor, pullThickness, style, group, inherit) {
	var sig = "size, color, penType, damp, spread, borderColor, borderWidth, end, paper, nib, cache, ctrlKey, cropScale, undo, undoKeys, move, onTop, deleteable, doubleClickDelete, holdDelete, immediateStop, lineAlpha, lineBlendMode, frame, dashed, pullColor, pullThickness, style, group, inherit";
	var duo; if (duo = zob(zim.Pen, arguments, sig, this)) return duo;
	this.zimContainer_constructor();
	this.type = "Pen";
	this.group = group;
	var DS = style===false?{}:zim.getStyle(this.type, this.group, inherit);

	if (zot(penType)) penType = DS.penType!=null?DS.penType:"line";
	var base = {
		size:2,
		color:zim.dark,
		spread:{min:5,max:20},
		borderColor:zim.darker,
		borderWidth:0,
	};
	var defaults = {
		line:{size:2},
		kitetail:{
			size:{min:5, max:20},
			color:zim.series(zim.pink,zim.blue,zim.green),
			borderColor:"rgba(0,0,0,.5)",
			borderWidth:1
		},
		grass:{
			color:zim.green,
			size:3,
			spread:{min:10, max:30}
		},
		hair:{
			color:[zim.pink, zim.blue],
			size:3,
			spread:{min:20, max:50}
		},
		city:{
			size:{min:30, max:70},
			spread:{min:50, max:200},
			color:[zim.dark,zim.darker,zim.grey],
		},
		barbwire:{},
		splatter:{
			size:{min:5, max:20},
			color:"rgba(0,0,0,.5)"
		}
	};
	if (zot(defaults[penType])) penType = "line";
	
	if (zot(size)) size = DS.size!=null?DS.size:zot(defaults[penType].size)?base.size:defaults[penType].size;
	if (zot(color)) color = DS.color!=null?DS.color:zot(defaults[penType].color)?base.color:defaults[penType].color;
	if (zot(damp)) damp = DS.damp!=null?DS.damp:.5;
	if (damp === false) damp = 1;
	if (zot(spread)) spread = DS.spread!=null?DS.spread:zot(defaults[penType].spread)?base.spread:defaults[penType].spread;
	if (zot(borderColor)) borderColor = DS.borderColor!=null?DS.borderColor:zot(defaults[penType].borderColor)?base.borderColor:defaults[penType].borderColor;
	if (zot(borderWidth)) borderWidth = DS.borderWidth!=null?DS.borderWidth:zot(defaults[penType].borderWidth)?base.borderWidth:defaults[penType].borderWidth;
	if (zot(end)) end = DS.end!=null?DS.end:(zim.isPick(size) || zim.isPick(color) || penType!="line")?"butt":"round"; // also round and square
	if (zot(cropScale)) cropScale = DS.cropScale!=null?DS.cropScale:1;
	if (zot(cache)) cache = DS.cache!=null?DS.cache:true;
	if (zot(undo)) undo = DS.undo!=null?DS.undo:0;
	if (zot(undoKeys)) undoKeys = DS.undoKeys!=null?DS.undoKeys:true;
	if (zot(move)) move = DS.move!=null?DS.move:true;
	if (zot(deleteable)) deleteable = DS.deleteable!=null?DS.deleteable:true;
	if (zot(doubleClickDelete)) doubleClickDelete = DS.doubleClickDelete!=null?DS.doubleClickDelete:true;
	if (zot(holdDelete)) holdDelete = DS.holdDelete!=null?DS.holdDelete:true;
	if (zot(onTop)) onTop = DS.onTop!=null?DS.onTop:true;
	if (zot(immediateStop)) immediateStop = DS.immediateStop!=null?DS.immediateStop:"both";
	if (zot(lineAlpha)) lineAlpha = DS.lineAlpha!=null?DS.lineAlpha:1;
	if (zot(lineBlendMode)) lineBlendMode = DS.lineBlendMode!=null?DS.lineBlendMode:"normal";
	
	if (zot(paper)) paper = DS.paper!=null?DS.paper:new zim.Container();
	if (zot(nib)) nib = DS.nib!=null?DS.nib.clone():null;
	if (zot(frame)) frame = DS.frame!=null?DS.frame:WW.zdf;
	if (zot(dashed)) dashed = DS.dashed!=null?DS.dashed:null;
	if (dashed === true) dashed = [3,100];
	if (zot(pullColor)) pullColor = DS.pullColor!=null?DS.pullColor:null;
	if (zot(pullThickness)) pullThickness = DS.pullThickness!=null?DS.pullThickness:1;
		
	var that = this;        
	this.dampX = new zim.Damp(null, damp);
	this.dampY = new zim.Damp(null, damp);
	var _draw = true;
	this.drawing = false;
	this.immediateStop = immediateStop;
	this.undoLevels = undo;
	this.undoKeys = undoKeys;
	this.move = move;		
	this.draggable = true;
	this.lineAlpha = lineAlpha;
	this.lineBlendMode = lineBlendMode;
	var shape = that.shape = new zim.Shape();
	var w = frame.stage.width;
	var h = frame.stage.height;
	var startXCheck;
	var startYCheck;
	var startLayer;
	var endLayer;
	that.paperNum = 0;
	that.sizeScale = 1;
	that.spreadScale = 1;
	that.sizeFactor = 1;
	that.spreadFactor = 1;
	that.pullThickness = pullThickness;
	that.stop = function() {};
	var stage;
	
	this.added(function (s) {
		stage = s;
		that.paper = paper;
		w = that.stage.width;
		h = that.stage.height;
		if (!zot(nib)) {
			if (that.zimDown) {
				that.nib = nib.addTo(that);					
			} else {
				// nib not added to that as that is placed by motionController probably...
				// and the two damping systems fight one another
				// so place pen at cursor - except when dragging
				that.nib = nib.addTo(that.parent, that.parent.getChildIndex(that));
				that.nib.x = that.x;
				that.nib.y = that.y;
				that.nib.mouseEnabled = false;
				that.stageDown = stage.on("stagemousedown", function(e) {
					stage = e.target.stage;
					// moveNib(e); // might want to do this but then would move if dragging existing
					that.nibEvent = stage.on("stagemousemove", moveNib);
					that.dispatchEvent("start");
				});				
				that.stageUp = stage.on("stagemouseup", function() {
					stage.off("stagemousemove", that.nibEvent);
				});					
			}				
		} else {
			that.stageDown = stage.on("stagemousedown", function(e) {					
				that.dispatchEvent("start");
			});	
		}
		function moveNib(e) {
			if (!that.drawing) return;
			that.nib.x = e.stageX/zim.scaX;
			that.nib.y = e.stageY/zim.scaY;
		}
		that.zimDragCheck = false;
	
		that.stop = function() {that.stopCheck(true);};
		that.infinite = false;
		that.stopCheck = function(override) {
	
			if (!override) {
				if (that.infinite) return;
				if (that.zimDragCheck) return;
				if (!that.immediateStop && that.drawing) return;
			} else { // override - stop for sure!
				that.drawing = false;
				that.infinite = false;
			}
			that.immediate(that.x, that.y);				
	
			setTimeout(function(){
				var line;
				if (that.drawing) return;
				if (cache) {
					shape.cache(-(w*cropScale-w)/2, -(h*cropScale-h)/2, w*cropScale, h*cropScale);
					line = paper.bitmap = new zim.Bitmap(shape.cacheCanvas).reg((w*cropScale-w)/2, (h*cropScale-h)/2).addTo(paper);
					shape.graphics.clear();
					shape.uncache();
					shape.top();
				} else {
					line = shape;
				}
				if (that.undoLevels > 0) {
					var data = {paper:paper, line:line};
					undo.push(data);
					if (undo.length > that.undoLevels) undo.shift(); // take off front
					that.dispatchEvent("recordUndo");
				}
				if (!cache)  shape = new zim.Shape().clone().addTo(paper);
				that.lastSegment = line;
				that.lastSelected = line;
				line.alpha = that.lineAlpha;
				line.blendMode = that.lineBlendMode;
				line.paper = paper;
				that.dispatchEvent("stop");
				that.dispatchEvent("change");
			}, (that.immediateStop||override?0:50));
		}; // end stopCheck
	
		var count = 0;
		that.dampX.immediate(that.x);
		that.dampY.immediate(that.y);
		that.lastX = that.lastMidX = that.drawX = that.x;
		that.lastY = that.lastMidY = that.drawY = that.y;
		
		
		var lastColor;
		var lastSize;

		that.ticker = zim.Ticker.add(function () {
	
			if (!that.parent) return;
				
			var newX = that.dampX.convert(_draw?that.x:that.finishX);
			var newY = that.dampY.convert(_draw?that.y:that.finishY);
			var newPoint = that.parent.localToLocal(newX, newY, shape);
	
			if (Math.abs(that.lastX-newX)+Math.abs(that.lastY-newY)<1) {
				if (that.drawing) {
					that.drawing = false;
					if (that.zimDown) { // being dragged
						that.x = that.lastX = that.lastMidX = _draw?that.x:that.finishX;
						that.y = that.lastY = that.lastMidY = _draw?that.y:that.finishY;							
						// that.dampX.immediate(that.x); // removed in ZIM ZIM 02 to avoid shards
						// that.dampY.immediate(that.y);
					} else {
						that.x = that.finishX = that.lastX = that.lastMidX = newX;
						that.y = that.finishY = that.lastY = that.lastMidY = newY;
					}
					that.stopCheck();
					lastColor = lastSize = null;
				}
				that.lastX = that.lastMidX = newX;
				that.lastY = that.lastMidY = newY;
				if (paper) {
					var mm = paper.localToLocal(0, 0, that.parent);					
					that.lastMidX-=mm.x;
					that.lastMidY-=mm.y;
				}
				return;
			} else { 
				var pickedSize = zim.Pick.choose(size)*that.sizeScale*that.sizeFactor;
				var pickedSpread = zim.Pick.choose(spread)*that.spreadScale*that.spreadFactor;
				if (!that.drawing) {
					// drawing starting again
					redo = [];
					if (paper.getChildIndex(shape) != paper.numChildren-1) shape.top();
				}
				that.drawing = true;
				that.dispatchEvent("drawing");
			}
					
			var lastPoint = that.parent.localToLocal(that.lastX, that.lastY, shape);
			
			var lastMidPoint;				
			if (paper) lastMidPoint = paper.localToLocal(that.lastMidX, that.lastMidY, shape);	
			else lastMidPoint = that.parent.localToLocal(that.lastMidX, that.lastMidY, shape);	
			
			var midX, midY, midPoint;
			if (penType == "splatter") {					
				for (var i=0; i<=3; i++) {
					var angle = zim.rand(360)*Math.PI/180;
					var d = pickedSpread;
					var loc = {x:newX+d*Math.cos(angle), y:newY+d*Math.sin(angle)};
					var point = that.parent.localToLocal(loc.x,loc.y, shape);
					shape.graphics.mt(point.x, point.y).f(zim.Pick.choose(color)).dc(point.x, point.y, pickedSize/2);
				}
			} else if (penType == "grass" || penType == "hair" || penType == "city") {
				if (penType == "grass" || penType == "hair") { // add more
					midX = that.lastX+(newX-that.lastX)/2;
					midY = that.lastY+(newY-that.lastY)/2;
					midPoint = that.parent.localToLocal(midX,midY, shape);
					shape.graphics
						.s(zim.Pick.choose(color)).ss(pickedSize, end)
						.mt(midPoint.x, midPoint.y)
						.lt(midPoint.x + zim.rand(-pickedSpread/4, pickedSpread/4), penType=="hair"?midPoint.y+pickedSpread:midPoint.y-pickedSpread);
				}
				if (penType == "grass" || penType == "hair" || (penType == "city" && count%3==0)) {
					shape.graphics
						.s(zim.Pick.choose(color)).ss(pickedSize, end)
						.mt(newPoint.x, newPoint.y)
						.lt(newPoint.x+(penType=="city"?0:zim.rand(-pickedSpread/4, pickedSpread/4)), penType=="hair"?midPoint.y+pickedSpread:newPoint.y-pickedSpread);
				}
				count++;
			} else {
				if (penType == "kitetail") {						
					shape.graphics.s(zim.Pick.choose(color)).ss(pickedSize, end);
				}
				if (penType == "barbwire") {
					shape.graphics.s(zim.Pick.choose(color)).ss(pickedSize, end);
					midX = that.lastX+(newX-that.lastX)/2+zim.rand(-pickedSpread, pickedSpread);
					midY = that.lastY+(newY-that.lastY)/2+zim.rand(-pickedSpread, pickedSpread);
				} else {
					midX = that.lastX+(newX-that.lastX)/2;
					midY = that.lastY+(newY-that.lastY)/2;
				}			
				midPoint = that.parent.localToLocal(midX,midY, shape);
				if (penType != "line") {							
					shape.graphics
						// .mt(lastMidPoint.x, lastMidPoint.y)							
						.qt(lastPoint.x, lastPoint.y, midPoint.x, midPoint.y); // adjusted Cat 04
				}
			}		
			
			if (borderWidth>0 && penType != "line") {
				midX = that.lastX+(newX-that.lastX)/2;
				midY = that.lastY+(newY-that.lastY)/2;
				midPoint = that.parent.localToLocal(midX,midY, shape);
				shape.graphics
					.s(zim.Pick.choose(borderColor))
					.ss(zim.Pick.choose(borderWidth))
					.mt(lastMidPoint.x, lastMidPoint.y)
					.qt(lastPoint.x, lastPoint.y, midPoint.x, midPoint.y); // adjusted Cat 04
				if (penType == "splatter") shape.graphics.es();
			}
	
			if (penType == "splatter") {
				shape.graphics.f(zim.Pick.choose(color));
			// } else if (penType == "kitetail") {
			} else if (penType != "kitetail") {
				if (penType=="line") {
					if (zim.isPick(color) || pickedSize != lastSize) {
						var colo = zim.Pick.choose(color);
						if (colo!=lastColor || pickedSize != lastSize) {
							shape.graphics.s(colo).ss(pickedSize, end);
						}
						lastColor = colo;
						lastSize = pickedSize;
					}
				} else shape.graphics.s(zim.Pick.choose(color)).ss(pickedSize, end);
			}
			if (penType != "grass" && penType != "hair" && penType != "city" && penType != "splatter") {				
				shape.graphics
					.mt(lastMidPoint.x, lastMidPoint.y)
					// .qt(midPoint.x, midPoint.y, newPoint.x, newPoint.y);
					.qt(lastPoint.x, lastPoint.y, midPoint.x, midPoint.y);
			}		
			if (dashed) shape.graphics.sd(dashed);	
		
			that.lastX = newX;
			that.lastY = newY;
			if (midPoint) {
				that.lastMidX = midPoint.x;
				that.lastMidY = midPoint.y;
			}
			that.drawX = that.lastMidX || 0;
			that.drawY = that.lastMidY || 0;
			
		}); // end Ticker


		if (!zot(pullColor)) addPullColor(stage);
		
		that.mouseChildren = false;			
	
	});


	function addPullColor(stage) {
		var F = stage.frame;			
		if (that.ppdEvent) that.off("drawing", that.ppdEvent);
		if (that.ppsEvent) that.off("stop", that.ppsEvent);
		if (that.ppmEvent) stage.off("stagemousedown", that.ppmEvent);
		if (!that.pullShape) that.pullShape = new zim.Shape();
		that.ppdEvent = that.on("drawing", function() {
			setTimeout(function(){
				var p = paper.localToGlobal(that.drawX, that.drawY);
				that.pullShape.c().s(pullColor).ss(that.pullThickness).mt(F.mouseX, F.mouseY).lt(p.x, p.y);
			},10);				
		});
		that.ppsEvent = that.on("stop", function() {
			that.pullShape.removeFrom();
			that.pullShape.c()
		});
		that.ppmEvent = stage.on("stagemousedown", function() {
			that.pullShape.addTo(stage);
			that.immediate(F.mouseX, F.mouseY);
			if (that.motionController) that.motionController.immediate(F.mouseX, F.mouseY);
		});
	}
	function removePullColor() {
		if (that.ppdEvent) that.off("drawing", that.ppdEvent);
		if (that.ppsEvent) that.off("stop", that.ppsEvent);
		if (that.ppmEvent) that.off("stagemousedown", that.ppmEvent);
		if (that.pullShape) that.pullShape.dispose();				
	}
	
	
	// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// UNDO REDO FUNCTIONALITY
	// undo data: {paper:Container, paperTransform:{}, line:Bitmap/Shape, lineTransform:{}}
	
	undo = [];
	var redo = [];
	
	this.saveState = function(obj, startLayer, endLayer) { // called by dragging paper or line segment
		var t = obj.type=="Container"?"paper":"line";
		var data = {paper:that.paper};
		data[t] = obj;
		data[t+"Transform"] = {x:obj.x, y:obj.y, r:obj.rotation, a:obj.alpha, rX:obj.regX, rY:obj.regY, sX:obj.scaleX, sY:obj.scaleY, skX:obj.skewX, skY:obj.skewY, v:obj.visible};
		if (!zot(startLayer) && !zot(endLayer) && startLayer != endLayer) {
			data.startLayer = startLayer;
			data.endLayer = endLayer;
		}
		undo.push(data);
		that.dispatchEvent("recordUndo");
		if (undo.length > that.undoLevels) undo.shift(); // take off front
	};
	var defaultTransform = {x:0, y:0, r:0, a:1, rX:0, rY:0, sX:1, sY:1, skX:0, skY:0, v:true};
	this.undo = function() {
		var data = undo.pop();
		if (data) {
			redo.push(data);
			if (data.clear) {
				for (var i=0; i<data.clear.length; i++) {
					data.paper.addChild(data.clear[i]);
				}
				data.paper.addChild(shape); // add back to top
			} else if (data.line && data.lineTransform) { // undo line transform
				if (!zot(data.startLayer)) { // watch - could be 0
					shape.top();
					data.paper.setChildIndex(data.line, startLayer);
				}
				undoObjTransform("line", data.line);
			} else if (data.line) { // undo add
				data.line.removeFrom();
			} else if (data.paperTransform) { // undo paper transform
				undoObjTransform("paper", data.paper);
			}
			that.lastPaper = that.paper;
			if (data.paper != that.paper) {
				that.paper = data.paper;
				that.dispatchEvent("paperChange");
			}
			that.dispatchEvent("change");
			that.dispatchEvent("undo");
			if ((!zim.OPTIMIZE&&(zns||!WW.OPTIMIZE)) && that.stage) that.stage.update();
		}
	};
	function undoObjTransform(type, obj) {
		var d;
		for (var i=undo.length-1; i>=0; i--) {
			if (undo[i][type]==obj && undo[i][type+"Transform"]) {
				d = undo[i][type+"Transform"];
				break;
			}
		}
		if (!d) d = zim.copy(defaultTransform);
		obj.x=d.x; obj.y=d.y; obj.alpha=d.a; obj.rotation=d.r; obj.regX=d.rX; obj.regY=d.rY; obj.scaleX=d.sX; obj.scaleY=d.sY; obj.skewX=d.skX; obj.skewY=d.skY; obj.visible=d.v;
		if (type=="line" && cache) { // if cached, the cropScale adjusts the starting registration of the Bitmap
			obj.regX = (w*cropScale-w)/2;
			obj.regY = (h*cropScale-h)/2;
		}
	}
	
	this.redo = function() {
		var data = redo.pop();
		var obj, d;
		if (data) {
			undo.push(data);
			if (data.clear) {
				data.paper.removeAllChildren();
				data.paper.addChild(shape);
			} else if (data.line && data.lineTransform) { // redo line transform
				if (!zot(data.endLayer)) { // watch - could be 0
					shape.top(); // may not matter if paper is not current paper as it will not have shape
					data.paper.setChildIndex(data.line, endLayer); // watch - data.paper not paper
				}
				d = data.lineTransform;
				obj = data.line;
			} else if (data.line) { // redo add
				data.line.addTo(data.paper);
			} else if (data.paperTransform) { // redo paper transform
				d = data.paperTransform;
				obj = data.paper;
			}
			if (obj) { // keep brackets as multiple statements - I keep trying to put on one line!
				obj.x=d.x; obj.y=d.y; obj.alpha=d.a; obj.rotation=d.r; obj.regX=d.rX; obj.regY=d.rY; obj.scaleX=d.sX; obj.scaleY=d.sY; obj.skewX=d.skX; obj.skewY=d.skY; obj.visible=d.v;
			}
			that.lastPaper = that.paper;
			if (data.paper != that.paper) {
				that.paper = data.paper;
				that.dispatchEvent("paperChange");
			}
			that.dispatchEvent("change");
			that.dispatchEvent("redo");
			if ((!zim.OPTIMIZE&&(zns||!WW.OPTIMIZE)) && that.stage) that.stage.update();
		}
	};

	that.ctrlKey = false;
	that.shiftKey = false;
	that.ctrlKeyCheck = false;
	that.zimkeydownEvent = function(e) {
		that.ctrlKey = e.ctrlKey;
		that.shiftKey = e.shiftKey;
		if (e.keyCode == 17 && move && !that.ctrlKeyCheck) { // ctrl key
			that.ctrlKeyCheck = true;
			paper.noDrag();
			paper.drag({onTop:onTop, all:true});
		}
		if (that.undoLevels <= 0) return;
		if (!that.undoKeys) return;
		if (e.ctrlKey && ((e.shiftKey && e.keyCode == 90) || e.keyCode == 89)) {
			that.redo();
		} else if (e.ctrlKey && e.keyCode == 90) {
			that.undo();
		}
		if (frame && frame.ctrlKey && that.write) {
			that.lastWrite = that.write;
			that.write = false;
		}
	}
	WW.addEventListener("keydown", that.zimkeydownEvent);
	
	that.zimkeyupEvent = function(e) {
		if (e.keyCode == 17 && move && that.ctrlKeyCheck) { // ctrl key
			that.ctrlKeyCheck = false;
			paper.noDrag();
			paper.drag({onTop:onTop});
		}
		if (!e.ctrlKey) that.ctrlKey = false;
		if (!e.shiftKey) that.shiftKey = false;
		if (frame && !frame.ctrlKey && that.write == false) {
			that.write = that.lastWrite;
		}
	}
	WW.addEventListener("keyup", that.zimkeyupEvent);

	Object.defineProperty(this, 'paper', {
		get: function() {
			return paper;
		},
		set: function(value) {
			if (value.type != "Container") return;
			paper = value;
			shape.addTo(paper, 0, false); // do not match shape in old container as paper might be moved
			paper.shape = shape;
			that.dampX.immediate(that.x);
			that.dampY.immediate(that.y);
			that.lastX = that.lastMidX = that.finishX = that.x;
			that.lastY = that.lastMidY = that.finishY = that.y;
			if (penType == "barbwire") {
				shape.graphics.s(zim.Pick.choose(color)).ss(zim.Pick.choose(size)*that.sizeScale*that.sizeFactor, end);
			}
			shape.graphics.mt(that.x, that.y);
	
			if (!paper.parent) paper.addTo(that.parent, that.parent.getChildIndex(that));
			if (zot(paper.paperNum)) {
				// apply paper events for delete and drag
				paper.paperNum = ++that.paperNum;
				if (deleteable) {
					paper.on("mousedown", function (e) {
						that.lastSelected = e.target;
						if (that.nibEvent) that.stage.off("stagemousemove", that.nibEvent); // do not place nib if dragging existing
						var currentPaper = e.target.paper;
						if (!currentPaper) return;
						if (currentPaper != that.paper) {
							that.paper = currentPaper;
							that.dispatchEvent("paperChange");
						}
						if (that.shiftKey) {
							if (that.ctrlKey) {
								that.clear(); // clear will dispatch change
							} else {
								// e.target.alpha = 0;
								e.target.visible = false;
								if (that.undoLevels > 0) that.saveState(e.target);
								that.dispatchEvent("change");
							}
						}
					});
				}
				if (doubleClickDelete) {
					paper.on("dblclick", function(e) {
						// e.target.alpha = 0;
						e.target.visible = false;
						if (that.undoLevels > 0) that.saveState(e.target);
					});
				}
				if (doubleClickDelete) {						
					paper.hold(function(e) {
						// e.target.alpha = 0;
						e.target.visible = false;
						if (that.undoLevels > 0) that.saveState(e.target);
					});
				}
				if (move) {
					paper.on("mousedown", function (e) {
						stage = e.target.stage;
						that.draggingCheck = true; // used so motioncontroller knows to ignore in stopCheck
						if (that.shiftKey && deleteable) return;
						startXCheck = e.stageX/zim.scaX;
						startYCheck = e.stageY/zim.scaY;
						shape.top();
						startLayer = paper.getChildIndex(e.target);
					});
					paper.drag({onTop:onTop});
					paper.on("pressup", function (e) {
						that.draggingCheck = false;
						if (deleteable && that.shiftKey) return;
						// if (e.target.alpha == 0) return;
						if (e.target.visible == false) return;
						if (that.undoLevels <= 0) return;
						if (Math.abs(e.stageX/zim.scaX-startXCheck)<1 && Math.abs(e.stageY/zim.scaY-startYCheck)<1) {
							if (onTop) {
								shape.top();
								endLayer = paper.getChildIndex(e.target);
								if (startLayer != endLayer) that.saveState(e.target, startLayer, endLayer);
							}
							return;
						}
						if (that.ctrlKey) {
							that.saveState(paper);
							that.dispatchEvent("paperChange");
						} else {
							shape.top();
							endLayer = paper.getChildIndex(e.target);
							that.saveState(e.target, startLayer, endLayer);
						}
						that.dispatchEvent("change");
						shape.top();
					});
				}
			}
		}
	});
	
	Object.defineProperty(this, 'write', {
		get: function() {
			return _draw;
		},
		set: function(value) {
			if (_draw && value===false) {
				that.finishX = that.x;
				that.finishY = that.y; // let damping finish
			}
			if (!_draw && value) {
				shape.graphics.es();
                    shape.graphics.ef();
				that.dampX.immediate(that.x);
				that.dampY.immediate(that.y);
				that.lastX = that.lastMidX = that.x;
				that.lastY = that.lastMidY = that.y;
			}
			_draw = value;
		}
	});
	
	Object.defineProperty(this, 'size', {
		get: function() {
			return size;
		},
		set: function(value) {
			shape.graphics.es();
			shape.graphics.ef();
			size = value;
			if (penType != "splatter") shape.graphics.ss(zim.Pick.choose(size)*that.sizeScale*that.sizeFactor, end);
		}
	});		
	
	Object.defineProperty(this, 'color', {
		get: function() {
			return color;
		},
		set: function(value) {
			color = value;
			shape.graphics.es();
			shape.graphics.ef();
			if (penType != "splatter") shape.graphics.s(zim.Pick.choose(color));
		}
	});
	
	Object.defineProperty(this, 'penType', {
		get: function() {
			return penType;
		},
		set: function(value) {
			penType = value;
			shape.graphics.ef().es();
			if (penType != "splatter") {
				shape.graphics.s(zim.Pick.choose(color));
			} else {
				shape.graphics.s(zim.Pick.choose(color)).ss(zim.Pick.choose(size)*that.sizeScale*that.sizeFactor, end);
			}
		}
	});
	
	Object.defineProperty(this, 'end', {
		get: function() {
			return end;
		},
		set: function(value) {
			end = value;	
		}
	});
	
	Object.defineProperty(this, 'damp', {
		get: function() {
			return damp;
		},
		set: function(value) {
			damp = value;
			that.dampX.damp = damp;
			that.dampY.damp = damp;
		}
	});
	
	Object.defineProperty(this, 'spread', {
		get: function() {
			return spread;
		},
		set: function(value) {
			spread = value;
		}
	});
	
	var startColor;
	var endColor;
	this.setColorRange = function(color1, color2) {
		if (zot(color2)) {
			startColor = that.color;
			endColor = color1;
		} else if (zot(color1)) {
			startColor = that.color;
			endColor = color2;
		} else {
			startColor = color1;
			endColor = color2;
		}
		return that;
	};
	var _colorRange = 0;
	Object.defineProperty(that, 'colorRange', {
		get: function() {
			return _colorRange;
		},
		set: function(value) {
			_colorRange = value;
			if (!zot(startColor) && !zot(endColor)) {
				that.color = zim.colorRange(startColor, endColor, value);
			}
		}
	});
	
	Object.defineProperty(this, 'borderColor', {
		get: function() {
			return borderColor;
		},
		set: function(value) {
			borderColor = value;
		}
	});
	
	Object.defineProperty(this, 'borderWidth', {
		get: function() {
			return borderWidth;
		},
		set: function(value) {
			borderWidth = value;
		}
	});

	Object.defineProperty(this, 'pullColor', {
		get: function() {
			return pullColor;
		},
		set: function(value) {
			if (zot(value || value==-1)) {
				removePullColor();
				pullColor = null;
			} else if (zot(pullColor)) {
				pullColor = value;
				var s = stage || zdf.stage  
				addPullColor(s);
			} else {
				pullColor = value;
			}
		}
	});
	
	// if (frame) {
	// 	frame.on("keydown", function () {
	// 		if (frame.ctrlKey && that.write) {
	// 			that.lastWrite = that.write;
	// 			that.write = false;
	// 		}
	// 	});
	// 	frame.on("keyup", function () {
	// 		if (!frame.ctrlKey && that.write == false) {
	// 			that.write = that.lastWrite;
	// 		}
	// 	});
	// }
	
	that.setPen = function(newPen) {
		that.dampX.immediate(that.x);
		that.dampY.immediate(that.y);
		that.lastX = that.lastMidX = that.finishX = that.x;
		that.lastY = that.lastMidY = that.finishY = that.y;
		if (zot(newPen)) newPen = penType;
		if (zot(defaults[newPen])) newPen = "line";
		that.penType = penType = newPen;
		var newDefaults = zim.merge(base, defaults[newPen]);
		for (var prop in newDefaults) {
			if (that[prop] != newDefaults[prop]) {
				that[prop] = newDefaults[prop];
			}
		}
		return that;
	};
	
	that.immediate = function(x, y) {
		if (!zot(x)) {
			that.x = x;
			that.dampX.immediate(that.x);
			that.lastX = that.lastMidX = that.finishX = that.x;
		}
		if (!zot(y)) {
			that.y = y;
			that.dampY.immediate(that.y);
			that.lastY = that.lastMidY = that.finishY = that.y;
		}
		return that;
	};
	
	that.clear = function() {
		// if (paper.numChildren <= 1) return; // shape does not count
		var data = []; 
		for (var i=0; i<paper.numChildren-1; i++) {
			data.push(paper.getChildAt(i));
		}
		undo.push({paper:paper, clear:data});
		if (undo.length > that.undoLevels) undo.shift(); // take off front
		that.dispatchEvent("recordUndo");
		paper.removeAllChildren();
		shape.graphics.clear();
		paper.addChild(shape);
		that.dispatchEvent("change");
		if (that.stage) that.stage.update();
		return that;
	};
	
	// REMOVED in ZIM Cat 04
	// this.delete = function(index) {
	// 	// paper.getChildAt(index).alpha = 0;
	// 	paper.getChildAt(index).visible = false;
	// 	if (that.undoLevels > 0) that.saveState(paper.getChildAt(index));
	// };
	
	this.deleteSegment = function(segment) {
		// segment.alpha = 0;
		segment.visible = false;
		if (that.undoLevels > 0) that.saveState(segment);
	};
        
        this.clone = function() {			
		return this.cloneChildren(this.cloneProps(new zim.Pen(size, color, penType, damp, spread, borderColor, borderWidth, end, paper, nib, cache, ctrlKey, cropScale, undo, undoKeys, move, onTop, deleteable, doubleClickDelete, holdDelete, immediateStop, lineAlpha, lineBlendMode, frame, dashed, pullColor, pullThickness, style, group, inherit)));
	};

	that.dispose = function(all, b, disposing) {
		if (zot(all)) all = true;
		WW.removeEventListener("keydown", that.zimkeydownEvent);
		WW.removeEventListener("keyup", that.zimkeyupEvent);
		if (stage && that.nibEvent) stage.off("stagemousemove", that.nibEvent);
		if (stage && that.stageDown) stage.off("stagemousedown", that.stageDown);
		if (stage && that.stageUp) stage.off("stagemouseup", that.stageUp);
		if (stage && that.upEvent) stage.off("stagemouseup", that.upEvent);
		zim.Ticker.remove(that.ticker);
		that.removeAllEventListeners();
		if (nib) nib.removeFrom();
		if (all) { // remove paper too
			if (that.shape) that.shape.dispose(); // does not get done in recursive dispose - outside pen container						
			if (that.paper) that.paper.dispose();									
			if (cache) {
				if (that.bitmap) that.bitmap.dispose();
				that.bitmap = null;
			} 
			that.zimContainer_dispose(disposing);
			paper = shape = that.shape = null;
		}			
				
		return true;
	};

	if (style!==false) zim.styleTransforms(this, DS);
};
zim.extend(zim.Pen, zim.Container, ["dispose","clone"], "zimContainer", false);