/*

Overrides of Prototype/Scriptaculous

*/

Ajax.InPlaceEditor.DefaultOptions.cancelControl = false; // we don't like cancel controls
Ajax.InPlaceEditor.DefaultOptions.okControl = false; // we don't like ok controls
Ajax.InPlaceEditor.DefaultOptions.submitOnBlur = true; // we like submiting on blur
Ajax.InPlaceEditor.DefaultOptions.htmlResponse = false; // we like returning chunks of javascript
Ajax.InPlaceEditor.DefaultOptions.customDataType = 'string'; // this is a custom option, used mostly for dates
Ajax.InPlaceEditor.DefaultOptions.okButtonSource = '/images/forms/buttons/small/save.gif'; // if we must use a button, it needs to be an image button
Ajax.InPlaceEditor.DefaultOptions.cancelButtonSource = '/images/forms/buttons/small/cancel.gif';
Ajax.InPlaceEditor.DefaultOptions.textBetweenControls = ' '; // if we must use buttons, let's put a little space between them
Ajax.InPlaceEditor.DefaultOptions.ajaxOptions = { method: 'post' }; // the built-in default is get, which doesn't work for really long query strings so let's use post instead

// we've overridden the listeners object because the default one has a keypress listener
// since these listeners are applied to the span/div that can be clicked to edit, a keypress listener does not make sense
Ajax.InPlaceEditor.Listeners = {
  click: 'enterEditMode',
  mouseover: 'enterHover',
  mouseout: 'leaveHover'
};

Ajax.InPlaceEditor.addMethods({
  getText: function() {
    // since we do in-place edits for the password field on the user settings page, we need to account for this case
    // and not fill in the input box with a bunch of asterisks
    if (this.element.id.toLowerCase().indexOf('password') > -1) return '';
    else return this.options.rawText || this.element.innerHTML;
  },
  handleFormCancellation: function(e){
    this.wrapUp();
    if (e) Event.stop(e);
    return false; // for IE when there are submit/cancel buttons on the IPE
  },
  createControl: function(mode, handler, extraClasses) {
    // this method is flatout overridden because we want to draw image-type buttons instead of normal submit buttons
    var control = this.options[mode + 'Control'];
    var text = this.options[mode + 'Text'];
    if ('button' == control) {
      var btn = document.createElement('input');
      btn.type = 'image';
      btn.value = text;
      btn.className = 'editor_' + mode + '_button image_button';
      btn.src = ( mode == 'ok' ? this.options.okButtonSource : this.options.cancelButtonSource);
      if ('cancel' == mode)
        btn.onclick = this._boundCancelHandler;
      this._form.appendChild(btn);
      this._controls[mode] = btn;
    } else if ('link' == control) {
      var link = document.createElement('a');
      link.href = '#';
      link.appendChild(document.createTextNode(text));
      link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
      link.className = 'editor_' + mode + '_link';
      if (extraClasses)
        link.className += ' ' + extraClasses;
      this._form.appendChild(link);
      this._controls[mode] = link;
    }
  }
});


// here we are opening wrapping a bunch of methods in the inplaceeditor so we can fix some broken funcitonality (key listener on form control) 
// and add new functionality
Ajax.InPlaceEditor.prototype.initialize = Ajax.InPlaceEditor.prototype.initialize.wrap( function(){
  var args = $A(arguments), proceed = args.shift();
  proceed.apply(this, args);
  // save the key listener, so we can attach/detach it later on
  this._boundKeyListener = this.checkForEscapeOrReturn.bindAsEventListener(this);
  SPICEWORKS.utils.unloader(document, args[2]['unloadOn'], this.dispose.bindAsEventListener(this));
});

Ajax.InPlaceEditor.prototype.enterEditMode = Ajax.InPlaceEditor.prototype.enterEditMode.wrap( function(){
  var args = $A(arguments), proceed = args.shift();
  proceed.apply(this, args);
  // attach the key listener if the control is an input box. textareas have a row count > 1
  // we don't want to attach key listeners to textarea because it causes unexpected behavior (submits the form on pressing 'enter')
  if ( this.options.rows == 1) this._controls.editor.observe('keydown', this._boundKeyListener);

  // we need to give our control an ID so that we can attach the calendar to it, when the datatype is a date
  var editorID = this._form.id + '_textbox';
  this._controls.editor.setAttribute( 'id', editorID );

  // this customDataType is a non-standard option we've added
  if ( this.options.customDataType == 'date' ){
    CalendarPopup.setup( editorID, null);
    $(editorID)['onclick']();
  }
});

