Using Script# to Create Compelling Cross-Platform Data Visualizations

Click for live demo!

In recent times, the HTML5 client platform is becoming an increasingly important platform to target for UI development. Not only is it supported on a large percentage of mobile devices, but it specifies an exciting new Canvas element that is especially interesting to those interested in creating graphics rich Data Visualizations on all platforms.

The promise of being able to write some logic to present client-side, highly interactive visualizations is extremely alluring, but presents interesting drawbacks to teams that are accustomed to leveraging the features of higher level languages to assert quality of product and to ensure ease of maintenance and enhancement. It also presents challenges to teams that already have large existing code libraries and experience in a higher level language (like C#, Java, etc.) that would like to reuse at least some of this logic on the browser client.

Overview

In this article we will discuss:

  • Some negatives of authoring and maintaining JavaScript for large and intricate projects.
  • The benefits and methods of authoring JavaScript indirectly through the use of a higher level language such as C#
  • The use of the Script# tool for translating C# into JavaScript code.
  • Strategies and a concrete example related to reusing C# code between multiple platforms, including a pure JavaScript client.
  • Some of the shortcomings of using Script# with an existing code base.
  • A Summary of our findings

JavaScript

Disclaimer: I'm about to discuss what I view as some of the short-comings of JavaScript for large projects, compared to languages such as C# or Java. The pros and cons of a particular language can often end up boiling down to matters of opinion and personal preference. To those whose opinions are aligned with mine on what makes a language nice to work with, the below should resonate, I hope. If it is your opinion that JavaScript is the right tool for projects of every scale, then the portions of this article about code reuse should still be interesting, I'll warrant, and areas discussing the benefits of using a high level language to indirectly author JavaScript can safely be ignored. The below is relevant to the topic of this article, I believe, so I hope I'll be forgiven this digression into programming language comparative analysis.

JavaScript is a nimble and amazingly flexible language, with many surprisingly cool features, but compared to a language like C#, it can be vastly frustrating to maintain large codebases in. Attributes like its dynamic, weak type system and reliance on global variables contribute to JavaScript being very terse and expressive, but do little to enforce or make discoverable the intent of the code's author to either the compiler/interpreter or even other developers that must work on the codebase. Confusion of intent can lead to subtle (but maddening) logic errors, or code being unintelligible to other people, or even the same person too long after it was originally written. JavaScript's interpretive nature can lead to many more classes of errors only being discovered at runtime rather than by the compiler.

It is possible to, given the correct discipline, write a large amount of JavaScript that is as beautiful and maintainable as good quality C#, but the ways to fall off the road to a maintainable and extensible codebase are more numerous and varied with JavaScript. Modern JavaScript frameworks such as jQuery (motto: write less, do more) do much to reduce the amount of JavaScript that must be written to accomplish a task, and thus help to mitigate a lot of the failings of JavaScript as a reliable language for large projects. However, these frameworks mainly focus on ease of manipulation of the DOM and other common tasks required by all web developers.

With a data visualization control, however, we may have large requirements for complex logic that have little bearing on DOM inspection and arrangement. Is there another way that we can more reliably maintain a larger code base that runs in a large majority of modern browsers? Silverlight allows us to write large and comparatively easy to maintain projects in C# that can deploy down to many browser clients, but Silverlight support on the mobile platform is currently very limited. What about the other clients?

And what if we want to release visualizations that target both JavaScript/HTML5 and also C# based platforms (Silverlight/WPF/etc.)? Must we maintain many mutually exclusive code bases?

JavaScript Translation

Due to some of the failings of JavaScript for large projects, as described above, some have created tools that leverage the syntactic similarity of C-like languages to JavaScript to enable translation or compilation of C-like higher level languages to JavaScript. One such project is Script#, and in the Java realm the Google Web Toolkit (GWT) provides a similar function. These strategies usually involve the code being written in the higher level language, compiled, and thus checked for errors using a much stricter set of requirements and higher level semantics (such as strong typing, interface implementation, etc.) and then either the resulting output is translated to JavaScript, or else the original high-level language syntax is directly syntactically translated into the equivalent JavaScript statements. The benefit of the latter technique being that the translated JavaScript will have a reliable similarity to the code written in the higher level language at the cost of the translator needing to be able to translate complex high level language concepts directly into JavaScript that otherwise may have been simplified away by the compilation process (e.g. linq). 

Another benefit is that there are compelling tools (FxCop, etc.) for assessing the quality of C#, Java, and other high level languages that can be brought to bear to aid in maintaining the quality and maintainability of the code base at that high level, regardless of the eventual syntax of the logic.

The ability of tools like Script# to take code written using higher level languages and run it directly on the client browser is startling and not to be underestimated.

Script# Translation Example

Here's an example of a translation performed by Script#:

Original C#:

public class Rectangle
    {
        public Rectangle(double width, double height)
        {
            _width = width;
            _height = height;
        }

        public double GetArea()
        {
            return _width * _height;
        }

        public bool IsSquare()
        {
            return _width == _height;
        }

        public int CompareSize(Rectangle other)
        {
            double thisArea = GetArea();
            double otherArea = other.GetArea();
            if (thisArea < otherArea)
            {
                return -1;
            }

            if (thisArea > otherArea)
            {
                return 1;
            }

            return 0;
        }

        private double _width;
        private double _height;
    }

Translated JavaScript using Script#:

ClassLibrary1.Rectangle = function ClassLibrary1_Rectangle(width, height) {
    this._width = width;
    this._height = height;
}
ClassLibrary1.Rectangle.prototype = {
    
    getArea: function ClassLibrary1_Rectangle$getArea() {
        return this._width * this._height;
    },
    
    isSquare: function ClassLibrary1_Rectangle$isSquare() {
        return this._width === this._height;
    },
    
    compareSize: function ClassLibrary1_Rectangle$compareSize(other) {
 var thisArea = this.getArea();
        var otherArea = other.getArea();
        if (thisArea < otherArea) {
            return -1;
        }
        if (thisArea > otherArea) {
            return 1;
        }
        return 0;
    },
    
    _width: 0,
    _height: 0
}


ClassLibrary1.Rectangle.registerClass('ClassLibrary1.Rectangle');

In the C# code above, you can see the benefit of strong typing in the CompareSize method where it is ensured that the other object passed in must be of Rectangle type. The C# compiler will assert that invocations of this method have the correct typed parameters at compile time. The intent of the method is also very clear, we are exclusively intending to compare the size of two Rectangles, not shapes of any other type.

In the JavaScript, you can see that Script# has emitted the class that we have defined, along with the requisite fields, constructor, and methods, which look syntactically very similar to the original C# methods. This similarity is likely due to Script# translating directly from the C# syntax, after the C# compile is completed, rather than moving backwards from the outputted IL code, which would likely make the logic flow more dissimilar from the original version.

 You'll also notice that in the compareSize method, in the JavaScript, there is no need to check that the parameter has the correct type at runtime due to the fact that the C# compiler asserted this at compile time. Technically some hand written JavaScript could pass in an object which did not define the necessary getArea method, but all code that we have written in C# (and compile with Script#) that invokes CompareSize will have their invocations checked for the appropriate types at compile time.

This increased strictness prevents a whole class of errors at compile time that would otherwise surprise us at runtime!

Well, let's see if this Silverlight Funnel Chart compiles using Script#!

Not so fast! We can't recompile the whole Silverlight runtime as JavaScript (much as we'd like to). And even if we could, a tool like Script# only re-implements a small subset of the BCL in JavaScript to be usable on the client. So we'd have to fill out the BCL a lot too. Also, certain C# language features are not available using the Script# compiler. The most painful missing features in terms of direct code compatibility are: generics, method overloading, extension methods, object initializers, auto-implemented properties, linq, and lambda expressions (although you can use anonymous methods). Clearly, if we want to share logic between the HTML5/JavaScript platform and more conventional C# based platforms, we will need some strategy to best leverage the subset of features that Script# can provide us on that platform.

