Ajax (Code Worth Recommending Project)

P

Peter Michaux

There is quite a bit tentative code now in the Code Worth Recommending
Project's repository. I think that there was some great discussion
spawned by the gathering of this code.

http://cljs.michaux.ca/trac/browser/trunk/src

The last piece of code that I want to add before finalizing
documentation and some sort of ratification is a "Ajax library". Many
of the pieces are already in the library like form serialization.

I have code for Ajax in a branch of the repository

http://cljs.michaux.ca/trac/browser/branches/peterAjax/src/ajax

and am posting the first bit of it here...

Most JavaScript Ajax libraries are a single blob of code and when new
features are added then everyone using the library gets the new
features and extra bloat. I think it is possible to build and Ajax
library as a core function that uses XHR or an image or iframe or some
other way of initiating communication with the server.

The following is the xhrCore function that can set the fundamental
parts of a request (eg, headers, body, url, callback) and make a
request. Wrappers are intended to add API sugar for the xhrCore.

-----------------------------------------------------------

// xhrCore('example.com/handler.php',
// {method:'POST',
// body:'foo=bar&asdf=qwerty',
// onComplete:function(){alert('complete');}});

if (typeof createXMLHttpRequest != 'undefined' &&
(typeof isXhrSufficientForXhrCore == 'undefined' ||
isXhrSufficientForXhrCore) &&
String.prototype.toUpperCase) {

var xhrCore;

(function() {

function empty() {}

xhrCore = function(url, options) {
options = options || {};

var xhr = createXMLHttpRequest(),
method = (options.method || 'GET').toUpperCase(),
callback = options.onComplete,
// does send really need null or can it be undefined?
body = options.body || null,
headers = options.headers || {};

if (callback) {
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
callback(xhr);
// for IE memory leak
xhr.onreadystatechange = empty;
}
};
}

if (method == 'GET' && body) {
url = url + '?' + body;
body = null;
}

xhr.open(method, url, true);

if (method == "POST") {
headers['Content-Type'] = headers['Content-Type'] ||
'application/x-www-form-urlencoded';
}

for (p in headers) {
xhr.setRequestHeader(p, headers[p]);
}

xhr.send(body);

return {xhr:xhr};
}
})();

}

-----------------------------------------------------------

Testing if a browser's xhr object is sufficient is done by the
isXhrSufficientForXhrCore function.

-----------------------------------------------------------

// perhaps don't want these checks on an intranet or behind a login
// where the browsers are known to be new.

if (typeof createXMLHttpRequest != 'undefined') {

var isXhrSufficientForXhrCore = (function() {

var xhr = createXMLHttpRequest();


// NN6.1 can create an XMLHttpRequest object but it doesn't
// function and after creation the object's readyState
// property is 'undefined' when it should be '0'.
// The XHR object in NN6.1 doesn't work.
if (xhr.readyState !== 0) {
return false;
}


// IE 5.01sp2 errors when x.setRequestHeader runs.
// The error says "object doesn't support this property"
// This may be because xhr.open() hasn't been called.
// yet. This is the reason for the try-catch. Since we cannot
// determine if the xhr object has setRequestHeader then
// we must put it down the degeradation path.
//
// Opera 8 XHR doesn't have setRequestHeader() and although
// it seems to be able to make a POST request with a body
// without this function This browser is still going to be
// put down the degredation path. Note Opera 8.0.1 does have
// setRequestHeader() and can POST.
try {
if (!xhr.setRequestHeader) {
return false;
}
} catch(e) {
return false;
}


// NN6.2 can't make POST requests because
// can't have arguments to send()
function cannotPost(xhr) {
try {
xhr.send('asdf');
} catch (e) {
// All calls to xhr.send() should error because there wasn't
// a call to xhr.open() however the normal error is something
// about "not initialized" as expected since xhr.open() was
// not called. NN6.2 gives a different error indicating
// xhr.send() cannot take arguments. Note that for NN6.2
// in other human languages the error is still in English.

// TODO check for toString and indexOf?
if (-1 !== e.toString().indexOf('Could not convert' +
// TRICKY that leading space in next string matters.
// using line break only to make postiong to group
possible
' JavaScript argument arg 0 [nsIXMLHttpRequest.send]')) {
return true;
}
}
return false;
}

if (cannotPost(xhr)) {
return false;
}


return true;

})();

}

