KSS in general
Introduction
KSS is an acronym for Kinetic Style Sheets. KSS is a client side framework for implementing rich user interfaces with AJAX functionality.
In this document, we assume the reader understands AJAX and its purpose: to make pages in a browser behave more like a desktop application. A primary AJAX characteristic is that, instead of the user loading or reloading a web page, the web client can contact the server and recieve information from it, then can change the page's content or appearance without leaving it. As a result, the user experiences faster interactions and a more "desktop"-like interface. Plus, several tasks can be implemented with AJAX that are normally not possible to achieve with static HTML pages.
The intention was not to create another JavaScript framework for AJAX. Rather, we wanted to reuse existing libraries. However, we also identified a few major problems in our everyday life as AJAX developers, and by creating KSS we are attempting to offer better solutions.
In many use cases, the "thin client" approach provides the best results. Instead of performing complex DOM modifications from JavaScript, the client consults the server, and the server sends back an HTML page which is simply used by the client to replace a part of the page.
Developing a "fat client" should seldom be necessary. But for the use cases when fat clients are desirable, the process should work in a reusable way, by encapsulation of plugins.
Developers have different skills and interests: site design, application programming, plug-in creation, and so forth. Designers, for example, are largely interested in tools that help them style a site's look-and-feel, while programmers want to focus on implementing that user interface with responsive, desktop-like efficiency. As a result, throughout this documention we highlight information peculiar to the interests of several types of KSS user:
- KSS designer. A completed application can be bound to a page by writing a KSS resource file. This task can be achieved by a designer or by a programmer without any JavaScript knowledge. The similarity of the KSS format to CSS assures the stylesheet is developer friendly.
- KSS application developer. A developer can create an application by reusing several KSS "plugins" that contain the JavaScript code necessary for most client tasks. A developer needs to write KSS resource files, and also must create server side methods that command the client. This activity requires you to know server side programming, but JavaScript knowledge is not required.
- KSS plugin developer. Creating a plugin is the most complex but least often needed activity. Plug-in development requires an extensive knowledge of both browser side JavaScript programming and the browsers themselves. Knowledge of the KSS plugin API is also needed.
In this document, we assume you are familiar with HTML and how web pages are created. We also assume that you know CSS, and, in particular, that you understand CSS selectors. We do not require you to be a Javascript expert to understand this document.
A typical KSS application has the following characteristics:
- There is no JavaScript code or event binding in the HTML itself (although we include JavaScript that contains the necessary client side software for KSS to work).
- The behaviour is defined by a KSS resource file. A resource file has a syntax similar to CSS, and its functionality is similar too. But instead of being a static stylesheet, a KSS resource file defines how the browser events should be bound to the HTML page.
- The client calls server side code, called "server actions," if certain browser events are triggered (such as a user clicking on a node). This server side code assembles a response that consists of "kss commands" which are sent back to the client and which execute the required behaviour.
Our goal in KSS to avoid the need to write JavaScript. JavaScript is still necessary for some use cases, but even such circumstance, we advise you choose JavaScript only as a last choice. That is, first develop a "thin client" solution, and turn it in to a "fat client" later, when you can't find another way to solve the programming problem or you decide to spend excess time on optimization. Besides, this gives you a possibility to deliver a working application, and thus to learn the application domain sooner. But often, it turns out that the thin client is "good enough" and the development of the fat client can be omitted altogether.
Architectural overview
In this section, we provide a high level overview of KSS' system architecture.
The principal characteristics of the KSS process are:
- Process control is embedded into the KSS resource file (including how to bind browser events to the plugins, and how to dispatch them to the server), and server action code (that decides what commands to marshal in return). Thus, application control logic is not implemented on the client side, but is the responsibility of the server.
- All JavaScript code (apart from the KSS core) is a KSS plugin, in most cases an event binder plugin or a client action plugin. Apart from this, no JavaScript code appears in the system. In particular, no JavaScript ever appears in the HTML pages.
This diagram shows the architecture of KSS. Magenta and yellow identify the two different types of plugins, and red highlights the controlling information that governs the actual AJAX functionality.
A major intent of the architecture of KSS is to separate design from implementation.
KSS provides the execution flow similar to a simple programming language, however yet does not have a "program." A command language can execute a set of parametrized commands.
You can start implementing a thin client solution by using the built-in set of KSS plugins. If it becomes necessary, you can gradually fatten your client by developing new custom plugins. Doing the latter naturally requires JavaScript programming, but even so the result should be a well-componentized implementation wherein the details are totally decoupled from the level of the skin design.
To understand the entire KSS process, contemplate the complete flow of events of an AJAX action. First, all actions are triggered from the client browser by browser events. These are the same events that a programmer normally would bind to JavaScript functions. Some of these events are generated by the user's interaction (such as clicking on the page) or are triggered in some other way (e.g. timer events).
Normally, JavaScript code on the page would handle these events; in KSS, however, the events are handled by event binder plugins. These plugins control both how the event is bound and the actual process that executes when the event is triggered.
Several event binder plugins are built into the KSS system. The simplest plugin binds to a browser event via a CSS selector. Other event type plugins are built-in. It is also possible to extend the system by writing a custom event binder plugin.
Such event binder plugins can bind to additional browser events and perform more complex functionality. The way to bind these event plugins to browser events is specified by the KSS resource file, a static resource file that accompanies the HTML page. The format of this resource is called KSS which is similar to CSS in syntax and nature; however, while CSS specifies the static style of the content, KSS binds the dynamic behaviour to a page.
The binding code for the event binder plugins runs at page loading time. Event binder plugins may also specify the code to be executed when the event is triggered, but usually they pass execution to the event rule action dispatcher. The dispatcher uses the KSS rule to decide what action to take. When the action is a server action (the most common), control is passed to the server. But client actions can also execute locally. It is also possible to execute multiple actions for the same event.
If an event triggers a server action, control is passed through an RPC service or direct URL request, which sends events in the form of XMLHttpRequests to the server.
Once the XMLHttpRequests are sent, they arrive on the server as RPC or normal URL requests. One server action is designated to reply to each request. The methods do whatever their designated task is, then command the client to do something in return.
This is a principal point of KSS: at this point in the process the server actions decide what should happen on the client. The server side event handlers do this by assembling a sequence of commands with parameters. This command calling sequence is sent back to the client as a response.
The commands consist of a selector (typically) and a client action that is called with parameters. When the command marshaller receives this sequence of commands, it looks up the nodes identified by the selector. The command marshaller calls each assigned client action in the required order, with the parameters supplied by the server. The client actions continue the flow of execution on the client side; this may result in manipulating the DOM content of the page, binding further events, and as a consequence, the execution of further actions. In the end, the change is visible for the user, finishing the entire AJAX cycle.
The client actions, like the event binders, ship as "core" plugins into the KSS system. The simplest and also most useful client action (replaceInnerHTML) replaces a selected tag in the DOM with a string value supplied by the server as a parameter of the command. This covers the most important KSS use case: in response to a user action, the browser replaces part of the page that is completely rendered on the server side. This results in a thin browser client where most tasks are delegated to the server; in many AJAX use cases, this is also the proper solution.
However, additional local actions can carry out other common tasks, and you can extend the system for your needs with very complex actions. In the more complex cases, you may even want the server to return a set of data to be turned into more complex DOM manipulation by the accompanying command plugin.
Syntax and semantics of the KSS resource file
In this section we detail what a KSS resource file contains and how to use it.
At-config rule
The main configuration is done within an @config rule:
@config { protocol: json-rpc; date-encoding: iso8601; timeout: 5000; endpoint: /path/to/service; }
protocol
The protocol to use. A value of "json-rpc" (default), "json-rpc-v1", "json-rpc-v2" (synonym for "json-rpc"), "xml-rpc", "url-encoded" or "json". The last two can be used if there is no RPC service available.
date-encoding
How dates should be encoded. A value of "iso8601" (default), "@timestamp@", "class-hinting" or "asp.net".
timeout
The timeout for server actions (defaults to 10000).
endpoint
The endpoint url to the RPC service.
csrf-selector
A CSS selector used to query for an element which holds the CSRF token. Either in its data attribute "data-csrftoken" or in its value attribute. Can be specified in addition to csrf-cookie.
csrf-cookie
The cookie name used to read the CSRF token. Can be specified in addition to csrf-selector.
csrf-header
The header name used to send back the CSRF token.
Simple cases
KSS is a resource format similar to CSS. It contains "rules." A rule selects a HTML element from the page, binds it to an event, and specifies the "actions" to happen when this event occurs.
A simple example is this:
#title_save:click { kss-action-server: saveTitle; }
This binds the node selected by the CSS selector #title_save
to the click
event. If this node is clicked, we want an action to happen. In this case, this is a "server action" executed on the server. A server action must be the name of a remote RPC method.
Another example:
#sportlet-main:timeout { evt-timeout-delay: 5000; kss-action-server: refreshPortlet; }
This binds the timeout
event to the HTML node selected by the CSS selector #portlet-main
. The evt-timeout-delay
specifies a parameter used during the binding of the event. In this example, it specifies how often the timeout event should poll. Similar to the previous case, we also execute a server action here. The method refreshPortlet
is called every five seconds, and results in refreshing a portlet in a page.
Complex cases
The goal is to enable the creation of 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. The key point to keep in mind is that once the events are triggered, we handle them within the system, as if they were normal browser events.
The timeout
event is contained in the "core" plugins of KSS.
The KSS stylesheet rules resemble CSS rules, but we attach a KSS event name at the end to designate the event to be bound to the nodes selected by CSS. The event name is prefixed with a colon.
The attributes inside the rule also follow a syntax very similar to CSS, but with different semantics. We explain more about the parametrizing semantics a bit later. Overall, however, the rule contains different properties that define:
- parameters for the event binding
- server and client actions, with parameters (there can be more of these)
A rule follows the following syntax:
[<CSS> <CSS> <CSS> ...] <CSS_SELECTOR>:<KSS_EVENT_NAME> { /* event parameters */ ... /* actions (server or client) */ ... }
Consider the following example:
div#thisnode a:timeout { /* event parameters */ evt-timeout-delay: 3000; /* actions */ kss-action-server: updateInfo; remark: 'Updating from timeout'; color: red; kss-action-client: log; message: 'Updating from timeout'; }
A timeout
event is bound; in this case the div#thisnode a
CSS selector selects any nodes. The event is bound to the selected nodes, but in case of the timeout event the node itself does not matter.
The parameter delay=3000
specifies a 3000ms timeout recurring tick. When the events are executed, two actions (updateInfo and log) are executed, each one with the supplied parameters. The remote action is executed on the server, and then sends back its command response to the client. The "log" action is a special client action that is implemented as a JavaScript action plugin; this particular one gives a log message.
Event selectors
The event selector follows the css selector with a colon in the KSS rule:
:eventname
Examples:
:click :timeout
The full event rule
The name of the event follow the CSS selector after a colon:
div#recent-portlet:click { ... } div.banner:timeout { ... }
The event name in KSS must be preceded by a normal CSS selector. It cannot stand by itself.
A full example for an event method rule is provided later in this document.
Specifying parameters
The general schema for the parameter specification:
.... { evt[-<eventname>]-preventdefault: [true|false]; evt[-<eventname>]-preventbubbling: [true|false]; evt[-<eventname>]-allowbubbling: [true|false]; evt-<eventname>-<key>: <value>; kss-selector: <selector>; kss-precondition: [true|false]; ... kss-action-[server|client]: <actionname>; kss-selector: <selector>; kss-precondition: [true|false]; kss-timeout: <milliseconds>; kss-includeform: [<form_name>|@currentform]; <key1>: <value1>; <key2>: <value2>; ... kss-action-[server|client]: <actionname>; kss-selector: <selector>; kss-precondition: [true|false]; kss-timeout: <milliseconds>; kss-includeform: [<form_name>|@currentform]; <key1>: <value1>; <key2>: <value2>; ... }
The keys and action names can be in camelcase (CamelCase). Although the original CSS disallows the usage of uppercase characters in the identifier, they are allowed in KSS.
"Event parameters" mean that these are parameters to the event binding itself. There are different possibilities to declare them:
evt-preventdefault: true; evt-preventdefault: false; evt-click-preventdefault: true; evt-submit-preventdefault: true;
Declarations with omitted event names are applied to all event types of the selector (that could be more than one).
Event actions can be defined with their parameters. An event action must be a server or a client action. It is possible to have more than one event action within a rule:
#thisnode:timeout { kss-action-server: updateInfo; remark: 'Updating from timeout'; color: red; kss-action-client: log; message: 'Updating from timeout'; }
Parameter producer functions
A value simply represents a value. Quotes are optional, but as seen in this example, both single and double quotes can be used also:
... { size: 12pt; typeface: "Bitstream Vera Sans"; typeface: 'Bitstream Vera Sans'; }
It is possible to create extensions to acquire a parameter in another way as a constant; these are called parameter producer functions. When calculating a value, these preset functions can take the event rules, the state of the event, and the page in consideration. We show some examples here, and we will give the complete list of the selectors, too, later:
... { node_id: nodeAttr(id); rownum: dataAttr("rownum", true); }
The parameter producer functions operate on the scope of the node which triggered the current event.
KSS action parameters
KSS action parameters are special predefined parameters. Strictly speaking, they are not really freely usable parameters to specify a value for an event; rather, they modify the way the (client or server) action works.
The action parameters are distinguishable from normal parameters by their name: they have the KSS prefix. (Consequently, normal parameters are not allowed to start with this prefix.)
Only a few action parameters are implemented, so we present them all here:
kss-selector
kss-selector
modifies the scope of the action. The default execution scope is the same node where the event was triggered. With kss-selector
, we can execute the action on a different or multiple nodes:
... { kss-action-client: actionName; kss-selector: selector; }
kss-precondition
kss-precondition
prevents the execution of all declared actions in a rule if its value evaluates to false
:
... { kss-action-server: actionName; kss-precondition: confirm('Are you sure?'); }
kss-timeout
kss-timeout
sets an alternative timeout for server actions:
... { kss-action-server: longRunning; kss-timeout: 100000; }
kss-includeform
kss-includeform
transparently adds an entire form to the parameters of a KSS action. It is global to the action: all field variables of the form are added with their name as they appear in the form:
... { kss-action-server: actionName; kss-includeform: formname; }
The value of the declaration can specify to submit a given form or the current form (in which the event has been triggered). Variations can be seen in the next example:
kss-includeform: formname; kss-includeform: @currentform;
Summarizing the rule definition basics
A complete example rule follows here:
div#portlet-recent:timeout { evt-timeout-delay: 2000; kss-action-server: replaceMacro; selector: #portlet-recent; macropath: portlet_recent/macros/portlet; }
Here, delay="2000"
is used as a parameter for the binding of the timeout event, and selector="#portlet-recent"
, macropath="portlet_recent/macros/portlet"
are used for calling the "replaceMacro" method. In this example, selector
is not a kss action property; it is a normal parameter passed to the server.
Another example:
div.menu-item:load { kss-action-server: replaceWithRenderedText; text: textContent(); size: 12pt; typeface: "Bitstream Vera Sans"; }
How rule merging is done
In contrast to CSS and the original KSS implementation, in KSS-RPC there is no rule merging done at all. So a declaration of two rules in the context of the same node results in the binding of two events.
Error handlers
It is possible to attach error handlers to server actions. This is implemented as an additional event type error
:
body:error { kss-action-client: alert; message: errorAttr('message'); }
The error actions can accept the special parameters provider errorAttr
to access properties of the current error object.
In practice, the main reasons that an error will occur during a remote action execution are server errors or timeouts. It is also possible for a server method to raise an exception on purpose, with the expectation that the error handler will address the case on the client side in a designated way.
Action names
Action names are declared following the kss-action-client or kss-action-server keys in the rule definition.
An action name is the name of either a server or client action, depending if kss-action-client or kss-action-server is applied. These are the two choices we have: pass the control flow to the server (the most common case), or handle an action locally on the client (in which case, a JavaScript plugin will be called).
If an action key is missing from an event or method rule, no action is carried out.
A rule definition can have multiple client and server actions defined. Actions are executed in the declared order but server actions are executed asynchronously.
Getting information from the DOM (parameter providers)
Actions must be executed with parameters. Although static parameters can be sufficient for some application, most of the time a program expects to fetch some information from the DOM (the page where the event executing the action has triggered). This pattern is solved by the "parameter providers" that we introduced earlier in chapter 1.3.
For those not familiar with the term, we mention that DOM stands for the Document Object Model. This refers to the internal representation of our HTML page. So when talking about the DOM, we mean accessing information from our page from a program or as in this case, from KSS. And this is the exact purpose of the KSS parameter providers.
A parameter provider looks like a function with a number of parameters, and can stand in place of the value of a parameter:
... { kss-action-server: doIt; whatisit: parmProvider(arg1, arg2, ... argN); }
There are two standard examples used most often. In the first case, we want to take an attribute of a node.
HTML:
<div id='our-node'>The content</div>
KSS:
div#our-node:click { kss-action-server: doIt; id: nodeAttr('id'); }
We won't examine this line-by-line, at this point, but the important piece to look at is the parameter 'id' that is passed with the value 'our-node'.
In the other use case, we want to include special markup with our HTML, but we can not use HTML attributes for that. Existing HTML attributes already have a specific purpose; using an attribute not contained in the HTML standard would result in non-validating pages. To avoid this problem, we use the HTML5 data-* attributes to achieve the same result. These attributes can be acquired by the dataAttr
parameter provider.
HTML:
<div id='our-node' data-marker='marker-id'>The content</div>
KSS:
div#our-node:click { kss-action-server: doIt; marker: dataAttr('marker'); }
The value 'marker-id' is marshalled to the doIt action. We can use any variable name, since they all become attributes within the data-* attributes.
It is a further enhancement of this use case to acquire these attributes in a recursive way. For example, let us consider a widget we created. Using KSS attributes recursively lets us mark up an entire widget with a single KSS attribute, and we can access the value of the single attribute from dataAttr
if an event happens anywhere inside the widget.
HTML:
<div id='our-node' data-widgetid="widget2000"> <a class='link' href='firstpage.htm'>First page</a> <a class='link' href='secondpage.htm'>Second page</a> </div>
KSS:
a.link:click { kss-action-server: doIt; href: nodeAttr(href); widgetid: dataAttr('widgetid', true); }
The second parameter in dataAttr('widgetid', true)
tells KSS to look further in the parents of the node, until it finds the attribute.
Introduction to commands
"KSS commands" serve a very important role in a KSS application. They enable the application to use server-side code to instruct what the browser should do on the client side. A command specifies:
- A client action that the command will execute. This action stands with parameters that travel in the command from the server to the client.
- In the majority of commands this is accompanied by a "selector" parameter which identifies the node or nodes on which the action needs to be executed. If there is no selector specified, the action is executed in the context of the node that triggered the event.
Several commands are preimplemented in the KSS plugins. Practically, all the client actions can be executed both from the client and from the server.
When a server action executes, it assembles a response which it will send back to the client. This response consists of a set of commands.
A command instructs KSS on the browser to execute a client action combined with a selector that is applied to select nodes for the execution. This means that the selector is executed first on the page, and the client action is executed on the resulting nodes. The execution may occur on zero or more nodes. Executing on zero nodes is permitted and results in no error condition.
Commands are constructed on the server side. To demonstrate a complete example, let's examine an example. Look at the doIt action:
from kss import KSSResponse @rpcmethod def doIt(widgetid, href): kss = KSSResponse() kss.replaceInnerHTML('div#result', html = '<p>Parameters: %s, %s</p>' % (widgetid, href)) return kss.response
The doIt action, as implemented above, commits a single command replaceInnerHTML
that causes the target node to be replaced by the value of the single parameter 'html' of the action.
The commands, as the response given by the server action, are returned in RPC format (either JSON or XML). This is the raw JSON result to be sent back to the client:
{"commands": [ { "selector": "div#result", "action": "replaceInnerHTML", "params": { "html": "<p>Parameters: widget2000, firstpage.html</p>" } } ]}