Personal tools
You are here: Home   Notes   KSS2  
Search
 
Document Actions

KSS2

by Balazs Ree last modified 2006-06-27 16:28

Revised KSS format -outdated

This documentation is now outdated. FInd the newer format.

KSS, the new generation


Yes, as soon as we are ready we already change it to something more expressive.

What is the motivation for the changes? Remember we just set up the KSS format together in Norway, thinking of copying the semantics of the existing XML format. There was no use case of it. Since now I have the working parser for the first sketch format, I am thinking and experimenting and want to change.

  1. I was the one that wanted to push the support for "more actions in one event". However while I still believe that there are use cases for this, I suggest to forget about this now. A particular problem is that all parameters will go into one line in our current  case, thus 90% of all selectors will be a one-liner and a large part of expressivity of KSS gets lost. I feel we are better off when we drop expressing the "more actions in one event" and get back the parameters to different rows We might find another way for extending the acions later on.
  2. The second problem is that in the framework diagram we have "event nodes" with more incoming arrows ("the events") and more outgoing ones "the actions". Till now we were mainly dealing with events that simply dispatch to calling a remote action, but for some use cases more is needed. When we want to enable the creation of more complex events, we need to support a kind of object oriented structure, where an "event instance" is created when the event is bound, and it operates like a state machine, being able to execute more different type of actions ("the methods"). We want to be able to map this schema in some way, event though we might not implement this right now. What we do want to implement, though, is exception handling, and the other thing we want immediately is better interfacing possibility to existing Bling software (although Godefroid is currently working on a totally transparent method that might not even need this.) In other words not only we need a bit more natural reading format but we also want to be able to extend it later on.
  3. A more sensible way to define action parameters will disqualify 90% of the need for creating new plugin actions. As it seemed till now that they were mostly necessary because we could not come up with a sensible way of parameters handling in actions.
  4.  Last but not least there were a bunch of problems with the original kss syntax, that in this case don't need to be solved.


Simple cases

These are the simple scenarios that more or less contain our original use cases. Here the new format does not add extra functionality, but let's have a look at how this would look like.

Examples:

#title_save click {
    kss-action: saveTitle;
}

The old, to-be-deprecated format for this would have been:

#title_save:click {
remote: saveTitle;
}


Another example:

#kukitportlet-main timeout {
evt-delay: 5000;
kss-action: refreshRecentPortletBody;
}

The old, to-be-deprecated format for this would have been:

#kukitportlet-main:timeout(delay=5000) {
remote: refreshRecentPortletBody;
}


Complex cases

The complex cases define the full kss syntax, of which the base case is only a specialization.

The goal of the complex cases is that the plugin programmer can develop complex event types with abstract events that are not equivalent to browser events. A simple example is "timeout": this is not a browser event but it is relatively simple to bind it. In even more complex cases, the plugin writer can freely implement how the events should be bound: main point is that once they are triggered and then we will handle them within the system - like as if they were normal browser events.

(Implementation note. In this case, the plugin writer will choose to hijack normal browser events and use those to trigger his own custom events. So we need an API for the hijacking these events because there can be more plugin component that want to hijack the same events. The mechanism should work in such a way that if a hijacker decides to skip the the event, it goes to the next  applicant. This is an implementation detail worth to mention here, but it has no consequence to the kss format itself.)

As referring to a previous strategical argument of the azax team, (this is, Godefroid and me at the seaside in the rain, watching birds), this biases the framework to the direction that we do not necessarily bind the low level browser events directly from the kss format. In the kss format we always bind our conceptual events, which in some cases ("native") map to real browser events, in some other cases however not. In the case of the more conceptual events the binding (hijacking) of the real events goes in a blackboxed way from the plugin implementation. I am writing this because we were talking of two different directions and I suggest that the currently described direction is the one, that makes more sense, because it opens the way to higer level, more abstract event implementations that I identify as an existing need.

Rules

There are two basic types of rules: the first type is the usual one, that attributes a normal css selection with a special kss event selector.

css_selector kss_event_selector {
   [evt-key: value;]...
kss-action: value;
[key: value;]...
}

The second form is used in special cases only and will be introduced later in more details.

kss_method_selector {
kss-action: value;
[key: value;]...
}

Event selectors

(the "input arrows" of the event node) This is a full-blown solution and remember, in most of the cases, you will only need the shortcut forms. I don't have enough use case examples to sufficiently demonstrate the use of this, this makes the concept more difficult to understand at first.

