#1184 new
Michael Morris

Ajax Extensibility issue - Ajax.Request#respondToReadyState has too much to do.

Reported by Michael Morris | December 14th, 2010 @ 07:58 PM

I've formed a habit of making very heavy use of prototype's auto eval of javascript responses. But there's one corner case that's giving me a bit of a problem - when I need to send the client back a large block of html and then some short javascript instructions on where to insert the block and what other actions to take (if any). I use this XML format.

<r>
  <h>
    <![CDATA[
      My html block here.
    ]]>
  </h>
  <j>
    <![CDATA[
      My javascript here.
    ]]>
  </j>
</r>

Now I have a function that gives Ajax.Request a callback to parse this recurrent xml structure that works fine. But I was wanting to do a bit more - to create a Requester object that extends off of Ajax.Request which I can then fire and forget. That child object would deal with the structure above, but if given any other callbacks to fire it would parse them after the server's javascript in the XML tree parses.

The problem with the framework I'm having is in the granularity of the Ajax.Request.respondToReadyState method. This method addresses in order...

Retrieves state
  IF state is complete fire onSuccess or onFailure handle
  Retrieve content type
    IF content type is javascript eval it.

Fire all other event handles
  IF state is complete empty the ready state handle so that we don't go into infinite loop.

The thing is, I want to tack on an 'IF content type is xml AND if server sends an X-HTTP header I will name later on then eval the XML response.

Now I hope the problem is clear - respondToReadyState needs to be broken up into more than one function so that this (and other unrelated extensions) are possible.

I know this sort of change isn't to be taken lightly. The outward facing API shouldn't change at all. Existing code that extends off of respondToReadyState should be ok since the method doesn't have a return.

I think I'm up to the task of breaking this function down into steps, but before I begin I'd like some input. I have a couple of options - breaking the function down is only attractive if it is done at a core level, otherwise I'm better off passing my special case as a handler. While this creates problems elsewhere for me, they are less than the problem of having to reconcile an overriding repsondToReadyState method with core each time there's a release.

Comments and changes to this ticket

  • Michael Morris

    Michael Morris December 14th, 2010 @ 08:22 PM

    After submitting the ticket I decided to do a rough sketch of what the code would look like if broken up so that each action of respondToReadyState is handled in a distinct method - as well as adding some member vars to prevent parameter juggling.

    This code is untested - it's just for illustrative purposes. It would go into the Ajax.Request object in place of the existing respondToReadyState function.

      _readyState: null,
      _response: null,
      _requestState: null,
      
      respondToReadyState: function(readyState) {
        this._setReadyState(readyState);
        this._setRequestState();
        this._setResponse();
            
        if (this._requestState == 'Complete') {
          this._handleSuccessOrFailure();
          this._handleAutoEvaluation();
        }
        
        this._handleStateChangeHandlers();
        
        if (this._requestState == 'Complete') {
          this._handleRequestCompletion();
        }
      },
      
      _setReadyState: function(readyState) {
        this._readyState = readyState;
      },
      
      _setRequestState: function() {
        this._requestState = Ajax.Request.Events[this._readyState];
      },
      
      _setResponse: function() {
        this._response = new Ajax.Response(this);
      },
        
      _handleSuccessOrFailure: function() {
        try {
            this._complete = true;
            (this.options['on' + this._response.status]
             || this.options['on' + (this.success() ? 'Success' : 'Failure')]
             || Prototype.emptyFunction)(this._response, this._response.headerJSON);
          } catch (e) {
            this.dispatchException(e);
          }
      },
      
      _handleAutoEvaluation: function () {
        var contentType = this._response.getHeader('Content-type');
        
        if (this.options.evalJS == 'force'
          || (this.options.evalJS && this.isSameOrigin() && contentType
          && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
          this.evalResponse();
      },
      
      _handleStateChangeHandlers: function () {
        try {
          (this.options['on' + state] || Prototype.emptyFunction)(this._response, this._response.headerJSON);
          Ajax.Responders.dispatch('on' + state, this, this._response, this._response.headerJSON);
        } catch (e) {
          this.dispatchException(e);
        }
      },
      
      _handleRequestCompletion: function () {
        this.transport.onreadystatechange = Prototype.emptyFunction;
      }
    
  • Maddy Ferry

Please Sign in or create a free account to add a new ticket.

With your very own profile, you can contribute to projects, track your activity, watch tickets, receive and update tickets through your email and much more.

New-ticket Create new ticket

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile ยป

The Prototype JavaScript library.

Shared Ticket Bins

People watching this ticket

Pages