Intermittent Operation Aborted Errors in IE when using MS Ajax
This issue has been plaguing our project: operation aborted errors, intermittent in nature, occurring sometimes as little as once/week. Even in IE 8 there were issues, although thankfully not the crazy dialog box we see in IE 6 and 7.
It turns out that the problem is due to a bug in Ajax itself. I won't go into all the details, since they're covered very well in these two blog posts:
- First post with background: http://seejoelprogram.wordpress.com/2008/06/08/when-sysapplicationinitialize-causes-operation-aborted-in-ie/
- Second post with bug fixes to the original bug fix: http://seejoelprogram.wordpress.com/2008/10/03/fixing-sysapplicationinitialize-again/
- Sys$_Application$initialize:
function Sys$_Application$initialize() {
if(!this._initialized && !this._initializing) {
this._initializing = true;
var u = window.navigator.userAgent.toLowerCase(),
v = parseFloat(u.match(/.+(?:rv|it|ml|ra|ie)[\/: ]([\d.]+)/)[1]);
var initializeDelegate = Function.createDelegate(this, this._doInitialize);
if (/WebKit/i.test(u) && v < 525.13)
{
this._load_timer = window.setInterval(function()
{
if (/loaded|complete/.test(document.readyState))
{
initializeDelegate();
}
}, 10);
}
else if (/msie/.test(u) && !window.opera)
{
document.attachEvent('onreadystatechange',
function (e) {
if (e && arguments.callee && document.readyState == 'complete') {
document.detachEvent('on'+e.type, arguments.callee);
initializeDelegate();
}
}
);
if (window == top) {
(function () {
try {
document.documentElement.doScroll('left');
} catch (e) {
setTimeout(arguments.callee, 10);
return;
}
initializeDelegate();
})();
}
}
else if (document.addEventListener
&& ((/opera\//.test(u) && v > 9) ||
(/gecko\//.test(u) && v >= 1.8) ||
(/khtml\//.test(u) && v >= 4.0) ||
(/webkit\//.test(u) && v >= 525.13))) {
document.addEventListener("DOMContentLoaded", initializeDelegate, false);
}
else
{
$addHandler(window, "load", initializeDelegate);
}
}
} - Sys$_Application$_doInitialize():
function Sys$_Application$_doInitialize() {
if (this._initialized) {
return;
}
Sys._Application.callBaseMethod(this, 'initialize');
if (this._load_timer !== null)
{
clearInterval(this._load_timer);
this._load_timer = null;
}
var handler = this.get_events().getHandler("init");
if (handler) {
this.beginCreateComponents();
handler(this, Sys.EventArgs.Empty);
this.endCreateComponents();
}
if (Sys.WebForms) {
if (this._onPageRequestManagerBeginRequest) this._beginRequestHandler = Function.createDelegate(this, this._onPageRequestManagerBeginRequest);
if (this._beginRequestHandler) Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(this._beginRequestHandler);
if (this._onPageRequestManagerEndRequest) this._endRequestHandler = Function.createDelegate(this, this._onPageRequestManagerEndRequest);
if (this._endRequestHandler) Sys.WebForms.PageRequestManager.getInstance().add_endRequest(this._endRequestHandler);
}
if (this.get_stateString){
var loadedEntry = this.get_stateString();
if (loadedEntry !== this._currentEntry) {
this._navigate(loadedEntry);
}
}
this.raiseLoad();
this._initializing = false;
} - Sys$_Application$_loadHandler():
function Sys$_Application$_loadHandler() {
if(this._loadHandlerDelegate) {
Sys.UI.DomEvent.removeHandler(window, "load", this._loadHandlerDelegate);
this._loadHandlerDelegate = null;
}
this._initializing = true;
this._doInitialize();
}
Next, here is how I overrode how the framework delivers the script:
<ajaxtoolkit:toolkitscriptmanager runat="server" enablepartialrendering="true" id="ScriptManager">
<scripts>
<asp:scriptreference name="MicrosoftAjax.js" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" path="~/js/MicrosoftAjax-withFix.js">
</scripts>
</ajaxToolkit:ToolkitScriptManager>
You do not need to use the ToolKitScriptManager - you can also do it this way:
<asp:scriptmanager runat="server" enablepartialrendering="true" id="ScriptManager">
<scripts>
<asp:scriptreference name="MicrosoftAjax.js" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" path="~/js/MicrosoftAjax-withFix.js">
</scripts>
</asp:ScriptManager>
Note that you can replace ~/js with the directory of your choosing.
Be aware that when the framework loads the file, it automatically adds .debug or .release onto the end of the file name, so the final file reference provided will be either MicrosoftAjax-withFix.debug.js or MicrosoftAjax-withFix.release.js.
If you don't want to bother incorporating these fixes into .NET 3.5 SP1 Ajax files, feel free to download them from here:
- Debug version: http://lerch.org/js/MicrosoftAjax-WithFix.debug.js
- Release version (simply a minified version of the debug js - run through YUI Compresssor): http://lerch.org/js/MicrosoftAjax-WithFix.release.js
Javascript Hacks: Using XHR to load binary data
I recently needed to get image data from a server using Javascript, base64 encode it, and post that data back to an application. While the details of why I needed to do this are a bit complex, I believe that getting image data through an XMLHttpRequest object and base 64 enconding it will become more valuable in terms of client-side image manipulation using the data URI scheme for image tags.
This would allow a Javascript developer, for instance, to load an existing image (say, a photo), without base64 encoding it on the server, load it into an image tag with a data URI, and make direct manipulations on that image.
Unfortunately, this area is relatively new and browsers have a lot of differences. Data URI support is still very new, inconsistent, and limited. In the meantime, here is how you get that base64 encoded image in the first place:
Internet Explorer:
IE has a property of XMLHttpRequest object for binary data ResponseBody. This contains exactly what we need, but unfortunately the property is not visible to Javascript. Since the string returned to Javascript by ResponseText will be terminated at the first null value, we must use ResponseBody. This requires a bit of VBScript, which can do one of the following things:
- Get the numeric value of each unsigned byte and turn that into a number in a string of comma delimited numbers. This is less efficient, but gets you in and out of VBScript as quickly as possible, allowing a generic base64 encoding routine. This is the route I followed (it may be less efficient, but it pales in comparison with the XHR request just made):
Function BinaryArrayToAscCSV( aBytes )
Dim j, sOutput
sOutput = "BinaryArrayToAscCSV"
For j = 1 to LenB(aBytes)
sOutput= sOutput & AscB( MidB(aBytes,j,1) )
sOutput= sOutput & ","
Next
BinaryArrayToAscCSV = sOutput
End Function - Base 64 encode it directly in VBScript.
Once this is done, we can then base64 encode it using a fairly generic function in Javascript:
Base64 = {
// private property
_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
encodeBinaryArrayAsString : function(input){
var ascArr;
var output = "";
var bytebuffer;
var encodedCharIndexes = new Array(4);
var inx = 0;
ascArr = input.substring("BinaryArrayToAscCSV".length, input.length - 1).split(',');
while(inx < ascArr.length){
// Fill byte buffer array
bytebuffer = new Array(3);
for(jnx = 0; jnx < bytebuffer.length; jnx++)
if(inx < ascArr.length)
bytebuffer[jnx] = parseInt(ascArr[inx++]);
else
bytebuffer[jnx] = 0;
// Get each encoded character, 6 bits at a time
// index 1: first 6 bits
encodedCharIndexes[0] = bytebuffer[0] >> 2;
// index 2: second 6 bits (2 least significant bits from input byte 1 + 4 most significant bits from byte 2)
encodedCharIndexes[1] = ((bytebuffer[0] & 0x3) << 4) | (bytebuffer[1] >> 4);
// index 3: third 6 bits (4 least significant bits from input byte 2 + 2 most significant bits from byte 3)
encodedCharIndexes[2] = ((bytebuffer[1] & 0x0f) << 2) | (bytebuffer[2] >> 6);
// index 3: forth 6 bits (6 least significant bits from input byte 3)
encodedCharIndexes[3] = bytebuffer[2] & 0x3f;
// Determine whether padding happened, and adjust accordingly
paddingBytes = inx - (ascArr.length - 1);
switch(paddingBytes){
case 2:
// Set last 2 characters to padding char
encodedCharIndexes[3] = 64;
encodedCharIndexes[2] = 64;
break;
case 1:
// Set last character to padding char
encodedCharIndexes[3] = 64;
break;
default:
break; // No padding - proceed
}
// Now we will grab each appropriate character out of our keystring
// based on our index array and append it to the output string
for(jnx = 0; jnx < encodedCharIndexes.length; jnx++)
output += this._keyStr.charAt(encodedCharIndexes[jnx]);
}
return output;
}
};
Firefox:
Firefox works a little differently, as there is no RequestBody property. In this case, RequestText is not truncated as long as you override the mime type coming from the server, forcing Firefox to pass the data unaltered. All we need to do is compensate for binary data coming back and being placed in a Unicode Javascript string. To compensate, we can AND each character with 0xFF to throw away the high-order byte (see https://developer.mozilla.org/En/Using_XMLHttpRequest#Handling_binary_data). The resulting encoding function looks like this:
Base64 = {
// private property
_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
encodeBinary : function(input){
var output = "";
var bytebuffer;
var encodedCharIndexes = new Array(4);
var inx = 0;
var paddingBytes = 0;
while(inx < input.length){
// Fill byte buffer array
bytebuffer = new Array(3);
for(jnx = 0; jnx < bytebuffer.length; jnx++)
if(inx < input.length)
bytebuffer[jnx] = input.charCodeAt(inx++) & 0xff; // throw away high-order byte, as documented at: https://developer.mozilla.org/En/Using_XMLHttpRequest#Handling_binary_data
else
bytebuffer[jnx] = 0;
// Get each encoded character, 6 bits at a time
// index 1: first 6 bits
encodedCharIndexes[0] = bytebuffer[0] >> 2;
// index 2: second 6 bits (2 least significant bits from input byte 1 + 4 most significant bits from byte 2)
encodedCharIndexes[1] = ((bytebuffer[0] & 0x3) << 4) | (bytebuffer[1] >> 4);
// index 3: third 6 bits (4 least significant bits from input byte 2 + 2 most significant bits from byte 3)
encodedCharIndexes[2] = ((bytebuffer[1] & 0x0f) << 2) | (bytebuffer[2] >> 6);
// index 3: forth 6 bits (6 least significant bits from input byte 3)
encodedCharIndexes[3] = bytebuffer[2] & 0x3f;
// Determine whether padding happened, and adjust accordingly
paddingBytes = inx - (input.length - 1);
switch(paddingBytes){
case 2:
// Set last 2 characters to padding char
encodedCharIndexes[3] = 64;
encodedCharIndexes[2] = 64;
break;
case 1:
// Set last character to padding char
encodedCharIndexes[3] = 64;
break;
default:
break; // No padding - proceed
}
// Now we will grab each appropriate character out of our keystring
// based on our index array and append it to the output string
for(jnx = 0; jnx < encodedCharIndexes.length; jnx++)
output += this._keyStr.charAt(encodedCharIndexes[jnx]);
}
return output;
};
Ideally we'd combine these two functions into a single encoding function, but I've left them separate for clarity. Note also that these techniques do not appear to work for Safari, Chrome or Opera. It should work for IE6 if using the correct ActiveX XHR object, but I was not supporting IE6. I did a spot check on Safari/Chrome/Opera and they were not working, but I did not investigate as they were not supported browsers for my implementation. The actual XHR function I used was:
LoadBinaryResource = function(url) {
var req = new XMLHttpRequest();
req.open('GET', url, false);
if (req.overrideMimeType)
req.overrideMimeType('text/plain; charset=x-user-defined');
req.send(null);
if (req.status != 200) return '';
if (typeof(req.responseBody) !== 'undefined') return BinaryArrayToAscCSV(req.responseBody);
return req.responseText;
}
LoadBinaryResourceAsBase64 = function(url) {
var data = LoadBinaryResource(url);
if (data.indexOf("BinaryArrayToAscCSV") !== -1)
return Base64.encodeBinaryArrayAsString(data);
else
return Base64.encodeBinary(data);
}
Labels: Javascript | 1 Comments
A little surprised no one caught this
So I just watched last week's SNL Weekend Update Thursdays on NBC.com, and decided to play around with their viewer. I haven't watched shows there before, but the Olympics were Silverlight and I wanted to know if they used Silverlight (no, they use Flash), and what features they had. I hit the info button (the thing with the arrow pointing to it), and was greeted with the interface you see below...the red box is mine (added for emphasis). Clearly they had some of the original mockup "Lorem Ipsum" in there and no one bothered to update it or correct it.
One more technical analysis link
I bumped across Stoxline during my recent foray into the technical analysis world. It provides a good quick summary of some key price points.
Excercising INTC stock options
So I've excercised the remaining stock options from Intel that remain above water. There are still a large number of options that will die worthless (my original options were priced at $67 back in 2001, current stock price is $23.52, and there were no splits in between). The next set of options that even have a shot at becoming in the money are priced around $26, and with 3 weeks out, it would be surprising if they became valuable. Here's the quick story of how the excercise went down.
I excercised in three batches. The first batch I got totally lucky and sold at $27.98, exactly $0.01 off the 52 week high. Dumb luck. I sold only half because word on the street was that Q1 would be great, and the price would keep going up. In retrospect, I think selling half was a good move, but my big mistake here was not to have sold the most expensively priced options. Those options at $26 would have actually been worth something.
The stock tanked this year, but recently began to gain ground. Oddly, it picked up momentum based on almost no news (the overall market was also up). Intel did get an upgrade that did pretty well for the stock. Due to what seemed to be a short term movement and the imminent expiration of my options, I dug out some of my technical analysis knowledge and went to town:
I watched the following indicators:
- MACD: I was looking pretty short-term (<1>
- Bollinger Bands: I thought this would be a fairly useless indicator, but in fact, it was just when the stock crossed the upper band that it turned negative.
- RSI: Fairly useful indicator in this scenario
- Slow Stochastic Oscillator: Fast stoch always seems too touchy for me. Maybe if I were trading several times a day it would be different, but the slow stoch was useful
- Parabolic SAR: My primary source of when the options should be sold.
At 25, the MACD, Bollinger Bands and RSI indicators all went bearish, so I thought I'd sell when the price started to dip. The next day it opened pretty low and then dropped below 24.13, which was the Parabolic SAR trigger point, so now all the indicators I was tracking went bearish. I sold at 24.08, and so far, it's down another 0.85 from there.
Lessons learned? The indicators can work...don't rely on just one, and make sure that you use the right indicator for your goal.
Another couple links of use:
- Scottrade chart: This chart is continuously updated, and summarizes what the indicators are saying (e.g. Parabolic Indicator is bearish/MACD is bullish). The conintuous updates made it more useful than Yahoo. It does, however, force you to re-add the indicators - no deep link is possible.
- Yahoo chart: I was originally using this chart from Yahoo. Since the URL includes all the options, it looks exactly as I was using. However, it only updates at the end of each day.
- TD Ameritrade Command Center 2.0: A Java app available for TD Ameritrade customers, it let me watch the stock in real-time during the day.
It could have been so much more effective with a little B&E
A couple days ago I got a flyer on my door advertising a security system and monitoring package. I'm not sure how many people actually respond to a blanket flyer like that, but it got me thinking...wouldn't the flyer have been more effective had they managed to leave it somewhere inside the house while we weren't home? Illegal - yes. Effective: definitely.

