Markup

Markup in Warp9 is an internal DSL. This means that your markup should be placed into javascript code. Such decision was made to allow composability and modularization.

To study Warp9's markup we analyse a simple application - reactive hello world. This app has an input box and when a user enter her name she immediately see a reaction (a greeting), also this app has a button to clear the name. By the way, here it is:

It's source:

warp9.render(nameApp, NAME(new warp9.Cell("Denis")));

function NAME(name) {
    var hello = name.when(
            function(name) { return name.length > 0; },
            function(name) { return "Hello, " + name + "!"; }
    );
    return ["div",
        ["div",
            ["input-text", {
                "css/background": hello.isSet().when(false, "red")
            }, name],
            ["button", {"!click": function() {
                name.unset();
            }}, "clear"]],
        hello.lift(function(hello) {
            return ["div", hello];
        })
    ];
}

You can see that function NAME maps model (reactive variable) to markup which then is rendered to to div with id 'nameApp'. If you are familiar with lisp you may recognise a structure similar to s-expression, but with square brackets instead of parentheses I prefer to call them z-expression ('z' relates to 's' as '[' to '(').

Z-expression has approximately the following grammar:

explicitChildren = '["' TAG_NAME '"' (',' ATTR)? (',' child)* ']'
implicitChildren = '["' TAG_NAME '"' (',' ATTR)? (reactive-list-of node) "]
child            = node | STRING | INT |
                   (reactive-variable-of node) |
                   (reactive-variable-of STRING) |
                   (reactive-variable-of INT)
node             = explicitChildren | implicitChildren

where TAG_NAME is a name of html or custom tag, STRING - string literal, INT - integer literal, ATTR - object which describes tag's css, other attributes and events.

If ATTR property's key starts with "css/" its value is processed as a value for css property which name matches the tail of the key. When property's key is prefixed with "!" its body must be a function and it is treated as handler for event named as the tail of the key, otherwise it is an attribute. Values for attributes and css are allowed to be an reactive variable. Actually an css property may be specified in two ways, one you've seen in the name app example:

["input-text", {
    "css/background": name.isSet().when(false, "red")
}, name]

and another way is to group all css properties to one object:

["input-text", {
    "css": {
        "background": name.isSet().when(false, "red")
    }
}, name]

Of cause you Warp9 supports custom attributes , so you can add some aspect to some elements via defining an new attribute.

Now you have should have all information to understand the previous example. Lets make a bit harder to study composability. Suppose we have an reactive list of reactive variables and we want to start a copy of the previous example for each of variable. Here is an app:

The app is extremely similar to the previous one, so we can reuse the previous to compose the new one. All we need to do is add NAMES function

function NAMES(names){
    return ["div", names.lift(NAME)];
}

and replace warp9.render(nameApp, NAME(new warp9.Cell("Denis"))); to

var names = new warp9.List();
warp9.render(namesApp, NAMES(names));
names.add(new warp9.Cell("Denis"));
names.add(new warp9.Cell("Lisa"));

The best part about it is that an app is a component itself and we can build another apps/components on top of it. It is great that we don't think should I implement this part as a template or it is better to wrap it to partial template or directive.

We saw how composability works in Warp9. But composability is not enough to create reusable apps/components - we need a way to modularize and distribute them. It is interesting that modularization wasn't on mind during Warp9 design process, moreover no line of code was written to support it, but it was discovered after the code was written. I think it proves that the right principles were chosen in the beginning. Since we don't separate UI markup and UI logic to fight with complexity (we use divide and conquer principle instead), at any level application/component is just a JS code, so we can use AMD to modularize and distribute controls.

To illustrate this I rewrote previous example (list of names) using an AMD, the app and the sources.