Ajax.InPlaceEditor.prototype.initialize=function(element, url, options) {
	this.url = url;
    this.element = $(element);

    this.options = Object.extend({
      okButton: true,
      okText: "提交",
      cancelLink: true,
      cancelText: "取消",
      cancelButtonText: "取消",
      savingText: "保存中...",
      clickToEditText: "点击修改",
      rows: 1,
      onComplete: function(transport, element) {
        new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
      },
      onFailure: function(transport) {
        alert("Error communicating with the server: " + transport.responseText.stripTags());
      },
      callback: function(form) {
        return Form.serialize(form);
      },
      onProcessAfterShowForm: function() {},
      onEnterEditMode: function() {},
      handleLineBreaks: true,
      loadingText: '正在加载...',
      savingClassName: 'inplaceeditor-saving',
      loadingClassName: 'inplaceeditor-loading',
      formClassName: 'inplaceeditor-form',
      highlightcolor: '#FFFFFF',
      externalControl: null,
      submitOnBlur: false,
      ajaxOptions: {},
      evalScripts: false,
      editOnEmpty: true,
      emptyText: "点击此处修改",
      warningText: null,
      hoverClassName: 'ajaxEdit_hover',
      emptyInfoClassName: 'ajaxEdit_emptyInfo',
      canEmpty: true
    }, options || {});

    if(!this.options.formId && this.element.id) {
      this.options.formId = this.element.id + "-inplaceeditor";
      if ($(this.options.formId)) {
        // there's already a form with that name, don't specify an id
        this.options.formId = null;
      }
    }
    
    if (this.options.externalControl) {
      this.options.externalControl = $(this.options.externalControl);
    }
    
    this.originalBackground = Element.getStyle(this.element, 'background-color');
    if (!this.originalBackground) {
      this.originalBackground = "transparent";
    }
    
    this.element.title = this.options.clickToEditText;
    
    if (this.options.editOnEmpty) {
    	if (this.element.innerHTML.length == 0) {
	    	this.isEmpty = true;
	    	this.element.innerHTML = this.options.emptyText;
	    	this.elementClassName = this.element.className;
	    	this.element.className = this.options.emptyInfoClassName;
	    } else {
	    	this.isEmpty = false;
	    }
    }
    
    this.onclickListener = this.enterEditMode.bindAsEventListener(this);
    this.mouseoverListener = this.enterHover.bindAsEventListener(this);
    this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
    Event.observe(this.element, 'click', this.onclickListener);
    Event.observe(this.element, 'mouseover', this.mouseoverListener);
    Event.observe(this.element, 'mouseout', this.mouseoutListener);
    if (this.options.externalControl) {
      Event.observe(this.options.externalControl, 'click', this.onclickListener);
      Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
      Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
    }
}

Ajax.InPlaceEditor.prototype.createForm=function() {
    this.form = document.createElement("form");
    this.form.id = this.options.formId;
    Element.addClassName(this.form, this.options.formClassName)
    this.form.onsubmit = this.onSubmit.bind(this);

    this.createEditField();

    if (this.options.textarea) {
      var br = document.createElement("br");
      this.form.appendChild(br);
    }
    
    if (this.options.warningText) {
      var div = document.createElement("div");
      div.innerHTML = this.options.warningText;
      div.className = "editor_warning";
      this.form.appendChild(div);
    }

    if (this.options.okButton) {
      okButton = document.createElement("input");
      okButton.type = "submit";
      okButton.value = this.options.okText;
      okButton.className = 'editor_ok_button';
      this.form.appendChild(okButton);
    }
    
	if (this.options.cancelButton) {
		cancelButton = document.createElement("input");
		cancelButton.type = "button";
		cancelButton.value = this.options.cancelButtonText;
		cancelButton.className = 'editor_cancel_button';
		cancelButton.onclick = this.onclickCancel.bind(this);
		this.form.appendChild(cancelButton);
	}
    if (this.options.cancelLink) {
      cancelLink = document.createElement("a");
      cancelLink.href = "#";
      cancelLink.appendChild(document.createTextNode(this.options.cancelText));
      cancelLink.onclick = this.onclickCancel.bind(this);
      cancelLink.className = 'editor_cancel';      
      this.form.appendChild(cancelLink);
    }
}

Ajax.InPlaceEditor.prototype.createEditField=function() {
	var text;
    if(this.options.loadTextURL) {
      text = this.options.loadingText;
    } else {
      text = this.getText();
    }

    var obj = this;
    
    if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) {
      this.options.textarea = false;
      var textField = document.createElement("input");
      textField.obj = this;
      textField.type = "text";
      textField.name = "value";
      textField.value = unEscapeJstlOutStr(text);
      textField.style.backgroundColor = this.options.highlightcolor;
      textField.className = 'editor_field';
	  if (this.options.maxLength) {
		textField.maxLength = this.options.maxLength;
	  }
      if (this.options.width) {
      	textField.style.width = this.options.width + "px";
      }
      if (this.options.submitOnBlur)
        textField.onblur = this.onSubmit.bind(this);
      this.editField = textField;
    } else {
      this.options.textarea = true;
      var textArea = document.createElement("textarea");
      textArea.obj = this;
      textArea.name = "value";
      textArea.value = unEscapeJstlOutStr(text);
      textArea.rows = this.options.rows;
      textArea.cols = this.options.cols || 40;
      if (this.options.width) {
      	textArea.style.width = this.options.width + "px";
      }
      if (this.options.height) {
      	textArea.style.height = this.options.height + "px";
      }
      textArea.className = 'editor_field';
      if (this.options.submitOnBlur)
        textArea.onblur = this.onSubmit.bind(this);
      this.editField = textArea;
    }
    
    if(this.options.loadTextURL) {
      this.loadExternalText();
    }
    this.form.appendChild(this.editField);
}

