docs: function(evt) {
  		if (canvas.spaceKey || evt.button === 1) {return;}
  
  		var right_click = evt.button === 2;
  	
  		if (evt.altKey) { // duplicate when dragging
  			svgCanvas.cloneSelectedElements(0, 0);
  		}
  	
  		root_sctm = $('#svgcontent g')[0].getScreenCTM().inverse();
  		
  		var pt = svgedit.math.transformPoint( evt.pageX, evt.pageY, root_sctm ),
  			mouse_x = pt.x * current_zoom,
  			mouse_y = pt.y * current_zoom;
  			
  		evt.preventDefault();
  
  		if (right_click) {
  			current_mode = "select";
  			lastClickPoint = pt;
  		}
  		
  		// This would seem to be unnecessary...
  //		if (['select', 'resize'].indexOf(current_mode) == -1) {
  //			setGradient();
  //		}
  		
  		var x = mouse_x / current_zoom,
  			y = mouse_y / current_zoom,
  			mouse_target = getMouseTarget(evt);
  		
  		if (mouse_target.tagName === 'a' && mouse_target.childNodes.length === 1) {
  			mouse_target = mouse_target.firstChild;
  		}
  		
  		// real_x/y ignores grid-snap value
  		var real_x = x;
  		r_start_x = start_x = x;
  		var real_y = y;
  		r_start_y = start_y = y;
  
  		if (curConfig.gridSnapping){
  			x = svgedit.utilities.snapToGrid(x);
  			y = svgedit.utilities.snapToGrid(y);
  			start_x = svgedit.utilities.snapToGrid(start_x);
  			start_y = svgedit.utilities.snapToGrid(start_y);
  		}
  
  		// if it is a selector grip, then it must be a single element selected, 
  		// set the mouse_target to that and update the mode to rotate/resize
  		
  		if (mouse_target == selectorManager.selectorParentGroup && selectedElements[0] != null) {
  			var grip = evt.target;
  			var griptype = elData(grip, "type");
  			// rotating
  			if (griptype == "rotate") {
  				current_mode = "rotate";
  			}
  			// resizing
  			else if (griptype == "resize") {
  				current_mode = "resize";
  				current_resize_mode = elData(grip, "dir");
  			}
  			mouse_target = selectedElements[0];
  		}
  		
  		startTransform = mouse_target.getAttribute("transform");
  		var i, stroke_w,
  			tlist = svgedit.transformlist.getTransformList(mouse_target);
  		switch (current_mode) {
  			case "select":
  				started = true;
  				current_resize_mode = "none";
  				if (right_click) {started = false;}
  				
  				if (mouse_target != svgroot) {
  					// if this element is not yet selected, clear selection and select it
  					if (selectedElements.indexOf(mouse_target) == -1) {
  						// only clear selection if shift is not pressed (otherwise, add 
  						// element to selection)
  						if (!evt.shiftKey) {
  							// No need to do the call here as it will be done on addToSelection
  							clearSelection(true);
  						}
  						addToSelection([mouse_target]);
  						justSelected = mouse_target;
  						pathActions.clear();
  					}
  					// else if it's a path, go into pathedit mode in mouseup
  					
  					if (!right_click) {
  						// insert a dummy transform so if the element(s) are moved it will have
  						// a transform to use for its translate
  						for (i = 0; i < selectedElements.length; ++i) {
  							if (selectedElements[i] == null) {continue;}
  							var slist = svgedit.transformlist.getTransformList(selectedElements[i]);
  							if (slist.numberOfItems) {
  								slist.insertItemBefore(svgroot.createSVGTransform(), 0);
  							} else {
  								slist.appendItem(svgroot.createSVGTransform());
  							}
  						}
  					}
  				} else if (!right_click){
  					clearSelection();
  					current_mode = "multiselect";
  					if (rubberBox == null) {
  						rubberBox = selectorManager.getRubberBandBox();
  					}
  					r_start_x *= current_zoom;
  					r_start_y *= current_zoom;
  //					console.log('p',[evt.pageX, evt.pageY]);					
  //					console.log('c',[evt.clientX, evt.clientY]);	
  //					console.log('o',[evt.offsetX, evt.offsetY]);	
  //					console.log('s',[start_x, start_y]);
  					
  					svgedit.utilities.assignAttributes(rubberBox, {
  						'x': r_start_x,
  						'y': r_start_y,
  						'width': 0,
  						'height': 0,
  						'display': 'inline'
  					}, 100);
  				}
  				break;
  			case "zoom": 
  				started = true;
  				if (rubberBox == null) {
  					rubberBox = selectorManager.getRubberBandBox();
  				}
  				svgedit.utilities.assignAttributes(rubberBox, {
  						'x': real_x * current_zoom,
  						'y': real_x * current_zoom,
  						'width': 0,
  						'height': 0,
  						'display': 'inline'
  				}, 100);
  				break;
  			case "resize":
  				started = true;
  				start_x = x;
  				start_y = y;
  				
  				// Getting the BBox from the selection box, since we know we
  				// want to orient around it
  				init_bbox = svgedit.utilities.getBBox($('#selectedBox0')[0]);
  				var bb = {};
  				$.each(init_bbox, function(key, val) {
  					bb[key] = val/current_zoom;
  				});
  				init_bbox = bb;
  				
  				// append three dummy transforms to the tlist so that
  				// we can translate,scale,translate in mousemove
  				var pos = svgedit.utilities.getRotationAngle(mouse_target) ? 1 : 0;
  				
  				if (svgedit.math.hasMatrixTransform(tlist)) {
  					tlist.insertItemBefore(svgroot.createSVGTransform(), pos);
  					tlist.insertItemBefore(svgroot.createSVGTransform(), pos);
  					tlist.insertItemBefore(svgroot.createSVGTransform(), pos);
  				} else {
  					tlist.appendItem(svgroot.createSVGTransform());
  					tlist.appendItem(svgroot.createSVGTransform());
  					tlist.appendItem(svgroot.createSVGTransform());
  					
  					if (svgedit.browser.supportsNonScalingStroke()) {
  						// Handle crash for newer Chrome and Safari 6 (Mobile and Desktop): 
  						// https://code.google.com/p/svg-edit/issues/detail?id=904
  						// Chromium issue: https://code.google.com/p/chromium/issues/detail?id=114625
  						// TODO: Remove this workaround once vendor fixes the issue
  						var isWebkit = svgedit.browser.isWebkit();
  
  						if (isWebkit) {
  							var delayedStroke = function(ele) {
  								var _stroke = ele.getAttributeNS(null, 'stroke');
  								ele.removeAttributeNS(null, 'stroke');
  								//Re-apply stroke after delay. Anything higher than 1 seems to cause flicker
  								setTimeout(function() { ele.setAttributeNS(null, 'stroke', _stroke); }, 0);
  							};
  						}
  						mouse_target.style.vectorEffect = 'non-scaling-stroke';
  						if (isWebkit) {delayedStroke(mouse_target);}
  
  						var all = mouse_target.getElementsByTagName('*'),
  							len = all.length;
  						for (i = 0; i < len; i++) {
  							all[i].style.vectorEffect = 'non-scaling-stroke';
  							if (isWebkit) {delayedStroke(all[i]);}
  						}
  					}
  				}
  				break;
  			case "fhellipse":
  			case "fhrect":
  			case "fhpath":
  				start.x = real_x;
  				start.y = real_y;
  				started = true;
  				d_attr = real_x + "," + real_y + " ";
  				stroke_w = cur_shape.stroke_width == 0 ? 1 : cur_shape.stroke_width;
  				addSvgElementFromJson({
  					"element": "polyline",
  					"curStyles": true,
  					"attr": {
  						"points": d_attr,
  						"id": getNextId(),
  						"fill": "none",
  						"opacity": cur_shape.opacity / 2,
  						"stroke-linecap": "round",
  						"style": "pointer-events:none"
  					}
  				});
  				freehand.minx = real_x;
  				freehand.maxx = real_x;
  				freehand.miny = real_y;
  				freehand.maxy = real_y;
  				break;
  			case "image":
  				started = true;
  				var newImage = addSvgElementFromJson({
  					"element": "image",
  					"attr": {
  						"x": x,
  						"y": y,
  						"width": 0,
  						"height": 0,
  						"id": getNextId(),
  						"opacity": cur_shape.opacity / 2,
  						"style": "pointer-events:inherit"
  					}
  				});
  				setHref(newImage, last_good_img_url);
  				preventClickDefault(newImage);
  				break;
  			case "square":
  				// FIXME: once we create the rect, we lose information that this was a square
  				// (for resizing purposes this could be important)
  			case "rect":
  				started = true;
  				start_x = x;
  				start_y = y;
  				addSvgElementFromJson({
  					"element": "rect",
  					"curStyles": true,
  					"attr": {
  						"x": x,
  						"y": y,
  						"width": 0,
  						"height": 0,
  						"id": getNextId(),
  						"opacity": cur_shape.opacity / 2
  					}
  				});
  				break;
  			case "line":
  				started = true;
  				stroke_w = cur_shape.stroke_width == 0 ? 1 : cur_shape.stroke_width;
  				addSvgElementFromJson({
  					"element": "line",
  					"curStyles": true,
  					"attr": {
  						"x1": x,
  						"y1": y,
  						"x2": x,
  						"y2": y,
  						"id": getNextId(),
  						"stroke": cur_shape.stroke,
  						"stroke-width": stroke_w,
  						"stroke-dasharray": cur_shape.stroke_dasharray,
  						"stroke-linejoin": cur_shape.stroke_linejoin,
  						"stroke-linecap": cur_shape.stroke_linecap,
  						"stroke-opacity": cur_shape.stroke_opacity,
  						"fill": "none",
  						"opacity": cur_shape.opacity / 2,
  						"style": "pointer-events:none"
  					}
  				});
  				break;
  			case "circle":
  				started = true;
  				addSvgElementFromJson({
  					"element": "circle",
  					"curStyles": true,
  					"attr": {
  						"cx": x,
  						"cy": y,
  						"r": 0,
  						"id": getNextId(),
  						"opacity": cur_shape.opacity / 2
  					}
  				});
  				break;
  			case "ellipse":
  				started = true;
  				addSvgElementFromJson({
  					"element": "ellipse",
  					"curStyles": true,
  					"attr": {
  						"cx": x,
  						"cy": y,
  						"rx": 0,
  						"ry": 0,
  						"id": getNextId(),
  						"opacity": cur_shape.opacity / 2
  					}
  				});
  				break;
  			case "text":
  				started = true;
  				var newText = addSvgElementFromJson({
  					"element": "text",
  					"curStyles": true,
  					"attr": {
  						"x": x,
  						"y": y,
  						"id": getNextId(),
  						"fill": cur_text.fill,
  						"stroke-width": cur_text.stroke_width,
  						"font-size": cur_text.font_size,
  						"font-family": cur_text.font_family,
  						"text-anchor": "middle",
  						"xml:space": "preserve",
  						"opacity": cur_shape.opacity
  					}
  				});
  //					newText.textContent = "text";
  				break;
  			case "path":
  				// Fall through
  			case "pathedit":
  				start_x *= current_zoom;
  				start_y *= current_zoom;
  				pathActions.mouseDown(evt, mouse_target, start_x, start_y);
  				started = true;
  				break;
  			case "textedit":
  				start_x *= current_zoom;
  				start_y *= current_zoom;
  				textActions.mouseDown(evt, mouse_target, start_x, start_y);
  				started = true;
  				break;
  			case "rotate":
  				started = true;
  				// we are starting an undoable change (a drag-rotation)
  				canvas.undoMgr.beginUndoableChange("transform", selectedElements);
  				break;
  			default:
  				// This could occur in an extension
  				break;
  		}
  		
  		var ext_result = runExtensions("mouseDown", {
  			event: evt,
  			start_x: start_x,
  			start_y: start_y,
  			selectedElements: selectedElements
  		}, true);
  		
  		$.each(ext_result, function(i, r) {
  			if (r && r.started) {
  				started = true;
  			}
  		});
  	}