Kean Walmsley


  • About the Author
    Kean on Google+

July 2014

Sun Mon Tue Wed Thu Fri Sat
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31    








« A mathematical F# application integrating with AutoCAD via .NET | Main | More fun with F# and AutoCAD: string extraction and manipulation »

November 09, 2007

Using AJAX to generate pages hosting Freewheel views

I touched on this subject a few weeks ago in part 2 of my DWF-related AU handout, but thought I'd come back and describe in more depth some of the fun (although a more accurate word might be "difficulty" :-) I had solving this problem.

I've been playing around with web services and XML since SaaS was still known as ASP, and have tried to stay up-to-date with the technology as best I can. I'm really a client-side programmer, all things considered: I've created some server-side code, but have mostly involved myself with desktop-oriented programming and creation of samples that developers can execute locally, rather than having to set up a host environment.

So when working with web services, I really want to provide a simple application that can be run locally and connects to these services via simple, understandable client code.

The problem I wanted to solve with my AU demo was fairly simple (at least so I thought): for a particular DWF file I wanted to create a page linking to - and embedding - Freewheel views of the DWF's various sheets. The logical way to do this was to use the DWFRender web service to query the number of sheets contained in the DWF, and then create a page using DHTML that had some content for each of these sheets.

The logic seemed sound, and I created a nice prototype with hardcoded sections for the two sheets of my DWF file. Here's the HTML code:

<html>

  <!-- saved from url=(0017)http://localhost/ -->

  <head>

    <title>Freewheel</title>

    <style>

      legend      { font-size: 10pt;

                    font-family: Calibri, Arial;

                    color: black; }

      button      { font-size: 10pt; }

      input      { font-size: 10pt;

                    font-family: Calibri, Arial; }

    </style>

  </head>

  <body bgcolor="#E6E6E6">

    <table width="620">

      <tr>

        <td>

          <fieldset>

            <legend>Sheet 1</legend>

            <table>

              <tr>

                <td valign="middle">

                  <fieldset>

                    <legend>Clickable Thumbnail</legend>

                    <a

                      href="http://freewheel.labs.autodesk.com/dwf.aspx?sec=1&dwf=http://through-the-interface.typepad.com/presentations/DWF/solids.dwf"

                      target="New"

                    >

                      <img

                        frameborder="0"

                        height="150"

                        width="200"

                        scrolling="no"

                        src="http://freewheel.labs.autodesk.com/dwfImage.aspx?page=1&width=200&height=150&path=http://through-the-interface.typepad.com/presentations/DWF/solids.dwf" /

                      >

                    </a>

                  </fieldset>';

                </td>

                <td>

                  <iframe

                    frameborder="0"

                    height="300"

                    width="400"

                    scrolling="no"

                    src="http://freewheel.labs.autodesk.com/dwf.aspx?sec=1&dwf=http://through-the-interface.typepad.com/presentations/DWF/solids.dwf">

                  </iframe>

                </td>

              </tr>

            </table>

          </fieldset>

        </td>

      </tr>

    <table>

    <table width="620">

      <tr>

        <td>

          <fieldset>

            <legend>Sheet 2</legend>

            <table>

              <tr>

                <td valign="middle">

                  <fieldset>

                    <legend>Clickable Thumbnail</legend>

                    <a

                      href="http://freewheel.labs.autodesk.com/dwf.aspx?sec=2&dwf=http://through-the-interface.typepad.com/presentations/DWF/solids.dwf"

                      target="New"

                    >

                      <img

                        frameborder="0"

                        height="150"

                        width="200"

                        scrolling="no"

                        src="http://freewheel.labs.autodesk.com/dwfImage.aspx?page=2&width=200&height=150&path=http://through-the-interface.typepad.com/presentations/DWF/solids.dwf"/>

                    </a>

                  </fieldset>';

                </td>

                <td>

                  <iframe

                    frameborder="0"

                    height="300"

                    width="400"

                    scrolling="no"

                    src="http://freewheel.labs.autodesk.com/dwf.aspx?sec=2&dwf=http://through-the-interface.typepad.com/presentations/DWF/solids.dwf">

                  </iframe>

                </td>

              </tr>

            </table>

          </fieldset>

        </td>

      </tr>

    <table>

  </body>

</html>

And here's what it looked like when displayed:

Embedding_and_linking_freewheel

So far, so good.

To generate the DHTML code for each of the sections, which could then be called in a loop once we knew the number of sheets, was also pretty straightforward:

var freewheel =

  'http://freewheel.labs.autodesk.com';


