// Place your application-specific JavaScript functions and classes here
// This file is automatically included by javascript_include_tag :defaults

var	isSafari = (navigator.userAgent.indexOf("Safari") != -1);
var	isFirefox = (navigator.userAgent.indexOf("Firefox") != -1);
var	isIE = (navigator.userAgent.indexOf("MSIE") != -1);

InPlaceEditor = Class.create();
InPlaceEditor.prototype = {
  initialize: function(element, url, options) {
    this.url = url;
    this.element = $(element);
	this.element.style.cursor = "pointer";

    this.options = Object.extend({
      okButton: true,
      okText: "ok",
      cancelLink: true,
      cancelText: "cancel",
      savingText: "Saving...",
      clickToEditText: "Click to edit",
      okText: "ok",
      rows: 1,
	  valueName: "value",
      onComplete: function(transport, element) {
        new Effect.Highlight(element, {startcolor: this.options.highlightcolor,
	 								   endcolor: this.options.highlightendcolor,
									   restorecolor: this.options.backgroundcolor});
      },
      onFailure: function(transport) {
        alert("Error communicating with the server: " + transport.responseText.stripTags());
      },
      callback: function(form) {
        return Form.serialize(form);
      },
      handleLineBreaks: true,
      loadingText: 'Loading...',
      savingClassName: 'inplaceeditor-saving',
      loadingClassName: 'inplaceeditor-loading',
      formClassName: 'inplaceeditor-form',
      highlightcolor: "#FFFF99",
      highlightendcolor: null,
      backgroundcolor: null,
      externalControl: null,
      submitOnBlur: false,
      ajaxOptions: {},
      evalScripts: false
    }, 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.element.title = this.options.clickToEditText;
    
    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);
    }
  },
  enterEditMode: function(evt) {
    if (this.saving) return;
    if (this.editing) return;
	if (eventElement(evt).nodeName.toLowerCase() == 'a') {
    	this.element.style.backgroundColor = this.options.backgroundcolor;
		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);
    Field.scrollFreeActivate(this.editField);
    // stop the event to avoid a page refresh in Safari
    if (evt) {
      Event.stop(evt);
    }
    return false;
  },
  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.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.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);
    }
  },
  hasHTMLLineBreaks: function(string) {
    if (!this.options.handleLineBreaks) return false;
    return string.match(/<br/i) || string.match(/<p>/i);
  },
  convertHTMLLineBreaks: function(string) {
    return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
  },
  createEditField: function() {
    var text;
    if(this.options.loadTextURL) {
      text = this.options.loadingText;
    } 
    else if(this.options.editedText) {
	  text = this.options.editedText.gsub("{#sq}", "'").gsub("{#dq}", '"').gsub("{#n}", "\n");
	}
	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 = this.options.valueName;
      textField.value = text;
      textField.className = 'editor_field';
      var size = this.options.size || this.options.cols || 0;
      if (size != 0) textField.size = size;
      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 = this.options.valueName;
      textArea.value = this.convertHTMLLineBreaks(text);
      textArea.rows = this.options.rows;
      textArea.cols = this.options.cols || 40;
      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);
  },
  getText: function() {
    return this.element.innerHTML;
  },
  loadExternalText: function() {
    Element.addClassName(this.form, this.options.loadingClassName);
    this.editField.disabled = true;
    new Ajax.Request(
      this.options.loadTextURL,
      Object.extend({
        asynchronous: true,
        onComplete: this.onLoadedExternalText.bind(this)
      }, this.options.ajaxOptions)
    );
  },
  onLoadedExternalText: function(transport) {
    Element.removeClassName(this.form, this.options.loadingClassName);
    this.editField.disabled = false;
    this.editField.value = transport.responseText.stripTags();
  },
  onclickCancel: function() {
    this.onComplete();
    this.leaveEditMode();
    return false;
  },
  onFailure: function(transport) {
    this.options.onFailure(transport);
    if (this.oldInnerHTML) {
      this.element.innerHTML = this.oldInnerHTML;
      this.oldInnerHTML = null;
    }
    return false;
  },
  onSubmit: function() {
    // onLoading resets these so we need to save them away for the Ajax call
    var form = this.form;
    var value = this.editField.value;
    
    // 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));
    }
	this.options.editedText = value;
    // stop the event to avoid a page refresh in Safari
    if (arguments.length > 1) {
      Event.stop(arguments[0]);
    }
    return false;
  },
  onLoading: function() {
    this.saving = true;
    this.removeForm();
    this.leaveHover();
    this.showSaving();
  },
  showSaving: function() {
    this.oldInnerHTML = this.element.innerHTML;
    this.element.innerHTML = this.options.savingText;
    Element.addClassName(this.element, this.options.savingClassName);
    this.element.style.backgroundColor = this.options.backgroundcolor;
    Element.show(this.element);
  },
  removeForm: function() {
    if(this.form) {
      if (this.form.parentNode) Element.remove(this.form);
      this.form = null;
    }
  },
  enterHover: function() {
    if (this.saving) return;
	if (this.options.highlightendcolor == null)
		this.options.highlightendcolor = backgroundColor(this.element);
	if (this.options.backgroundcolor == null)
		this.options.backgroundcolor = this.element.getStyle("background-color");
    this.element.style.backgroundColor = this.options.highlightcolor;
    if (this.effect) {
      this.effect.cancel();
    }
    Element.addClassName(this.element, this.options.hoverClassName)
  },
  leaveHover: function() {
    if (this.options.backgroundColor) {
      this.element.style.backgroundColor = this.oldBackground;
    }
    Element.removeClassName(this.element, this.options.hoverClassName)
    if (this.saving) return;
    this.effect = new Effect.Highlight(this.element, {
      startcolor: this.options.highlightcolor,
      endcolor: this.options.highlightendcolor,
      restorecolor: this.options.backgroundcolor
    });
  },
  leaveEditMode: function() {
    Element.removeClassName(this.element, this.options.savingClassName);
    this.removeForm();
    this.leaveHover();
    this.element.style.backgroundColor = this.options.backgroundcolor;
    Element.show(this.element);
    if (this.options.externalControl) {
      Element.show(this.options.externalControl);
    }
    this.editing = false;
    this.saving = false;
    this.oldInnerHTML = null;
    this.onLeaveEditMode();
  },
  onComplete: function(transport) {
    this.leaveEditMode();
    this.options.onComplete.bind(this)(transport, this.element);
  },
  onEnterEditMode: function() {},
  onLeaveEditMode: function() {},
  dispose: function() {
    if (this.oldInnerHTML) {
      this.element.innerHTML = this.oldInnerHTML;
    }
    this.leaveEditMode();
    Event.stopObserving(this.element, 'click', this.onclickListener);
    Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
    Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
    if (this.options.externalControl) {
      Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
      Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
      Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
    }
  }
};