-----------------------------------------------------------

I'm not completely happy with how all the feature testing is packaged
in the project so far. I'm more interested in getting correct
algorithms right now. However if someone has a bright idea about how
to package necessary feature tests in a library with such small
granularity, I'd be happy to read about it.

With the xhrCore function wrappers can be added for API sugar. Below
is the status.js wrapper which allows the developer to specify

-----------------------------------------------------------

if (Function.prototype.apply &&
String.prototype.match) {

var statusWrapper = function(original) {

return function(url, options) {
options = options || {};

var handlers = {};
for (var p in options) {
if (p.match(/^on(\d+|Success|Failure|Complete)/)) {
handlers[p] = options[p];
// do this delete because don't want inner wrappers
// to need to work on "branches". Waste of time.
delete options[p];
}
}

options.onComplete = function(xhr) {
var h;
// exploit short circuting of ||
if ((h = handlers['on'+xhr.status]) ||
(h = handlers.onSuccess &&
xhr.status>=200 && xhr.status<300) ||
(h = handlers.onFailure &&
(xhr.status<200 || xhr.status>=300)) ||
(h = handlers.onComplete)) {
h.apply(this, arguments);
}
};

return original(url, options);
}

};

}

-----------------------------------------------------------

With the xhrCore and the statusWrapper, a developer can use these
building blocks to easily build an Ajax library with the API he likes.
Here is an example...

-----------------------------------------------------------

// make a custom "sendAjax" function with the API
// and no extra bloat for API features I don't use
//
if (typeof statusWrapper != 'undefined' &&
typeof xhrCore != 'undefined') {
var sendAjax = statusWrapper(xhrCore);
}

// Use the new sendAjax function`
//
function testAjaxRequest() {

if (typeof sendAjax != 'undefined') {

sendAjax('serverFiles/handler.php',
{on200: function(xhr) {
alert('on200');
},
onComplete:function(xhr) {
alert('onComplete');
}
});
}

}

-----------------------------------------------------------

Many other wrappers and even one other core are in the branch of the
repository I linked to at the top of this post. I'm particularly
curious if Randy Webb's system of script insertion could be written as
a different core. I think so. I believe that another core could be
written for file uploads using an iframe (aka old fashioned remote
scripting).

I think this modular approach (some core functions and various
optional wrappers) is exactly what is needed to avoid the hugely
bloated Ajax libraries out there on the web.

I look forward to reading people's thoughts.
 
D

David Mark

There is quite a bit tentative code now in the Code Worth Recommending
Project's repository. I think that there was some great discussion
spawned by the gathering of this code.

I need to update a few of the tickets I posted.
http://cljs.michaux.ca/trac/browser/trunk/src

The last piece of code that I want to add before finalizing
documentation and some sort of ratification is a "Ajax library". Many
of the pieces are already in the library like form serialization.

I have code for Ajax in a branch of the repository

http://cljs.michaux.ca/trac/browser/branches/peterAjax/src/ajax

and am posting the first bit of it here...

Most JavaScript Ajax libraries are a single blob of code and when new
features are added then everyone using the library gets the new
features and extra bloat. I think it is possible to build and Ajax
library as a core function that uses XHR or an image or iframe or some
other way of initiating communication with the server.

The following is the xhrCore function that can set the fundamental
parts of a request (eg, headers, body, url, callback) and make a
request. Wrappers are intended to add API sugar for the xhrCore.

-----------------------------------------------------------

// xhrCore('example.com/handler.php',
//         {method:'POST',
//          body:'foo=bar&asdf=qwerty',
//          onComplete:function(){alert('complete');}});

