W3C NOTE-STTS3-19981111

 

Simple Tree Transformation Sheets 3

W3C Note, 11-Nov-1998

This Version:
http://www.w3.org/TR/1998/NOTE-STTS3-19981111/
Latest Version:
http://www.w3.org/TR/NOTE-STTS3
Author:
Daniel Glazman , Electricité de France , Research and Development Division

Status of this document

This document has been submitted to the World Wide Web Consortium (see Submission Request, W3C staff comment).

This document is a NOTE made available by W3C for discussion only. This indicates no endorsement of its content, nor that W3C has, is, or will be allocating any resources to the issues addressed by the NOTE.

Table of Contents


Introduction

The release of Cascading Style Sheets in the World Wide Web universe the 17 december 1996 has been a major evolution of Web publishing. For the first time in very common software tools, it is possible to separate content and presentation in data. This old dream of SGML gurus, fighting against the rest of the world to make people realize this is important, is now a reality. Each HTML element can carry presentation styles and forget presentation HTML attributes..

The main effect of this evolution is the deprecation of several HTML elements and attributes. It is then necessary to describe the set of transformations that should be applied to a HTML document containing these deprecated items in order to make it conformant to HTML Clean and take advantage of CSS.

Furthermore, there is a solution to manipulate the contents of a document as an author may need it, such as automatic building of a table of contents. Very simple content generation operations are possible using W3C selectors in a new way described in this specification.

The current specification does not want to re-invent the wheel and it should not be extended to a very large set of operations. The goal of the current specification is only to provide a simple and quick implementation of a transformation algorithm of documents that a HTTP server or a site management tool could for instance handle on the fly .

STTS use the core CSS syntax, while extending the CSS selectors. There is no restriction on CSS existing selectors.

Changes from STTS 2 to STTS 3

The main change between STTS 2 and STTS 3 is related to selectors. While STTS 2 use them only in order to describe conditions, STTS 3 use them also for fragment descriptions. Main other changes :

Definitions and conventions

A STTS transformations sheet is made of rules. A rule is itself made of two parts : conditions and a block of declarations .

Declarations contained in a STTS rule are applied to an element if the context of this element makes true one of the conditions attached to the rule.

A STTS sheet can contain comments that should be ignored.

Error handling rules are the same than in CSS.

Comments

A comment begins with a /* and ends with */.

Initial terminator must not be contained in character string surrounded by quotes or double quotes. Otherwise, comments can be placed anywhere in a STTS sheet but in the middle of an identifier. Tools must ignore comment terminators and the sequence of characters between these terminators. A comment should not be considered as a space.

Conditions

A condition is a conditional link between the declarations of the STTS rule containing this link and the tree representing the document the rule is applied to.

A condition is a selector or a group of selectors. STTS are based on CSS 2 selectors (without pseudo-elements) and extended by the current specification.

A selector represents a true condition if all simple selectors and combinators comprising this selector represent themselves a true condition.

Fragment descriptions

A fragment description is a description of a HTML or XML subtree, including attributes and textual content, based on selectors' syntax.

A fragment description is made of one selector, that can contain one or more sequences of simple selectors, separated by combinators. Groups of selectors are not allowed in fragment descriptions.

Extensions to W3C Selectors

New pseudo-classes

The following list is derived from first XSL public draft.

:first-of-type
The element is the first sibling of its type.
:not-first-of-type
The element is not the first sibling of its type.
:last-of-type
he element is the last sibling of its type.
:not-last-of-type
The element is not the last sibling of its type.
:first-of-any
Equivalent to :first-child.
:not-first-of-any
The element is not the first sibling element of any type.
:last-of-any
The element is the last sibling element of any type.
:not-last-of-any
The element is not the last sibling element of any type.
:only-of-type
The element has no element siblings of the same type.
:not-only-of-type
The element has one or more element sibling(s) of the same type.
:only-of-any
The element has no element siblings at all.
:not-only-of-any
The element has one or more element siblings.

Indirect adjacent combinator

