Offline HTML5 Application vs IE8

I have been researching ways to achieve an offline HTML5 application in a corporate environment. I would certainly have no issues convincing the powers that be that HTML5 applications are the way to go and we should be developing new apps along that path.

alas, I am defeated by IE8. I’ll list the options that I came up with to make an HTML5 offline app viable in corporate-land.

  1. Use a Cache Manifest to get the browser to cache the files for the app and still open when offline.
    IE8 does not support Cache Manifests, and neither does IE9. While we are constrained to supporting IE this won’t work until IE10 is approved through the company.
  2. Use Google Gears.
    This means going through the hassle of getting google gears deployed to machines, and then building the app to specifically support google gears. Since gears is now deprecated this isn’t a very forward-looking solution.
  3. Winforms Wrapper. The idea was to make a windows application that included all the html/css/javascript/image files and opened these local files in an embedded web browser control, with ajax calls to a web service to save/fetch data while storing temporary work in localstorage.
    Firstly, using the winforms WebBrowser control does not work as it defaults to IE6 rendering or something, so doesn’t know about local storage or any other web developments in the last ten years. I found suggestions that you can force it to run in IE8 rendering with some registry hacks, but that’s not something I’d like to be mucking around with on user’s machines just to get it to work. Also, this is a rubbish solution from Microsoft, having to change registry values instead of API methods/properties on the control itself.
  4. Create a WPF Wrapper Application.
    Creating a simliar wrapper application with WPF was slightly better in that it ran as IE8 rendering and actually rendered the page properly. Problem is that LocalStorage is not available.
  5. Console application to launch IE8 with local file
    Next attempt was making a console app as a Launcher to open the browser directly to the local file. This is not as tidy as the previous options, but at least it would work i thought. but no. Turns out files opened locally in IE cannot access LocalStorage as they don’t have a domain. This will have been what was blocking the option above as well it seems.
  6. WebKit.Net Browser Control
    I grabbed the WebKit.Net project to be able to host a webkit browser control inside a wrapper application. This looked good at first – the app loaded and had no errors dealing with LocalStorage! if I saved data in LocalStorage and refreshed the page, it would still be there! looking good. Except that when you close the application the LocalStorage is cleared, obviously only held in memory while the control instance exists. If there was some way to capture the localstorage events through the webkit.net control then we could handle saving/loading ourselves, but there is nothing there right now so would be a fairly significant undertaking to get the LocalStorage working.

So, after all these options I’m pretty much out. Getting an HTML5 app working in offline mode in a corporate environment restricted to IE8 is certainly no trivial task.

Posted in HTML5, Javascript, jQuery, Web Devleopment | Tagged , , | Leave a comment

Detecting change in CuteEditor for ASP.NET

In a project I am using CuteEditor for wysiwig html editing, in this case, for web pages. I found that I need to know in javascript if the user has made changes to the html so that I can pop up a confirm dialog before posting back and losing any potential changes.

I tried for a while to use the javascript API as docuemented on the CuteEditor site, trying attachEvent and addEventListener to set a flag on keyup or keydown, but to no avail. Adding events to CuteEditor turned out to be rather difficult, and not particularly well documented.

Time for another approach – by inspecting the editor object in javascript I found that magically is has an IsDirty() method! this returns a boolean telling me if the user has made changes. Goodbye to adding event listeners and setting flags as the CuteEditor knows when it’s been changed!

 

var editor1 = document.getElementById("<% =Editor1.ClientID %>");
if (editor1.IsDirty() == true) 
alert("HTML has been changed!");
Posted in ASP.NET, Web Devleopment | Tagged | Leave a comment

WCF service suddenly stops accepting connections

So, I’m debugging a client application which connects to a net.tcp endpoint of some WCF services hosted in IIS7, and all of a sudden the client can’t connect to the services any more, coming back with an error along the lines of “No connection could be made because the target machine actively refused it”. Trouble is it just happened randomly and even after a restart of my machine it still wouldn’t work.

 

  • not a missing binding on the IIS site
  • not a firewall or security issue blocking the connection
  • not a lack of WAS being installed
finally found the culprit – the local service “Net.Tcp Listener Adapter” wasn’t running. It was set to automatic start and still wasn’t running even after a reboot. Started that service, and voila, problem fixed.
Posted in IIS | Tagged , | Leave a comment

asp:radiobuttonlist table columns

I just found something handy – when using an ASP.NET RadioButtonList with default layout (html tables are used for laying out the options) you can inject </td><td> into  a listitem’s Text property as a hack to get it to layout the data in multiple columns. this is useful if you have say, a list of products with prices like so:

<asp:RadioButtonList ID=”list1″ runat=”server”>

<asp:ListItem Value=”0″ Text=”First Product </td><td>$5.00″</asp:ListItem>