if (typeof createXMLHttpRequest != 'undefined' &&
    (typeof isXhrSufficientForXhrCore == 'undefined' ||
     isXhrSufficientForXhrCore) &&
    String.prototype.toUpperCase) {

That last one seems unnecessary.
  var xhrCore;

  (function() {

    function empty() {}

    xhrCore = function(url, options) {
      options = options || {};

      var xhr = createXMLHttpRequest(),
          method = (options.method || 'GET').toUpperCase(),
          callback = options.onComplete,
          // does send really need null or can it be undefined?

I think it needs to be null.
          body = options.body || null,
          headers = options.headers || {};

      if (callback) {
        xhr.onreadystatechange = function() {
          if (xhr.readyState == 4) {
            callback(xhr);
            // for IE memory leak
            xhr.onreadystatechange = empty;
          }
        };
      }

      if (method == 'GET' && body) {
        url = url + '?' + body;
        body = null;
      }

      xhr.open(method, url, true);

      if (method == "POST") {
        headers['Content-Type'] = headers['Content-Type'] ||
                             'application/x-www-form-urlencoded';
      }

Add the X-Requested-With header if not specified?
      for (p in headers) {
        xhr.setRequestHeader(p, headers[p]);
      }

      xhr.send(body);

      return {xhr:xhr};
    }
  })();

}

-----------------------------------------------------------

Testing if a browser's xhr object is sufficient is done by the
isXhrSufficientForXhrCore function.

-----------------------------------------------------------

// perhaps don't want these checks on an intranet or behind a login
// where the browsers are known to be new.

if (typeof createXMLHttpRequest != 'undefined') {

  var isXhrSufficientForXhrCore = (function() {

    var xhr = createXMLHttpRequest();

    // NN6.1 can create an XMLHttpRequest object but it doesn't
    // function and after creation the object's readyState
    // property is 'undefined' when it should be '0'.
    // The XHR object in NN6.1 doesn't work.
    if (xhr.readyState !== 0) {
      return false;
    }

    // IE 5.01sp2 errors when x.setRequestHeader runs.

When it runs or when it is evaluated?
    // The error says "object doesn't support this property"
    // This may be because xhr.open() hasn't been called.
    // yet. This is the reason for the try-catch. Since we cannot
    // determine if the xhr object has setRequestHeader then
    // we must put it down the degeradation path.
    //
    // Opera 8 XHR doesn't have setRequestHeader() and although
    // it seems to be able to make a POST request with a body
    // without this function This browser is still going to be
    // put down the degredation path. Note Opera 8.0.1 does have
    // setRequestHeader() and can POST.
    try {
      if (!xhr.setRequestHeader) {

You didn't run it here. (?)
        return false;
      }
    } catch(e) {
      return false;
    }

    // NN6.2 can't make POST requests because
    // can't have arguments to send()
    function cannotPost(xhr) {
      try {
        xhr.send('asdf');
      } catch (e) {
        // All calls to xhr.send() should error because there wasn't
        // a call to xhr.open() however the normal error is something
        // about "not initialized" as expected since xhr.open() was
        // not called. NN6.2 gives a different error indicating
        // xhr.send() cannot take arguments. Note that for NN6.2
        // in other human languages the error is still in English.

        // TODO check for toString and indexOf?
        if (-1 !== e.toString().indexOf('Could not convert' +
            // TRICKY that leading space in next string matters.
            // using line break only to make postiong to group
possible
            ' JavaScript argument arg 0 [nsIXMLHttpRequest.send]')) {
          return true;
        }
      }
      return false;
    }

    if (cannotPost(xhr)) {
      return false;
    }

    return true;

  })();

}

Yes, this should be optional. NS6 is pretty much dead.
-----------------------------------------------------------

I'm not completely happy with how all the feature testing is packaged
in the project so far. I'm more interested in getting correct
algorithms right now. However if someone has a bright idea about how
to package necessary feature tests in a library with such small
granularity, I'd be happy to read about it.

