/**
 * RightJS-UI InEdit v2.2.0
 * http://rightjs.org/ui/in-edit
 *
 * Copyright (C) 2009-2011 Nikolay Nemshilov
 */
var InEdit = RightJS.InEdit = (function(document, RightJS) {
/**
 * This module defines the basic widgets constructor
 * it creates an abstract proxy with the common functionality
 * which then we reuse and override in the actual widgets
 *
 * Copyright (C) 2010-2011 Nikolay Nemshilov
 */

/**
 * The widget units constructor
 *
 * @param String tag-name or Object methods
 * @param Object methods
 * @return Widget wrapper
 */
function Widget(tag_name, methods) {
  if (!methods) {
    methods = tag_name;
    tag_name = 'DIV';
  }

  /**
   * An Abstract Widget Unit
   *
   * Copyright (C) 2010 Nikolay Nemshilov
   */
  var AbstractWidget = new RightJS.Class(RightJS.Element.Wrappers[tag_name] || RightJS.Element, {
    /**
     * The common constructor
     *
     * @param Object options
     * @param String optional tag name
     * @return void
     */
    initialize: function(key, options) {
      this.key = key;
      var args = [{'class': 'rui-' + key}];

      // those two have different constructors
      if (!(this instanceof RightJS.Input || this instanceof RightJS.Form)) {
        args.unshift(tag_name);
      }
      this.$super.apply(this, args);

      if (RightJS.isString(options)) {
        options = RightJS.$(options);
      }

      // if the options is another element then
      // try to dynamically rewrap it with our widget
      if (options instanceof RightJS.Element) {
        this._ = options._;
        if ('$listeners' in options) {
          options.$listeners = options.$listeners;
        }
        options = {};
      }
      this.setOptions(options, this);

      return (RightJS.Wrapper.Cache[RightJS.$uid(this._)] = this);
    },

  // protected

    /**
     * Catches the options
     *
     * @param Object user-options
     * @param Element element with contextual options
     * @return void
     */
    setOptions: function(options, element) {
      if (element) {
        options = RightJS.Object.merge(options, new Function("return "+(
          element.get('data-'+ this.key) || '{}'
        ))());
      }

      if (options) {
        RightJS.Options.setOptions.call(this, RightJS.Object.merge(this.options, options));
      }

      return this;
    }
  });

  /**
   * Creating the actual widget class
   *
   */
  var Klass = new RightJS.Class(AbstractWidget, methods);

  // creating the widget related shortcuts
  RightJS.Observer.createShortcuts(Klass.prototype, Klass.EVENTS || []);

  return Klass;
}


/**
 * A shared module to create textual spinners
 *
 * Copyright (C) 2010-2011 Nikolay Nemshilov
 */
var Spinner = new RightJS.Class(RightJS.Element, {
  /**
   * Constructor
   *
   * @param Number optional spinner size (4 by default)
   * @return void
   */
  initialize: function(size) {
    this.$super('div', {'class': 'rui-spinner'});
    this.dots = [];

    for (var i=0; i < (size || 4); i++) {
      this.dots.push(new RightJS.Element('div'));
    }

    this.dots[0].addClass('glowing');
    this.insert(this.dots);
    RightJS(this.shift).bind(this).periodical(300);
  },

  /**
   * Shifts the spinner elements
   *
   * @return void
   */
  shift: function() {
    if (this.visible()) {
      var dot = this.dots.pop();
      this.dots.unshift(dot);
      this.insert(dot, 'top');
    }
  }
});


/**
 * In-Edit plugin initalization
 *
 * Copyright (C) 2010 Nikolay Nemshilov
 */
var R = RightJS,
    $ = RightJS.$,
    $w = RightJS.$w,
    Xhr     = RightJS.Xhr,
    Object  = RightJS.Object,
    Element = RightJS.Element,
    Input   = RightJS.Input;









/**
 * An inline editor feature
 *
 * Copyright (C) 2009-2011 Nikolay Nemshilov
 */
var InEdit = new Widget('FORM', {
  extend: {
    version: '2.2.0',

    EVENTS: $w('show hide send update'),

    Options: {
      url:    null,    // the url address where to send the stuff
      name:   'text',  // the field name
      method: 'put',   // the method

      type:   'text',  // the input type, 'text', 'file', 'password' or 'textarea'

      toggle:  null,   // a reference to an element that should get hidden when the editor is active

      update:  true,   // a marker if the element should be updated with the response-text

      Xhr: {}          // additional Xhr options
    },

    i18n: {
      Save:   'Save',
      Cancel: 'Cancel'
    },

    current: null      // currently opened editor
  },

  /**
   * Constructor
   *
   * @param mixed an element reference
   * @param Object options
   * @return void
   */
  initialize: function(element, options) {
    this.element = $(element);

    this
      .$super('in-edit', options)
      .set('action', this.options.url)
      .insert([
        this.field   = new Input({type: this.options.type, name: this.options.name, 'class': 'field'}),
        this.spinner = new Spinner(4),
        this.submit  = new Input({type: 'submit', 'class': 'submit', value: InEdit.i18n.Save}),
        this.cancel  = new Element('a', {'class': 'cancel', href: '#', html: InEdit.i18n.Cancel})
      ])
      .onClick(this.clicked)
      .onSubmit(this.send);
  },

  /**
   * Shows the inline-editor form
   *
   * @return InEdit this
   */
  show: function() {
    if (InEdit.current !== this) {
      if (InEdit.current) { InEdit.current.hide(); }

      this.oldContent = this.element.html();

      if (!R(['file', 'password']).include(this.options.type)) {
        this.field.setValue(this.oldContent);
      }

      this.element.update(this);

      this.spinner.hide();
      this.submit.show();

      if (this.options.toggle) {
        $(this.options.toggle).hide();
      }
    }

    if (this.options.type !== 'file') {
      this.field.focus();
    }

    InEdit.current = this;
    return this.fire('show');
  },

  /**
   * Hides the form and brings the content back
   *
   * @param String optional new content
   * @return InEdit this
   */
  hide: function() {
    this.element._.innerHTML = this.oldContent;

    if (this.xhr) {
      this.xhr.cancel();
    }

    return this.finish();
  },

  /**
   * Triggers the form remote submit
   *
   * @return InEdit this
   */
  send: function(event) {
    if (event) { event.stop(); }

    this.spinner.show().resize(this.submit.size());
    this.submit.hide();

    this.xhr = new Xhr(this.options.url, Object.merge(this.options.Xhr, {
      method:     this.options.method,
      spinner:    this.spinner,
      onComplete: R(this.receive).bind(this)
    })).send(this);

    return this.fire('send');
  },

// protected

  // finishes up with the form
  finish: function() {
    if (this.options.toggle) {
      $(this.options.toggle).show();
    }

    InEdit.current = null;
    return this.fire('hide');
  },

  // the xhr callback
  receive: function() {
    if (this.options.update) {
      this.element.update(this.xhr.text);
      this.fire('update');
    }

    this.xhr = null;

    this.finish();
  },

  // catches clicks on the element
  clicked: function(event) {
    if (event.target === this.cancel) {
      event.stop();
      this.hide();
    }
  }

});


/**
 * The document hooks for in-edit form
 *
 * Copyright (C) 2010 Nikolay Nemshilov
 */
$(document).onKeydown(function(event) {
  // processing the `ESC` button
  if (event.keyCode === 27 && InEdit.current) {
    InEdit.current.hide();
  }
});


/**
 * The element level inline editor extension
 *
 * Copyright (C) 2009-2010 Nikolay Nemshilov
 */
Element.include({
  /**
   * Triggers an inline-editor feature on the element
   *
   * @param Object options for the InEdit class
   * @return InEdit object
   */
  inEdit: function(options) {
    return new InEdit(this, options).show();
  }
});


var embed_style = document.createElement('style'),                 
    embed_rules = document.createTextNode("div.rui-spinner,div.rui-spinner div{margin:0;padding:0;border:none;background:none;list-style:none;font-weight:normal;float:none;display:inline-block; *display:inline; *zoom:1;border-radius:.12em;-moz-border-radius:.12em;-webkit-border-radius:.12em}div.rui-spinner{text-align:center;white-space:nowrap;background:#EEE;border:1px solid #DDD;height:1.2em;padding:0 .2em}div.rui-spinner div{width:.4em;height:70%;background:#BBB;margin-left:1px}div.rui-spinner div:first-child{margin-left:0}div.rui-spinner div.glowing{background:#777}form.rui-in-edit,form.rui-in-edit .cancel{margin:0;padding:0;float:none;position:static}form.rui-in-edit{display:inline-block; *display:inline; *zoom:1;border:none;background:none}form.rui-in-edit div.rui-spinner{margin-right:.2em}form.rui-in-edit div.rui-spinner div{margin-top:.2em}form.rui-in-edit textarea.field{width:100%;margin-bottom:.5em}form.rui-in-edit .field,form.rui-in-edit .submit{margin-right:.2em}form.rui-in-edit,form.rui-in-edit .field,form.rui-in-edit .submit,form.rui-in-edit div.rui-spinner,form.rui-in-edit .cancel{vertical-align:middle}");      
                                                                   
embed_style.type = 'text/css';                                     
document.getElementsByTagName('head')[0].appendChild(embed_style); 
                                                                   
if(embed_style.styleSheet) {                                       
  embed_style.styleSheet.cssText = embed_rules.nodeValue;          
} else {                                                           
  embed_style.appendChild(embed_rules);                            
}                                                                  


return InEdit;
})(document, RightJS);