/notes/kss2/kss_nodes.png

Events are declared by different event classes, it is possible to declare new event classes in a plugin. An event class is implemented by a piece of javascript code that declares how the event is bound. Each event class has "incoming" arrows that are the kss events (not equal to browser events!) and outgoing arrows, the kss actions. It is an important principle that the names of events and actions are in the namespace of the event class.

The outgoing arrow in many case has the same name as the incoming arrow, this means that the event pass through the plugin unbothered.

The binding of kss actions to different nodes, and the specification of what a given action will execute (in other words the "wiring" of the events) is specified in the kss resource file. For this reason we first need to get acquainted to our event selectors.

General form:

eventname[class=eventclass,id=eventid]

example:

drag[class=dnd,id=originals]
drop[class=dnd,id=originals]
drag[class=dnd,id=inchart]
drop[class=dnd,id=inchart]

In this case, the "dnd" event type binder is instantiated in two instances, with two ids (originals and inchart), and the drag and drop are selector classes that the event recognizes.

Notice the analogy of classes and ids with the object oriented programming model. Also notice that an "event instance" is never created when an event is triggered, but rather when an event is bound to a particular node or nodes. So event instance creation happens at page loading time (or when page content is replaced from an action, and events are rebound).

Shortcut forms:

Id-s can be omitted. In this case each event class creates a singleton instance with a default id. That means that all events share the same object space. (Since most event binder lacks a logic that would require to share data between two consecutively triggered events, this is the expected behaviour anyway.)

drag[class=dnd]
click[class=native]
timeout[class=timeout]

There is no class. In this case the default class is specified in the following two ways:

  • same as the name of the event type name ([class=timeout], in the first example) This is handy for event classes with only one incoming and outgoing arrow.
  • is the builtin event type "native" ([class=native], in the second example). This covers all the browser events.
timeout
click

The full event rule

The event selectors behave like a "child of the html node". They follow the CSS selector.

div#recent-portlet click { ... }
div.warehouse-item drag[class=dnd,id=originals] { ... }

This is differing from the first sketch format, where they were attached to the last selector part with a colon. According to this new suggestion we get a more natural and easier readable format.

The kss event selector must be preceded by a normal css selector. Cannot stand in itself.

(Implementation remark. The class could also be registered for an event name and would be acquired automatically. I think this could be done but it is better to avoid it for the following reasons: 1. It complicates plugin registration. 2. If you are using a native event type or a custom event type with one output only, class can be omitted anyway. 3. It comes into effect with complex custom events where it is better to be explicit. 4. namespace problems could arise.)

Method selectors

These are specific methods that the event can execute. They cannot be preceded by selectors, they stand by itself. They are just used to specify what action to be taken, if an event decides to execute this method. You can think about these as fake events, that are generated not by the browser itself but are triggered automatically by other plugin components.

General form:

method.eventclass
method#eventid

Eventclass and eventid must be existing ones, or else the selected rule will never execute

Example:

node-clicked.customEventType
node-clicked#originals

Full example for an event method rule is later in this document.

Specifying parameters

The general schema for this:

.... {
key1: descriptor1;
key3: descriptor2;
...
}

The keys themselves cannot contain -, as this has a special semantics in kss.

If a key starts with the prefix "evt-", it designates a parameter to the event type binder, that is, has an effect at page loading (or rebinding) time. Otherwise it denotes a parameter to the action to be called.

evt-key
Parameter to the event binder method (typically run at page load time)
key
Parameter to the action (run when the event is triggered)

There are a few special keys that stand in themselves. They start with the prefix "kss-".

kss-action
represents the action itself
kss-any
gives a possibility to marshall a set of parameters in one line. This is used for marshalling all variables in a form as parameters.
kss-error
is used to specify a command name to handle exceptions

The new way of handling parameters is different from the first sketch format, where all the parameters were to be specified in the same row as arguments to the method call. We figured this out because we wanted to support more actions within the same event. However this would be rather handy, the new proposal drops this idea and proposes a way that matches the original CSS anatomy better. This is in general better for expressibility and easier to follow.

(Implementation remark. The usage with dash-prefixes aims that otherwise our kss descriptor file is 100% valid css format.)

Descriptors


a value simply represents a value. Can stand with or without quotes.

size:     12pt;
typeface: "Bitstream Vera Sans";
typeface: 'Bitstream Vera Sans';

