Pasta/CLS 仕様
適用対象
CLSは、ウェブページなどのHTMLおよびXMLベースの動的なページを生成するためのテンプレート言語です。 処理の対象となるテンプレートは、XMLモデルとして扱うことができなければなりません。 すなわち、HTMLであれば、ページの生成の過程で、整形式のXML(XHTML)モデルに変換されます。
動作モデル
制御モデル
CLSはインタプリタ方式の言語です。一般の言語処理系と同様にプログラム・カウンタに相当するものを持ち、 ジャンプ、繰り返し、条件分岐といった制御が可能です。 CLSプロセッサはXML形式のテンプレートの先頭から処理を開始し、各種フロー制御などを経て、 その過程で通過したノードを結果として出力します。 これは、一般的なテンプレート言語と変わりありません。
制御モデルについての詳細はLogicのセクションで説明します。
フィルター・チェイン
CLSの出力結果はXMLとなります。 処理の過程で通過したテンプレート上の内容を順に出力しますが、その前にフィルター・チェインという機構を通ります。 これは、XMLを変換するフィルタを連結させたもので、テンプレートの処理中に動的に変化します。
フィルター・チェインについての詳細はFilterのセクションで説明します。
データ供給モデル
背景
従来から使われているPerl,CによるCGI、サーブレットや単純なPHPやJSPの方式は「プッシュ型」と呼ばれます。 このような方式ではプログラムによって動的にデータを生成し、固定したHTMLのタグや文章と織り交ぜてクライアントに対して「押し出し」ます。
それに対して、テンプレートエンジン (Struts, Smarty, Velocityなど)では「プル型」という方式が用いられます。 これはデータの供給元をあらかじめ用意し、テンプレートから「引き出す」方式です。
一般に後者の方式の方が、データを生成するためのロジックと、テンプレートによるデザインを綺麗に切り分けることができます。 ただし、あらかじめデータを生成する処理に時間がかかったり、データを蓄えるためにメモリを大量に消費することがあるという欠点もあります。
Pastaは後者の方式を採用しつつ、データ生成にかかる時間と、メモリの使用量を減らすために、 データの生成とテンプレートの処理を並行して行うというアプローチを取っています。
スクリーン
スクリーンはPastaのデータ供給モデルの基礎となるコンポーネントです。 これは、名前が付けられたパイプが束ねられた構造になっています。 各パイプは様々なオブジェクトをキューイングすることができ、なおかつスレッドセーフです。 (javadoc参照)
スクリーンの入り口は、複数のデータを供給するコンポーネントに接続され、出口はテンプレートに接続されています。 すなわち、データ供給コンポーネントによって供給されたデータが製麺機かところてんのように押し出され、テンプレートはそれを消費することになります。 これは典型的な生産者-消費者パターンです。
Push (生産者)
Push(javadoc参照) はスクリーン上の1つのパイプに接続し、データを「生産」します。
スクリーン ____________
Push(1)-data-> |==パイプ(1)==| -data-> |テンプレート|
Push(2)-data-> |==パイプ(2)==| -data-> |____________|
...
全てのPush、そしてテンプレートは並行に動作します。 テンプレートのデータ取り出しよりもPushの処理が先行している場合は、 テンプレートが未取得のデータはスクリーンの各パイプにキューイングされます。
Pushを使ってテンプレートにデータを供給する方法は一般に、 最初のデータが画面に表示されるまでの時間が短く、体感速度が速くなります。 また、Pushが特に大量のデータを出力する場合は、 テンプレートに表示済みのデータは直ちに破棄できるため、 メモリの使用効率が高くなります。
ただし、Pushの数だけスレッドを起動するため、 Pushの数が極めて多い場合は、 システムのリソースをかえって消費するという欠点があります。
Pull (反復子)
Pull(javadoc参照) は生産者-消費者パターンの生産者には該当せず、一種の反復子(iterator)として動作します。 なおかつ、このコンポーネントはスクリーン中のパイプの出口に「偽装」されます。
スクリーン ____________ x==Pull(1)==| -data-> |テンプレート| x==Pull(2)==| -data-> |____________| ...
Pushがあるにも関わらずこのようなコンポーネントが用意されている理由は、 Pullにはスレッドを起動しなくてもよいという利点があるためです。 特に、データがあまり多くない場合や、データの供給源が高速である場合は有利です。 実際、そのような状況が多いはずです。
また、Java言語の場合反復子として動作する既存のオブジェクトの java.util.Iteratorあるいは、 java.sql.ResultSetと接続しやすいため、 Collection系オブジェクトやデータベースからのデータ取得に適しています。
Module
Module(javadoc参照) はScreenに取り付き、複数のパイプを占有するコンポーネントです。 HTTPリクエスト・パラメータ、クッキーのような辞書的なデータに一括してアクセスするという、現実的な要求のために用意されたものです。
Moduleは占有しているパイプにアクセスがあった時点で仮想的な出口(Pullに相当するもの)を生成します。
Replacer (置き換えタグ)
置き換えタグは、スクリーンから取り出したデータで置き換えられる文字列です。 ${式}という形式で、テンプレートの属性、テキストなどに記述することができます。 これは多くのテンプレート言語に見られるやり方です。
式の形式はカスタマイズ可能ですが、普通はパイプの出口にあるデータを表示する場合は${パイプ名} という形式で記述します。 ${#パイプ名}とすると、パイプの先頭の値を取り出し、その値で置き換えられます。
詳細はReplacerの詳細のセクションで説明します。
スクリーン・スタック (局所変数?)
同じスクリーン上で、既にコンポーネント(Push/PullおよびModuleによって生成された出口)が接続されているパイプに、 さらにコンポーネントを接続しようとすると、古いコンポーネントが破棄され、新しいコンポーネントが接続されます。
しかし、CLSの処理系ではスクリーンは1つではなく、XMLの要素およびブロック構造(後述)ごとにスクリーンが生成されます。 生成されたスクリーンは要素またはブロックが終了した時点で破棄されます。 テンプレートはまず最後に生成されたスクリーンにアクセスを試み、値があった場合はその値を使います。 値がなければ、上位のレベルのスクリーンにアクセスを試みます。 以降、これを最上位まで繰り返します。 一方、各コンポーネントの接続は、最後に生成されたスクリーンに対して行われます。 スクリーンが破棄されると同時に、コンポーネントも破棄されます。
これは一般的な言語処理系の局所変数スタックに似ています。
インラインロジック宣言構文(cls-logic属性)
CLSの構文には、Logic,Filter,Action,PushPull,Moduleというコンポーネントと、制御構造をまとめるBlockがあります。 各要素にcls-logicという名前の属性を付けることで、これらのコンポーネントをXML要素に割り振ることができます。 cls-logicの構文はCSSのstyle属性と非常によく似ていて、"(コンポーネントの種類を表す記号)コンポーネント名前: 値1 値2 ...;"という形式の繰り返しです。 以下にその構文を示します。
[1] LogicDeclaration ::= (Logic|Action|Filter|Push/Pull|Module|Block)*
[2] Argument ::= Ident|Literal|Expr
[2.1] Ident ::= [^{space}]*
[2.2] Literal ::= '"'[^"]*'"'
| "'"[^']*"'"
[2.3] Expr ::= '${'[^\{]*'}'
[3] Name ::= [^:;'"{space}]+
[4] Logic ::= [^@@|=$:;'"{space}]Name ';'
| [^@|=$:;'"{space}]Name ':' Argument* ';'
[5] Action ::= '@'Name ';'
| '@'Name ':' Argument* ';'
[6] Filter ::= '|'Name ';'
| '|'Name ':' Argument* ';'
[7] Push/Pull ::= '='Name('/'Name)? ';'
| '='Name('/'Name)? ':' Argument* ';'
[8] Module ::= '$'Name('/'Name)? ';'
| '$'Name('/'Name)? ':' Argument* ';'
[9] Block ::= '[' LogicDeclaration number?']'
以下の例では、要素liに[restore Filter,iterate Logic,not-null Logic]というリストを対応させ、 restoreには空、iterateには["foo","a"]、not-nullには["a"]という引数が与えられます
<ul>
<li cls-logic="|restore; iterate: foo a; not-null: a;">
${a}
</li>
</ul>
各コンポーネントに与えられる引数には、識別子(Ident)、リテラル(Literal)、式(Expr)の3種類があります。 識別子はクオートでくくられない文字列で、リテラルはクオートでくくられた文字列です。 式は${...}という形式のもので、後述する置き換えタグに類似しています。 識別子、またはリテラルは文字列としてコンポーネントに渡されますが、式では式によって得られたオブジェクト(数値、boolean、nullなど)がそのまま渡されます。 引数が明らかに識別子として認識されない状況では、識別子はしばしばリテラルとして解釈されます。 また、引数が識別子かリテラルとして認識されるかが曖昧な状況では、式の値は識別子として認識されることが優先されます。 確実にリテラルとして式を認識させる場合は、リテラル内に置き換えタグを記述した形式 '${...}' を用います。
コンポーネントの機能の詳細
Logic (制御構造)
Logicは、プログラミング言語の制御文に相当するもので要素あるいはブロック(後述)単位で非表示・表示・繰り返しを行います。 以下は、cls-logic属性によって要素にLogicのリストを対応させた例です。
<ul>
<li cls-logic="present: foo ; iterate: foo a; not-null: a;">
${a}
</li>
</ul>
この記述は、li要素にLogicのリスト[present Logic,iterate Logic,not-null Logic]を対応させます。
CLSプロセッサがこの要素を見つけると、対応させているLogicのリストを先頭から実行します。 上の例では、presentが最初に実行を開始します。 このとき、present Logicは以下の3つの動作のうち1つをCLSプロセッサに指示することができます。
- 何もしない
- CLSプロセッサは、何もせずに次のiterate Logicを開始します。
- スキップする
- CLSプロセッサは、後のLogic(iterate,not-null)を飛ばして、li要素の終了タグの直後に処理を移します。
- 現在の位置をマークする
- CLSプロセッサは現在の位置(li要素の1番目のLogic)を記憶して、次のiterate Logicを開始します。
Logicの実行開始時に何もしないか、内容をスキップした場合は、直ちにLogicの実行は終了します。 現在の位置をマークしてli要素の直後に達すると、さらに2つの選択肢を選ぶことができます。
- 何もしない
- present Logicは終了し、そのまま処理を続行します。
- マークした位置に戻る
- present Logicを終了せずにli要素の1番目のLogicの次のLogic、つまりiterateに処理を移します。 li要素の後に到達したとき、present Logicは再び、何もしないか、マークした位置に戻るか選ぶことができます。
iterate、あるいはnot-nullが現在位置をマークした場合は、li要素の後で起こる処理の選択は、Logicが実行を開始したのと逆順に行われます。
cls-logicに記述された制御を開始した時点では、その要素自身の開始タグは出力されないことに注意してください。 かつ、Logicによる制御の対象はその要素の終了タグの直後までです。 Logicが要素をスキップする場合、その要素自身が表示されないことになり、 繰り返しが行われる場合は要素自身が繰り返されます。
Action (状態の変化)
'@'(actionのa)という記号はActionを表します。 Actionは実行時にバックエンドでなんらかの変化を起こさせるものです。 それ自身が直接制御の流れを変えることはありませんが、状態の変化が後のコンポーネントに影響を与えます。
以下の例ではp要素に[load Action,present Logic,load Action]というリストが対応付けられています。
<p cls-logic="@load: foo ; present: foo; @load: bar;">
${#bar}
</p>
仮に、load Actionは引数で与えられた名前のパイプに(例えばデータベース等から)値を読み込み、 present Logicは引数で与えられた名前のパイプに値が存在すれば処理を続行し、存在しなければ処理をスキップするとします。
上の例では最初にパイプfooに値が読み込まれ、present Logicがfooに値が存在するのを確認して処理を続行し、さらにbarに値が読み込まれます。 そして、${#bar}という置き換えタグがbarの値で置き換えられます。
最初のload Logicがfooへの値の読み込みに失敗したらどうなるでしょうか? この場合、present Logicは処理をスキップし、p要素の後に制御が移ります。 よって、@load: bar;というActionは実行されないことになります。
この例のように、Action,Logic,Actionという並びでは、Logicの後のActionがLogicによる制御の影響を受けます。 仮にpresent Logicが繰り返しを行ったとすれば、@load: bar;が複数回実行される可能性がありますが、 @load: foo;は必ず1度しか実行されません。
後で解説するPush/Pull,Module,Filterを表す記号('=','$','|')は実際はスクリーン・スタックまたはフィルター・チェイン対する状態変化を起こさせるActionとして機能します。 ただし、スクリーン・スタックはブロック単位で、フィルター・チェインは要素単位で追加されたコンポーネントが有効になることに注意しなければなりません。
Filter (出力結果のフィルタリング)
'|'(U*IXのshellのパイプ記号)という記号はFilterを表します。 Filterは要素単位で内容をフィルタリングします。 CLSプロセッサがテンプレートを処理するとき、制御の道筋で通過したノードを出力します。 そこにFilterが存在した場合、CLSプロセッサは結果の出力をFilterに振り向けます。 1つ、あるいは複数連結されたFilterの末端の出力が最終的な出力結果ということになります。 以下のテンプレートは、li要素にpresent Logic,text Filter,unescape Filterというリストを対応させる例です。
<ul>
<li cls-logic="present: foo ; |text: '<b>${a}</b>'; |unescape;"></li>
</ul>
初期状態では、CLSプロセッサの制御が通過した部分のノードがそのまま出力されます。
入力XML -> CLSプロセッサ -> 出力XML
present Logicが内容をスキップしなかった場合、次のtext Filterに制御が移り、フィルター・チェイン(括弧でくくられた部分)にtext Filterが連結されます。
入力XML -> CLSプロセッサ -> (text) -> 出力XML
さらに、unescape Filterが連結されます。
入力XML -> CLSプロセッサ -> (text -> unescape) -> 出力XML
このとき、入力された要素liとその内容は、textロジックによって変換され、さらにunescapeロジックによって順に変換されます。
要素liの末端(終了タグ)に達すると、追加されたのと逆の順番でunescape,textフィルタが外されます。 そして、前に述べたとおり、Logicは何もせずに終了するか、繰り返すかを選択することができます。
以下のように、Filter,Logic,Filterの順で指定されている場合、Actionと同様にLogicの後に記述されているFilterだけがLogicによる制御の影響を受けます。 すなわち、present Logicがどのような振る舞いをしてもtext Filterがli要素に適用されますが、 present Logicがli要素を飛ばすという判断を下した場合、unescape Filterはフィルター・チェインに追加されません。 (もっともこの場合li要素自身が出力されないのでtext Filterによるフィルタリングは「空振り」することになります。)
<ul>
<li cls-logic="|text: '<b>${a}</b>'; present: foo; |unescape;"></li>
</ul>
以下は、ul要素にtext Filterを適用し、内部のli要素にpresent Logicとunescape Logicを適用する場合です。
<ul cls-logic="|text: '<b>${a}</b>';">
<li cls-logic="present: foo; |unescape;"></li>
</ul>
ul要素が開始した時点では、フィルター・チェインにはtext Filterが連結された状態になります。 さらに、li要素でpresent Logicが制御をスキップしなかった場合、さらにunescape Filterが連結されます。 unescape Filterはli要素の終了時に外され、text Filterはul要素の終了時に外されます。 結果として、ul要素はtext Filterの影響を受け、li要素はtext Filterとunescape Filterの両方の影響を受けることになります。
フィルター・チェインはhttp://sax.sourceforge.net/イベントで連結され、 Chain of Responsibility(CoR)パターンによりパイプライン処理されます。
CoRによるパイプライン処理を行うという点はApache Cocoonの設計概念によく似ています。 CLSは要素単位でパイプラインの構成を動的に切り替えられるのが大きな特徴です。
Push/Pull (データの供給・反復)
'='(UN*Xのls -Fコマンドのソケット記号)という記号は、パイプに対してPushまたはPullを接続するための一種のアクションです。 PushとPullの両方に同じ記号が使われるのは、どちらもテンプレート側からはパイプにデータが供給されているように見えるため、 区別する必要がないためです。
以下の例を見てください。Push/Pullの記述で'/'に続く部分は、データが供給されるパイプの名前です。 省略すると、Push/Pullの名前自体がパイプ名になります。
<ul>
<li cls-logic="=load/foo; iterate: foo; =change: foo;">${change}</li>
</ul>
iterateが引数で与えられたパイプに存在する値の数だけ処理を繰り返すものとすると、 =load/foo;によってfooへ供給される値が尽きるまで=change: foo;の実行と、 li要素の表示を繰り返すことになります。
以前に説明したActionでもパイプにデータを押し込むことはできますが、 同じパイプに何百、何千というデータを押し込んで後で繰り返す場合、 Actionの実行中に一時的にページの生成がストップしてしまうことになります。 Push/Pullを使うと、全てのデータが供給される前にページの生成を開始できるという点でActionより優れています。
Module
'$'(ScreenのSに取り付く)の記号は、パイプに対してModuleを取り付かせるための一種のアクションです。
Push/Pullは1つだけのパイプと結びつくことができますが、Moduleは複数のパイプに対するアクセスを監視し、 アクセスがあった時点でデータの供給を開始します。
以下の例は、"par."で始まる名前のパイプに対するアクセスを監視します。
<body cls-logic="$parameters/par;">
こんにちは${par.name}さん。
</body>
'/'に続く部分は監視するパイプの名前の先頭部分で、実際には後に'.'が付加されます。 これを省略すると、Moduleの名前自身が使われます。 Moduleが結び付けられた時点では何も起きませんが、${par.name}という置き換えタグが "par."で始まる名前のパイプにアクセスしているので、この時点でデータの供給が開始されます。
仮に$parameters/par;は"par.リクエストパラメータ名"という形式のパイプ名へのアクセスに反応し、 対応するリクエストパラメータの値を供給するものとすれば、 例のページはnameパラメータに渡された人名を表示して、その人に挨拶することになります。
ブロック構造
今までの説明ではLogicによる制御の単位はXMLの要素でしたが、 ロジック宣言内の一部を'[]'で囲むことにより、ブロック構造を作ることができます。 ブロック構造によって、ロジック宣言内でさらに処理を制御できるようになります。
通常、Logicは要素の終了タグの直後までの処理を制御しますが、 ブロック内に書かれたLogic、ブロックの末端までを制御の対象とします。
以下の例では、present Logicによって|include: b;の実行・スキップ・繰り返しが行われますが、 ブロックの外の=load/a; iterate: a;は影響を受けません。
<a cls-logic="[iterate: b; |include: b;] =load/a; iterate: a;" href="${a:uri}">${a:title}</a>
さらに多くのプログラミング言語と同様に、ブロックの脱出レベルを指定することができます。 ']'の直前に数値を記述すると、ブロック終了時その数だけ上位ブロックを飛ばします。 数値の後にスペースを入れてはいけません。 以下の例では、ブロック内の|include: b;が実行された場合はトップレベルブロックがスキップされるため、 ブロックの外の=load/a; iterate: a;は実行されません。
<a cls-logic="[iterate: b; |include b; 1] =load/a; iterate: a;" href="${a:uri}">${a:title}</a>
これはPHPのbreak文によく似ています。
ロジックシート
cls-logic属性によるインラインロジック宣言よりも、このセクションで説明するロジックシートを使うことを推奨します。 それには、以下の2つの理由があります。
- ロジックを完全にテンプレートから排除するので、テンプレートの可読性が上がる。
- ロジックシート内のロジック宣言が事前コンパイルされるため、パフォーマンスが上がる。
ロジックシートをテンプレートに結び付けるには、2種類の方法があります。 まず、テンプレート中の任意の場所で、cls-logic要素内に記述することです。 cls-logic要素が終わった直後のノードからロジックシートが有効になるため、 それ以前の部分には影響しません。 そのため、例えば以下のようにページのなるべく先頭に記述することをお勧めします。
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<cls-logic>
...ロジックシート...
</cls-logic>
<title>タイトル</title>
</head>
<body>
...
</body>
</html>
ロジックシートはCSS(カスケーディングスタイルシート)と同様の形式で記述します。 ただし、CSSのセレクタに相当するものは、クラスセレクタと子孫セレクタだけをサポートします。 (将来拡張される可能性がありますが、適用対象がほとんどの場合HTML/XHTMLという現状では、 要素セレクタなどはあまり役に立ちません。)
また、CSS同様のディレクティブを使うことができます。用意されているディレクティブは@charsetと@importです。 ディレクティブは必ず、全てのセレクタの前になければなりません。 以下に構文を示します。 (@importディレクティブの引き数にurl(...)は使えず、文字列を使うことに注意してください。)
[10] LogicSheet ::= Directive* Selector*
[11] Directive ::= '@import' Argument ';'
|'@charset' Argument ';'
[12] Selector ::= '.'ClassName '{' LogicDeclaration '}'
[13] ClassName ::= {name}
テンプレート中で要素にクラスを対応させるには、cls-class属性を用います。 cls-classの値は、ロジックシートに存在するクラス名です。
以下のli要素はcls-logic="|unescape: foo; iterate: foo a; not-null: a;" という属性を持ったのと全く同じ振る舞いをします。
<cls-logic>
.foo {
|unescape: foo;
iterate: foo a;
not-null: a;
}
</cls-logic>
<ul
<li cls-class="foo">
${a}
</li>
</ul>
CSS同様に、ロジックシートは、外部のファイルとして分離することができます。 cls-import要素は、href属性によって指定した外部のロジックシートをテンプレートに対応させるもので、 指定されたロジックシートをcls-logic要素によって埋め込んだのと同じ振る舞いをします。
以下の例では、module.clsをロジックシートに用います。
<cls-import href="module.cls">
また、importディレクティブを以下のように使った場合も同じ振る舞いをします。
<cls-logic>@import: 'module.cls';</cls-logic>
XML文書のルート要素外でロジックシートを指定するために、xml-logicsheet処理命令が用意されています。 上記の2つのインポート処理は、以下の記述でも代用することができます。
<?xml-logicsheet href="module.cls"?>
以下は外部のロジックシートの例です。 ロジックシートファイルのデフォルトのキャラクタエンコーディングは"UTF-8"です。 他のキャラクタエンコーディングを用いる場合は、@charsetディレクティブで指定します。
@charset "ISO8859-1";
.foo {
|unescape: foo;
iterate: foo a;
not-null: a;
}
スクリプト
これは、ロジック宣言を要素と関連させずに、単なるスクリプトとして実行する機能です。 スクリプトはcls-action要素内に記述されます。 形式はロジック宣言に似ていますが、対応する要素がないため、Filterを使うことはできません。 制御構造は、cls-action要素の開始タグの直後から、cls-action要素の終了タグの直前までになります。
[14] Script ::= (Logic|Action|Push/Pull|Module|Block)*
Replacerの詳細
置き換えタグが適用されるのは、1) XMLのテキストノード 2) XMLの属性の値 3) CLSコンポーネントの与えられる引数 です。 このうち、CLSコンポーネントの引数に置き換えタグを使う場合、必ず引用符でくくらなければならないことに注意してください。
以下は置き換えタグの使用例です。
<cls-logic>
.foo {
|unescape: foo;
iterate: foo a;
not-equal: '${a}' '$$';
}
</cls-logic>
<ul>
<li cls-class="foo">
<a href="${a}">${a}</a>
</li>
</ul>
置き換えタグ内の式の形式は特に規定されません。 PastaのReplacerコンポーネントを切り替えることでカスタマイズ可能です。
用意されているReplacerの構文はReplacerのドキュメントを参照してください。
サイレントモード
テンプレートのルート要素に設定可能なcls-silent属性はエラーの際に処理を続行するかどうかを設定します。
trueに設定すると、clsの構文エラーなどが発生した際に、処理を続行します。 このとき、CLSプロセッサはエラーの内容をログに出力できます。
falseに設定すると、エラーの際は直ちに処理を中断します。 大抵の場合は、ウェブサーバーによってユーザーにエラーメッセージが表示されます。
最終的な出力で除去される内容
CLSプロセッサは、最終的に出力される内容から、CLS特有の属性や要素を全て除去します。 そのため、最終的名出力結果は標準に忠実なHTMLとすることが可能です。
cls-span要素は、テンプレートの構造上のブロックを設定するためのものです。 HTMLのspan要素と異なり、最終的な出力では消去できるため、文書の妥当性を壊すことがありません。
cls-mark要素は、cls-span要素と同様の働きをしますが、こちらは終了タグがありません。
XML名前空間による拡張
CLSに関連する要素、属性を"http://port4.info/2002/CLS"という名前空間に分ける方法が用意されています。 この記述方法は、XHTMLなどのXML文書で文書の妥当性を壊さないという利点があります。
以下の表は、事前にxmlns:cls="http://port4.info/2002/CLS"という宣言がされているものとして、各要素、属性の記述方法を説明したものです。
| 要素名 | 名前空間付きの記述 | 使用場所 | 終了タグ |
|---|---|---|---|
| cls-import | cls:import | headまたはbody要素内 | なし |
| cls-logic | cls:logic | headまたはbody要素内 | あり |
| cls-action | cls:action | headまたはbody要素内 | あり |
| cls-span | cls:span | 文書中のどこでも | あり |
| cls-mark | cls:mark | 文書中のどこでも | なし |
| 属性名 | 名前空間付きの記述 |
|---|---|
| cls-logic | cls:logic |
| cls-class | cls:class |
| cls-silent | cls:silent |