Can you elaborate?
With the xhrCore function wrappers can be added for API sugar. Below
is the status.js wrapper which allows the developer to specify

-----------------------------------------------------------

if (Function.prototype.apply &&
    String.prototype.match) {

  var statusWrapper = function(original) {

    return function(url, options) {
      options = options || {};

      var handlers = {};
      for (var p in options) {
        if (p.match(/^on(\d+|Success|Failure|Complete)/)) {
          handlers[p] = options[p];
          // do this delete because don't want inner wrappers
          // to need to work on "branches". Waste of time.
          delete options[p];

I don't follow this, but I think it is a bad idea to mutate options
(this function doesn't own that object.)
        }
      }

      options.onComplete = function(xhr) {
        var h;
        // exploit short circuting of ||
        if ((h = handlers['on'+xhr.status])  ||
            (h = handlers.onSuccess &&
              xhr.status>=200 && xhr.status<300) ||
            (h = handlers.onFailure &&
              (xhr.status<200 || xhr.status>=300)) ||
            (h = handlers.onComplete)) {
          h.apply(this, arguments);

Why the apply?
        }
      };

      return original(url, options);
    }

  };

}

-----------------------------------------------------------

With the xhrCore and the statusWrapper, a developer can use these
building blocks to easily build an Ajax library with the API he likes.
Here is an example...

-----------------------------------------------------------

// make a custom "sendAjax" function with the API
// and no extra bloat for API features I don't use
//
if (typeof statusWrapper != 'undefined' &&
    typeof xhrCore != 'undefined') {
  var sendAjax = statusWrapper(xhrCore);

This is sort of weird. If the developer includes the statusWrapper
function in the build, then it should automatically wrap itself around
the old function. The next included wrapper would do the same. See
the Effects/DirectX enhancements to show/changeImage/setElementHtml on
my builder page.
}

// Use the new sendAjax function`
//
function testAjaxRequest() {

  if (typeof sendAjax != 'undefined') {

    sendAjax('serverFiles/handler.php',
             {on200: function(xhr) {
                        alert('on200');
                     },

That's clever. I might have to copy that. All in all, I like this,
but I think I am going to keep using my old Ajax objects for the
moment. I am going to add them to my repository (along with several
other updates) in the next few days. At some point in the near future
I will open it up to the group so that proposed code can be cobbled
together and unit tested as it is discussed.
              onComplete:function(xhr) {
                           alert('onComplete');
                         }
              });
  }

}

-----------------------------------------------------------

Many other wrappers and even one other core are in the branch of the
repository I linked to at the top of this post. I'm particularly
curious if Randy Webb's system of script insertion could be written as
a different core. I think so. I believe that another core could be
written for file uploads using an iframe (aka old fashioned remote
scripting).

I'm sure both are possible. But what would be the benefit(s)?
I think this modular approach (some core functions and various
optional wrappers) is exactly what is needed to avoid the hugely
bloated Ajax libraries out there on the web.

Same for any other sort of JS library.
I look forward to reading people's thoughts.

That would be a nice ability to have. Are you working on an ESP
project too?
 
P

Peter Michaux

That last one seems unnecessary.

Based on the rules we have come to (somewhat) agree upon it is
unnecessary.
var xhrCore;
(function() {
function empty() {}
xhrCore = function(url, options) {
options = options || {};
var xhr = createXMLHttpRequest(),
method = (options.method || 'GET').toUpperCase(),
callback = options.onComplete,
// does send really need null or can it be undefined?

I think it needs to be null.
body = options.body || null,
headers = options.headers || {};
if (callback) {
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
callback(xhr);
// for IE memory leak
xhr.onreadystatechange = empty;
}
};
}
if (method == 'GET' && body) {
url = url + '?' + body;
body = null;
}
xhr.open(method, url, true);
if (method == "POST") {
headers['Content-Type'] = headers['Content-Type'] ||
'application/x-www-form-urlencoded';
}

Add the X-Requested-With header if not specified?

I didn't know about this and it seems like a very good idea. Is this a
real standard, ad-hoc standard that some people are using? Are headers
starting with "X" mean something in particular.

Do you do this and have you had no issues?

I see that jQuery does this too with the following line which I assume
it means it must be working for some people and some browsers
xml.setRequestHeader("X-Requested-With", "XMLHttpRequest");

for (p in headers) {
xhr.setRequestHeader(p, headers[p]);
}
xhr.send(body);

return {xhr:xhr};
}
})();
-----------------------------------------------------------