What to render, not how

One strategy we can use, especially when creating a new visualization is to separate the "what to render" logic from the "how to render" logic. And separating the "what to do about an interaction"  from the "how to sense an interaction has occurred". There are many design patterns that people leverage to decouple the logic for these kinds of activities including MVC, MVP, MVVM, etc. But the key here is that the "what to..." shouldn't be dependent on the capabilities of the particular client platform, and that the "how to..." should contain as little code as possible (as it differs per client platform). It represents a platform specific implementation of how to render a visualization and how to shuttle interactions back to the associated controlling logic. In Data Visualization, especially, we can structure our logic such that most of the complexity and volume of the code expresses the "what" and we can keep our code that expresses the "how" as minimal as possible. The idea is that, if we are careful, we should be able to compile all of the "what" logic unmodified against both Script# and the C# compiler (for use in Silverlight/WPF/Windows Forms/etc.) And only have to maintain separate "how" logic on each platform.

 

Show me!

When tasked to create a new Silverlight funnel chart, which releases as CTP in the next volume release of NetAdvantage for Silverlight Data Visualization, I used the above described pattern to drive a wedge of separation between the what of the chart, and the how. The resulting Controller ("what") logic of the funnel chart is entirely shared between Silverlight/WPF and Script# projects and so compiles to either a Silverlight binary, or to client executable JavaScript. I was then able to write the View ("how") of the funnel chart separately as a Silverlight project and then a Script# project, which translates down the JavaScript. In the case of the Silverlight "how" project, the how involves Silverlight Canvases and Paths, and Popups for tooltips, and in the case of the Script# "how" project, the how involves HTML5 Canvases and drawing, and CSS positioned tooltips. The "how" is not a large body of code compared to the "what", and even on the HTML5 platform, is entirely authored in C# and then compiled to JavaScript using Script#.

As a further experiment I wrapped the resulting JavaScript in a hand-written jQuery plug-in styled interface to make the usage story as consistent with a jQuery plug-in as possible. Below you can experiment with the live result, which is extremely similar in functionality to the upcoming Silverlight version of the control, and shares a large majority of its code directly. Some of its most complex behaviors such as the animation, positioning, weighting, selection management, and segmented curve generation are entirely shared logic with little to no requirement of platform specific customizations.

