function JsonProperty(object){
	this.object = object || {};
}
JsonProperty.prototype.reset = function(object){
	return this.object = object;
}
JsonProperty.prototype.set = function(key,value){
	return this.object[key] = value;
}
JsonProperty.prototype.get = function(key){
	return this.object[key]
}
function SpiderEditor(options){
	options = options || {};
	var emptyFunction = function(){}
	this.selectedCellListener = options.selectedCellListener || emptyFunction;
	if(mxClient.isBrowserSupported()){
		this.editor = new mxEditor();
		this.editor.setGraphContainer(options.element);
		this.graph = this.editor.graph;
		this.graph.setConnectable(true);
		this.graph.setMultigraph(false);	//禁止重复连接
		this.graph.setAllowLoops(true);		//允许自己连自己
		this.graph.isHtmlLabel = function(cell){
			return !this.isSwimlane(cell);
		}
		mxConstants.MIN_HOTSPOT_SIZE = 16;
		mxGraphHandler.prototype.guidesEnabled = true
		//注册json编码器
		this.registerJsonCodec();
		//配置样式
		this.configureStylesheet();
		var _this = this;
		var pasteCount = 0;
		mxEvent.addListener(options.element,'paste',function(e){
			var pasteText = e.clipboardData.getData("Text");
			var doc = mxUtils.parseXml(pasteText);
			if(doc){
				var root = doc.documentElement;
				var dec = new mxCodec(root.ownerDocument);
				var cells = dec.decode(root);
				if(cells&&cells.length > 0){
					pasteCount++;
					_this.graph.setSelectionCells(_this.graph.importCells(cells, pasteCount * 10, pasteCount * 10, _this.graph.getDefaultParent()));
				}else{
					_this.execute('paste');
				}
			}else{
				_this.execute('paste');
			}
		});
		this.keyHandler = new mxKeyHandler(this.graph);
		this.keyHandler.getFunction = function(evt) {
			if (evt != null)
			{
				return (mxEvent.isControlDown(evt) || (mxClient.IS_MAC && evt.metaKey)) ? this.controlKeys[evt.keyCode] : this.normalKeys[evt.keyCode];
			}
			return null;
		}
		this.bindKeyAction();
		var _this = this;
		//选择节点事件
		this.graph.getSelectionModel().addListener(mxEvent.CHANGE,function(sender,evt){
			_this.onSelectedCell(sender,evt);
		});
	}
}
SpiderEditor.prototype.bindKeyAction = function(){
	var _this = this;
	this.keyHandler.bindKey(46,function(){	//按Delete
		_this.deleteSelectCells();
	});
	this.keyHandler.bindControlKey(90,function(){	//Ctrl+Z
		_this.execute('undo');
	})
	this.keyHandler.bindControlKey(89,function(){	//Ctrl+Y
		_this.execute('redo');
	})
	this.keyHandler.bindControlKey(88,function(){ // Ctrl+X
		_this.execute('cut');
	});
	this.keyHandler.bindControlKey(67, function(){	// Ctrl+C
		_this.executeCopy();
	});
	this.keyHandler.bindControlKey(65,function(){	// Ctrl+A
		editor.execute('selectAll');
	});
}
SpiderEditor.prototype.configureStylesheet = function(){
	var style = new Object();
	style = this.graph.getStylesheet().getDefaultEdgeStyle();
	style[mxConstants.STYLE_EDGE] = mxConstants.EDGESTYLE_ORTHOGONAL;
	style[mxConstants.STYLE_STROKECOLOR] = 'black';
	style[mxConstants.STYLE_STROKEWIDTH] = '2';
	style = this.graph.getStylesheet().getDefaultVertexStyle();
	style[mxConstants.STYLE_STROKECOLOR] = 'black';
	style[mxConstants.STYLE_FONTSIZE] = 12;
	this.graph.alternateEdgeStyle = 'elbow=vertical';
}
SpiderEditor.prototype.deleteSelectCells = function(){
	this.graph.escape();
	var selectCells = this.graph.getDeletableCells(this.graph.getSelectionCells());
	if (selectCells != null && selectCells.length > 0){
		var cells = [];
		for(var i =0,len = selectCells.length;i<len;i++){
			var cell = selectCells[i];
			if((!cell.isVertex())||(cell.data&&cell.data.get('shape') != 'start')){
				cells.push(cell);
			}
		}
		if(cells.length == 0){
			return;
		}
		var parents = (this.graph.selectParentAfterDelete) ? this.graph.model.getParents(cells) : null;
		this.graph.removeCells(cells, true);
		if (parents != null){
			var select = [];
			for (var i = 0; i < parents.length; i++){
				if (this.graph.model.contains(parents[i]) &&
					(this.graph.model.isVertex(parents[i]) ||
					this.graph.model.isEdge(parents[i]))){
					select.push(parents[i]);
				}
			}
			this.graph.setSelectionCells(select);
		}
	}
}
SpiderEditor.prototype.addShape = function(shape,label,element,defaultAdd){
	var style = new Object();
	if(shape == 'comment'){
		style[mxConstants.STYLE_FILLCOLOR] = 'none';
		style[mxConstants.STYLE_STROKECOLOR] = 'none';
	}else{
		style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_IMAGE;
		style[mxConstants.STYLE_VERTICAL_ALIGN] = mxConstants.ALIGN_TOP;
		style[mxConstants.STYLE_IMAGE_WIDTH] = 32;
		style[mxConstants.STYLE_IMAGE_HEIGHT] = 32;
		style[mxConstants.STYLE_IMAGE] = element.src;
		style[mxConstants.STYLE_PERIMETER] = mxPerimeter.RectanglePerimeter;
		style[mxConstants.STYLE_SPACING_TOP] = -26;
	}
	style[mxConstants.STYLE_FONTCOLOR] = '#000000';
	this.graph.getStylesheet().putCellStyle(shape,style);
	var _this = this;
	var funct = function(graph, evt, cell, x, y){
		var parent = _this.graph.getDefaultParent();
		var model = _this.graph.getModel();
		model.beginUpdate();
		var cell;
		try{
			cell = _this.graph.insertVertex(parent, null, label, x, y, 32, 32,shape);
			cell.data = new JsonProperty();
			cell.data.set('shape',shape);
		}finally{
			model.endUpdate();
		}
		_this.graph.setSelectionCell(cell);
	}
	var dragElt = document.createElement('div');
	dragElt.style.border = 'dashed black 1px';
	dragElt.style.width = '32px';
	dragElt.style.height = '32px';
	var ds = mxUtils.makeDraggable(element, this.graph, funct, dragElt, 0, 0, true, true);
	ds.setGuidesEnabled(true);
	if(defaultAdd){
		var parent = this.graph.getDefaultParent();
		var model = this.graph.getModel();
		model.beginUpdate();
		try{
			cell = this.graph.insertVertex(parent, null, label, 80, 80, 32, 32,shape);
			cell.data = new JsonProperty();
			cell.data.set('shape',shape);
		}finally{
			model.endUpdate();
		}
	}
}
SpiderEditor.prototype.registerJsonCodec = function(){
	var codec = new mxObjectCodec(new JsonProperty());
	codec.encode = function(enc,obj){
		var node = enc.document.createElement('JsonProperty');
		mxUtils.setTextContent(node, JSON.stringify(obj.object));
		return node;
	}
	codec.decode = function(dec, node, into){
		return new JsonProperty(JSON.parse(mxUtils.getTextContent(node)));
	}
	mxCodecRegistry.register(codec);
}
SpiderEditor.prototype.onSelectedCell = function(sender,evt){
	this.selectedCellListener(this.getSelectedCell());
}
SpiderEditor.prototype.getModel = function(){
	return this.graph.getModel();
}
SpiderEditor.prototype.executeCopy = function(){
	this.editor.execute('copy');
	var cells = this.graph.getSelectionCells();
	if(cells && cells.length > 0){
		var copyText = document.createElement('textarea');
		copyText.style = 'position:absolute;left:-99999999px';
		document.body.appendChild(copyText);
		copyText.innerHTML = mxUtils.getPrettyXml(new mxCodec().encode(cells));
		copyText.readOnly = false;
		copyText.select();
		copyText.setSelectionRange(0, copyText.value.length);
		document.execCommand("copy");
		document.body.removeChild(copyText);
	}
}
SpiderEditor.prototype.execute = function(action){
	if('copy' == action){
		this.executeCopy();
	}else{
		this.editor.execute(action);
	}
}
SpiderEditor.prototype.getSelectedCell = function(){
	var cell = this.graph.getSelectionCell() || this.graph.getModel().getRoot();
	cell.data = cell.data || new JsonProperty();
	return cell;
}
SpiderEditor.prototype.getXML = function(){
	return mxUtils.getPrettyXml(new mxCodec(mxUtils.createXmlDocument()).encode(this.graph.getModel()));
}
SpiderEditor.prototype.selectCell = function(cell){
	this.graph.setSelectionCell(cell);
}
SpiderEditor.prototype.valid = function(){
	var cells = editor.graph.getModel().cells;
	for(var key in cells){
		var cell = cells[key];
		if(cell&&cell.edge){
			if(cell.source == null || cell.target == null){
				return cell;
			}
		}
	}
	return null;
}
SpiderEditor.prototype.setXML = function(xml){
	var doc = mxUtils.parseXml(xml);
	var root = doc.documentElement;
	var dec = new mxCodec(root.ownerDocument);
	dec.decode(root,this.getModel());
	this.selectedCellListener(this.getSelectedCell());
}
SpiderEditor.prototype.importFromUrl = function(url){
	var req = mxUtils.load(url);
	var root = req.getDocumentElement();
	var dec = new mxCodec(root.ownerDocument);
	dec.decode(root, this.getModel());
}