Combining CSS gradients and border-radius in IE9

While working on the diet branch, we found ourselves in need of a new solution for an old problem that affects Internet Explorer 9 if border-radius and a gradient filter background are applied to a DOM element.

Consider a div element with rounded corners:

#test {
  width: 200px;
  height: 100px;
  border-radius: 20px;
  border: 5px solid red;
}

To give it a color gradient background, we add a CSS linear-gradient which is supported in most modern browsers (Firefox, Chrome, Opera, IE10):

background-image: linear-gradient(#0000FF 0%, #FFFF00 100%);
Element with CSS linear-gradient

Element with CSS linear-gradient (Chrome)

Internet Explorer 9 doesn’t support linear-gradient, but it features the proprietary Filters and Transitions API which we can use to achieve mostly the same result:

filter: progid:DXImageTransform.Microsoft.Gradient(GradientType=0, StartColorStr='#FF0000FF', EndColorStr='#FFFFFF00';);
Element with DXImageTransform gradient filter

Element with DXImageTransform gradient filter (IE9)

And there’s the problem: The gradient filter doesn’t respect the border-radius property so the background overlaps the corners. A common solution for this is to wrap two elements, applying the border-radius to the outer element and the gradient to the inner element. qooxdoo’s decorators take this approach as well, but for diet our goal was to eliminate the decorator elements entirely and apply all styles directly to the content element, so we had to come up with a new solution.

Fortunately, IE9 supports Canvas and drawing a gradient is pretty simple:

var startColor = "#0000FF";
var startColorPosition = 0;
var endColor = "#FFFF00";
var endColorPosition = 100;
var width = 1;
var height = 200;
 
var canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext('2d');
 
var lingrad = ctx.createLinearGradient(0, 0, 0, height);
lingrad.addColorStop(startColorPosition / 100, startColor);
lingrad.addColorStop(endColorPosition / 100, endColor);
 
ctx.fillStyle = lingrad;
ctx.fillRect(0, 0, width, height);

In qx.desktop’s theming system the gradient is rendered only once and will then be applied to any widget using the same decorator. Since we don’t know in advance how tall each widget is going to be, we use a fixed size of 1 x 200 pixels. Finally, we convert the drawing into a data URL and apply it like a regular background image, stretching it to fill out the element:

var el = document.getElementById("test");
el.style["background-image"] = 'url(' + canvas.toDataURL() + ')';
el.style["background-size"] = "100% 100%";
Element with Canvas-generated background image

Element with Canvas-generated background image (IE9)

And that’s it, border-radius and background gradient combined without a wrapper element.

Branching in Configuration Files

In general there is no way of conditional branching in the configuration DSL of the tool chain. qooxdoo configuration files are JSON-based and have no “if” construct of any kind. So there is no way of directly expressing e.g. “If the value of this macro is true include this list of classes, otherwise include a different list”. But you can achieve much of the same by using the value of macros in references to other job names. Here is how to do that.

Includer Jobs

In qooxdoo configurations the general way to inject settings into a job is by using the extend key. So if you want settings from one job to propagate into another job you make the second job extend the first:

"jobA" : {
  "environment" : { "foo" : "bar" }
},

"jobB" : {
  "extend" : [ "jobA" ],
  ... // more job settings
}

So now jobB will get the environment setting from jobA as if you had written them into jobB directly (There is some conflict resolution going on if jobB already has an environment key). This of course makes more sense if you want to have more than one job inherit these settings, like when you substitute “jobB” with “source” and “build”. It’s a common way in qooxdoo configs to maintain multiply used settings in a single place.

As in this example the job names in the extend key can refer to jobs that do nothing on their own and are just provided to hold some setting to be used in other jobs (often referred to as “includer jobs”).

Using Macros in Job Names

What’s also interesting here is that the names in the extend key can contain macros. This allows you to select a job according to the value of some macro.

"jobA1" : {
  "environment" : { "foo" : "bar" }
},

"jobA2" : {
   "environment" : { "foo" : "xyz" }
},

"jobB" : {
  "extend" : [ "job${JobSuffix}" ]
  ...
}

By setting the value of the JobSuffix macro to either “A1″ or “A2″ you now select which job is being included into the extending job, and by that select the configuration keys and values that come with it.

In this example you could either specify the concrete value of JobSuffix in the global “let” section of the configuration file

"let" : {
  "JobSuffix" : "A1"     // or "A2"
}

or you could provide it on the command line when invoking the generator:

generate.py -m JobSuffix:A2 ...

Pairs of Includers

Often you will create pairs of includer jobs like in the above example for the same set of settings, to provide alternative values. To pick up the example from the beginning, to provide two different include lists to a source job you could write:

"oneInclude" : {
  "=include" : [ "foo.ClassA", "foo.theme.ThemeA" ]
},

"otherInclude" : {
  "=include" : [ "foo.ClassB", "foo.theme.ThemeB" ]

"source" : {
  "extend" : [ "${IncJob}Include" ]
}

(Don’t worry about the equal sign in "=include" for the moment). Now you only have to provide the value for the IncJob macro, either "one" or "other", and your source job will use the corresponding include lists.

This basically is it, you can now construct different jobs by just assigning different values to macros. Mind that this kind of selecting includer jobs will only work with values of macros, and only if those values are strings. This also means you cannot e.g. refer to the value of a specific environment setting. But in many cases you can work the other way round and make the value of an environment setting part of an includer job which is then selected by the value of a macro.

qooxdoo on a diet

You may have noticed that we’ve been busy working on a big topic recently: Improving UI responsiveness by reducing the amount of DOM elements created for each desktop widget.

Those additional elements were necessary in older browsers to allow for fully-featured rich user interfaces. Now we can take advantage of native features in modern browsers while trying to implement graceful degradation in older browsers and discontinuing support for some legacy browsers.

Every desktop widget now consists of just one content element. Before, each widget consisted of at least two (often even 3-5) DOM elements and their corresponding JavaScript object representations. So the new element or object count is significantly reduced by about 3:1.

3D DOM View: master vs. diet

qooxdoo Widget Browser in Firefox' 3D DOM view: master branch (left) vs. diet branch

This work happens in a feature branch appropriately named ‘diet’, which we now consider ready for public testing. We’ve prepared a document explaining the changes in detail. This also includes a migration guide for anyone willing to test the modifications. If you don’t use your own custom themes, layouts or widgets, it should be particularly easy to build your app against the diet branch. Give us feedback on the usual channels, e.g the mailing list or Bugzilla.

If you don’t have the time to migrate your applications, you can still help us out by testing the framework’s demo applications in your favorite (modern) browser.

We’re planning to merge the diet branch into master in about two weeks. Anyone currently using the master branch is strongly encouraged to test their application using diet before this time to prevent any unpleasant surprises.

Since these are major modifications including some API changes, they will be published along with a next major release qooxdoo 3.0 this summer.

Compile-time Globals Checking

If you are slightly familiar with qooxdoo’s tool chain you probably came across the lint job that scrutinizes your code for known pitfalls. One of the major checks lint is doing is to find unknown global symbols. These are name references in your code that are neither known built-in classes, objects or functions (like Date, RegExp, arguments or isNaN), nor qooxdoo-style classes found in any of the involved libraries. These unknown globals can potentially break your code at runtime if you don’t take any measure outside the qooxdoo application to provide them, like including another script that contains their definitions in the index.html.

During compile time, i.e. when creating a runnable application from the library classes, the Generator does a restricted lint checking to detect some of the more fundamental issues. Unknown globals is one of them, and necessarily so as the Generator uses unresolved globals to add classes to the application build (so called “class list/dependency exploration”). When the new lint module was introduced all this checking was offloaded onto it with a suitable configuration, which worked fine functionally but was considered too slow performance-wise.

With recent master this has been amended. Unknown globals-checking is again done during dependency exploration to take advantage of the already existing logic. This was much faster but a major issue with the old implementation was the lack of support for @-hints embedded in JSDoc type code comments. These hints allow you to silence warnings about unknown symbols that you know will be available at runtime, and to guide further dependency exploration.

In order to add @-hint support and make it fast a new approach was taken. With every syntax tree of a class an adjacent tree of hint nodes is now calculated, derived from the corresponding JSDoc comments. Later when checking unresolved symbols this hint tree can be traversed and inspected very quickly, in order to decide whether an unknown symbols should be pursued, the corresponding class be added to the build, warned about or left alone.

The result is a fast unknown globals check that adds nearly no performance penalty to the existing compile functionality.