/** Script to be included in pages that must provide in place editing facilities.

In the HTML, elements that are to be editable: 
- must be given the class defined in variable 'editableElementsClass'
- must be given a class shaping like 'item:NAME_OF_THE_ELEMENT'
- must be given an id
- can be given a class 'editable_textarea' if they are to be edited using a textarea rather than a normal input field
- can be given a class 'editable_combo' if they are to be edited using a combo rather than a normal input field
- can be given an id (not a class 'id', here we mean a real HTML id)

data for combo cab be defined using the variable 'combodata' which is ana rray indexed by the NAME_OF_THE_ELEMENT's which values are arrays collecting the options for the combo.

The url of ajax script is defined in variable 'ur'.

Parameters can be passed to the ajax script via variable 'parameters', each parameters will be transformed in an hidden field in the editing form; the value of the hidden field created for a parameter P is the value of field P of the form (parameters are a means to pass witht the ajax call values stored in the form's fields).

Classes can be overriden using editableElementsClass variabel or using 'classes' variable; classes that can be overriden using 'classes' variable are:
- overCl: the class that defines how editable elements behave when mouse passes over them (default 'over')
- hiddenCl: the class that defines how elements are hidden when replaced by their editing form (default 'hidden')
- textareaToEditCl: the class that identifies elements to be edited using a textarea rather than a normal input field (default 'editable_textarea')
- comboToEditCl: the class that identifies elements to be edited using a combo rather than a normal input field (default 'editable_combo')
- saveCl: (default 'inplace_save')
- cancelCl: the class for the cancel button (default 'inplace_cancel')
- inputCl: the class for input button (default 'inplace_input')
- textareaCl: the class for textareas (default 'inplace_textarea')
- comboCl: the class for combo (default 'inplace_textarea')
- editableFormCl: the calss for the form created for editing (default 'editable_form')
- emptyCl: the class that identifies the elements will null value (default 'inplace_empty')
An example of overriding:
options = {
 overCl: 'myover',
 hiddenCl: 'myhidden' ,
 textareaToEditCl: 'myeditable_textarea' ,
}

Ajax calls are sent with post method; $_POST will contain:
- NAME_OF_THE_ELEMENT => value_of_the_element
- ID_OF_THE_ELEMENT => id_of_the_element
- a couple PARAMETER_NAME => PARAMETER_VALUE for each parameter defined in variable 'parameters'

It is possible to fill in the body of onComplete function to define what must happen on ajax call completion.

it is possible to force evaluation of ajax response when it is javascript code setting to true variable 'evaluateResponseScript'';
example:
  print "<pre>".print_r($_POST, true)."</pre>";
  echo '<script type="text/javascript">';
  echo "  alert('evaScripts says: I\'m loaded!');";
  echo '</script>';
will get the result of 'print "<pre>".print_r($_POST, true)."</pre>";' as ajax call output, and will display an alert with string 'I'm loaded!'.

it is possible to force evaluation of ajax response regardless its form (it could be javascript, PHP...) setting to true variable 'evaluateResponseAlways''; 
example:
  header('Content-Type: text/javascript');
  echo "alert('evalResponse was enabled!')";
will get 'alert('evalResponse was enabled!')' ajax call output, and will display an alert with string 'evalResponse was enabled!'. Note that code will give the same result also with 'evaluateResponseAlways' set to true.
 */

/** the class of editable elements */
var editableElementsClass = 'editable';
/** the url of the ajax script */
var url = 'http://be_framework:82/edit.dhtml';
/** the container for parameters to be sent with the ajax call */
var parameters = {};
/** the default classes */
var standardClasses = {
  overCl: 'over',
  hiddenCl: 'hidden',
  editableCl: editableElementsClass,
  textareaToEditCl: 'editable_textarea',
  comboToEditCl: 'editable_combo',
  saveCl: 'inplace_save',
  cancelCl: 'inplace_cancel',
  inputCl: 'inplace_input',
  textareaCl: 'inplace_textarea',
  comboCl: 'inplace_combo',
  editableFormCl: 'editable_form',
  emptyCl: 'inplace_empty'
}
/** the container for option to override default class names */
var options = {};
/** the string used to fill empty elements */
var emptyString = '[empty]'
/** what to do on ajax call completion */
var onCompleteFunction = function(id) {
  el = $(id);
  if (el.get('text') == '') {
    el.set('html', emptyString);
    el.addClass(standardClasses.emptyCl);
  }
  else {
    el.removeClass(standardClasses.emptyCl);
  }
}
/** evaluation of ajax response when it is javascript */
evaluateResponseScript = false;
/** evaluation of ajax response regardless its form */
evaluateResponseAlways = false;
/** the settings for the mouseover effect of editable elemens */
var editableFadeEffect_duration_in = 0;
var editableFadeEffect_duration_out = 500;
var editableFadeEffect_initialColor = 'ffffff';
var editableFadeEffect_finalColor = 'FFFFD3';
/** the data for possible combobox */
var combodata= [];
combodata['elemento2'] = ['value11', 'value12', 'value13'];

/**
 * @author 
 *
 * Description:
 *  A class to edit in place.
 */