Click for Live Sample! (A browser supporting HTML5 canvas is required for the HTML5 version.)

Pretty wild huh?

Here's an example of a method from the shared C# code:

private void AddBezierPoints(SliceAppearance sliceAppearance, double currentTop, double currentBottom, double plotAreaCenter, double offsetx, double offsety)
        {
            BezierPoint top = Bezier.GetPointAt(currentTop);
            BezierPoint bottom = Bezier.GetPointAt(currentBottom);
            PointList points = new PointList();
            PointList rightPoints = new PointList();
            int startIndex = top.Index;
            int endIndex = bottom.Index;

            for (int i = startIndex; i <= endIndex; i++)
            {
                points.Add(
                    new Point(
                    ((BezierPoint)Bezier.Points[i]).Point.X - offsetx,
                    ((BezierPoint)Bezier.Points[i]).Point.Y - offsety));
            }

            for (int i = endIndex; i >= startIndex; i--)
            {
                Point p = ((BezierPoint)Bezier.Points[i]).Point;
                double dist = plotAreaCenter - p.X;
                Point rightPoint = new Point(plotAreaCenter + dist - offsetx, p.Y - offsety);
                rightPoints.Add(rightPoint);
            }

            sliceAppearance.BezierPoints = points;
            sliceAppearance.RightBezierPoints = rightPoints;
        }

And the translated JavaScript created by Script#:

_addBezierPoints: function Infragistics_Controls_Charts__xamFunnelController$_addBezierPoints(sliceAppearance, currentTop, currentBottom, plotAreaCenter, offsetx, offsety) {
        var top = this.get_bezier().getPointAt(currentTop);
        var bottom = this.get_bezier().getPointAt(currentBottom);
        var points = new Infragistics.Controls.Charts.PointList();
        var rightPoints = new Infragistics.Controls.Charts.PointList();
        var startIndex = top.index;
        var endIndex = bottom.index;
        for (var i = startIndex; i <= endIndex; i++) {
            points.add(new Infragistics.Controls.Charts.Point((this.get_bezier().get_points()[i]).point.get_x() - offsetx, (this.get_bezier().get_points()[i]).point.get_y() - offsety));
        }
        for (var i = endIndex; i >= startIndex; i--) {
            var p = (this.get_bezier().get_points()[i]).point;
            var dist = plotAreaCenter - p.get_x();
            var rightPoint = new Infragistics.Controls.Charts.Point(plotAreaCenter + dist - offsetx, p.get_y() - offsety);
            rightPoints.add(rightPoint);
        }
        sliceAppearance.set_bezierPoints(points);
        sliceAppearance.set_rightBezierPoints(rightPoints);
    }

Script#

The use of a tool such as Script# for this purpose is immensely empowering and satisfying. Though it has still to reach V1, it demonstrates a surprising maturity, and can make it a dream to create even large JavaScript targeting projects. Working with Script# is also, it seems, at least to me, much faster than working with JavaScript directly, with fewer logic errors to fix during implementation and less problems where the detection is deferred until runtime. If you are careful, and are starting a new project, you can structure your logic so as to optimize the amount of code you can directly share between JavaScript and other platforms.

The picture is a bit murkier however, when adapting existing logic for cross-platform usage. Here is a short list of some pain points in the form of missing Script# features:

  • Lack of generics.
  • Lack of object initializers.
  • Lack of extension methods.
  • Lack of some common math constants and methods.
  • Lack of lambda expressions.
  • Lack of overloading.
  • Smaller BCL.
  • Enumeration values must be explicitly numbered.

Most of these you can work-around in straightforward ways, but each workaround can result in the injection of risk into an existing body of logic.

Though Script# was created primarily as a tool to author C# for the sole consumption of browser (rather than to share logic between platforms), its utility as a tool to help create a truly cross-platform library is undeniable. The removal of some of these limitations would help to foster that utility. Using Script# to develop cross platform libraries will, of course, have its dangers for when JavaScript has differences in semantics or performance, but, with care, one can create a pool of shared logic of immense value, enabling excellent, compelling cross-platform scenarios.

Summary

  • The HTML5 platform is becoming an increasingly exciting target for data visualizations.
  • To foster a high quality code base we can use tools such as Script# to author JavaScript libraries using C# with its stricter type system and higher level semantics.
  • Given a common language to express JavaScript client code and Silverlight/WPF code we can enable very interesting scenarios for code reuse.
  • We've seen a concrete example of logic being shared between a Silverlight control and an HTML5 canvas based solution through leveraging Script# and some decoupling of logic.
  • We've discussed the excellence of Script# as a tool for this purpose and lamented a few of the barriers to its more general application.
  • Hope you had a good time!

 


Comments  (1 )

_pisees_
on Thu, Apr 5 2012 11:09 AM

Are there plans to make some Infragistics controls cross platform, Silveright and HTML5 using this technique?

Thanks,

Add a Comment

Please Login or Register to add a comment.