There are possible extensions to acquire a parameter in another way as a constant.

There is a possibility to send an attribute of the selected node.

node_id: nodeattr(id);

or the textual content of a node.

text: nodecontent();

There is a possibility to send a given form variable. Parameters can optionally be quoted.

member: formvar(edit, member);
member: formvar("edit", "member");
member: formvar('edit', 'member');

member: currentformvar(member);

There is a possibility to send over all variables of a form. Parameters can optionally be quoted.

kss-any: formvars(edit);
kss-any: formvars("edit");
kss-any: formvars('edit');

kss-any: currentformvars();

There is a possibility to send over the same CSS selector that selected this rule (without the kss postfix).

selector: css_selector();

To send over the xpath selector: (in contrary to the previous example, this can only select one node, while the css selector could select more nodes that the selector selects):

selector: xpath_selector();

REMARK call the commands with the rule and the node as params??? in this case we don't need this

(Implementation note. The parameter descriptors should also be implemented plugin-like.)

A complete example follows here:

div#portlet-recent timeout {
evt-delay:    2000;
kss-action: replaceMacro;
selector: #portlet-recent;
macropath: portlet_recent/macros/portlet;
}

Here delay="2000" is used as a parameter for the binding of the timeout macro, and selector="div#portlet-recent", macropath="portlet_recent/macros/portlet" are used for calling the "replaceMacro" method.

The old, to-be-deprecated corresponding form with the first sketch version would have been:

#kukitportlet-recent:timeout(delay=2000) {
replaceMacro: (selector=#portlet-recent, macropath=portlet_recent/macros/portlet);
}

Another example:

div.menu-item load {
kss-action: replaceWithRenderedText;
text: node_content();
size: 12pt;
typeface: "Bitstream Vera Sans";
selector: same_node();
}

load is an example for a method that is executed on page loading time. There is a particular form of load:

html load {
...
}

that would be executed unconditonally once a page is loaded. So to say "html" is an universal selector that binds to each page, exactly once.

How rule merging is done

Like in CSS, parameters with the same key can be overwritten in a selected node. So it is possible to say

div#portlet-recent timeout {
evt-delay:    2000;
kss-action: replaceMacro
selector: #portlet-recent;
macropath: portlet_recent/macros/portlet;
}

#portlet-recent timeout {
evt-delay:    3000;
}

in which case delay will be 3000.

The merging of the rules is similar to how you would think if you think in terms of CSS: the  last secion, the kss selector counts too. Simply, all rules with the same action name and same event id are merged on the same node.

This means that rules are not merged

  • Rules fot two different event classes are not merged: for example, "click" and "timeout" can be applied to the same node.
  • if they name differs for the kss selector, even if within the same event: for example, a "drag" and a "drop" rule of the dnd event can be applied to the same node.
  • if the event id is different

However the kss-action is not significant from the point of view of the merging, and can simply be overwritten in a following rule if applies to the same node.

As a consequence, for example you cannot specify two "timeout" events to the same node. They will be simply merged and there will be only one timeout event. However if you do deliberately want two timeout events triggered by the presence of the same node, you can specify explicit event ids and this will explicitely make two events. (You could apply the same approach on two "click" events too, but obviously it would not work: because they share the same physical event, so one event would hijack the other in this case, even if they are unmerged.)

Method rules

Method rules are special cases of an "outgoing" arrow from the event node. They represent actions that can be triggered programmatically from an event plugin implementation. Method rules are selected by kss method selectors.

As an example, let us consider the annoyClicker event type plugin. The goal of the plugin is to annoy users. The click event of the plugin can be bound to any node just like an ordinary click event. However the event instantiates itself and starts counting the incoming clicks. The action specified in the main rule is only executed every 5th time. For all the other clicks, the annoy action is executed. This extra action can be declared in the method selector rule.

/notes/kss2/kss_nodes2.png

The method selector in this example will be annoy.annoyClicker. This enables binding the action that will be carried out if our annoyClicker event decides so in its implementation.

The method selectors can be parametrized in the same way, as the event selectors, but obviously evt-keys (parameters for the event binder) are not allowed here. Also it is not allowed to prefix the selector with normal css selectors. It must stand in itself. As this denotes an event that is to be triggered programmatically, and thus is cannot be conditioned with any selectors.

.clickable click[class=annoyClicker,id=annoy-me] {
kss-action: clickedNode;
id: nodeattr(id);
}