Ajax.InPlaceEditor.prototype.removeForm = Ajax.InPlaceEditor.prototype.removeForm.wrap( function(){
  var args = $A(arguments), proceed = args.shift();
  // before the editor control is destroyed, detach the key listener
  if ( this._controls.editor && this.options.rows == 1 ) this._controls.editor.stopObserving('keydown', this._boundKeyListener);
  proceed.apply(this, args);
});

Ajax.InPlaceEditor.prototype.registerListeners = Ajax.InPlaceEditor.prototype.registerListeners.wrap( function(){
  var args = $A(arguments), proceed = args.shift();

  // here we have overridden inplaceeditor to allow for "double-click to edit" behavior
  // due to how the listeners are defined, we need to save off the values from the old hash and re-apply the saved hash (while switching click to dblclick) to the listeners object
  if ( this.options.onDoubleClick ){
    var newListeners = {};
    // iterate over the listeners object
    $H(Ajax.InPlaceEditor.Listeners).each( function( pair ){
      // as long as the listener isn't 'click', then just copy it directly
      if ( pair.key != 'click' ) newListeners[pair.key] = pair.value;
      // otherwise use the same listener, but use a new key
      else newListeners['dblclick'] = pair.value;
    });
    Ajax.InPlaceEditor.Listeners = newListeners;
  }

  proceed.apply(this, args);
});

Ajax.InPlaceEditor.prototype.handleFormSubmission = Ajax.InPlaceEditor.prototype.handleFormSubmission.wrap( function(proceed, e){
  if (this._controls.editor && $F(this._controls.editor) == this.options.clickToEditText) this.handleFormCancellation(e);
  else proceed(e);
});

Ajax.PeriodicalUpdater.addMethods({
  _dummyUpdater: function(){
    // for the case when a delayed start occurs, we need to spoof the updater object since it does not get instantiated until the start method is called
    // if we don't do this, then a javascript exception will occur when you call the stop method for the start method has been called
    return { options:{
      onComplete: Prototype.emptyFunction
    } };
  },
  updateComplete: function(response) {
    if (this.options.decay) {
      this.decay = (response.responseText == this.lastText ?
        this.decay * this.options.decay : 1);

      if (this.options.maxDecay && this.decay > this.options.maxDecay) this.decay = this.options.maxDecay;
      this.lastText = response.responseText;
    }
    this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
  }
});

Ajax.PeriodicalUpdater.prototype.start = Ajax.PeriodicalUpdater.prototype.start.wrap( function(){
  var args = $A(arguments), proceed = args.shift();
  if (this.options.delayedStart){
    // create our dummy object, which will be overwritten once the start method is called
    this.updater = this._dummyUpdater();
    this.delayedTimer = proceed.curry.apply(proceed.bind(this), args).delay(this.options.delayedStart);
  } else {
    proceed.apply(this, args);
  }
});

Ajax.PeriodicalUpdater.prototype.stop = Ajax.PeriodicalUpdater.prototype.stop.wrap( function(){
  var args = $A(arguments), proceed = args.shift();
  if (this.delayedTimer) clearTimeout(this.delayedTimer);
  this.delayedTimer = null;
  proceed.apply(this, args);
});

Ajax.PeriodicalUpdater.prototype.updateComplete = Ajax.PeriodicalUpdater.prototype.updateComplete.wrap( function(){
  var args = $A(arguments), proceed = args.shift();
  
  if (this.options.maxFrequency && this.options.decay && (this.decay * this.options.decay * this.frequency) > this.options.maxFrequency ) this.decay = (this.options.maxFrequency / this.frequency) / this.options.decay;
  proceed.apply(this, args);
});

Autocompleter.Base.addMethods({
  markPrevious: function() {
    // override the builtin method, we don't want the scrollIntoView behavior
    if(this.index > 0) this.index--;
    else this.index = this.entryCount-1;
  },

  markNext: function() {
    // override the builtin method, we don't want the scrollIntoView behavior
    if(this.index < this.entryCount-1) this.index++;
    else this.index = 0;
  }
});