var eip = new Class({

    /**
     * Initialize
     * @param string action the path to the file to target with form.
     * @param object params (optional) Any extra parameters you would like to send with the AJAX call.
     * @param object options (optional) Override the default classes with this.
     */
    initialize: function(action, params, options) {
    // Default options
    this.options = Object.extend(standardClasses, options || {} );
    
    // collects the editable elements and prepares them for editing
    els = $$('.'+this.options.editableCl);
    els.each(function(el){
      this.prepForm(el);
    }.bind(this));

    // Store the action (path to file) and params
    this.action = action;
    this.params = params;
  },


  /**
     * Add events to element
     * @param element el Your target element.
     */
  prepForm: function(el) {
    var obj = this;
    var fx_in = new Fx.Morph(el, {duration:editableFadeEffect_duration_in, wait:false});
    var fx_out = new Fx.Morph(el, {duration:editableFadeEffect_duration_out, wait:false});
    el.addEvents({
      'mouseover': function(){fx_in.start({'background-color' : '#'+editableFadeEffect_finalColor});},
      'mouseout': function(){fx_out.start({'background-color' : '#'+editableFadeEffect_initialColor});},
      'click': function(){obj.showForm(this);}
    });
    if(el.get('text') == '') {
      el.addClass(this.options.emptyCl);
      el.set('html', emptyString);
    }
  },


  /**
     * Build and/or show form
     * @param element el Your target element.
     */
  showForm: function(el) {
    // Get the name (target) and id from your element
    var classes = el.getProperty('class').split(" ");
    for (i=classes.length-1;i>=0;i--) {
      if (classes[i].contains('item:')) {
        var target = classes[i].split(":")[1];
      }
    }
    var id = el.getProperty('id');
    if(!id) {
      id = target;
      el.setProperty('id', id);
    }

    // Hide your target element
    el.addClass(this.options.hiddenCl);

    // If the form exists already, let's show that
    if (el.form) {
      el.form.removeClass(this.options.hiddenCl);
      el.form[target].focus();
      return;
    }

    // Create new form
    var form = new Element('form', {
      'id': 'form_' + el.getProperty('id'),
      'action': this.action,
      'class': this.options.editableFormCl
    });

    // Store new form in the element
    el.form = form;
  
    //alert(this.options.comboToEditCl);
    
    // Create a textarea or input for user
    if (el.hasClass(this.options.textareaToEditCl)) {
      var input = new Element('textarea', {
        'name': target,
        'class': this.options.textareaCl
      });
      val = '';
      if(!el.hasClass(this.options.emptyCl))
        val = el.get('html');
      input.appendText(val).injectInside(form);      
    }
    else if (el.hasClass(this.options.comboToEditCl)) {
      var input = new Element('select', {
        'name': target,
        'value': combodata[target][0],
        'class': this.options.comboCl
      })
      numOfOptions = combodata[target].length;
      for (i = 0; i < numOfOptions; i++) {input.adopt( new Element('option').set('html', combodata[target][i]));}
      input.injectInside(form);
    }
    else {
      val = el.get('html');
      if(el.hasClass(this.options.emptyCl))
        val = '';
        
      var input = new Element('input', {
        'name': target,
        'value': val,
        'class': this.options.inputCl
      }).injectInside(form);
    }
    
    var sp = new Element('span');
    sp.set('html', "<br />");
    sp.injectInside(form);

    // Need this to pass to the buttons
    var obj = this;

    // Add a submit button
    new Element('input', {
      'type': 'submit',
      'value': 'save',
      'class': this.options.saveCl,
      'events': {
        'click': function(evt){
          (new Event(evt)).stop();
          el.empty();
          el.appendText('saving...');
          obj.hideForm(form, el);
          /*
          form.send({
            update: el,
            onComplete: function(){onCompleteFunction(id)},
            evalScripts: evaluateResponseScript,
            evalResponse: evaluateResponseAlways}
          );
          */
          var myHTMLRequest = new Request.HTML({
            url:form.action,
            evalScripts: evaluateResponseScript,
            evalResponse: evaluateResponseAlways,
            update:el,
            onComplete: function(){onCompleteFunction(id)}
            }).post(form);
        }
      }
    }).injectInside(form);
    
    var sp = new Element('span');
    sp.set('html', "&nbsp;OR&nbsp;");
    sp.injectInside(form);

    // Add a cancel button
    new Element('input', {
      'type': 'button',
      'value': 'cancel',
      'class': this.options.cancelCl,
      'events': {
        'click': function(form, el){
          obj.hideForm(form, el);
        }.pass([form, el])
      }
    }).injectInside(form);

    // For every param, add a hidden input
    for (param in this.params) {
      new Element('input', {
        'type': 'hidden',
        'name': param,
        'value': this.params[param]
      }).injectInside(form);
    }

    //
    new Element('input', {
      'type': 'hidden',
      'name': 'id',
      'value': id
    }).injectInside(form);

    // Add the form after the target element
    form.injectAfter(el);

    // Focus on the input
    input.focus();
    input.select();
  },

  /**
    * Hide form
    * @param element form Your target form.
    * @param element el Your target element.
    */
  hideForm: function(form, el) {
    form.addClass(this.options.hiddenCl);
    el.removeClass(this.options.hiddenCl);
  }
});

window.addEvent('load', function() {
  new eip(url, parameters, options);
});