<asp:ListItem Value=”1″ Text=”Second Product </td><td>$10.00″</asp:ListItem>

<asp:ListItem Value=”2″ Text=”Third Product </td><td>$15.00″</asp:ListItem>

</asp:RadioButtonList>

 

injecting the </td><td> into each item will make the prices line up nicely in their own column. handy.

Posted in ASP.NET | Tagged | Leave a comment

Adding AutoCompleteExtenders dynamically on client side

In my current efforts to build rich client webforms in html/javascript I came across a need to dynamically add an AutoCompleteExtender for a newly created element on the page. Basically the page contains a table which the user can click an “Add” button to add a new row, and in each new row I wanted the user to get autocomplete functionality without a round trip to the server.

 

Upon inspecting the rendered page containing an AutoCompleteExtender we will see the following javascript:

 

1 Sys.Application.add_init(function() {
2     $create(Sys.Extended.UI.AutoCompleteBehavior, {"completionInterval":500,"completionListCssClass":"AutoExtender","completionListItemCssClass":"AutoExtenderList","completionSetCount":20,"delimiterCharacters":"","highlightedItemCssClass":"AutoExtenderHighlight","id":"acAccessory","minimumPrefixLength":2,"serviceMethod":"GetSuggestions","servicePath":"../../AJAX/Products.asmx"}, {"itemSelected":ItemSelected}, null, $get("MyClientID"));
3 });

 

So, we can see that the autocompleteextender registers an initialization function to create the autocomplete functionality for a given control’s clientID. We can take that same code and put it in our javascript (such as the onclick handler for the “Add” button) and use it to add autocomplete to any text input we like.

Here is my function that adds a new row to the table and gives one of the text inputs autocomplete:

 

01 function AddUpgradeOption(index, name, price, productId)
02 {
03     var row = tblOptions.insertRow(tblOptions.rows.length);
04     var cell = row.insertCell(0);
05     cell.innerHTML = 'Name: <input type="text">';
06     var cell = row.insertCell(1);
07     cell.innerHTML = 'Price: <input type="text">';
08     var cell = row.insertCell(2);
09     cell.innerHTML = 'Select Item: <input id="txtSearchItem' + index + '" type="text">';
10
11     $create(Sys.Extended.UI.AutoCompleteBehavior,
12     {"completionInterval":500,
13     "completionListCssClass":"AutoExtender",
14     "completionListItemCssClass":"AutoExtenderList",
15     "completionSetCount":20,"delimiterCharacters":"",
16     "highlightedItemCssClass": "AutoExtenderHighlight",
17     "id": "AutoComplete1",
18     "minimumPrefixLength": 2,
19     "serviceMethod": "GettSuggestions",
20     "servicePath": "../../AJAX/Products.asmx"
21 },
22      { "itemSelected": ItemSelected }, null, $get("txtSearchItem" + index));
23 }

 

 

You will of course need to have at least one server-side autocompleteextender on the page to start with so that the necessary javascript resources are included in the page.

Posted in ASP.NET, Javascript | Tagged | Leave a comment

Javascript + Dates + jQuery + .Net Web Service, plenty of date related issues

Firstly, let me say that working with dates in javascript is a real pain. I can’t comment on why there is a lack of standardization for dates between browsers, but it sure can make things difficult.

Here are 3 things that I had to overcome that were far from trivial so that I could get my web client sending objects with date properties to my .asmx web service

Problem 1

First problem was that I am sending a date value to a web service using jQuery and Sys.Serialization.JavaScriptSerializer.serialize to serialize the object I am sending. I was getting messages saying that my dates were not valid, even though they were in correct JSON format:

 

1 "Date":"/Date(-62135596800000)/"

 

It turns out this is because the .Net guys have a little trick going on with date serialization where  “Date()” must be surrounded with “\/” instead of just “/” (though it then becomes “\\/” in your javascript to escape the first slash). The stupid thing was that Sys.Serialization.JavaScriptSerializer.serialize was not adding the extra \, which seems a bit of an oversight. When you consider this simple example where an object is retrieved and then sent back to the server:

 

01 $.ajax({
02         type: "POST",
03         url: "/AJAX.asmx/GetObject",
04         data: "{\"id\":123}",
05         contentType: "application/json; charset=utf-8",
06         dataType: "json",
07         success: function (result)
08         {
09             //get the object which contains a date property
10             var myObject = result.d;
11             SaveObject(myObject);
12         },
13     });
14 function SaveObject(o)
15 {
16    //serialize the object
17     var x = Sys.Serialization.JavaScriptSerializer.serialize(o);
18     //SAVE THE OBJECT, BUT THIS FAILS DUE TO INCORRECT DATE SERIALIZATION
19     $.ajax({
20         type: "POST",
21         url: "/AJAX.asmx/SaveObject",
22         data: "{\"object\":" + x + "}",
23         contentType: "application/json; charset=utf-8",
24         dataType: "json",
25         success: function (result)
26         {
27             alert(result.d);
28         }
29    });
30 }

 

 