An indirect adjacent combinator is described by two sequences of simple selectors, separated by the combinator - . It represents two siblings, the second one (represented by the second sequence of simple selectors) occurring after the first one (represented by the first sequence of simple selectors) in the list of children of their parent element. For instance, the condition :

EM - TT

is true for all TT elements following a EM in the list of children of their common parent element.

Warning : indirect adjacent combinators are not allowed in fragment descriptions.

Selected element selector

If document's subtree matches a condition, the element selected by the condition is by default the element of the subtree represented by the last sequence of simple selectors in the condition. If one sequence of simple selectors in the condition contains a selected element selector, the selected element is the subtree's element represented by this sequence.

The selected element selector is described by a pseudo-element functional notation :selected() which can be called without argument or with a condition or group of conditions passed as argument.

If there is no argument, this notation represents the selected element on which the declarations of the rule apply. :selected() can be used in conditions and fragment descriptions. For instance :

AUTHOR:selected() > FIRSTNAME { .... }

H1 { modify-context : HR + :selected() + HR }

Declarations contained in the first rule apply on all AUTHOR which contain a FIRSTNAME. The second rule adds a HR just before and just after all H1, these HR and H1 having the same parent element.

If a condition or a group of condition is passed to this notation contained in a fragment description, it defers the application of the declaration to all elements matching the conditions passed to :selected(). For instance

SPAN.classified {
insert-inside-first : *:selected(H1,H2,H3,H4) > IMG[SRC="classfd.gif"]
}

This rule adds the IMAGE classfd.gif as the first sibiling of all H1, H2, H3 and H4 for each SPAN carrying class classified in the document. The SPAN itself is not modified at all.

Only one selected element selector is allowed per condition or fragment description. More than one should be considered as an error.

Content simple selector

A content selector represents a textual content of an element. Such a selector is described by a character string surrounded by double brackets [[ ... ]] . Between the double brackets is a succession of space-separated values :

In a condition, it is a true/false test on the textual content of the selected element. The test is true if the selected character string is a substring of the concatenation of all #PCDATA contained in the subtree this selected element is the root of. For instance :

P[["hello"]] { ... }

will be applied to paragraphs P containing substring hello. For instance, it will match:

<P>The teacher came in the room and said 
     <STRONG>hello</STRONG> like every day.</P>

The character set used in content simple selectors is Unicode.

Warning : content selectors are not allowed in fragment descriptions.

Explosive descriptor

Explosive descriptors are only allowed in fragment descriptions. An explosive selector always precedes a simple selector. No character is allowed between the explosive descriptor and its simple selector. An explosive selector modifies the meaning of the attached simple selector. Explosive descriptors are indicated by a circumflex accent ^.

^ followed by meaning
a class selector remove this class from the classes carried by the element
an attribute existence selector remove this attribute from attributes carried by the element
a type element selector or universal selector replaces the element by its contents

Example 1 :

P[ALIGN="center"] { modify-context : ^[align].centered }

removes the ALIGN attribute from all paragraphs that carry it and adds the centered class to these paragraphs.

Example 2 :

SPAN.warning { modify-context : .important^.warning }

Adds class important to all SPAN carrying class warning. Also removes class warning.

Example 3 :

P > CENTER { modify-context : ^*:selected() }

applied to

<P><CENTER>bla <EM>bla</EM> bla</CENTER></P>

will result in

<P>bla <EM>bla</EM> bla </P>

Warning : this operation is performed regardless to the DTD.

Groups of conditions

Conditions opening on the same declarative section can be grouped in a single rule. Conditions are then separated by commas.

For instance

DIV P.abstract { modify-context : .centered }
H2 { modify-context : .centered }
TABLE#b12 { modify-context : .centered }

and

DIV P.abstract, H2, TABLE#b12 {
  modify-context : .centered
}

are equivalent because the three selectors composing the group are valid selectors.

A group of conditions can also be passed as argument of a selected element selector.

Declarations