Testing if a browser's xhr object is sufficient is done by the
isXhrSufficientForXhrCore function.
-----------------------------------------------------------

// perhaps don't want these checks on an intranet or behind a login
// where the browsers are known to be new.
if (typeof createXMLHttpRequest != 'undefined') {
var isXhrSufficientForXhrCore = (function() {
var xhr = createXMLHttpRequest();
// NN6.1 can create an XMLHttpRequest object but it doesn't
// function and after creation the object's readyState
// property is 'undefined' when it should be '0'.
// The XHR object in NN6.1 doesn't work.
if (xhr.readyState !== 0) {
return false;
}
// IE 5.01sp2 errors when x.setRequestHeader runs.

When it runs or when it is evaluated?

When the code below runs, that is the code checks for the
xhr.setRequestHeader property, IE 5 says "Object doesn't support this
property or method". I suppose there is something like a broken
[[has]]. I'm surprised this is the situation since I am using multiple
IE on a single system. I would think the xml dll file would be more in
control of this.

// The error says "object doesn't support this property"
// This may be because xhr.open() hasn't been called.
// yet. This is the reason for the try-catch. Since we cannot
// determine if the xhr object has setRequestHeader then
// we must put it down the degeradation path.
//
// Opera 8 XHR doesn't have setRequestHeader() and although
// it seems to be able to make a POST request with a body
// without this function This browser is still going to be
// put down the degredation path. Note Opera 8.0.1 does have
// setRequestHeader() and can POST.
try {
if (!xhr.setRequestHeader) {

You didn't run it here. (?)
return false;
}
} catch(e) {
return false;
}
// NN6.2 can't make POST requests because
// can't have arguments to send()
function cannotPost(xhr) {
try {
xhr.send('asdf');
} catch (e) {
// All calls to xhr.send() should error because there wasn't
// a call to xhr.open() however the normal error is something
// about "not initialized" as expected since xhr.open() was
// not called. NN6.2 gives a different error indicating
// xhr.send() cannot take arguments. Note that for NN6.2
// in other human languages the error is still in English.
// TODO check for toString and indexOf?
if (-1 !== e.toString().indexOf('Could not convert' +
// TRICKY that leading space in next string matters.
// using line break only to make postiong to group
possible
' JavaScript argument arg 0 [nsIXMLHttpRequest.send]')) {
return true;
}
}
return false;
}
if (cannotPost(xhr)) {
return false;
}
return true;

}

Yes, this should be optional. NS6 is pretty much dead.


-----------------------------------------------------------
I'm not completely happy with how all the feature testing is packaged
in the project so far. I'm more interested in getting correct
algorithms right now. However if someone has a bright idea about how
to package necessary feature tests in a library with such small
granularity, I'd be happy to read about it.

Can you elaborate?

As you were saying before, many feature tests are being repeated and
that makes for code bulk.

With the xhrCore function wrappers can be added for API sugar. Below
is the status.js wrapper which allows the developer to specify

if (Function.prototype.apply &&
String.prototype.match) {
var statusWrapper = function(original) {
return function(url, options) {
options = options || {};
var handlers = {};
for (var p in options) {
if (p.match(/^on(\d+|Success|Failure|Complete)/)) {
handlers[p] = options[p];
// do this delete because don't want inner wrappers
// to need to work on "branches". Waste of time.
delete options[p];

I don't follow this, but I think it is a bad idea to mutate options
(this function doesn't own that object.)

I agree and didn't like this also. If a wrapper needs to modify the
options object, then perhaps the wrapper should make a shallow copy of
the options object, modify that copy and send that copy to the core.
How does that sound?


options.onComplete = function(xhr) {
var h;
// exploit short circuting of ||
if ((h = handlers['on'+xhr.status]) ||
(h = handlers.onSuccess &&
xhr.status>=200 && xhr.status<300) ||
(h = handlers.onFailure &&
(xhr.status<200 || xhr.status>=300)) ||
(h = handlers.onComplete)) {
h.apply(this, arguments);

Why the apply?

Because in the wrapper I don't know what the "this" object will be
when the onComplete handler runs. There is another wrapper that allows
the developer to set the "this" object with a "scope" option. I know
you don't like that name for that option but no alternative was
presented when I asked.

This is sort of weird. If the developer includes the statusWrapper
function in the build, then it should automatically wrap itself around
the old function. The next included wrapper would do the same. See
the Effects/DirectX enhancements to show/changeImage/setElementHtml on
my builder page.

I initially had the autowrapping but then when I added a second core,
I realized I didn't know what to automatically wrap since the names of
the cores are different. I've been burned several times when I make a
foundational library do things automatically and then later I want to
load the library to just use some other part of it.

By having developer manually create the sendAjax function allows the
developer to compose different similar functions with different
combinations of wrappers.

That's clever. I might have to copy that. All in all, I like this,
but I think I am going to keep using my old Ajax objects for the
moment. I am going to add them to my repository (along with several
other updates) in the next few days. At some point in the near future
I will open it up to the group so that proposed code can be cobbled
together and unit tested as it is discussed.


I'm sure both are possible. But what would be the benefit(s)?

In a single page it could be there is XHR for form submission, an
iframe hack for a file upload form and also there could be use of the
img core to send information to a third party site that just tracks
where the user clicks, for example.


Same for any other sort of JS library.




That would be a nice ability to have. Are you working on an ESP
project too?

:)
 
D

David Mark

That last one seems unnecessary.

Based on the rules we have come to (somewhat) agree upon it is
unnecessary.
I think it needs to be null.
          body = options.body || null,
          headers = options.headers || {};
      if (callback) {
        xhr.onreadystatechange = function() {
          if (xhr.readyState == 4) {
            callback(xhr);
            // for IE memory leak
            xhr.onreadystatechange = empty;
          }
        };
      }
      if (method == 'GET' && body) {
        url = url + '?' + body;
        body = null;
      }
      xhr.open(method, url, true);
      if (method == "POST") {
        headers['Content-Type'] = headers['Content-Type'] ||
                             'application/x-www-form-urlencoded';
      }
Add the X-Requested-With header if not specified?

I didn't know about this and it seems like a very good idea. Is this a
real standard, ad-hoc standard that some people are using? Are headers
starting with "X" mean something in particular.

I think the "X-" prefix indicates it isn't a real standard. Certainly
some people are using it.
Do you do this and have you had no issues?

Yes I use it to indicate Ajax requests. Certainly Web servers don't
pay it any mind.
I see that jQuery does this too with the following line which I assume
it means it must be working for some people and some browsers
        xml.setRequestHeader("X-Requested-With", "XMLHttpRequest");

The browsers don't pay any attention to it.
      for (p in headers) {
        xhr.setRequestHeader(p, headers[p]);

Forgot to note that this should be filtered.
When it runs or when it is evaluated?

When the code below runs, that is the code checks for the
xhr.setRequestHeader property, IE 5 says "Object doesn't support this
property or method". I suppose there is something like a broken
[[has]]. I'm surprised this is the situation since I am using multiple
IE on a single system. I would think the xml dll file would be more in
control of this.

Use isHostMethod.
You didn't run it here. (?)
        return false;
      }
    } catch(e) {
      return false;
    }
    // NN6.2 can't make POST requests because
    // can't have arguments to send()
    function cannotPost(xhr) {
      try {
        xhr.send('asdf');
      } catch (e) {
        // All calls to xhr.send() should error because there wasn't
        // a call to xhr.open() however the normal error is something
        // about "not initialized" as expected since xhr.open() was
        // not called. NN6.2 gives a different error indicating
        // xhr.send() cannot take arguments. Note that for NN6..2
        // in other human languages the error is still in English.
        // TODO check for toString and indexOf?
        if (-1 !== e.toString().indexOf('Could not convert' +
            // TRICKY that leading space in next string matters.
            // using line break only to make postiong to group
possible
            ' JavaScript argument arg 0 [nsIXMLHttpRequest..send]')) {
          return true;
        }
      }
      return false;
    }
    if (cannotPost(xhr)) {
      return false;
    }
    return true;
  })();
}
Yes, this should be optional.  NS6 is pretty much dead.
Can you elaborate?

As you were saying before, many feature tests are being repeated and
that makes for code bulk.

Indeed. At the very least we need a small base module that includes
isHostMethod, isRealObjectProperty, etc. Also, modules that are
documented to require other modules need not feature test functions
that are defined unconditionally.
With the xhrCore function wrappers can be added for API sugar. Below
is the status.js wrapper which allows the developer to specify
-----------------------------------------------------------
if (Function.prototype.apply &&
    String.prototype.match) {
  var statusWrapper = function(original) {
    return function(url, options) {
      options = options || {};
      var handlers = {};
      for (var p in options) {
        if (p.match(/^on(\d+|Success|Failure|Complete)/)) {
          handlers[p] = options[p];
          // do this delete because don't want inner wrappers
          // to need to work on "branches". Waste of time.
          delete options[p];
I don't follow this, but I think it is a bad idea to mutate options
(this function doesn't own that object.)

I agree and didn't like this also. If a wrapper needs to modify the
options object, then perhaps the wrapper should make a shallow copy of
the options object, modify that copy and send that copy to the core.
How does that sound?

That's what I was thinking.
        }
      }
      options.onComplete = function(xhr) {
        var h;
        // exploit short circuting of ||
        if ((h = handlers['on'+xhr.status])  ||
            (h = handlers.onSuccess &&
              xhr.status>=200 && xhr.status<300) ||
            (h = handlers.onFailure &&
              (xhr.status<200 || xhr.status>=300)) ||
            (h = handlers.onComplete)) {
          h.apply(this, arguments);
Why the apply?

Because in the wrapper I don't know what the "this" object will be
when the onComplete handler runs. There is another wrapper that allows
the developer to set the "this" object with a "scope" option. I know
you don't like that name for that option but no alternative was
presented when I asked.

I must have missed that post. I use "context."
I initially had the autowrapping but then when I added a second core,
I realized I didn't know what to automatically wrap since the names of
the cores are different. I've been burned several times when I make a

Don't use different names?
foundational library do things automatically and then later I want to
load the library to just use some other part of it.

By having developer manually create the sendAjax function allows the
developer to compose different similar functions with different
combinations of wrappers.




In a single page it could be there is XHR for form submission, an
iframe hack for a file upload form and also there could be use of the
img core to send information to a third party site that just tracks
where the user clicks, for example.

I see. So the cores would need different names as more than one could
be used.
 
P

Peter Michaux

[snip]
With the xhrCore function wrappers can be added for API sugar. Below
is the status.js wrapper which allows the developer to specify
-----------------------------------------------------------
if (Function.prototype.apply &&
String.prototype.match) {
var statusWrapper = function(original) {
return function(url, options) {
options = options || {};
var handlers = {};
for (var p in options) {
if (p.match(/^on(\d+|Success|Failure|Complete)/)) {
handlers[p] = options[p];
// do this delete because don't want inner wrappers
// to need to work on "branches". Waste of time.
delete options[p];
I don't follow this, but I think it is a bad idea to mutate options
(this function doesn't own that object.)
I agree and didn't like this also. If a wrapper needs to modify the
options object, then perhaps the wrapper should make a shallow copy of
the options object, modify that copy and send that copy to the core.
How does that sound?

That's what I was thinking.

This points out a real problem with simulating named parameters in a
function call.

Suppose there is a function called "delayedAlert" and it takes two
parameters "string1" and "string2" that will be concatenated and shown
in an alert after 1 second.

If the parameters are just normal parameters then the implementation
and expected results are clear.

function delayedAlert(string1, string2) {
setTimeout(function() {
alert(string1 + ' ' + string2)
},
1000);
}

delayedAlert('foo', 'bar');

// alert will show "foo bar"

If the parameters are sent using simulated named parameters the
correct implementation and expected results become arguable.

OPTION 1 - early binding

function delayedAlert(params) {
var string1 = params.string1;
var string2 = params.string2;
setTimeout(function() {
alert(string1 + ' ' + string2)
},
1000);
}

delayedAlert({string1:'foo', string2:'bar'});

// alert will show "foo bar"


OPTION 2 - late binding

function delayedAlert(params) {
setTimeout(function() {
alert(params.string1 + ' ' + params.string2)
},
1000);
}

var o = {string1:'foo', string2:'bar'};
delayedAlert(o); // will show "foo bar" in alert
o.string2 = 'baz';

// alert will show "foo baz"


--------------

This relates to the Ajax library.

If the libraries policy is that the options object does not belong to
the library, then it belongs to the user. If it belongs to the user
then the user may think that modifying that object should result in
the late binding behavior.

If the libraries policy is that the options object belongs to the
library, then the user cannot trust it to be the same after the call
to sendAjax. The user will need to expect the early binding behavior.

I think that the desired behavior is early binding. If the library
does or does not "own" the options object, the documentation will need
to say what is happening.

[snip]
 
P

Peter Michaux

[snip]
As you were saying before, many feature tests are being repeated and
that makes for code bulk.

Indeed. At the very least we need a small base module that includes
isHostMethod, isRealObjectProperty, etc. Also, modules that are
documented to require other modules need not feature test functions
that are defined unconditionally.

The repository of code can take many different philosophies.

For each function there can be a number of different functionalities.
*For example*, serializing a form that has only text inputs vs.
serializing a form that has text and select inputs. That seems easy
enough but then Richard Cornford pointed out that Ice Browser has a
bug in serializing select elements and that workaround may not be
necessary to all developers is Ice Browser is out of the target set of
browsers. So either the current implementations bloat or the number of
implementations goes up. Really having the number of implementations
go up is the correct philosophy for the repository. That is what many
implementations is all about. However if the number of implementations
goes up then the amount of code that needs to be maintained goes up
and there is duplicate code. This is not a desirable situation either.

Now for each implementation there are various levels of feature
testing. Various levels of language features can be tested vs.
assumed, various levels of host objects can be tested vs. assumed.
Each combination of testing level and implementation is multiplying
the number of implementations to maintain and duplicate code. There
could be a system of documenting which features are assumed and which
are actually tested but this gets messy and some automated way of
converting the documentation into tests may be require.

For a project, I see the situation is as a bit discouraging. However
the project has already produced some really good discussion and some
valuable information being brought forward.

What I'm wondering about is what the product of the project should be.
Would it be better if the product is a set of articles rather than
trying to make some ready to use code?

[snip]
 
D

David Mark

On Jan 16, 3:11 pm, Peter Michaux <[email protected]> wrote:
[snip]
      for (p in headers) {
        xhr.setRequestHeader(p, headers[p]);
Forgot to note that this should be filtered.

What do you mean by "filtered"? Do you mean using hasOwnProperty()?

It isn't supported well enough. Perhaps something like:

typeof headers.constructor.prototype[p] == 'undefined'

Something like this should be part of the base module (e.g.
isOwnProperty.) Perhaps hasOwnProperty could be used if it is
present, but I seem to recall that there are problems with it in IE.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top