The simple workaround that I have is just to do a replace in the serialized string to insert the extra “\” characters

 

1 x = x.replace(/\/Date\(/g, "\\/Date(").replace(/\)\//g, ')\\/');

A bit hackish and subject to other parts of the serialization not having )\/ in them, but it seems to work… and now I can send the received object back to the service and it will arrive exactly the same as when it left.

 

 

Problem 2

Trying to figure out why my date’s Year said one thing and did another:

 

1 var date = new Date(2010, 10, 30);
2 alert(date.getDate() + '\/' + date.getMonth() + '\/' + date.getYear());
3 //popup shows "30/10/110"

It seems that different browsers implement getYear() differently; IE will return 2010 but safari will return 110 (the number of years since 1900). To get this working right I needed to use date.getFullYear() which is consistent in returning 2010 in this case.

 

Problem 3

Localization. I was taking my date string “30/10/2010”, splitting it up by the “/” character, and using the date constructor:

 

1 var parts = control.value.split('/');
2 var day = parseInt(parts[0]);
3 var month = parseInt(parts[1]);
4 var year = parseInt(parts[2]);
5 var d = new Date(year, month-1, day);

 

This looked fine on the client, but in the service method on the server the date was showing something like “29/10/2010 12:00pm” instead of “30/10/2010 0:00” – turns out it was a localized time being generated in javascript, so when converted into ticks it was 12 hours out (timezone here is GMT+12). To sort this out UTC needs to be used on the client:

 

1 var parts = control.value.split('/');
2    var day = parseInt(parts[0]);
3    var month = parseInt(parts[1]);
4    var year = parseInt(parts[2]);
5    var d = new Date(Date.UTC(year, month-1, day));
6    //d is now safe to be converted into ticks and serialized to JSON

 

 

 

And there endeth todays javascript date lesson. I hope this saves someone the time that I spent getting it working.

Posted in Javascript, jQuery, Web Devleopment | Tagged , , | Leave a comment

Mouse events in mobile safari on the iPhone and iPad

There are lots of nifty things we can do in javascript in the web browser, and one of those nifty tricks was to allow users to drag-to-resize elements on a dashboard in my main line-of-business web app. This is really handy for the users as not only can they add whichever graphs they might need, but they can resize them to suit. To handle this resizing I wrote a few javascript functions to handle the mousedown, mousemove and mouseup events when the user grabs the bottom right corner of a box and drags it around.

I then tried the app on an iPad to see how it behaved, and the graphs on the dashboard all displayed brilliantly, but I couldn’t for the life of me get it to drag-to-resize. A bit of investigation shows that the touch-based mobile safari doesn’t raise the mousemove event at all, probably because holding down a finger and moving it is used for scrolling, and apart from the location or zoom changing the browser isn’t aware that anything has happened.

Normally with this sort of thing there is some kind of workaround that you can do, but in this case the browser literally doesn’t raise events so there really isn’t anything that can be done, at least not with dragging. For now this will just have to be a known limitation on touch based interfaces. If I really wanted to make it work I would have to subscribe to mousedown on the drag panel to initiate a resize, and then watch for a mousedown somewhere else on the parent container to indicate this is where the new bottom right corner should be. Not at all intuitive or elegant, but probably the only method of doing it in a mousemove-less environment.

 

Here is the apple developer guide that explains what javascript events are raised in mobile safari: http://developer.apple.com/library/safari/#documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html

so, to sum it up, if your page has javascript to do any of the following things, it likely won’t work as expected on an iphone or ipad:

 

  1. Mouse events (Move, over, out etc)  over a non-input element (apple has a workaround for this one, see below)
  2. Double Click on an element
  3. Scrolling (apple says it will raise scroll event if user does a 2-finger pan gesture, but given then most pages simply expand vertically rather than showing a scrollable element this is of limited use)
the workaround for the mouse events, apple handily tells you in the link above that it will work if you make the element clickable, meaning it has an onclick attribute. They suggest adding onclick=”void(0)” as a dummy onclick hander so that mobile safari recognises the element as clickable and will then raise the events.
If you are developing specifically for mobile safari then Apple gives us some specific touch events to work with, but I would be hesitant to make mobile-safari specific pages as that defeats the platform-independent reason for using html in the first place:
<div
  ontouchstart="touchStart(event);"
  ontouchmove="touchMove(event);"
  ontouchend="touchEnd(event);"
  ontouchcancel="touchCancel(event);"
></div>
Posted in Javascript, Web Devleopment | Tagged , | Leave a comment