Ajax.InPlaceEditor.prototype.onSubmit=function() {
	// onLoading resets these so we need to save them away for the Ajax call
    var form = this.form;
    this.editField.value = this.editField.value.strip();
    var value = this.editField.value;
    var needSubmit = true;
    var oldValue = this.element.innerHTML;
    if (this.isEmpty) {
    	oldValue = '';
    }
    if (escapeJstlOutStr(value) == oldValue) {
    	needSubmit = false;
	} else if (!this.options.canEmpty && value.length == 0) {
		needSubmit = false;
	} else if (this.options.maxLength) {
		if (value.length > this.options.maxLength) {
			alert("错误:长度不能大于" + this.options.maxLength + "!");
			this.editField.focus();
			return false;
		}
	}
	if (this.options.validate) {
		if (this.options.validate == "validate-split-comma-5") {
			if (value.split(/[,，]/).length > 5) {
				alert("错误:数量不允许多于5个");
				this.editField.focus();
				return false;
			}
		}
	}
    
    if (needSubmit) {
	    // do this first, sometimes the ajax call returns before we get a chance to switch on Saving...
	    // which means this will actually switch on Saving... *after* we've left edit mode causing Saving...
	    // to be displayed indefinitely
	    this.onLoading();
	    if (this.options.evalScripts) {
	      new Ajax.Request(
	        this.url, Object.extend({
	          parameters: this.options.callback(form, value),
	          onComplete: this.onComplete.bind(this),
	          onFailure: this.onFailure.bind(this),
	          asynchronous:true, 
	          evalScripts:true
	        }, this.options.ajaxOptions));
	    } else  {
	      new Ajax.Updater(
	        { success: this.element,
	          // don't update on failure (this could be an option)
	          failure: null }, 
	        this.url, Object.extend({
	          parameters: this.options.callback(form, value),
	          onComplete: this.onComplete.bind(this),
	          onFailure: this.onFailure.bind(this)
	        }, this.options.ajaxOptions));
	    }
	} else {
		this.leaveEditMode();
	}
    // stop the event to avoid a page refresh in Safari
    if (arguments.length > 1) {
      Event.stop(arguments[0]);
    }
    return false;
}

Ajax.InPlaceEditor.prototype.enterEditMode=function(evt) {
    if (this.saving) return;
    if (this.editing) return;
    this.editing = true;
    this.onEnterEditMode();
    if (this.options.externalControl) {
      Element.hide(this.options.externalControl);
    }
    Element.hide(this.element);
    this.createForm();
    this.element.parentNode.insertBefore(this.form, this.element);
    this.processAfterShowForm();
    if (!this.options.loadTextURL) Field.scrollFreeActivate(this.editField);
    // stop the event to avoid a page refresh in Safari
    if (evt) {
      Event.stop(evt);
    }
    return false;
}

Ajax.InPlaceEditor.prototype.processAfterShowForm=function() {
	this.options.onProcessAfterShowForm.bind(this)();
}

Ajax.InPlaceEditor.prototype.onComplete=function(transport) {
    this.leaveEditMode();
    this.options.onComplete.bind(this)(transport, this.element);
    if (transport && transport.status == 200) {
    	if (this.options.editOnEmpty) {
    		if (this.isEmpty) {
    			if (transport.responseText.length != 0) {
    				this.isEmpty = false;
					this.element.className = this.elementClassName;
    			} else {
    				this.element.innerHTML = this.options.emptyText;
    			}
    		} else {
    			if (transport.responseText.length == 0) {
    				this.isEmpty = true;
    				this.element.innerHTML = this.options.emptyText;
		    		this.element.className = this.options.emptyInfoClassName;
    			}
    		}
    	}
    }
}

Ajax.InPlaceEditor.prototype.getText=function() {
	if (this.options.editOnEmpty && this.isEmpty) {
		return "";
	} else {
    	return this.element.innerHTML;
    }
}

Ajax.InPlaceEditor.prototype.enterHover=function() {
    if (this.saving) return;
    this.element.style.backgroundColor = this.options.highlightcolor;
    if (this.effect) {
      this.effect.cancel();
    }
    Element.addClassName(this.element, this.options.hoverClassName)
}

Ajax.InPlaceEditor.prototype.leaveHover=function() {
    if (this.options.backgroundColor) {
      this.element.style.backgroundColor = this.oldBackground;
    }
    Element.removeClassName(this.element, this.options.hoverClassName)
    if (this.saving) return;
    this.element.style.backgroundColor = this.originalBackground;
}

Ajax.InPlaceEditor.prototype.leaveEditMode=function() {
    Element.removeClassName(this.element, this.options.savingClassName);
    this.removeForm();
    this.leaveHover();
    this.element.style.backgroundColor = this.originalBackground;
    Element.show(this.element);
    if (this.options.externalControl) {
      Element.show(this.options.externalControl);
    }
    this.editing = false;
    this.saving = false;
    this.oldInnerHTML = null;
    this.onLeaveEditMode();
}