annoy.annoyClicker {
kss-action: alert;
message: "You are an idiot! Ha ha ha. (But just keep on trying...)";
}

In the previous example we did not use an event id, as it was not necessary. We just bound by event class. But let's see the same example in case we need to specify an event id. This may be possible, for example, if we want to instantiate different counters to different selectors, and count separately.

#button-one click[class=annoyClicker,id=annoy-me] {
kss-action: clickedNode;
id: nodeattr(id);
}

annoy#annoy-me {
kss-action: alert;
message: "You are an idiot! Ha ha ha. (But just keep on trying...)";
}

#button-two click[class=annoyClicker,id=annoy-you] {
evt-count: 3;
kss-action: clickedNode;
id: nodeattr(id);
}

annoy#annoy-you {
kss-action: alert;
message: "You are an idiot too, but not so big as I am.";
}

Error handlers

It is also possible to attach error handlers to a rule. An error handler is a (registered) command that in case there is an error during the remote
method execution (or a timeout), will be executed locally. It will be called with the same parameters as the action would be.

#title_save click {
kss-action: saveTitle;
kss-error: handleError;
}

handleError is a command in the same sense as are the commands that the remote procedure executes. We did not talk about these as they are out of the reach of kss (and would lead to the topics of marshalling commands from the server to the client, and how to implement a command plugin in javascript).

Action names

Action names are declared following the kss-action key in the definition. There can be only one action in each rule. (They can be overwritten too.)

An action name is either a remote action name, or, if an action name is defined in one of the plugins, then the local action will be executed. This allows a local javascript method to "take over" the place of a remote action, or in other words, "wrap it".

This means that everything is a remote action unless there is a local action defined with that name. This gives a certain impliciteness to the system but in my opinion this is good, because first it is the logical way of how people think, and second it gives the possibility to add post and pre local actions to a remote call, just by changing the implementation and without changing the kss resource.

This is a bit change against the first sketch model, where all the remote methods were called from a single action "remote" and all the local actions were called with their explicit name. The new suggested way removes this inconsistency in usage (that no one would be able to follow, really).

If an kss-action key is missing from an event or method rule, means that no action will be carried out. That could also be a use case that we did not cover here: an incoming-only event with no action to be taken.

Implementation notes

Worries about if KSS parsing would be slow

Comparing to the original XML resource parsing, the KSS parsing can take more CPU resources. This is happening in the client browser at page loading time and occasinally when new content is injected into the page. Comaring with the XML parser that is implemented as a native library in the various browser implementations, for the KSS parsing we use a parser implemented in javascript. The main reason for this is that at first we want to provide a library that is independent from the server side.

I might also add that the overhead of KSS parsing will not be substantial, in my opinion, in typical cases with a few or a dozen rules per page. However it is worth considering the possible implementation enhancements such as:

  1. Use a native CSS parser. Since KSS is valid CSS syntax and in fact only uses a subset of CSS, any CSS parser could be used to parse KSS. At this time I am not aware of browsers offering their internal CSS parser through an API, but situation can change.
  2. Use a "cooked" format. This way the KSS will get parsed on the server side and the browser will get a cacheable response containing pre-cooked data. We plan to implement this soon and the flexible JSON format will be used for data transfer. The JSON format requires in the client only a plain evaluation of the data by the browser's internal javascript parser, so it will be an effective speedup, however it requires a parser to be written for the server.
  3. Use the old-style XML resources, this is disabled at the moment but we will re-enable it.

As the library can accept and handle multiple format resources, the "raw" KSS, "cooked" KSS and old-style XML resource formats will be enabled simultaneously, even for use within the same page.

Features

The implementation is partial:

  • class, id (like drag[class=dnd,id=originals] ) in the event selector is not done yet
  • currently the only implemented attribute methods are these:
    • nodeattr
    • formvar
    • currentformvar

Which demos work:

  • azaxdemo 1-6 (all), except "Two selects" is not supposed to work on IE, but that's not a KSS issue
  • kukiportlets
  • ChimeraAzax: this is using Benjamin Saller's Chimera library to replace texts with rendered images. It is not a real AJAX application actually, but it demonstrates the usage of KSS nicely.

If you notice slow page loads, that's not kss parsing, the problem is that ResourceRegistries repacks all files each time. If you switch on browser side caching or otherwise have a cache between the client and the server, you will see the difference.