The declarative section (the block) of a rule follows conditions and is surrounded by curly braces.

{ ... }

A declaration is made of a property and a value for this property, separated by a colon : . A property defines a transformation of the selected element or of the context of the selected element.

If the declarative section contains more than one declaration, they are separated by semi-colons ; .

Important : order of declarations matters : first read, first applied.

Functional notations

In a STTS rule, property values can access to the values of all attributes carried by the selected element. These values are available through a call to the functional notation attr(X) where X represents the name of the attribute.

A second argument can be passed to attr(). If attr() has two arguments, it retrieves the value of the attribute carried by the first element in the document matching the condition being the second argument.

Warning : if the element does not explicitly carry an ID, a call to attr(ID) or attr(ID, ...) in order to retrieve the value of this attribute should reply a unique ID generated by the STTS parser and given to the element.

Another functional notation is available :content() retrieves the concatenation of all #PCDATA in the subtree the target element is the root of. A selector can be passed as an argument to content(). If content() has an argument, it retrieves the concatenation of all #PCDATA in the subtree the first element in the document matching the selector given as argument is the root of.

The second argument of attr(x,y) and content(x,y) can also be one of the following notations :

For instance :

BODY {
        modify-context : ^[BGCOLOR]^[TEXT] ;
        add-style : "background-color : " attr(bgcolor) ;
        add-style : "color : " attr(text) ;
       }
A { modify-context : [TITLE=content()] }

Properties

All STTS properties defined in this document show a table like the following one :

Value possible values or possible types of values
Applies to elements this property applies to

Add a style : add-style

Value [ <string> | attr(X) | attr(X,selector) | parent() | ancestor(n) | content() | content(selector) ]+
Applies to all elements

This property adds the STYLE attribute to the target element if necessary. It adds the value of the property to the contents of this attribute. If the attribute was not empty, a semi-colon is appended to the initial content before appending the value.

For instance :

H1[ALIGN] {
  modify-element : ^[ALIGN] ;
  add-style : "text-align : " attr(align)
}

applied to

<H1 ALIGN=CENTER>Chapter 1</H1>

will result in

<H1 STYLE="text-align : CENTER">Chapter 1</H1>

Add a Style Sheet : add-css

Value value is made of two or three data :
  • a keyword
    • link if a LINK reference to an external CSS style sheet is to be added to the current document (for HTML documents). XML case not already solved.
    • style if the contents of the declared style sheet is to be integrated in the document in a new STYLE element (for HTML documents). XML case not yet solved.
  • a URL containing the style sheet ; url() functional notation should be used here.

  • an optional list of comma-separated CSS media-types
Applies to HEAD only

As the application of CSS rules to XML documents is still being discussed at the W3C, the application of this property is restricted to HTML documents for the moment.

This property adds to the contents of the selected element a new LINK to a CSS style sheet or a new STYLE containing the contents of the style sheet for the given media.

For instance :