function createViews(sheets)

{

  var container = document.getElementById('views');

  var htm = '<table width="620"><tr><td>';

  for(i=1; i<= sheets; i++)

  {

    htm += '<fieldset><legend>Sheet ' + i + '</legend>'

    htm += '<table><tr><td valign="middle">';

    htm += '<fieldset><legend>Clickable Thumbnail</legend>'

    htm += '<a href="' + freewheel + '/dwf.aspx?'

    htm += 'sec=' + i + '&dwf=' + path + '" target="New">';

    htm += '<img frameborder="0" height="150" ';

    htm += 'width="200" scrolling="no" src="' + freewheel;

    htm += '/dwfImage.aspx?page=' + i + '&';

    htm += 'width=200&height=150&path=' + path + '"/>'

    htm += '</a></fieldset>';

    htm += '</td><td>';

    htm += '<iframe frameborder="0" height="300" ';

    htm += 'width="400" scrolling="no" src="' + freewheel;

    htm += '/dwf.aspx?sec=' + i + '&dwf=' + path + '">';

    htm += '</iframe>';

    htm += '</td></tr>';

    htm += '</table></fieldset>';

  }

  htm += '</td></tr><table>';

  container.innerHTML = htm;

}

The only challenge remaining was to call our DWFRender web service from out JavaScript code.

Back in the old days I used a component from Microsoft to call web services from HTML & JavaScript, called the Web Service Behavior, or webservice.htc. This seemed to work fine, but is no longer supported, so I thought I'd find a more modern approach, something from the AJAX bandwagon, perhaps. AJAX is "Asynchronous JavaScript and XML", and is the latest & greatest technology for developing smart yet thin applications. Or so the buzz goes.

After searching around a while, I ended up trying a couple of approaches. The first was based a technique provided by IBM. It took me a while to get the code to run, and even then it wasn't getting the result I wanted. So I implemented another approach using a technique shown at The Code Project.

Once again it didn't return anything helpful, but then I noticed this statement on the page:

Please note that many browsers do not allow cross-domain calls for security reasons.