PartialEditor = Class.create();
PartialEditor.prototype = {
  initialize: function(element_id, id, classname, partial, options) {
    this.element = $(element_id);
    this.element_id = element_id;
    this.id = id;
    this.classname = classname;
    this.partial = partial;
	
    this.options = Object.extend({
      highlightcolor: "#FFFF99",
      highlightendcolor: null,
	  backgroundcolor: null,
      scroll_offset: null
    }, options || {});

    this.editing = false;
	this.element.style.cursor = "pointer";

	this.bind();
  },
  bind: function() {
    this.mouseoverListener = this.enterHover.bindAsEventListener(this);
    this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
    this.onclickListener = this.showAsEditable.bindAsEventListener(this);

    Event.observe(this.element, "mouseover", this.mouseoverListener);
    Event.observe(this.element, "mouseout", this.mouseoutListener);
    Event.observe(this.element, "click", this.onclickListener);

	this.onsubmitListener = this.onReadonly.bindAsEventListener(this);
	this.oncancelListener = this.showAsReadonly.bindAsEventListener(this);
  },
  enterHover: function() {
	if (this.editing) return;
	if (this.options.highlightendcolor == null)
		this.options.highlightendcolor = backgroundColor(this.element);
	if (this.options.backgroundcolor == null)
		this.options.backgroundcolor = this.element.getStyle("background-color");
    this.element.style.backgroundColor = this.options.highlightcolor;
	if (this.effect)
	    this.effect.cancel();
  },
  leaveHover: function() {
	if (this.editing) return;
    this.effect = new Effect.Highlight(this.element, {
      startcolor: this.options.highlightcolor,
      endcolor: this.options.highlightendcolor,
      restorecolor: this.options.backgroundcolor
    });
  },
  showAsEditable: function(event) {
	if (this.editing) return;
	if (eventElement(event).nodeName.toLowerCase() == 'a') {
    	this.element.style.backgroundColor = this.options.backgroundcolor;
		return;
	}
 	new Ajax.Updater({success: this.element, failure: null}, 
        			 "/update_partial/" + this.id + "?editable=true&class_name=" + this.classname + "&partial=" + this.partial,
					 Object.extend({onComplete: this.onEditable.bind(this)}, {})
					);
	return false;
  },
  onEditable: function() {
    this.editing = true;
	this.element.style.cursor = "default";
    this.element.style.backgroundColor = this.options.backgroundcolor;
	$("cancel_" + this.element_id).style.cursor = "pointer";
	
	if (this.options.scroll_offset != null)
		new Effect.ScrollTo(this.element_id, {offset: this.options.scroll_offset});
	
    Event.observe($("form_" + this.element_id), "submit", this.onsubmitListener);
    Event.observe($("cancel_" + this.element_id), "click", this.oncancelListener);
  },
  showAsReadonly: function() {
	if (!this.editing) return;
 	new Ajax.Updater({success: this.element, failure: null}, 
        			 "/update_partial/" + this.id + "?editable=false&class_name=" + this.classname + "&partial=" + this.partial,
					 Object.extend({onComplete: this.onReadonly.bind(this)}, {})
					);
	return false;
  },
  onReadonly: function() {
    this.editing = false;
	this.element.style.cursor = "pointer";
	
    Event.stopObserving($("form_" + this.element_id), 'submit', this.onsubmitListener);
    Event.stopObserving($("cancel_" + this.element_id), 'click', this.oncancelListener);
  },
  dispose: function() {
    Event.stopObserving(this.element, 'click', this.onclickListener);
    Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
    Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
  }
};