HEAD {
  add-css : link url(http://www.edf.fr/styles/default.css)  visual, aural
}

applied to

...
<HEAD>
<TITLE>title of this document</TITLE></HEAD>
...

will result in

...
<HEAD>
<TITLE>title of this document</TITLE>
<LINK REL=stylesheet TYPE="text/css"
      HREF="http://www.edf.fr/styles/default.css"
      MEDIA="visual,aural">
</HEAD>
...

Add a rule to a local Style Sheet : add-rule and add-unique-rule

Value [ <string> | attr(X) | attr(X,selector) | parent() | ancestor(n) | content() | content(selector) ]+
Applicable à all elements

The application of this property is restricted to HTML documents for the moment.

This property adds to the current document, in the last STYLE element in HEAD document's header or a new STYLE element created in HEAD, the given CSS rule. If this rule is added to an existing STYLE element, the value of its MEDIA attribute should be exactly the given list of media ; otherwise, a new STYLE should be created.

If the rule's definition contains double-quotes " , these quotes have to be preceeded by a backslash \ .

For instance :

BODY[LINK] {
     modify-context : ^[LINK] ;
     add-rule : "A:LINK { color : " attr(link) " } "
    }

Important warning : this rule will be added each time conditions are matching an element in the HTML tree. Replace add-rule by add-unique-rule if you want this declaration to be applied only once over the whole document. For instance, if you want to define a CSS rule only if a certain CLASS exists in the document :

.thisClass {
    add-unique-rule : ".otherClass { color : red }"
}

Note : factorization of CSS rules if they have the same selector, and factorization of CSS rules having the same declarative part but different selectors is a more complex job, but implementable. The way add-rule and add-unique-rule add CSS rules to the document depends only on the implementation of the User Agent.

Modifying element's context and contents : modify-context

Value a fragment description
Applies to all elements

It is possible to completely modify the context of the selected element of a rule adding attributes, inserting content inside, before or after the element, encapsulating the element in another one, ...

The value of this property describes the modified context of the selected element.

Warning : this operation is performed regardless of the DTD.

For instance :

P[ALIGN="CENTER"] { modify-context : ^[ALIGN].centered }

removes the ALIGN attribute of P paragraphs when its value is CENTER and adds class centered.

Effects on the target element

Selectors in the fragment description have the following effect on the selected element :

Examples :

CENTER { modify-context : DIV.centered }
Sequences of selectors at the left of the selected element

Sequences of selectors at the left of the selected element in the fragment description indicate elements that must be created and inserted as ancestors or predecessors of the selected element.

Allowed selectors have here the following meaning :

Examples :

P.abstract {
     modify-context : DIV > P.centered.emphasize[["Abstract"]] + :selected() }

SPAN.glossaryEntry  {
     modify-context : A[HREF="#" content()] > :selected()
}
Sequences of selectors at the right of the selected element

Sequences of selectors at the right of the selected element in the fragment description indicate elements that must be created and inserted as descendants or successors of the selected element.

Selectors have here the same meaning than in the previous section. Pseudo-classes :first-child and :last-child are here allowed in the second sequence of simple selectors of a child combinator if the first one describes the selected element. It then indicates where should be inserted the second element of the combinator.

Example :

P.warning {
  modify-context : :selected() > SPAN.strong[["WARNING !"]]:first-child + BR
  }

Element insertion

Five properties are specially meant for element insertion as shortcuts to modify-context when its value is only composed of one fragment description.

Value a fragment description without selected element selector
Applies to all elements
insert-before : xxxx
Shortcut for modify-context : xxxx + :selected()
insert-after : xxxx
Shortcut for modify-context : :selected() + xxxx
insert-inside-first : xxxx
Shortcut for modify-context : :selected() > xxxx:first-child
insert-inside-last : xxxx
Shortcut for modify-context : :selected() > xxxx:last-child
encapsulate-in : xxxx
Shortcut for modify-context : xxxx > :selected()

STTS, HTML, CSS, CAS, ...

STTS rules applied to a document are always applied before CSS and CAS rules. STTS rules do not modify the document itself but modify the internal view of the document in a conformant software.

STTS application to XML is unresolved.

Examples

Example 1 : dynamic association of a class to a language

[LANG] { modify-context : *[CLASS~=attr(LANG) "Styles"] }

applied to

<H1 LANG=en>Title of this section</H1>

will result in

<H1 LANG=en CLASS=enStyles>Title of this section</H1>

Example 2 : deprecation of all CENTER elements in favor of DIV elements carrying the ALIGN attribute (being itself deprecated by HTML 4.0...)

CENTER  { modify-context : DIV[ALIGN=CENTER] }

deprecation of all CENTER elements in favor of DIV elements carrying style text-align : center .

CENTER  { modify-context : DIV ;
          add-style : "text-align : center" } 

Example 3 : insert "Section : " before the contents of all H1 :

H1 { insert-inside-first : [["Section : "]] }

Example 4 : to add a legend after all images carrying class standalone, a legend being a centered paragraph carrying class legend and displaying the URL of the refered image

IMG.standalone {
  insert-after : P.legend[["URL : " attr(src)]] ;
  add-unique-rule : "P.legend { text-justify : center }"
}

applied to :

<IMG CLASS=standalone SRC="http://www.edf.fr/logo.gif">

will result in :

<STYLE TYPE="text/css">
  P.legend { text-justify : center }
</STYLE>
...
<IMG CLASS=standalone HREF="http://www.edf.fr/logo.gif">
<P CLASS=legend>URL : http://www.edf.fr/logo.gif</P>

Example 5 : automatic generation of a table of contents (level 1 sections only) at the end of the document, each item of the list being hyperlink to the refered section

BODY { insert-inside-last : HR + H1[["Table of contents"]] + OL.TDM }
H1   {
  insert-inside-first : A[NAME="ref" attr(ID)] ;
  modify-context :
       :selected(OL.TDM) > LI:last-child > A[HREF="#ref" attr(ID)][[content()]]
}

Example 6 : automatic generation of a table of contents (level 1 and 2 sections) just after a DIV with ID abstract. Level 1 sections are DIV elements carrying class sec1 and containing all the contents of the section, including subsections. Level 1 section titles are H1 included in the DIV. Level 2 section titles are H2.

/* add a HR and then an ordered list with class */
/* TDM ; it will contain the table of contents */

DIV#abstract {
  insert-after : HR + H1[["Table of contents"]] + OL.TDM }