I researched this for some time, and realised that my JavaScript code was hitting a browser security problem. It seems that cross-domain SOAP requests are an issue: if your web-page is considered to be in one domain, it will not call out across to a web service hosted on another domain. Even the Web Service Behavior I'd used in the past didn't solve the problem (thinking back I'd been calling web services hosted in the same domain, so it hadn't been an issue I'd come across).

The security problem is actually with the XMLHTTP component, which is at the core of AJAX and is now implemented by various (probably all, but I'm no expert) browsers.

There are a few ways around this limitation: it seems that most commonly developers implement server-based code that calls across to the problematic domain, basically provided a local cache of the results. In terms of clarifying the problem and spelling out the alternatives, I found this site extremely helpful, as well as this follow-up post.

Anyway, it turns out that for the purposes of my demo I could cobble something that worked together based on fairly simply client-side code. I didn't go to the effort of implementing a cross-browser solution, as the main purpose is to demonstrate calling the Freewheel web service and not to solve the problem of World (well, Internet) Peace.

There were two points that I hadn't realised were issues. The first was the Mark of the Web (MOTW). I had added this to my HTML header sometime in the past, to gain Internet Exporer's trust for it not to bother me with requests to approve the running of ActiveX Controls:

<!-- saved from url=(0017)http://localhost/ -->

Removing this was part of the trick - the other part was to adjust my browser settings to allow cross-domain access:

Ie_security

That then allowed my client code to run and actually get results. Here's the HTML code for the page:

<html>

  <head>

    <title>Freewheel</title>

    <style>

      legend      { font-size: 10pt;

                    font-family: Calibri, Arial;

                    color: black; }

      button      { font-size: 10pt; }

      input      { font-size: 10pt;

                    font-family: Calibri, Arial; }

    </style>

    <script type="text/javascript">

      var freewheel =

        'http://freewheel.labs.autodesk.com';


      var http_request = false;

      var xmldoc;

      var path;


      function makeRequest(url)

      {

        try

        {

          http_request =

            new ActiveXObject("Msxml2.XMLHTTP");

        }

        catch (e)

        {

          try

          {

            http_request =

              new ActiveXObject("Microsoft.XMLHTTP");

          }

          catch (e)

          { }

        }

        if (http_request)

        {

          http_request.onreadystatechange = alertContents;

          http_request.open('GET', url, true);

          http_request.send(null);

        }

      }


      function alertContents()

      {

        if (http_request.readyState == 4)

        {

          if (http_request.status == 200)

          {

            var string = http_request.responseText;

            var xmldoc;

            xmldoc = http_request.responseXML;

            var secs =

              xmldoc.getElementsByTagName(

                "unsignedInt"

              )[0].firstChild.nodeValue;

            createViews(secs);

          }

          else

          {

            alert('There was a problem with the request.');

          }

        }

      }


      function replaceAll(strText, strTarget, strSubString)

      {

        var intIndexOfMatch = strText.indexOf( strTarget );

        while (intIndexOfMatch != -1)

        {

          strText = strText.replace( strTarget, strSubString )

          intIndexOfMatch = strText.indexOf( strTarget );

        }

        return( strText );

      }


      function createViews(sheets)

      {

        var container = document.getElementById('views');

        var htm = '<table width="620"><tr><td>';

        for(i=1; i<= sheets; i++)

        {

          htm += '<fieldset><legend>Sheet ' + i + '</legend>'

          htm += '<table><tr><td valign="middle">';

          htm += '<fieldset><legend>Clickable Thumbnail</legend>'

          htm += '<a href="' + freewheel + '/dwf.aspx?'

          htm += 'sec=' + i + '&dwf=' + path + '" target="New">';

          htm += '<img frameborder="0" height="150" ';

          htm += 'width="200" scrolling="no" src="' + freewheel;

          htm += '/dwfImage.aspx?page=' + i + '&';

          htm += 'width=200&height=150&path=' + path + '"/>'

          htm += '</a></fieldset>';

          htm += '</td><td>';

          htm += '<iframe frameborder="0" height="300" ';

          htm += 'width="400" scrolling="no" src="' + freewheel;

          htm += '/dwf.aspx?sec=' + i + '&dwf=' + path + '">';

          htm += '</iframe>';

          htm += '</td></tr>';

          htm += '</table></fieldset>';

        }

        htm += '</td></tr><table>';

        container.innerHTML = htm;

      }


      function OnGenerateSheetViews()

      {

        path = document.getElementById('URL').value;

        var url = freewheel + '/dwfrender.asmx/sectionCount?path=';

        var dwf = path;

        dwf = replaceAll(dwf, '/', '%2F');

        dwf = replaceAll(dwf, ':', '%3A');

        url += dwf;      


        makeRequest(url);

      }


    </script>

  </head>

  <body bgcolor="#E6E6E6">

    <input

      type="text"

      id="URL"

      size="60"

      value="http://through-the-interface.typepad.com/presentations/DWF/solids.dwf" /

    >

    <input

      id="GenerateSheetViews"

      type="button"

      value="Generate Sheet Views"

      onclick="return OnGenerateSheetViews()" /

    >

    <div id="views"></div>

  </body>

</html>

You'll notice that the page is nearly all script: the static elements are minimal - just somewhere to enter the URL of our DWF file and a <div> for our DHTML tags to be squirted into.

When the page loads there's not a great deal to see:

Blank_freewheel_generator

And it generates our sample pretty successfully:

Completed_freewheel_generator

The application comes into its own when we run it with a DWF with lots of sheets: the sample file, http://freewheel.autodesk.com/sample/Hotel5.dwf, has 29 sheets in case you really want to put it through its paces. :-)

Additional note:

A colleague kindly reminded me about something I had meant to mention. The code in this post makes use of the Autodesk Labs Freewheel server (http://freewheel.labs.autodesk.com). When you're developing applications based on Freewheel, you should really use the production Freewheel server (http://freewheel.autodesk.com), wherever possible. I used the Labs version (which is hosted on a single server, rather than being on a load-balanced, production-capable server farm) simply because it was able to render the specific DWF I was hosted in my site: fixes are generally rolled out first on the Labs server and migrate over time to the production server, as you'd expect, and in this case my DWF wasn't yet viewable via the production system.

To adjust the above code to use the production server, simply change the value of the "freewheel" variable:

var freewheel =

  'http://freewheel.autodesk.com';

TrackBack

TrackBack URL for this entry:
http://www.typepad.com/services/trackback/6a00d83452464869e200e54f91dceb8834

Listed below are links to weblogs that reference Using AJAX to generate pages hosting Freewheel views:

» Using AJAX to generate pages hosting Freewheel views from CadKicks.com
You've been kicked (a good thing) - Trackback from CadKicks.com [Read More]

» A breve la traduzione di from Be.St.Blog
A breve la traduzione di alcuni post di Kean Walmsley dal blog through-the-interface [Read More]

blog comments powered by Disqus

10 Random Posts