Effect.BlindLeft = function(element) {
  element = $(element);
  element.makeClipping();
  return new Effect.Scale(element, 0, 
    Object.extend({ scaleContent: false, 
      scaleY: false, 
      restoreAfterFinish: true,
      afterFinishInternal: function(effect) {
        effect.element.hide();
        effect.element.undoClipping();
      } 
    }, arguments[1] || {})
  );
}

Effect.BlindRight = function(element) {
  element = $(element);
  var elementDimensions = element.getDimensions();
  return new Effect.Scale(element, 100, 
    Object.extend({ scaleContent: false, 
      scaleY: false,
      scaleFrom: 0,
      scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
      restoreAfterFinish: true,
      afterSetup: function(effect) {
        effect.element.makeClipping();
        effect.element.setStyle({width: '0px'});
        effect.element.show(); 
      },  
      afterFinishInternal: function(effect) {
        effect.element.undoClipping();
      }
    }, arguments[1] || {})
  );
}

Effect.Reflect = function(element) {
  element = $(element);
  options = $H({
    amount: 1 / 3,
    opacity: 1 / 3
  }).merge(arguments[1] || {});
  
  var p = element.parentNode, n = element.nextSibling;
  var d = 1.0 / (element.height * options.amount);
    
  (element.height * options.amount).times(function(line) {
    var h = Builder.node("div", {style: "height: 1px; overflow: hidden"},
      [Builder.node("img", {src: element.src, 
        style: "margin-top:-" + (element.height - line - 1) + "px"
      })]);
    p.insertBefore(h, n);
    $(h).setOpacity((1 - d * line) * options.opacity);
  });
}

function collect(args, element) {
	var result = [];

	args = args.split(" ").join("").split(":");
	if (args.length > 1) 
		element = $(args.shift());	
	if (args.length > 0) 
		args = args.join("").split(">");		
	var types = args.shift().split(",");
	
	var counters = [];
	var limit = "";
	types.each(
		function(value, index) {
			var count = value.split('!');
			if (count.length == 1)
				count.push("-");
			
			types[index] = count[0];
			limit += count[1];
			
			counters.push(0);
		}
	);
	
	for (var i = 0; i < element.childNodes.length; i++) {
		var index = types.indexOf(element.childNodes[i].nodeName.toLowerCase());
		if (index != -1) {
			var e = element.childNodes[i];
			
			if (args.length == 0) 
				result.push(e);
			else
				result.push([e, collect(args.join(""), e)]);
				
			counters[index]++;
		}
		if (counters.join("") == limit)
			break;
	}
	
	return result;
}

function eventElement(event) {
	if (isFirefox)
		return event.target;
	else
		return event.srcElement;
}

function backgroundColor(element) {
	var node = element;
	
	while (node.parentNode != null) {
		var color = color_to_hex(node.getStyle("background-color"))
		if (color != "")
			return color;
		node = node.parentNode;
	}
	
	return "#FFFFFF";
}

var decChars = '0123456789';

function color_to_hex(colorString) {
	if (colorString == null)
		return "";
	
	var string = "";
	for (var i = 0; i < colorString.length; i++) {
		var c = colorString[i];
		if ((c == ',') || (decChars.indexOf(c) != -1))
			string += c;
	}
	
	if (string == "")
		return "";
	
	var hex = "";
	var decArray = string.split(",");
	for (var i = 0; i < 3; i++)
		hex += dec_to_hex(decArray[i]);
	
	if (hex == "000000")
		hex = ""
	
	return hex == "" ? hex : "#" + hex;
}

var hexChars = "0123456789ABCDEF";

function dec_to_hex(dec) {
	var a = dec % 16;
	var b = (dec - a) / 16;
	
	var hex = "" + hexChars.charAt(b) + hexChars.charAt(a);
	return hex;
}

function toggleSelect(element, container, hidden_field) {
	if (Element.hasClassName(element, "selected"))
		Element.removeClassName(element, "selected");
	else
		Element.addClassName(element, "selected");
	
	if (container && hidden_field) {
		var rows = collect(container + ": tbody > tr")[0][1];
		var ids = [];
		for (var i = 0; i < rows.length; i++) {
			if (Element.hasClassName(rows[i], "selected"))
				ids.push(rows[i].id);
		}
		$(hidden_field).value = ids.join("_");
	}
}