/* for each H1, add an item to the list */
/* plus an empty list of subsections*/

DIV.sec1 > H1 {
  modify-context : :selected(OL.TDM) > LI[[content()]] + OL
}

/* for each H2, add an item to the last created sublist */

DIV.sec1 > H1 - H2 {
  modify-context : :selected(OL.TDM > LI:last-child > OL) > LI[[content()]]                   
 }

Example 7 : all images in the document are thumbnails. Hyperlinks from these icons to the real size images should be automatically added. Icons are in the document's directory but real size images are located in the photos subdirectory.

IMG { encapsulate-in : A[HREF="photos/" attr(SRC)] }

Example 8 : deprecation of HTML 3.2 structures in favor of CSS

Here is an example of a very simple set of STTS rules one can apply to a HTML 3.2 document in order to replace some deprecated attributes and elements by CSS styles. Warning : this set is not complete and is provided as is without any guarantee of any kind !

XMP, PLAINTEXT, LISTING { modify-context : PRE }

CENTER { modify-context : DIV.centered }

TT     { modify-context : SPAN.monospace }

I      { modify-context : SPAN.italicized }

DIR, MENU { modify-context : UL }

BODY {
add-style : "background-color : " attr(bgcolor) ; add-style : "color : " attr(text) ; add-unique-rule : "A:link { color : " attr(link) " }" ; add-unique-rule : "A:active { color : " attr(alink) " }" ; add-unique-rule : "A:visited { color : " attr(vlink) " }" ; modify-context : ^[BGCOLOR]^[TEXT]^[LINK]^[ALINK]^[VLINK] ; add-unique-rule : ".centered { text-transform : center }" ; add-unique-rule : ".monospace { font-family : monospace }" ; add-unique-rule : ".italicized { font-style : italic }" }

References

Cascading Style Sheets Level 1 , W3C, 17 Dec 1996
http://www.w3.org/TR/REC-CSS1
Cascading Style Sheets Level 2 , W3C, 24 April 1998
http://www.w3.org/TR/REC-CSS2
Simple Tree Transformation Sheets 2 , Electricité de France, 17 Oct 1997
http://www.w3.org/Submission/1997/16/
Action Sheets : a modular way of defining behavior for XML and HTML, Netscape Corporation
http://www.w3.org/Submission/1998/10/

Acknowledgements

Special thanks to

I also wish to thank the W3C HTML Working Group and CSS+FP Working Group chairmen and members who welcomed me in their Ali-Baba magic cave on behalf of Electricité de France. The permanent brainstorming and discussions happening there are the best seeds for imagination and creativity.