changeset 15:67544736317e

add slides for recital.
author kent <kent@cr.ie.u-ryukyu.ac.jp>
date Tue, 16 Feb 2010 17:25:21 +0900
parents 064c4bd99db0
children d9b85f041908
files recital-slide/figures/call-return.png recital-slide/figures/continuation.png recital-slide/figures/gcc-flow.png recital-slide/figures/gcc-flow2.png recital-slide/figures/gcc-repository.png recital-slide/figures/tailcall.png recital-slide/figures/tailcallstack.png recital-slide/slide.css recital-slide/slide.html recital-slide/slidy.css recital-slide/slidy.js
diffstat 11 files changed, 4379 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
Binary file recital-slide/figures/call-return.png has changed
Binary file recital-slide/figures/continuation.png has changed
Binary file recital-slide/figures/gcc-flow.png has changed
Binary file recital-slide/figures/gcc-flow2.png has changed
Binary file recital-slide/figures/gcc-repository.png has changed
Binary file recital-slide/figures/tailcall.png has changed
Binary file recital-slide/figures/tailcallstack.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/recital-slide/slide.css	Tue Feb 16 17:25:21 2010 +0900
@@ -0,0 +1,175 @@
+
+body {
+	font-family: "MS ゴシック";
+	/*font-size: 14pt;*/
+	/*background-color: #eeeeee;*/
+        /*overflow: hidden;*/
+        /*overflow: scroll;*/
+}
+div.background {
+	height: 2.7em;
+	background-color: #009900;
+	background-color: gray;
+}
+div.header {
+	position: absolute;
+	z-index: 2;
+	left: 0;
+	right: 0;
+	top: 0;
+	bottom: auto;
+	width: 100%;
+	padding: 0;
+	margin: 0;
+	border: none;
+
+	height: 0.2em;
+	/*background-color: #33ff00;*/
+	background-color: #001111;
+	border-top: solid 1.8em #114444;
+	border-bottom: solid 0.7em #004444;
+}
+div.footer {
+	/*background-color: #33ff00;*/
+	background-color: #001111;
+	position: fixed;
+	color: #eeeeff;
+	overflow: hidden;
+
+	z-index: 50;
+	left: 0;
+	right: 0;
+	top: auto;
+	bottom: 0;
+	height: 1.4em;
+	margin: 0;
+	font-size: 80%;
+	font-weight: bold;
+	padding: 0.3em 0 0 1em;
+}
+div.slide {
+	margin: 0;
+	padding: 0;
+	/*background-color: #eeeeee;*/
+}
+div.slide h1 {
+	position: relative;
+	background-color: #114444;
+	left: 0;
+	right: 0;
+	font-size: 140%;
+
+	padding: 0.3em 1.1em 0 1em;
+	margin: 0;
+	margin-right: auto;
+	/*margin: 0 auto 1em 3em;*/
+	/*width: -moz-fit-content;*/
+	min-width: 60%;
+
+	/* 高さは div.headerと調節  */
+	height: 1.62em;
+
+	color: white;
+	line-height: 1.1em;
+
+}
+div.slide h2.uptitle {
+}
+#w3c-logo {
+	background-color: transparent;
+	width: auto;
+	height: auto;
+	margin-bottom: 1em;
+	overflow: hidden;
+	position: absolute;
+	bottom: 0;
+	right: 0;
+	display: none;
+}
+/*
+#w3c-logo-fallback {
+background-color: transparent;
+width: 100%;
+height: 100%;
+margin: 0;
+}
+ */
+
+div.slide.top {
+}
+div.slide.top h1 {
+	font-size: 180%:
+	width: 80%;
+	height: auto;
+	margin: 3em auto 0;
+	padding: 0;
+	background: none;
+	color: black;
+}
+
+div.slide.final h2 {
+	text-align: center;
+	vertical-align: center;
+	width: 80%;
+	margin: 7em auto 0;
+	padding: 0
+	background: none:
+	color: black;
+}
+
+p.subhead {
+}
+
+ul li {
+	list-style-type: circle;
+	list-style-image: url("circle1.png");
+	background: none;
+	padding-left: 0;
+	margin-left: 1em;
+}
+li li {
+	list-style-type: circle;
+	list-style-image: url("");
+	background: none;
+} 
+
+.subtitle {
+	font-weight: bold;
+}
+
+em {
+	color: brown;
+	font-style: normal;
+	font-weight: bold;
+}
+
+em.weak {
+	color: black;
+	font-style: normal;
+	font-weight: bold;
+}
+
+dfn {
+	color: brown;
+	font-style: normal;
+	font-weight: bold;
+}
+
+pre {
+	font-style: normal;
+}
+
+
+dl > dt {
+	padding: 0;
+	margin: 0.3em 1em 0.3em;
+	width: 10%;
+	float: left;
+}
+
+dl > dd {
+	padding: 0;
+	margin: 0.3em 1em 0.3em;
+	width: 70%;
+	float: left;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/recital-slide/slide.html	Tue Feb 16 17:25:21 2010 +0900
@@ -0,0 +1,938 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
+<head>
+  <title>Continuation based C</title>
+  <meta name="copyright" content="Copyright &#169; 2009 KSL: Yogi KENT" />
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <meta name="font-size-adjustment" content="1" />
+  <link rel="stylesheet" href="slidy.css"
+        type="text/css" media="screen, projection, print" />
+  <link rel="stylesheet" href="slide.css"
+        type="text/css" media="screen" />
+  <!--link rel="stylesheet" href="../Slidy/w3c-blue2.css"
+        type="text/css" media="screen, projection, print" /-->
+  <style type="text/css">
+      .right {
+	  float: right;
+          width: 40%;
+      }
+      .left {
+	  float: left;
+          width: 40%;
+      }
+      div.slide {
+          vertical-align: middle;
+      }
+      div.top h1 {
+	  width: 70%;
+	  padding: 0 1em 0;
+	  text-align: center;
+      }
+      #frame {
+	position: fixed;
+	left: -1px;
+	top: -1px;
+	width: 800px;
+	height: 600px;
+	border: solid 1px red;
+	visibility: visible;
+      }
+      .speak {
+
+        visibility: hidden;
+
+        font-size: 80%;
+        line-height: 1.0;
+        position: fixed;
+        right: 0.5em;
+        bottom: 1.5em;
+        max-width: 60%;
+        background-color: green;
+        opacity: 0.90;
+        color: black;
+        -moz-border-radius: 8px;
+        -webkit-border-radius: 8px;
+      }
+      ul.narrow li {
+	margin-right: 0;
+      }
+      table {
+        border-collapse: collapse;
+	border: solid 1px black;
+      }
+      table td {
+	border: solid 1px black;
+      }
+      table th {
+        text-align: center;
+	border: solid 1px black;
+      }
+  </style>
+  <script src="slidy.js"
+          charset="utf-8" type="text/javascript">
+  </script>
+  <script type="text/javascript">
+      sizes = new Array("14pt", "15pt", "16pt", "17pt", "18pt", "19pt", "20pt", "21pt", "22pt","23pt",  "24pt", "26pt", "28pt", "30pt", "32pt");
+      sizeIndex = 1;
+      mouseClickEnabled = false;
+  </script>
+</head>
+<body>
+<!-- this defines the slide background -->
+<div id="frame"></div>
+
+<div class="background">
+  <div class="header">
+    <!-- sized and colored via CSS -->
+  </div>
+
+  <!--img id="head-icon" alt="graphic with four colored squares"
+      src="../Slidy/icon-blue.png" /-->
+
+  <div class="footer">
+    <object id="w3c-logo" data="kent-logo2.svg" type="image/svg+xml" title="KENT logo">
+      <a href="http://www.w3.org/">
+	<img alt="W3C logo" id="w3c-logo-fallback" src="kent-logo2.png" />
+      </a>
+    </object>
+
+    <!-- modify the following text as appropriate -->
+    組み込み向け言語CbCのGCC上の実装 <span style="font-size:70%;">http://www.cr.ie.u-ryukyu.ac.jp/~kent/slide/final.html</span><br />
+    <!--Event, Location, Month Year-->
+  </div>
+</div>
+
+<div class="slide top">
+  <h1>組み込み向け言語Continuation based CのGCC上の実装</h1>
+  <p>
+    与儀健人 (並列信頼研究室)
+    &lt;<a href="mailto:">kent@cr.ie.u-ryukyu.ac.jp</a>&gt;
+  </p>
+  <!--img src="../Slidy/keys.jpg" class="cover"
+  alt="W3C as letters on 3 plastic buttons from a keyboard" /-->
+  <!--h2>ゼミ, 河野研, Sep, 2009</h2-->
+</div>
+
+<div class="slide">
+  <h1>研究の背景</h1>
+  <ul>
+    <li>ソフトウェアは今も大規模・複雑化が続いている</li>
+    <li>しかし、ソフトウェアのバグを発見するのは難しい</li>
+    <li style="marker:none;"/>
+    <li>組込みやReal-time処理の需要も増大してる</li>
+    <li>高速な応答が要求される組込み処理にはハードウェアに近い言語が適している</li>
+  </ul>
+  <p class="subtitle">なにが問題になるのか?</p>
+  <ul>
+    <li>組込みソフト、Real-time処理、通信プロトコル記述、どれも状態遷移ベース</li>
+    <li>現存する記述言語は状態遷移の記述に向いていない</li>
+    <li>スタックが状態を隠蔽するため、分割しにくい、検証が難しい</li>
+  </ul>
+</div>
+
+<div class="slide" style="font-size:95%">
+  <h1>研究目的</h1>
+  <p class="subtitle">
+  状態遷移記述をベースとした、より細かい単位でのプログラミングを実現する
+  </p>
+  <ul>
+    <li>組込み、通信プロトコル、Real-time処理などの記述に向いている</li>
+    <li>状態遷移を直接記述するため、タブロー法での検証に有利</li>
+    <li>関数より細かく、ステートメントより大きい処理単位</li>
+    <li>細かい単位でソースコードレベルの最適化を可能にする</li>
+  </ul>
+  <p class="subtitle">条件</p>
+  <ul>
+    <li>既存のソフトウェアは膨大であり、無駄にはできない</li>
+    <li>互換性が必須条件</li>
+    <li>Cからの変換、Cへの変換ができる事が望ましい</li>
+  </ul>
+</div>
+
+<div class="slide">
+  <h1>Continuation based Cの提案</h1>
+  <p class="subtitle">継続を基本とする記述言語CbC</p>
+  <ul>
+    <li>環境を保持しない継続、<dfn>軽量継続</dfn>を導入</li>
+    <li>軽量継続で<em class="weak">状態遷移が明確</em>になる</li>
+    <li>関数の代わりとなる処理単位<dfn>コードセグメント</dfn></li>
+    <li>関数 &gt; コードセグメント &gt; ステートメント</li>
+    <li>for, whileなどのループも軽量継続で実現できる</li>
+    <li>Cとの相互利用のための構文<dfn>環境付き継続</dfn>
+      <ul>
+	<li>このCとの相互利用可能なCbCは<em>C with Continuation</em>と呼ばれる</li>
+      </ul>
+    </li>
+  </ul>
+  <p class="subtitle"></p>
+</div>
+
+<div class="slide" style="font-size:95%;">
+  <h1>コードセグメントと軽量継続の記述</h1>
+  <pre style="float:right; width-max:45%">
+<code>typedef code (*NEXT)(int);
+int main(int argc, char **argv) {
+  int i;
+  i = atoi(argv[1]);
+  goto factor(i, print_fact);
+}
+<em>code factor(int x, NEXT next)</em> {
+  goto factor0(1, x, next);
+}
+code factor0(int prod,int x,NEXT next) {
+  if (x &gt;= 1) {
+    goto factor0(prod*x, x-1, next);
+  } else {
+    <em>goto (*next)(prod);</em>
+  }
+}
+code print_fact(int value) {
+  printf("factorial = %d\n", value);
+  exit(0);
+} </code></pre>
+  <p class="subtitle">実際のプログラム記述は?</p>
+  <ul>
+    <li>コードセグメント定義
+    <ul>
+      <li><code>codeキーワードで宣言</code></li>
+      <li>書式は関数と同じ</li>
+    </ul>
+    </li>
+    <li>軽量継続制御
+    <ul>
+      <li><code>goto</code>キーワードと引数</li>
+      <li>コードセグメントの最初に飛ぶ</li>
+      <li>コードセグメントポインタによる間接継続も可能</li>
+    </ul>
+    </li>
+  </ul>
+</div>
+
+<div class="slide">
+  <h1>これまでのCbC</h1>
+  <p class="subtitle"></p>
+  <dl>
+    <dt>2000</dt>
+    <dd>micro-cをベースとしたコンパイラの完成<br/>
+      x86, PowerPC, ARM, MIPS.
+    </dd>
+    <dt>2002</dt>
+    <dd>CbCを用いた分散計算</dd>
+    <dt>2005</dt>
+    <dd>CbCを用いたプログラム分割手法</dd>
+    <dt>2006</dt>
+    <dd>CbCによるSPUマシンのシミュレータ</dd>
+    <dt>2007</dt>
+    <dd>時相論理をベースとしたCbCプログラムの検証</dd>
+    <dt>2008</dt>
+    <dd>GCCをベースとしたコンパイラが開発される</dd>
+    <dt>2010</dt>
+    <dd>GCCベースコンパイラを実用レベルに拡張</dd>
+  </dl>
+</div>
+
+<div class="slide">
+  <h1>本研究での取り組み</h1>
+  <p class="subtitle">取り組み</p>
+  <dl>
+    <dt>First</dt>
+    <dd>GCCにて実用レベルのCbCプログラムを動作可能にする
+      <ul>
+        <li>軽量継続の実装、これまでの制限の除去</li>
+	<li>x86アーキテクチャにて高速化を行った</li>
+	<li>PowerPCアーキテクチャでの間接継続の追加</li>
+      </ul>
+    </dd>
+    <dt>Second</dt>
+    <dd>C言語との相互利用を可能にした</dd>
+    <dt>Third</dt>
+    <dd>ソースコードメンテナンス性の向上</dd>
+  </dl>
+</div>
+
+
+
+<div class="slide">
+  <h1>GNU コンパイラコレクション (GCC)</h1>
+  <div style="width:50%;float:right;">
+  <p class="subtitle">GCCでのコンパイルの流れ</p>
+  <ul style="padding-left:3em">
+    <li>フロントエンド</li>
+    <li>ミドルエンド</li>
+    <li>バックエンド</li>
+  </ul>
+  </div>
+  <img style="width:80%;position:relative;top:-15%;" src="figures/gcc-flow.png" />
+</div>
+
+<div class="slide">
+  <h1>GNU コンパイラコレクション (GCC)</h1>
+  <div style="width:50%;float:right;">
+  <p class="subtitle">GCCでのコンパイルの流れ</p>
+  <ul style="padding-left:3em">
+    <li>フロントエンド</li>
+    <li>ミドルエンド</li>
+    <li>バックエンド</li>
+  </ul>
+  </div>
+  <img style="width:80%;position:relative;top:-15%;" src="figures/gcc-flow2.png" />
+</div>
+
+
+<div class="slide">
+  <h1>First: 軽量継続の実装</h1>
+  <p class="subtitle">軽量継続を実装するには?</p>
+  <ul>
+    <li>河野先生の作ったmicro-cは元より軽量継続を考慮して良く設計されている</li>
+    <li>micro-Cと同じ命令列を出力させるのは難しい</li>
+    <li>関数コール(call命令)ではもちろんダメ</li>
+    <li>必ず<em>jmp命令</em>を出力しないといけない</li>
+    <li>スタックを拡張してはいけない</li>
+    <li>しかしGCCでは<em>関数をベース</em>にしなければならない</li>
+  </ul>
+  <p class="subtitle"><dfn>末尾呼出</dfn>をGCCに<em>強制</em>させる必要がある</p>
+</div>
+
+<div class="slide">
+  <h1>First: 軽量継続の実装</h1>
+  <p class="subtitle">末尾呼出ってなに?</p>
+  <img style="float:right; width:50%; margin:1em; " src="figures/tailcall.png" />
+  <ul>
+    <li>リターンの直前の関数呼び出しのこと</li>
+    <li>GCCが最適化してくれる (<em>TCE</em>)</li>
+    <li>元の関数に戻らないため少し高速に</li>
+    <li>スタックも積まなくてよいため、大幅なメモリ節約、アクセス軽減</li>
+  </ul>
+</div>
+
+<div class="slide">
+  <h1>First: 軽量継続の実装</h1>
+  <p class="subtitle">末尾呼出ってなに?</p>
+  <img style="float:right; width:50%; margin:1em; " src="figures/tailcallstack.png" />
+  <ul>
+    <li>リターンの直前の関数呼び出しのこと</li>
+    <li>GCCが最適化してくれる (<em>TCE</em>)</li>
+    <li>元の関数に戻らないため少し高速に</li>
+    <li>スタックも積まなくてよいため、大幅なメモリ節約、アクセス軽減</li>
+  </ul>
+  <p class="subtitle incremental">この末尾呼出(TCE)を強制して軽量継続を実装!</p>
+</div>
+
+<div class="slide">
+  <h1>First: x86における高速化</h1>
+  <p class="subtitle">軽量継続は実装されたが、やはりmicro-cに比べると遅い</p>
+  <ul>
+    <li>特にx86アーキテクチャ</li>
+    <li><em class="weak">あくまで関数がベース</em>なので</li>
+    <li>関数呼出規約に従い全ての引数をスタックに格納してしまう</li>
+    <li>これをレジスタにすれば高速化が可能</li>
+  </ul>
+  <p class="subtitle">fastcallの導入</p>
+  <ul>
+    <li>GCCの独自拡張機能</li>
+    <li>引数の最初の<em>2つのみレジスタに</em>保持するようになる</li>
+  </ul>
+</div>
+
+<div class="slide">
+  <h1>First: x86における高速化</h1>
+  <p class="subtitle">fastcallの強制</p>
+  <ul>
+    <li>通常は以下の様に定義される
+<pre><code>__code current(int a, int b, int c) __attribute__((fastcall));
+</code></pre></li>
+    <li>しかしこれを毎回ユーザが書くのは変</li>
+    <li>やはりフロントエンドにて、強制するべき</li>
+    <li>型の構文木を生成した際にfastcall属性を付加</li>
+  </ul>
+  <p class="subtitle incremental">これで軽量継続制御が高速化される!</p>
+</div>
+
+<div class="slide">
+  <h1>First: CbCコンパイラ実装の評価</h1>
+  <p class="subtitle">CbCGCCとmicro-cで性能の比較</p>
+  <ul>
+    <li>CbCGCCが実用的になったことで、micro-cとの比較が可能に</li>
+    <li>コンパイラの出力した実行ファイルを比較</li>
+    <li>CbCでのquicksort例題を用意</li>
+    <li>実行速度、ファイルサイズ</li>
+    <li>比較対象はまずは旧CbCGCC、それとmicro-c</li>
+  </ul>
+  <p class="subtitle">実行環境</p>
+  <ul>
+    <li>CbCGCC、micro-cでともに実行可能な環境を選択</li>
+    <li>アーキテクチャは x86, PowerPC(Cell含む)</li>
+    <li>OSはLinuxとOS Xを使用する</li>
+  </ul>
+</div>
+
+<div class="slide">
+  <h1>First: 性能評価(速度比較) vs.旧ver</h1>
+  <p class="subtitle">速度測定結果(単位:秒)</p>
+  <table>
+    <tr>
+      <th></th>
+      <th colspan="2">新CbCGCC</th>
+      <th colspan="2">旧CbCGCC</th>
+    </tr>
+    <tr>
+      <td></td>
+      <th>最適化無し</th>
+      <th>最適化有り</th>
+      <th>最適化無し</th>
+      <th>最適化有り</th>
+    </tr>
+    <tr>
+      <td>x86/OS X</td>
+      <td>5.907</td>
+      <td>2.434</td>
+      <td>4.668</td>
+      <td>3.048</td>
+    </tr>
+    <tr>
+      <td>x86/Linux</td>
+      <td>5.715</td>
+      <td>2.401</td>
+      <td>4.525</td>
+      <td>2.851</td>
+    </tr>
+  </table>
+  <p class="subtitle">評価</p>
+  <ul>
+    <li>最適化無の場合は遅くなった </li>
+    <li>最適化を行うと、<em>約20%の高速化に成功</em></li>
+    <li>fastcallの効果が十分に出ている</li>
+  </ul>
+</div>
+
+
+<div class="slide">
+  <h1>First: 性能評価(速度比較)</h1>
+  <p class="subtitle">速度測定結果(単位:秒)</p>
+  <table>
+    <tr>
+      <td></td>
+      <td>最適化なしのGCC</td>
+      <td>最適化付きのGCC</td>
+      <td>micro-c</td>
+    </tr>
+    <tr>
+      <td>x86/OS X</td>
+      <td>5.901</td>
+      <td>2.434</td>
+      <td>2.857</td>
+    </tr>
+    <tr>
+      <td>x86/Linux</td>
+      <td>5.732</td>
+      <td>2.401</td>
+      <td>2.254</td>
+    </tr>
+    <tr>
+      <td>ppc/OS X</td>
+      <td>14.875</td>
+      <td>2.146</td>
+      <td>4.811</td>
+    </tr>
+    <tr>
+      <td>ppc/Linux</td>
+      <td>19.793</td>
+      <td>3.955</td>
+      <td>6.454</td>
+    </tr>
+    <tr>
+      <td>ppc/PS3</td>
+      <td>39.176</td>
+      <td>5.874</td>
+      <td>11.121</td>
+    </tr>
+  </table>
+  <p class="subtitle">結果(micro-cとの比較)</p>
+  <ul>
+    <li>x86では速度にあまり差が出なかった</li>
+    <li>x86に特化しているmicro-cと差がないのは<em>とても良い結果</em></li>
+    <li>PowerPCではCbCGCCが<em>2倍ほど早い</em></li>
+  </ul>
+  <p class="subtitle">この違いはどこから?</p>
+  <ul style="font-size:95%;">
+    <li>実際にアセンブラを出力して比較、その結果</li>
+    <li>x86は自由に使えるレジスタが少ないため、CbCGCCの最適化が効きにくい</li>
+    <li>演算の度にメモリ読み込み、演算、書き込みが発生する</li>
+    <li><em>レジスタの多いアーキテクチャではCbCGCCが断然有利になる</em></li>
+    <li>またCbC言語そのものもレジスタが多いアーキテクチャで有利</li>
+  </ul>
+</div>
+
+
+<div class="slide">
+  <h1>Second: Cとの相互利用</h1>
+  <p class="subtitle">なぜそれが必要か</p>
+  <ul>
+    <li>既存のソフトウェアを無駄にはできない</li>
+    <li></li>
+    <li>ソースコード上での互換性がある事が望ましい</li>
+    <li>CbCからCの関数を呼び出すのは問題ない</li>
+    <li>CからCbCのコードセグメントに継続するとスタックが保持されない</li>
+  </ul>
+  <p class="subtitle"><dfn>環境付き継続</dfn>の導入</p>
+  <ul>
+    <li>軽量継続に、スタックの情報を加える</li>
+    <li>関数からのみ使用可能</li>
+  </ul>
+</div>
+
+<div class="slide" style="font-size:95%;">
+  <h1>Second: Cとの相互利用</h1>
+  <pre style="float:right; width-max:45%">
+<code>typedef code (*NEXT)(int);
+int main(int argc, char **argv) {
+  int i,a;
+  i = atoi(argv[1]);
+  <em>a = factor(i);</em>
+  printf("%d! = %d\n", a);
+}
+int factor(int x) {
+  NEXT ret = <em>__return</em>;
+  goto factor0(1, x, ret);
+}
+code
+factor0(int prod,int x,NEXT next) {
+  if (x &gt;= 1) {
+    goto factor0(prod*x, x-1, next);
+  } else {
+    <em>goto (*next)(prod);</em>
+  }
+}
+</code></pre>
+  <p class="subtitle">環境付き継続の使用例</p>
+  <ul>
+    <li><code><em>__retunr</em></code>で表される特殊なコードセグメント</li>
+    <li>コードセグメントからは通常のコードセグメントポインタに見える</li>
+    <li>この<code>__return</code>に継続すると、元の関数の環境にリターン</li>
+  </ul>
+</div>
+
+<div class="slide" style="font-size:95%;">
+  <h1>Second: Cとの相互利用</h1>
+  <p class="subtitle">内部関数を用いた実装</p>
+  <ul>
+    <li><code>__return</code>が参照された場合にGCCが自動で内部関数を定義する</li>
+    <li>内部関数の中からは外の関数にgotoして脱出</li>
+  </ul>
+  <pre><code>int factor(int x) {
+   int retval;
+
+   <em class="weak">code __return(int val) {
+      retval = val;
+      goto label;
+   }
+   if (0) {
+     label:
+      return retval;
+   }</em>
+
+   NEXT ret = <em>__return</em>;
+   goto factor0(1, x, ret);
+} </code></pre>
+</div>
+
+
+<div class="slide" style="font-size:95%;">
+  <h1>Second: Cとの相互利用・評価</h1>
+  <p class="subtitle">この取り組みにより</p>
+  <ul>
+    <li>これにより、<dfn>C with Continuation</dfn> の仕様を満たした</li>
+    <li>ソースコードレベルで、Cと相互に利用することが可能になった</li>
+  </ul>
+</div>
+
+
+
+<div class="slide">
+  <h1>まとめ</h1>
+  <p class="subtitle">本研究での取り組み</p>
+  <dl>
+    <dt>First</dt>
+    <dd>CbCGCCにて実用レベルのCbCプログラムが動作可能となった
+      <ul>
+	<li><em>軽量継続における引数順序の制限を取り除いた</em></li>
+	<li>PowerPCでの間接継続の制限を取り除いた</li>
+	<li><em>x86アーキテクチャにて高速化を行った</em></li>
+      </ul>
+    </dd>
+    <dt>Second</dt>
+    <dd><em>Cとの相互利用性の向上</em></dd>
+    <dt>Third</dt>
+    <dd>ソースコードメンテナンス性の向上</dd>
+  </dl>
+</div>
+
+<div class="slide" style="font-size:95%;">
+  <h1>まとめ</h1>
+  <p class="subtitle">本研究での成果</p>
+  <dl>
+    <dt>成果1</dt>
+    <dd>CbCGCCがCとの相互利用も含むCbCのフルセットとして利用可能になった
+    <dt>成果2</dt>
+    <dd>CbCが多数のアーキテクチャに対応
+      <ul>
+        <li>20以上のアーキテクチャ</li>
+	<li>特に64bitのx86, SPUがうれしい</li>
+      </ul> </dd>
+    <dt>成果3</dt>
+    <dd>CbCの高速化
+      <ul>
+	<li>x86においてmicro-cと互角の速度を達成</li>
+	<li>PowerPCでは2倍の速度</li>
+      </ul></dd>
+  </dl>
+</div>
+
+<div class="slide">
+  <h1>今後の課題</h1>
+  <p class="subtitle"></p>
+  <ul>
+    <li>Real-time、組込み向けに実用的なCbCプログラムの例題が欲しい</li>
+    <li>タブロー方を用いた検証</li>
+    <li>TaskManagerのCbC実装</li>
+  </ul>
+  <p class="subtitle">CbC言語の今後</p>
+  <ul>
+    <li>オブジェクティブなCbCの設計</li>
+    <li>データセグメントの導入</li>
+    <li>スケジューラのためのリフレクション</li>
+  </ul>
+</div>
+
+
+<div class="slide">
+  <h1>おわり</h1>
+  <p class="subtitle">ありがとうございました</p>
+</div>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<div class="slide">
+  <h1>Continuation based C</h1>
+  <ul>
+    <li>言語仕様</li>
+    <li>return-callから軽量継続へ</li>
+    <li>コードセグメント</li>
+    <li>状態遷移に適した言語</li>
+    <li>Cとの互換性</li>
+  </ul>
+</div>
+
+
+<div class="slide">
+  <h1></h1>
+  <p class="subtitle"></p>
+  <ul>
+    <li></li>
+    <li></li>
+  </ul>
+</div>
+<div class="slide">
+  <h1></h1>
+  <p class="subtitle"></p>
+  <ul>
+    <li></li>
+    <li></li>
+  </ul>
+</div>
+
+<div class="slide">
+  <h1></h1>
+  <p class="subtitle"></p>
+  <ul>
+    <li></li>
+    <li></li>
+  </ul>
+</div>
+
+
+<div class="slide">
+  <h1>First: PowerPCでの間接継続</h1>
+  <p class="subtitle"></p>
+  <ul>
+    <li></li>
+  </ul>
+  <p class="subtitle"></p>
+  <div style="width:70%;margin:1em auto 0;">
+<pre><code>
+</code></pre>
+  </div>
+</div>
+
+<div class="slide">
+  <h1>継続制御での並列代入</h1>
+  <p class="subtitle" style="margin:0 1em 0.1em;">
+    本当に最適化で余分なコードが消えるのか?
+  </p>
+  <div style="width:45%;float:left;margin-left:1em;">
+  最適化しない場合
+<pre style="margin-top:0"><code> _test:
+    stwu r1,-64(r1)
+    mr r30,r1
+    stw r3,88(r30)
+    stw r4,92(r30)
+    stw r5,96(r30)
+    lwz r0,92(r30)
+    stw r0,32(r30)
+    lwz r0,96(r30)
+    addic r0,r0,1
+    stw r0,28(r30)
+    lwz r0,88(r30)
+    stw r0,24(r30)
+    lwz r3,32(r30)
+    lwz r4,28(r30)
+    lwz r5,24(r30)
+    addi r1,r30,64
+    lwz r30,-8(r1)
+    lwz r31,-4(r1)
+    b L_next$stub
+</code></pre>
+  </div>
+  <div style="width:45%;float:right;margin-right:1em;">
+  最適化した場合
+<pre><code>
+_test:
+    mr r0,r3
+    mr r3,r4
+    mr r4,r5
+    mr r5,r0
+    b L_next$stub
+</code></pre>
+  </div>
+  <div style="width:50%;float:right">
+  <ul>
+    <li>r3:=a, r4:=b, r5:=c</li>
+    <li>最適化しないとload, storeが満載</li>
+    <li>最適化すると無駄なload, store命令が消えている</li>
+    <li>実際はr0を使って4命令で入れ替えられる!</li>
+  </ul>
+  </div>
+</div>
+
+
+<div class="slide">
+  <h1>継続とはなんなのか?</h1>
+  <p class="subtitle">継続</p>
+  <ul>
+    <li>現在の処理を続行するための情報
+      <ul>
+        <li>Cならば続く命令のアドレスや</li>
+        <li>命令に必要な値、</li>
+        <li>スタックなど、その環境全てを含む</li>
+      </ul>
+    </li>
+  </ul>
+  <p class="subtitle">CbCでの軽量継続</p>
+  <ul>
+    <li>継続からスタックに関する情報を落とす</li>
+    <li>続く命令とデータのみのシンプルな継続</li>
+    <li>命令は<em>コードセグメント</em>、引数は<em>インタフェイス</em>と呼ばれる</li>
+  </ul>
+</div>
+
+<div class="slide" style="font-size:95%;">
+  <h1>コードセグメントと軽量継続の記述</h1>
+  <pre style="float:right; width-max:45%">
+<code>typedef code (*NEXT)(int);
+int main(int argc, char **argv) {
+  int i;
+  i = atoi(argv[1]);
+  goto factor(i, print_fact);
+}
+<em>code factor(int x, NEXT next)</em> {
+  goto factor0(1, x, next);
+}
+code factor0(int prod,int x,NEXT next) {
+  if (x &gt;= 1) {
+    goto factor0(prod*x, x-1, next);
+  } else {
+    <em>goto (*next)(prod);</em>
+  }
+}
+code print_fact(int value) {
+  printf("factorial = %d\n", value);
+  exit(0);
+} </code></pre>
+  <p class="subtitle">実際のプログラム記述は?</p>
+  <ul>
+    <li>コードセグメント定義
+    <ul>
+      <li><code>codeキーワードで宣言</code></li>
+      <li>書式は関数と同じ</li>
+    </ul>
+    </li>
+    <li>軽量継続制御
+    <ul>
+      <li><code>goto</code>キーワードと引数</li>
+      <li>コードセグメントの最初に飛ぶ</li>
+      <li>コードセグメントポインタによる間接継続も可能</li>
+    </ul>
+    </li>
+  </ul>
+</div>
+
+<div class="slide">
+  <h1>Cとの比較について</h1>
+  <p class="subtitle">quicksort例題をCと比較すると</p>
+  <ul>
+    <li>現在のところ、遅くなる</li>
+    <li>問題はquicksortという例題では必ずスタックが必要だということ</li>
+    <li>例題ではスタックを自前の構造体で用意している</li>
+    <li>そのため、ハードウェアで考慮されたスタックよりは遅い</li>
+    <li>状態遷移ベースの例題を作りたい</li>
+  </ul>
+</div>
+
+
+<div class="slide" style="font-size:95%;">
+  <h1>fastcall</h1>
+  <p class="subtitle">実際の出力アセンブラ</p>
+  <div style="width:50%;float:left;margin-left:auto;">
+    <p style="margin:0;text-align:center">fastcallにした場合</p>
+<pre><code>current:
+    subl    $12, %esp
+    movl    $30, 16(%esp)
+    movl    $20, %edx
+    movl    $10, %ecx
+    addl    $12, %esp
+    jmp     next
+</code></pre>
+  </div>
+  <div style="width:50%;float:right;margin-right:auto;">
+    <p style="margin:0;text-align:center">normalcallの場合</p>
+<pre><code>current:
+    pushl   %ebp
+    movl    %esp, %ebp
+    movl    $30, 16(%ebp)
+    movl    $20, 12(%ebp)
+    movl    $10, 8(%ebp)
+    leave
+    jmp     next
+</code></pre>
+  </div>
+  <br clear="all" />
+  <ul>
+    <li>命令数ではほとんど変化はない</li>
+    <li>引数2つがレジスタecxとedxに格納されるようになった</li>
+    <li>そのためメモリアクセスが減る</li>
+    <li>これで高速化されるはず</li>
+  </ul>
+</div>
+
+<div class="slide">
+  <h1>First: 性能評価(サイズ比較)</h1>
+  <p class="subtitle">ファイルサイズの比較</p>
+  <ul>
+    <li>組み込み系ではメモリ使用量が肝心</li>
+    <li>CbCGCCのサイズ最適化、速度最適化も対象とする</li>
+    <li>デバグ情報を付加しない、strip後のファイルサイズを比較</li>
+  </ul>
+  <p class="subtitle">結果</p>
+  <table>
+    <tr>
+      <td></td>
+      <th>CbCGCC<br/>速度最適化</th>
+      <th>CbCGCC<br/>サイズ最適化</th>
+      <th>micro-c</th>
+    </tr>
+    <tr>
+      <td>x86/OS X</td>
+      <td>9176</td>
+      <td>9176</td>
+      <td>9172</td>
+    </tr>
+    <tr>
+      <td>x86/Linux</td>
+      <td>5752</td>
+      <td>5752</td>
+      <td>5796</td>
+    </tr>
+    <tr>
+      <td>ppc/OS X</td>
+      <td>8576</td>
+      <td>8576</td>
+      <td>12664</td>
+    </tr>
+    <tr>
+      <td>ppc/Linux</td>
+      <td>10068</td>
+      <td>10068</td>
+      <td>9876</td>
+    </tr>
+    <tr>
+      <td>ppc/PS3</td>
+      <td>6960</td>
+      <td>6728</td>
+      <td>8636</td>
+    </tr>
+  </table>
+  <p class="subtitle">結果考察</p>
+  <ul>
+    <li>x86ではファイルサイズの差がない</li>
+    <li>ppcではOSによって違うが、OS Xでは3分の2に抑えることができている</li>
+    <li>サイズ最適化は必要ない、<em>速度最適化で充分</em></li>
+  </ul>
+</div>
+
+<div class="slide">
+  <h1>並列代入</h1>
+  <p class="subtitle">ある条件で末尾呼出が行われなくなる</p>
+  <ol>
+    <li><del>呼出先関数の全引数が占めるスタックサイズが、呼出元関数のそれより大きい場合</del> <em class="weak">解決済み</em></li>
+    <li><em>引数を順にスタックに格納すると、書き込み前のデータが上が着されてしまう場合</em></li>
+  </ol>
+  <p class="subtitle">問題となる例</p>
+<pre><code>code somesegment(int a, int b, int c) {
+  /∗ do something ∗/
+  goto nextsegment(b, c, a);
+}
+</code></pre>
+  <ul>
+    <li><code>(a,b,c) = (b,c,a)</code>と本質的に同じ。これが<dfn>並列代入</dfn></li>
+    <li><code>a=b,b=c,c=a</code>ではだめ。aの値が失われる</li>
+    <li>必ず一つ(1ワード)以上の一時変数が必要になる</li>
+  </ul>
+  <p class="subtitle">次の様に構文木を変更する</p>
+<pre><code>code somesegment(int a, int b, int c) {
+  int a1, b1, c1;
+  /∗ do something ∗/
+  a1=a; b1=b; c1=c;
+  goto nextsegment(b1, c1, a1);
+}
+</code></pre>
+  <ul>
+    <li>これにより、引数順序を考える必要はなくなる</li>
+    <li>代わりに、メモリアクセスが大量に発生</li>
+    <li>しかし、これはGCCの最適化で除去される</li>
+  </ul>
+</div>
+
+
+
+</body>
+</html>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/recital-slide/slidy.css	Tue Feb 16 17:25:21 2010 +0900
@@ -0,0 +1,317 @@
+/* slidy.css
+
+   Copyright (c) 2005 W3C (MIT, ERCIM, Keio), All Rights Reserved.
+   W3C liability, trademark, document use and software licensing
+   rules apply, see:
+
+   http://www.w3.org/Consortium/Legal/copyright-documents
+   http://www.w3.org/Consortium/Legal/copyright-software
+*/
+body
+{
+  margin: 0 0 0 0;
+  padding: 0 0 0 0;
+  width: 100%;
+  height: 100%;
+  color: black;
+  background-color: white;
+  font-family: "Gill Sans MT", "Gill Sans", GillSans, sans-serif;
+  font-size: 14pt;
+}
+
+.hidden { display: none; visibility: hidden }
+
+div.toolbar {
+  position: fixed; z-index: 200;
+  top: auto; bottom: 0; left: 0; right: 0;
+  height: 1.2em; text-align: right;
+  padding-left: 1em;
+  padding-right: 1em; 
+  font-size: 60%;
+  color: red; background: rgb(240,240,240);
+}
+
+div.background {
+  display: none;
+}
+
+div.handout {
+  margin-left: 20px;
+  margin-right: 20px;
+}
+
+div.slide.titlepage {
+  text-align: center;
+}
+
+div.slide.titlepage.h1 {
+  padding-top: 40%;
+}
+
+div.slide {
+  z-index: 20;
+  margin: 0 0 0 0;
+  padding-top: 0;
+  padding-bottom: 0;
+  padding-left: 20px;
+  padding-right: 20px;
+  border-width: 0;
+  top: 0;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  line-height: 120%;
+  background-color: transparent;
+}
+
+/* this rule is hidden from IE 6 and below which don't support + selector */
+div.slide + div[class].slide { page-break-before: always;}
+
+div.slide h1 {
+  padding-left: 0;
+  padding-right: 20pt;
+  padding-top: 4pt;
+  padding-bottom: 4pt;
+  margin-top: 0;
+  margin-left: 0;
+  margin-right: 60pt;
+  margin-bottom: 0.5em;
+  display: block; 
+  font-size: 160%;
+  line-height: 1.2em;
+  background: transparent;
+}
+
+div.toc {
+  position: absolute;
+  top: auto;
+  bottom: 4em;
+  left: 4em;
+  right: auto;
+  width: 60%;
+  max-width: 30em;
+  height: 30em;
+  border: solid thin black;
+  padding: 1em;
+  background: rgb(240,240,240);
+  color: black;
+  z-index: 300;
+  overflow: auto;
+  display: block;
+  visibility: visible;
+}
+
+div.toc-heading {
+  width: 100%;
+  border-bottom: solid 1px rgb(180,180,180);
+  margin-bottom: 1em;
+  text-align: center;
+}
+
+pre {
+ font-size: 80%;
+ font-weight: bold;
+ line-height: 120%;
+ padding-top: 0.2em;
+ padding-bottom: 0.2em;
+ padding-left: 1em;
+ padding-right: 1em;
+ border-style: solid;
+ border-left-width: 1em;
+ border-top-width: thin;
+ border-right-width: thin;
+ border-bottom-width: thin;
+ border-color: #95ABD0;
+ color: #00428C;
+ background-color: #E4E5E7;
+}
+
+li pre { margin-left: 0; }
+
+@media print {
+  div.slide {
+     display: block;
+     visibility: visible;
+     position: relative;
+     border-top-style: solid;
+     border-top-width: thin;
+     border-top-color: black;
+  }
+  div.slide pre { font-size: 60%; padding-left: 0.5em; }
+  div.handout { display: block; visibility: visible; }
+}
+
+blockquote { font-style: italic }
+
+img { background-color: transparent }
+
+p.copyright { font-size: smaller }
+
+.center { text-align: center }
+.footnote { font-size: smaller; margin-left: 2em; }
+
+a img { border-width: 0; border-style: none }
+
+a:visited { color: navy }
+a:link { color: navy }
+a:hover { color: red; text-decoration: underline }
+a:active { color: red; text-decoration: underline }
+
+a {text-decoration: none}
+.navbar a:link {color: white}
+.navbar a:visited {color: yellow}
+.navbar a:active {color: red}
+.navbar a:hover {color: red}
+
+ul { list-style-type: square; }
+ul ul { list-style-type: disc; }
+ul ul ul { list-style-type: circle; }
+ul ul ul ul { list-style-type: disc; }
+li { margin-left: 0.5em; margin-top: 0.5em; }
+li li { font-size: 85%; font-style: italic }
+li li li { font-size: 85%; font-style: normal }
+
+div dt
+{
+  margin-left: 0;
+  margin-top: 1em;
+  margin-bottom: 0.5em;
+  font-weight: bold;
+}
+div dd
+{
+  margin-left: 2em;
+  margin-bottom: 0.5em;
+}
+
+
+p,pre,ul,ol,blockquote,h2,h3,h4,h5,h6,dl,table {
+  margin-left: 1em;
+  margin-right: 1em;
+}
+
+p.subhead { font-weight: bold; margin-top: 2em; }
+
+.smaller { font-size: smaller }
+.bigger { font-size: 130% }
+
+td,th { padding: 0.2em }
+
+ul {
+  margin: 0.5em 1.5em 0.5em 1.5em;
+  padding: 0;
+}
+
+ol {
+  margin: 0.5em 1.5em 0.5em 1.5em;
+  padding: 0;
+}
+
+ul { list-style-type: square; }
+ul ul { list-style-type: disc; }
+ul ul ul { list-style-type: circle; }
+ul ul ul ul { list-style-type: disc; }
+
+ul li { 
+  list-style: square;
+  margin: 0.1em 0em 0.6em 0;
+  padding: 0 0 0 0;
+  line-height: 140%;
+}
+
+ol li { 
+  margin: 0.1em 0em 0.6em 1.5em;
+  padding: 0 0 0 0px;
+  line-height: 140%;
+  list-style-type: decimal;
+}
+
+li ul li { 
+  font-size: 85%; 
+  font-style: italic;
+  list-style-type: disc;
+  background: transparent;
+  padding: 0 0 0 0;
+}
+li li ul li { 
+  font-size: 85%; 
+  font-style: normal;
+  list-style-type: circle;
+  background: transparent;
+  padding: 0 0 0 0;
+}
+li li li ul li {
+  list-style-type: disc;
+  background: transparent;
+  padding: 0 0 0 0;
+}
+
+li ol li {
+  list-style-type: decimal;
+}
+
+
+li li ol li {
+  list-style-type: decimal;
+}
+
+/*
+ setting class="outline on ol or ul makes it behave as an
+ ouline list where blocklevel content in li elements is
+ hidden by default and can be expanded or collapsed with
+ mouse click. Set class="expand" on li to override default
+*/
+
+ol.outline li:hover { cursor: pointer }
+ol.outline li.nofold:hover { cursor: default }
+
+ul.outline li:hover { cursor: pointer }
+ul.outline li.nofold:hover { cursor: default }
+
+ol.outline { list-style:decimal; }
+ol.outline ol { list-style-type:lower-alpha }
+
+ol.outline li.nofold {
+  padding: 0 0 0 20px;
+  background: transparent url(nofold-dim.gif) no-repeat 0px 0.5em;
+}
+ol.outline li.unfolded {
+  padding: 0 0 0 20px;
+  background: transparent url(fold-dim.gif) no-repeat 0px 0.5em;
+}
+ol.outline li.folded {
+  padding: 0 0 0 20px;
+  background: transparent url(unfold-dim.gif) no-repeat 0px 0.5em;
+}
+ol.outline li.unfolded:hover {
+  padding: 0 0 0 20px;
+  background: transparent url(fold.gif) no-repeat 0px 0.5em;
+}
+ol.outline li.folded:hover {
+  padding: 0 0 0 20px;
+  background: transparent url(unfold.gif) no-repeat 0px 0.5em;
+}
+
+ul.outline li.nofold {
+  padding: 0 0 0 20px;
+  background: transparent url(nofold-dim.gif) no-repeat 0px 0.5em;
+}
+ul.outline li.unfolded {
+  padding: 0 0 0 20px;
+  background: transparent url(fold-dim.gif) no-repeat 0px 0.5em;
+}
+ul.outline li.folded {
+  padding: 0 0 0 20px;
+  background: transparent url(unfold-dim.gif) no-repeat 0px 0.5em;
+}
+ul.outline li.unfolded:hover {
+  padding: 0 0 0 20px;
+  background: transparent url(fold.gif) no-repeat 0px 0.5em;
+}
+ul.outline li.folded:hover {
+  padding: 0 0 0 20px;
+  background: transparent url(unfold.gif) no-repeat 0px 0.5em;
+}
+
+/* for slides with class "title" in table of contents */
+a.titleslide { font-weight: bold; font-style: italic }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/recital-slide/slidy.js	Tue Feb 16 17:25:21 2010 +0900
@@ -0,0 +1,2949 @@
+/* slidy.js
+
+   Copyright (c) 2005-2009 W3C (MIT, ERCIM, Keio), All Rights Reserved.
+   W3C liability, trademark, document use and software licensing
+   rules apply, see:
+
+   http://www.w3.org/Consortium/Legal/copyright-documents
+   http://www.w3.org/Consortium/Legal/copyright-software
+*/
+
+var ns_pos = (typeof window.pageYOffset!='undefined');
+var khtml = ((navigator.userAgent).indexOf("KHTML") >= 0 ? true : false);
+var opera = ((navigator.userAgent).indexOf("Opera") >= 0 ? true : false);
+var ie = (typeof document.all != "undefined" && !opera);
+var ie7 = (!ns_pos && navigator.userAgent.indexOf("MSIE 7") != -1);
+var ie8 = (!ns_pos && navigator.userAgent.indexOf("MSIE 8") != -1);
+var slidy_started = false;
+
+// added by kent.
+var showspeak = false;
+
+if (ie && !ie8)
+  document.write("<iframe id='historyFrame' src='javascript:\"<html"+"></"+"html>\"' height='1' width='1' style='position:absolute;left:-800px'></iframe>");
+
+// IE only event handlers to ensure all slides are printed
+// I don't yet know how to emulate these for other browsers
+if (typeof beforePrint != 'undefined')
+{
+  window.onbeforeprint = beforePrint;
+  window.onafterprint = afterPrint;
+}
+
+// to avoid a clash with other scripts or onload attribute on <body>
+// we use something smarter than window.onload
+//window.onload = startup;
+
+
+if (ie)
+  setTimeout(ieSlidyInit, 100);
+else if (document.addEventListener)
+  document.addEventListener("DOMContentLoaded", startup, false);
+
+function ieSlidyInit()
+{
+  if (//document.readyState == "interactive" ||
+      document.readyState == "complete" ||
+      document.readyState == "loaded")
+  {
+    startup();
+  }
+  else
+  {
+    setTimeout(ieSlidyInit, 100);
+  }
+}
+
+setTimeout(hideSlides, 50);
+
+function hideSlides()
+{
+  if (document.body)
+    document.body.style.visibility = "hidden";
+  else
+    setTimeout(hideSlides, 50);
+}
+
+var slidenum = 0;     // integer slide count: 0, 1, 2, ...
+var slides;           // set to array of slide div's
+var slideNumElement;  // element containing slide number
+var notes;            // set to array of handout div's
+var backgrounds;      // set to array of background div's
+var toolbar;          // element containing toolbar
+var title;            // document title
+var lastShown = null; // last incrementally shown item
+var eos = null;       // span element for end of slide indicator
+var toc = null;       // table of contents
+var outline = null;   // outline element with the focus
+var selectedTextLen;  // length of drag selection on document
+
+var viewAll = 0;      // 1 to view all slides + handouts
+var wantToolbar = 1;   // 0 if toolbar isn't wanted
+var mouseClickEnabled = true;  // enables left click for next slide
+var scrollhack = 0;   // IE work around for position: fixed
+
+var helpAnchor;  // used for keyboard focus hack in showToolbar()
+var helpPage = "http://www.w3.org/Talks/Tools/Slidy/help.html";
+var helpText = "Navigate with mouse click, space bar, Cursor Left/Right, " +
+               "or Pg Up and Pg Dn. Use S and B to change font size.";
+
+var sizeIndex = 0;
+var sizeAdjustment = 0;
+var sizes = new Array("10pt", "12pt", "14pt", "16pt", "18pt", "20pt",
+                      "22pt", "24pt", "26pt", "28pt", "30pt", "32pt");
+var okayForIncremental = incrementalElementList();
+
+// needed for efficient resizing
+var lastWidth = 0;
+var lastHeight = 0;
+
+// Needed for cross browser support for relative width/height on
+// object elements. The work around is to save width/height attributes
+// and then to recompute absolute width/height dimensions on resizing
+var objects;
+
+// updated to language specified by html file
+var lang = "en";
+
+//var localize = {};
+
+// for each language there is an associative array
+var strings_es = {
+  "slide":"pág.",
+  "help?":"Ayuda",
+  "contents?":"Índice",
+  "table of contents":"tabla de contenidos",
+  "Table of Contents":"Tabla de Contenidos",
+  "restart presentation":"Reiniciar presentación",
+  "restart?":"Inicio"
+   };
+
+strings_es[helpText] =
+    "Utilice el ratón, barra espaciadora, teclas Izda/Dcha, " +
+    "o Re pág y Av pág. Use S y B para cambiar el tamaño de fuente.";
+
+var strings_ca = {
+ "slide":"pàg..",
+ "help?":"Ajuda",
+ "contents?":"Índex",
+ "table of contents":"taula de continguts",
+ "Table of Contents":"Taula de Continguts",
+ "restart presentation":"Reiniciar presentació",
+ "restart?":"Inici"
+  };
+
+strings_ca[helpText] =
+   "Utilitzi el ratolí, barra espaiadora, tecles Esq./Dta. " +
+   "o Re pàg y Av pàg. Usi S i B per canviar grandària de font.";
+
+var strings_nl = {
+  "slide":"pagina",
+  "help?":"Help?",
+  "contents?":"Inhoud?",
+  "table of contents":"inhoudsopgave",
+  "Table of Contents":"Inhoudsopgave",
+  "restart presentation":"herstart presentatie",
+  "restart?":"Herstart?"
+   };
+
+strings_nl[helpText] =
+    "Navigeer d.m.v. het muis, spatiebar, Links/Rechts toetsen, " +
+    "of PgUp en PgDn. Gebruik S en B om de karaktergrootte te veranderen.";
+
+var strings_de = {
+  "slide":"Seite",
+  "help?":"Hilfe",
+  "contents?":"Übersicht",
+  "table of contents":"Inhaltsverzeichnis",
+  "Table of Contents":"Inhaltsverzeichnis",
+  "restart presentation":"Präsentation neu starten",
+  "restart?":"Neustart"
+   };
+
+strings_de[helpText] =
+    "Benutzen Sie die Maus, Leerschlag, die Cursortasten links/rechts oder " +
+    "Page up/Page Down zum Wechseln der Seiten und S und B für die Schriftgrösse.";
+
+var strings_pl = {
+  "slide":"slajd",
+  "help?":"pomoc?",
+  "contents?":"spis treści?",
+  "table of contents":"spis treści",
+  "Table of Contents":"Spis Treści",
+  "restart presentation":"Restartuj prezentację",
+  "restart?":"restart?"
+   };
+
+strings_pl[helpText] =
+    "Zmieniaj slajdy klikając myszą, naciskając spację, strzałki lewo/prawo" +
+    "lub PgUp / PgDn. Użyj klawiszy S i B, aby zmienić rozmiar czczionki.";
+
+var strings_fr = {
+  "slide":"page",
+  "help?":"Aide",
+  "contents?":"Index",
+  "table of contents":"table des matières",
+  "Table of Contents":"Table des matières",
+  "restart presentation":"Recommencer l'exposé",
+  "restart?":"Début"
+  };
+
+strings_fr[helpText] =
+    "Naviguez avec la souris, la barre d'espace, les flèches " +
+    "gauche/droite ou les touches Pg Up, Pg Dn. Utilisez " +
+    "les touches S et B pour modifier la taille de la police.";
+
+var strings_hu = {
+  "slide":"oldal",
+  "help?":"segítség",
+  "contents?":"tartalom",
+  "table of contents":"tartalomjegyzék",
+  "Table of Contents":"Tartalomjegyzék",
+  "restart presentation":"bemutató újraindítása",
+  "restart?":"újraindítás"
+   };
+
+strings_hu[helpText] =
+    "Az oldalak közti lépkedéshez kattintson az egérrel, vagy " +
+    "használja a szóköz, a bal, vagy a jobb nyíl, illetve a Page Down, " +
+    "Page Up billentyűket. Az S és a B billentyűkkel változtathatja " +
+    "a szöveg méretét.";
+
+var strings_it = {
+  "slide":"pag.",
+  "help?":"Aiuto",
+  "contents?":"Indice",
+  "table of contents":"indice",
+  "Table of Contents":"Indice",
+  "restart presentation":"Ricominciare la presentazione",
+  "restart?":"Inizio"
+   };
+
+strings_it[helpText] =
+    "Navigare con mouse, barra spazio, frecce sinistra/destra o " +
+    "PgUp e PgDn. Usare S e B per cambiare la dimensione dei caratteri.";
+
+var strings_el = {
+  "slide":"σελίδα",
+  "help?":"βοήθεια;",
+  "contents?":"περιεχόμενα;",
+  "table of contents":"πίνακας περιεχομένων",
+  "Table of Contents":"Πίνακας Περιεχομένων",
+  "restart presentation":"επανεκκίνηση παρουσίασης",
+  "restart?":"επανεκκίνηση;"
+   };
+
+strings_el[helpText] =
+  "Πλοηγηθείτε με το κλίκ του ποντικιού, το space, τα βέλη αριστερά/δεξιά, " +
+  "ή Page Up και Page Down. Χρησιμοποιήστε τα πλήκτρα S και B για να αλλάξετε " +
+  "το μέγεθος της γραμματοσειράς.";
+
+var strings_ja = {
+  "slide":"スライド",
+  "help?":"ヘルプ",
+  "contents?":"目次",
+  "table of contents":"目次を表示",
+  "Table of Contents":"目次",
+  "restart presentation":"最初から再生",
+  "restart?":"最初から"
+};
+
+strings_ja[helpText] =
+    "マウス左クリック ・ スペース ・ 左右キー " +
+    "または Page Up ・ Page Downで操作, S ・ Bでフォントサイズ変更";
+
+var strings_zh = {
+  "slide":"幻灯片",
+  "help?":"帮助?",
+  "contents?":"内容?",
+  "table of contents":"目录",
+  "Table of Contents":"目录",
+  "restart presentation":"重新启动展示",
+  "restart?":"重新启动?"
+};
+
+strings_zh[helpText] =
+  "用鼠标点击, 空格条, 左右箭头, Pg Up 和 Pg Dn 导航. " +
+  "用 S, B 改变字体大小.";
+
+var strings_ru = {
+  "slide":"слайд",
+  "help?":"помощь?",
+  "contents?":"содержание?",
+  "table of contents":"оглавление",
+  "Table of Contents":"Оглавление",
+  "restart presentation":"перезапустить презентацию",
+  "restart?":"перезапуск?"
+   };
+
+strings_ru[helpText] =
+    "Перемещайтесь кликая мышкой, используя клавишу пробел, стрелки" +
+    "влево/вправо или Pg Up и Pg Dn. Клавиши S и B меняют размер шрифта.";
+
+
+// each such language array is declared in the localize array
+// used indirectly as in help.innerHTML = "help".localize();
+var localize = {
+     "es":strings_es,
+     "ca":strings_ca,
+     "nl":strings_nl,
+     "de":strings_de,
+     "pl":strings_pl,
+     "fr":strings_fr,
+     "hu":strings_hu,
+     "it":strings_it,
+     "el":strings_el,
+     "jp":strings_ja,
+     "zh":strings_zh,
+     "ru":strings_ru
+   };
+
+/* general initialization */
+function startup()
+{
+   if (slidy_started)
+   {
+      alert("already started");
+      return;
+   }
+   slidy_started = true;
+
+   // find human language from html element
+   // for use in localizing strings
+   lang = document.body.parentNode.getAttribute("lang");
+
+   if (!lang)
+     lang = document.body.parentNode.getAttribute("xml:lang");
+
+   if (!lang)
+     lang = "en";
+
+   document.body.style.visibility = "visible";
+   title = document.title;
+   toolbar = addToolbar();
+   wrapImplicitSlides();
+   slides = collectSlides();
+   notes = collectNotes();
+   objects = document.body.getElementsByTagName("object");
+   backgrounds = collectBackgrounds();
+   patchAnchors();
+
+   slidenum = findSlideNumber(location.href);
+   window.offscreenbuffering = true;
+   sizeAdjustment = findSizeAdjust();
+   hideImageToolbar();  // suppress IE image toolbar popup
+   initOutliner();  // activate fold/unfold support
+
+   if (slides.length > 0)
+   {
+      var slide = slides[slidenum];
+      slide.style.position = "absolute";
+   
+      if (slidenum > 0)
+      {
+         setVisibilityAllIncremental("visible");
+         lastShown = previousIncrementalItem(null);
+         setEosStatus(true);
+      }
+      else
+      {
+         lastShown = null;
+         setVisibilityAllIncremental("hidden");
+         setEosStatus(!nextIncrementalItem(lastShown));
+      }
+
+      setLocation();
+   }
+
+   toc = tableOfContents();
+   hideTableOfContents();
+
+   // bind event handlers
+   document.onclick = mouseButtonClick;
+   document.onmouseup = mouseButtonUp;
+   document.onkeydown = keyDown;
+   window.onresize  = resized;
+   window.onscroll = scrolled;
+   window.onunload = unloaded;
+   singleSlideView();
+
+
+   setLocation();
+   resized();
+
+   if (ie7)
+     setTimeout("ieHack()", 100);
+
+   showToolbar();
+   setInterval("checkLocation()", 200); // for back button detection
+}
+
+// add localize method to all strings for use
+// as in help.innerHTML = "help".localize();
+String.prototype.localize = function()
+{
+  if (this == "")
+    return this;
+
+  // try full language code, e.g. en-US
+  var s, lookup = localize[lang];
+
+  if (lookup)
+  {
+    s = lookup[this];
+
+    if (s)
+      return s;
+  }
+
+  // try en if undefined for en-US
+  var lg = lang.split("-");
+
+  if (lg.length > 1)
+  {
+    lookup = localize[lg[0]];
+
+    if (lookup)
+    {
+      s = lookup[this];
+
+      if (s)
+        return s;
+    }
+  }
+
+  // otherwise string as is
+  return this;
+}
+
+// suppress IE's image toolbar pop up
+function hideImageToolbar()
+{
+  if (!ns_pos)
+  {
+    var images = document.getElementsByTagName("IMG");
+
+    for (var i = 0; i < images.length; ++i)
+      images[i].setAttribute("galleryimg", "no");
+  }
+}
+
+// hack to persuade IE to compute correct document height
+// as needed for simulating fixed positioning of toolbar
+function ieHack()
+{
+   window.resizeBy(0,-1);
+   window.resizeBy(0, 1);
+}
+
+function unloaded(e)
+{
+  //alert("unloaded");
+}
+
+// Firefox reload SVG bug work around
+function reload(e)
+{
+   if (!e)
+      var e = window.event;
+
+   hideBackgrounds();
+   setTimeout("document.reload();", 100);
+
+   stopPropagation(e);
+   e.cancel = true;
+   e.returnValue = false;
+
+   return false;
+}
+
+// Safari and Konqueror don't yet support getComputedStyle()
+// and they always reload page when location.href is updated
+function isKHTML()
+{
+   var agent = navigator.userAgent;
+   return (agent.indexOf("KHTML") >= 0 ? true : false);
+}
+
+function resized()
+{
+   var width = 0;
+
+   if ( typeof( window.innerWidth ) == 'number' )
+      width = window.innerWidth;  // Non IE browser
+   else if (document.documentElement && document.documentElement.clientWidth)
+      width = document.documentElement.clientWidth;  // IE6
+   else if (document.body && document.body.clientWidth)
+      width = document.body.clientWidth; // IE4
+
+   var height = 0;
+
+   if ( typeof( window.innerHeight ) == 'number' )
+      height = window.innerHeight;  // Non IE browser
+   else if (document.documentElement && document.documentElement.clientHeight)
+      height = document.documentElement.clientHeight;  // IE6
+   else if (document.body && document.body.clientHeight)
+      height = document.body.clientHeight; // IE4
+
+   if (height && (width/height > 1.05*1024/768))
+   {
+     width = height * 1024.0/768;
+   }
+
+   // IE fires onresize even when only font size is changed!
+   // so we do a check to avoid blocking < and > actions
+   if (width != lastWidth || height != lastHeight)
+   {
+      if (width >= 1100)
+         sizeIndex = 5;    // 4
+      else if (width >= 1000)
+         sizeIndex = 4;    // 3
+      else if (width >= 800)
+         sizeIndex = 3;    // 2
+      else if (width >= 600)
+         sizeIndex = 2;    // 1
+      else if (width)
+         sizeIndex = 0;
+
+      // add in font size adjustment from meta element e.g.
+      // <meta name="font-size-adjustment" content="-2" />
+      // useful when slides have too much content ;-)
+
+      if (0 <= sizeIndex + sizeAdjustment &&
+             sizeIndex + sizeAdjustment < sizes.length)
+        sizeIndex = sizeIndex + sizeAdjustment;
+
+      // enables cross browser use of relative width/height
+      // on object elements for use with SVG and Flash media
+      adjustObjectDimensions(width, height);
+
+      document.body.style.fontSize = sizes[sizeIndex];
+
+      lastWidth = width;
+      lastHeight = height;
+
+      // force reflow to work around Mozilla bug
+      //if (ns_pos)
+      {
+         var slide = slides[slidenum];
+         hideSlide(slide);
+         showSlide(slide);
+      }
+
+      // force correct positioning of toolbar
+      refreshToolbar(200);
+   }
+}
+
+function scrolled()
+{
+   if (toolbar && !ns_pos && !ie7)
+   {
+      hackoffset = scrollXOffset();
+      // hide toolbar
+      toolbar.style.display = "none";
+
+      // make it reappear later
+      if (scrollhack == 0 && !viewAll)
+      {
+         setTimeout(showToolbar, 1000);
+         scrollhack = 1;
+      }
+   }
+}
+
+// used to ensure IE refreshes toolbar in correct position
+function refreshToolbar(interval)
+{
+   if (!ns_pos && !ie7)
+   {
+     hideToolbar();
+     setTimeout(showToolbar, interval);
+   }
+}
+
+// restores toolbar after short delay
+function showToolbar()
+{
+   if (wantToolbar)
+   {
+      if (!ns_pos)
+      {
+         // adjust position to allow for scrolling
+         var xoffset = scrollXOffset();
+         toolbar.style.left = xoffset;
+         toolbar.style.right = xoffset;
+
+         // determine vertical scroll offset
+         //var yoffset = scrollYOffset();
+
+         // bottom is doc height - window height - scroll offset
+         //var bottom = documentHeight() - lastHeight - yoffset
+
+         //if (yoffset > 0 || documentHeight() > lastHeight)
+         //   bottom += 16;  // allow for height of scrollbar
+
+         toolbar.style.bottom = 0; //bottom;
+      }
+
+      toolbar.style.display = "block";
+      toolbar.style.visibility = "visible";
+   }
+
+   scrollhack = 0;
+
+
+   // set the keyboard focus to the help link on the
+   // toolbar to ensure that document has the focus
+   // IE doesn't always work with window.focus()
+   // and this hack has benefit of Enter for help
+
+   try
+   {
+     if (!opera)
+       helpAnchor.focus();
+   }
+   catch (e)
+   {
+   }
+}
+
+function hideToolbar()
+{
+   toolbar.style.display = "none";
+   toolbar.style.visibility = "hidden";
+   window.focus();
+}
+
+// invoked via F key
+function toggleToolbar()
+{
+   if (!viewAll)
+   {
+      if (toolbar.style.display == "none")
+      {
+         toolbar.style.display = "block";
+         toolbar.style.visibility = "visible";
+         wantToolbar = 1;
+      }
+      else
+      {
+         toolbar.style.display = "none";
+         toolbar.style.visibility = "hidden";
+         wantToolbar = 0;
+      }
+   }
+}
+
+function scrollXOffset()
+{
+   if (window.pageXOffset)
+      return self.pageXOffset;
+
+   if (document.documentElement && 
+             document.documentElement.scrollLeft)
+      return document.documentElement.scrollLeft;
+
+   if (document.body)
+      return document.body.scrollLeft;
+
+    return 0;
+}
+
+
+function scrollYOffset()
+{
+   if (window.pageYOffset)
+      return self.pageYOffset;
+
+   if (document.documentElement && 
+             document.documentElement.scrollTop)
+      return document.documentElement.scrollTop;
+
+   if (document.body)
+      return document.body.scrollTop;
+
+    return 0;
+}
+
+// looking for a way to determine height of slide content
+// the slide itself is set to the height of the window
+function optimizeFontSize()
+{
+   var slide = slides[slidenum];
+
+   //var dh = documentHeight(); //getDocHeight(document);
+   var dh = slide.scrollHeight;
+   var wh = getWindowHeight();
+   var u = 100 * dh / wh;
+
+   alert("window utilization = " + u + "% (doc "
+      + dh + " win " + wh + ")");
+}
+
+function getDocHeight(doc) // from document object
+{
+  if (!doc)
+    doc = document;
+
+  if (doc && doc.body && doc.body.offsetHeight)
+    return doc.body.offsetHeight;  // ns/gecko syntax
+
+  if (doc && doc.body && doc.body.scrollHeight)
+    return doc.body.scrollHeight;
+
+  alert("couldn't determine document height");
+}
+
+function getWindowHeight()
+{
+  if ( typeof( window.innerHeight ) == 'number' )
+    return window.innerHeight;  // Non IE browser
+
+  if (document.documentElement && document.documentElement.clientHeight)
+    return document.documentElement.clientHeight;  // IE6
+
+  if (document.body && document.body.clientHeight)
+    return document.body.clientHeight; // IE4
+}
+
+
+
+function documentHeight()
+{
+   var sh, oh;
+
+   sh = document.body.scrollHeight;
+   oh = document.body.offsetHeight;
+
+   if (sh && oh)
+   {
+      return (sh > oh ? sh : oh);
+   }
+
+   // no idea!
+   return 0;
+}
+
+function smaller()
+{
+   if (sizeIndex > 0)
+   {
+      --sizeIndex;
+   }
+
+   toolbar.style.display = "none";
+   document.body.style.fontSize = sizes[sizeIndex];
+   var slide = slides[slidenum];
+   hideSlide(slide);
+   showSlide(slide);
+   setTimeout(showToolbar, 300);
+}
+
+function bigger()
+{
+   if (sizeIndex < sizes.length - 1)
+   {
+      ++sizeIndex;
+   }
+
+   toolbar.style.display = "none";
+   document.body.style.fontSize = sizes[sizeIndex];
+   var slide = slides[slidenum];
+   hideSlide(slide);
+   showSlide(slide);
+   setTimeout(showToolbar, 300);
+}
+
+// enables cross browser use of relative width/height
+// on object elements for use with SVG and Flash media
+// with thanks to Ivan Herman for the suggestion
+function adjustObjectDimensions(width, height)
+{
+   for( var i = 0; i < objects.length; i++ )
+   {
+      var obj = objects[i];
+      var mimeType = obj.getAttribute("type");
+
+      if (mimeType == "image/svg+xml" || mimeType == "application/x-shockwave-flash")
+      {
+         if ( !obj.initialWidth ) 
+            obj.initialWidth = obj.getAttribute("width");
+
+         if ( !obj.initialHeight ) 
+            obj.initialHeight = obj.getAttribute("height");
+
+         if ( obj.initialWidth && obj.initialWidth.charAt(obj.initialWidth.length-1) == "%" )
+         {
+            var w = parseInt(obj.initialWidth.slice(0, obj.initialWidth.length-1));
+            var newW = width * (w/100.0);
+            obj.setAttribute("width",newW);
+         }
+
+         if ( obj.initialHeight && obj.initialHeight.charAt(obj.initialHeight.length-1) == "%" )
+         {
+            var h = parseInt(obj.initialHeight.slice(0, obj.initialHeight.length-1));
+            var newH = height * (h/100.0);
+            obj.setAttribute("height", newH);
+         }
+      }
+   }
+}
+
+function cancel(event)
+{
+  if (event)
+  {
+     event.cancel = true;
+     event.returnValue = false;
+
+    if (event.preventDefault)
+      event.preventDefault();
+  }
+
+  return false;
+}
+
+//  See e.g. http://www.quirksmode.org/js/events/keys.html for keycodes
+function keyDown(event)
+{
+    var key;
+
+    if (!event)
+      var event = window.event;
+
+    // kludge around NS/IE differences 
+    if (window.event)
+       key = window.event.keyCode;
+    else if (event.which)
+       key = event.which;
+    else
+       return true; // Yikes! unknown browser
+
+    // ignore event if key value is zero
+    // as for alt on Opera and Konqueror
+    if (!key)
+       return true;
+
+    // check for concurrent control/command/alt key
+    // but are these only present on mouse events?
+
+    if (event.ctrlKey || event.altKey || event.metaKey)
+       return true;
+
+    // dismiss table of contents if visible
+    if (isShownToc() && key != 9 && key != 16 && key != 38 && key != 40)
+    {
+      hideTableOfContents();
+
+      if (key == 27 || key == 84 || key == 67)
+        return cancel(event);
+    }
+
+    if (key == 34) // Page Down
+    {
+       if (viewAll)
+         return true;
+
+       nextSlide(false);
+       return cancel(event);
+    }
+    else if (key == 33) // Page Up
+    {
+       if (viewAll)
+         return true;
+
+       previousSlide(false);
+       return cancel(event);
+    }
+    else if (key == 32) // space bar
+    {
+       nextSlide(true);
+       return cancel(event);
+    }
+    else if (key == 37) // Left arrow
+    {
+       previousSlide(!event.shiftKey);
+       return cancel(event);
+    }
+    else if (key == 36) // Home
+    {
+       firstSlide();
+       return cancel(event);
+    }
+    else if (key == 35) // End
+    {
+       lastSlide();
+       return cancel(event);
+    }
+    else if (key == 39) // Right arrow
+    {
+       nextSlide(!event.shiftKey);
+       return cancel(event);
+    }
+    else if (key == 13) // Enter
+    {
+       if (outline)
+       {
+          if (outline.visible)
+            fold(outline);
+          else
+            unfold(outline);
+          
+         return cancel(event);
+       }
+    }
+    else if (key == 188)  // < for smaller fonts
+    {
+       smaller();
+       return cancel(event);
+    }
+    else if (key == 190)  // > for larger fonts
+    {
+       bigger();
+       return cancel(event);
+    }
+    else if (key == 189 || key == 109)  // - for smaller fonts
+    {
+       smaller();
+       return cancel(event);
+    }
+    else if (key == 187 || key == 191 || key == 107)  // = +  for larger fonts
+    {
+       bigger();
+       return cancel(event);
+    }
+    else if (key == 83)  // S for smaller fonts
+    {
+       smaller();
+       return cancel(event);
+    }
+    else if (key == 66)  // B for larger fonts
+    {
+       bigger();
+       return cancel(event);
+    }
+    else if (key == 90)  // Z for last slide
+    {
+       lastSlide();
+       return cancel(event);
+    }
+    else if (key == 70)  // F for toggle toolbar
+    {
+       toggleToolbar();
+       return cancel(event);
+    }
+    else if (key == 65)  // A for toggle view single/all slides
+    {
+       toggleView();
+       return cancel(event);
+    }
+    else if (key == 75)  // toggle action of left click for next page
+    {
+       // commented out by kent 20100210.
+       /*
+       mouseClickEnabled = !mouseClickEnabled;
+       alert((mouseClickEnabled ? "enabled" : "disabled") +  " mouse click advance");
+       return cancel(event);
+       */
+    }
+    else if (key == 84 || key == 67)  // T or C for table of contents
+    {
+       if (toc)
+         showTableOfContents();
+
+       return cancel(event);
+    }
+    else if (key == 72) // H for help
+    {
+       window.location = helpPage;
+       return cancel(event);
+    }
+
+    // added by kent 20100210
+    else if (key == 77) {
+      showspeak = !showspeak;
+      var value = (showspeak? "visible":"hidden");
+      var elems = document.getElementsByClassName("speak");
+
+      for (var i=0; i<elems.length; i++) {
+        elems[i].style.visibility = value;
+      }
+      return cancel(event);
+    }
+
+    //else if (key == 93) // Windows menu key
+      //alert("lastShown is " + lastShown);
+    //else alert("key code is "+ key);
+
+
+    return true;
+}
+
+// make note of length of selected text
+// as this evaluates to zero in click event
+function mouseButtonUp(e)
+{
+  selectedTextLen = getSelectedText().length;
+}
+
+// right mouse button click is reserved for context menus
+// it is more reliable to detect rightclick than leftclick
+function mouseButtonClick(e)
+{
+   var rightclick = false;
+   var leftclick = false;
+   var middleclick = false;
+   var target;
+
+   if (!e)
+      var e = window.event;
+
+   if (e.target)
+      target = e.target;
+   else if (e.srcElement)
+      target = e.srcElement;
+
+   // work around Safari bug
+   if (target.nodeType == 3)
+      target = target.parentNode;
+
+   if (e.which) // all browsers except IE
+   {
+      leftclick = (e.which == 1);
+      middleclick = (e.which == 2);
+      rightclick = (e.which == 3);
+   }
+   else if (e.button)
+   {
+      // Konqueror gives 1 for left, 4 for middle
+      // IE6 gives 0 for left and not 1 as I expected
+
+      if (e.button == 4)
+        middleclick = true;
+
+      // all browsers agree on 2 for right button
+      rightclick = (e.button == 2);
+   }
+   else leftclick = true;
+
+   //alert("selected text length = "+selectedTextLen);
+
+   if (selectedTextLen > 0)
+   {
+      stopPropagation(e);
+      e.cancel = true;
+      e.returnValue = false;
+      return false;
+   }
+
+   // dismiss table of contents
+   hideTableOfContents();
+
+   // check if target is something that probably want's clicks
+   // e.g. embed, object, input, textarea, select, option
+
+   if (mouseClickEnabled && leftclick &&
+        target.nodeName != "EMBED" &&
+        target.nodeName != "OBJECT" &&
+        target.nodeName != "VIDEO" &&
+        target.nodeName != "INPUT" &&
+        target.nodeName != "TEXTAREA" &&
+        target.nodeName != "SELECT" &&
+        target.nodeName != "OPTION")
+   {
+      nextSlide(true);
+      stopPropagation(e);
+      e.cancel = true;
+      e.returnValue = false;
+   }
+}
+
+function previousSlide(incremental)
+{
+   if (!viewAll)
+   {
+      var slide;
+
+      if ((incremental || slidenum == 0) && lastShown != null)
+      {
+         lastShown = hidePreviousItem(lastShown);
+         setEosStatus(false);
+      }
+      else if (slidenum > 0)
+      {
+         slide = slides[slidenum];
+         hideSlide(slide);
+
+         slidenum = slidenum - 1;
+         slide = slides[slidenum];
+         setVisibilityAllIncremental("visible");
+         lastShown = previousIncrementalItem(null);
+         setEosStatus(true);
+         showSlide(slide);
+      }
+
+      setLocation();
+
+      if (!ns_pos)
+         refreshToolbar(200);
+   }
+}
+
+function nextSlide(incremental)
+{
+   if (!viewAll)
+   {
+      var slide, last = lastShown;
+
+      if (incremental || slidenum == slides.length - 1)
+         lastShown = revealNextItem(lastShown);
+
+      if ((!incremental || lastShown == null) && slidenum < slides.length - 1)
+      {
+         slide = slides[slidenum];
+         hideSlide(slide);
+
+         slidenum = slidenum + 1;
+         slide = slides[slidenum];
+         lastShown = null;
+         setVisibilityAllIncremental("hidden");
+         showSlide(slide);
+      }
+      else if (!lastShown)
+      {
+         if (last && incremental)
+           lastShown = last;
+      }
+
+      setLocation();
+
+      setEosStatus(!nextIncrementalItem(lastShown));
+
+      if (!ns_pos)
+         refreshToolbar(200);
+   }
+}
+
+// to first slide with nothing revealed
+// i.e. state at start of presentation
+function firstSlide()
+{
+   if (!viewAll)
+   {
+      var slide;
+
+      if (slidenum != 0)
+      {
+         slide = slides[slidenum];
+         hideSlide(slide);
+
+         slidenum = 0;
+         slide = slides[slidenum];
+         lastShown = null;
+         setVisibilityAllIncremental("hidden");
+         showSlide(slide);
+      }
+
+      setEosStatus(!nextIncrementalItem(lastShown));
+      setLocation();
+   }
+}
+
+
+// to last slide with everything revealed
+// i.e. state at end of presentation
+function lastSlide()
+{
+   if (!viewAll)
+   {
+      var slide;
+
+      lastShown = null; //revealNextItem(lastShown);
+
+      if (lastShown == null && slidenum < slides.length - 1)
+      {
+         slide = slides[slidenum];
+         hideSlide(slide);
+         slidenum = slides.length - 1;
+         slide = slides[slidenum];
+         setVisibilityAllIncremental("visible");
+         lastShown = previousIncrementalItem(null);
+
+         showSlide(slide);
+      }
+      else
+      {
+         setVisibilityAllIncremental("visible");
+         lastShown = previousIncrementalItem(null);
+      }
+
+      setEosStatus(true);
+      setLocation();
+   }
+}
+
+// first slide is 0
+function gotoSlide(num)
+{
+  //alert("going to slide " + (num+1));
+  var slide = slides[slidenum];
+  hideSlide(slide);
+  slidenum = num;
+  slide = slides[slidenum];
+  lastShown = null;
+  setVisibilityAllIncremental("hidden");
+  setEosStatus(!nextIncrementalItem(lastShown));
+  document.title = title + " (" + (slidenum+1) + ")";
+  showSlide(slide);
+  showSlideNumber();
+}
+
+function setEosStatus(state)
+{
+   if (eos)
+      eos.style.color = (state ? "rgb(240,240,240)" : "red");
+}
+
+function showSlide(slide)
+{
+   syncBackground(slide);
+   window.scrollTo(0,0);
+   slide.style.visibility = "visible";
+   slide.style.display = "block";
+}
+
+function hideSlide(slide)
+{
+   slide.style.visibility = "hidden";
+   slide.style.display = "none";
+}
+
+function beforePrint()
+{
+   showAllSlides();
+   hideToolbar();
+}
+
+function afterPrint()
+{
+   if (!viewAll)
+   {
+      singleSlideView();
+      showToolbar();
+   }
+}
+
+function printSlides()
+{
+  beforePrint();
+  window.print();
+  afterPrint();
+}
+
+function toggleView()
+{
+   if (viewAll)
+   {
+      singleSlideView();
+      showToolbar();
+      viewAll = 0;
+   }
+   else
+   {
+      showAllSlides();
+      hideToolbar();
+      viewAll = 1;
+   }
+}
+
+// prepare for printing
+function showAllSlides()
+{
+   var slide;
+
+   for (var i = 0; i < slides.length; ++i)
+   {
+      slide = slides[i];
+
+      slide.style.position = "relative";
+      slide.style.borderTopStyle = "solid";
+      slide.style.borderTopWidth = "thin";
+      slide.style.borderTopColor = "black";
+
+      try {
+        if (i == 0)
+          slide.style.pageBreakBefore = "avoid";
+        else
+          slide.style.pageBreakBefore = "always";
+      }
+      catch (e)
+      {
+        //do nothing
+      }
+
+      setVisibilityAllIncremental("visible");
+      showSlide(slide);
+   }
+
+   var note;
+
+   for (var i = 0; i < notes.length; ++i)
+   {
+      showSlide(notes[i]);
+   }
+
+   // no easy way to render background under each slide
+   // without duplicating the background divs for each slide
+   // therefore hide backgrounds to avoid messing up slides
+   hideBackgrounds();
+}
+
+// restore after printing
+function singleSlideView()
+{
+   var slide;
+
+   for (var i = 0; i < slides.length; ++i)
+   {
+      slide = slides[i];
+
+      slide.style.position = "absolute";
+
+      if (i == slidenum)
+      {
+         slide.style.borderStyle = "none";
+         showSlide(slide);
+      }
+      else
+      {
+         slide.style.borderStyle = "none";
+         hideSlide(slide);
+      }
+   }
+
+   setVisibilityAllIncremental("visible");
+   lastShown = previousIncrementalItem(null);
+
+   var note;
+
+   for (var i = 0; i < notes.length; ++i)
+   {
+      hideSlide(notes[i]);
+   }
+}
+
+// the string str is a whitespace separated list of tokens
+// test if str contains a particular token, e.g. "slide"
+function hasToken(str, token)
+{
+   if (str)
+   {
+      // define pattern as regular expression
+      var pattern = /\w+/g;
+
+      // check for matches
+      // place result in array
+      var result = str.match(pattern);
+
+      // now check if desired token is present
+      for (var i = 0; i < result.length; i++)
+      {
+         if (result[i] == token)
+            return true;
+      }
+   }
+
+   return false;
+}
+
+function getClassList(element)
+{
+  if (typeof element.className != 'undefined')
+    return element.className;
+
+  var clsname = (ns_pos||ie8) ? "class" : "className";
+  return element.getAttribute(clsname);
+}
+
+function hasClass(element, name)
+{
+  var regexp = new RegExp("(^| )" + name + "\W*");
+
+  if (typeof element.className != 'undefined')
+    return regexp.test(element.className);
+
+  var clsname = (ns_pos||ie8) ? "class" : "className";
+  return regexp.test(element.getAttribute(clsname));
+}
+
+function removeClass(element, name)
+{
+  var regexp = new RegExp("(^| )" + name + "\W*");
+  var clsval = "";
+
+  if (typeof element.className != 'undefined')
+  {
+     clsval = element.className;
+
+     if (clsval)
+     {
+       clsval = clsval.replace(regexp, "");
+       element.className = clsval;
+     }
+  }
+  else
+  {
+    var clsname = (ns_pos||ie8) ? "class" : "className";
+    clsval = element.getAttribute(clsname);
+
+    if (clsval)
+    {
+      clsval = clsval.replace(regexp, "");
+      element.setAttribute(clsname, clsval);
+    }
+  }
+}
+
+function addClass(element, name)
+{
+  if (!hasClass(element, name))
+  {
+    if (typeof element.className != 'undefined')
+      element.className += " " + name;
+    else
+    {
+      var clsname = (ns_pos||ie8) ? "class" : "className";
+      var clsval = element.getAttribute(clsname);
+      clsval = clsval ? clsval + " " + name : name;
+      element.setAttribute(clsname, clsval);
+    }
+  }
+}
+
+// wysiwyg editors make it hard to use div elements
+// e.g. amaya loses the div when you copy and paste
+// this function wraps div elements around implicit
+// slides which start with an h1 element and continue
+// up to the next heading or div element
+function wrapImplicitSlides()
+{
+   var i, heading, node, next, div;
+   var headings = document.getElementsByTagName("h1");
+
+   if (!headings)
+     return;
+
+   for (i = 0; i < headings.length; ++i)
+   {
+      heading = headings[i];
+
+      if (heading.parentNode != document.body)
+         continue;
+
+      node = heading.nextSibling;
+
+      div = document.createElement("div");
+      addClass(div, "slide");
+      document.body.replaceChild(div, heading);
+      div.appendChild(heading);
+
+      while (node)
+      {
+         if (node.nodeType == 1 &&    // an element
+                  (node.nodeName == "H1" ||
+                   node.nodeName == "h1" ||
+                   node.nodeName == "DIV" ||
+                   node.nodeName == "div"))
+            break;
+
+         next = node.nextSibling;
+         node = document.body.removeChild(node);
+         div.appendChild(node);
+         node = next;
+      } 
+   }
+}
+
+// return new array of all slides
+function collectSlides()
+{
+   var slides = new Array();
+   var divs = document.body.getElementsByTagName("div");
+
+   for (var i = 0; i < divs.length; ++i)
+   {
+      div = divs.item(i);
+
+      if (hasClass(div, "slide"))
+      {
+         // add slide to collection
+         slides[slides.length] = div;
+
+         // hide each slide as it is found
+         div.style.display = "none";
+         div.style.visibility = "hidden";
+
+         // add dummy <br/> at end for scrolling hack
+         var node1 = document.createElement("br");
+         div.appendChild(node1);
+         var node2 = document.createElement("br");
+         div.appendChild(node2);
+      }
+      else if (hasClass(div, "background"))
+      {  // work around for Firefox SVG reload bug
+         // which otherwise replaces 1st SVG graphic with 2nd
+         div.style.display = "block";
+      }
+   }
+
+   return slides;
+}
+
+// return new array of all <div class="handout">
+function collectNotes()
+{
+   var notes = new Array();
+   var divs = document.body.getElementsByTagName("div");
+
+   for (var i = 0; i < divs.length; ++i)
+   {
+      div = divs.item(i);
+
+      if (hasClass(div, "handout"))
+      {
+         // add slide to collection
+         notes[notes.length] = div;
+
+         // hide handout notes as they are found
+         div.style.display = "none";
+         div.style.visibility = "hidden";
+      }
+   }
+
+   return notes;
+}
+
+// return new array of all <div class="background">
+// including named backgrounds e.g. class="background titlepage"
+function collectBackgrounds()
+{
+   var backgrounds = new Array();
+   var divs = document.body.getElementsByTagName("div");
+
+   for (var i = 0; i < divs.length; ++i)
+   {
+      div = divs.item(i);
+
+      if (hasClass(div, "background"))
+      {
+         // add slide to collection
+         backgrounds[backgrounds.length] = div;
+
+         // hide named backgrounds as they are found
+         // e.g. class="background epilog"
+         if (getClassList(div) != "background")
+         {
+            div.style.display = "none";
+            div.style.visibility = "hidden";
+         }
+      }
+   }
+
+   return backgrounds;
+}
+
+// show just the backgrounds pertinent to this slide
+function syncBackground(slide)
+{
+   var background;
+   var bgColor;
+
+   if (slide.currentStyle)
+      bgColor = slide.currentStyle["backgroundColor"];
+   else if (document.defaultView)
+   {
+      var styles = document.defaultView.getComputedStyle(slide,null);
+
+      if (styles)
+          bgColor = styles.getPropertyValue("background-color");
+      else // broken implementation probably due Safari or Konqueror
+      {
+          //alert("defective implementation of getComputedStyle()");
+          bgColor = "transparent";
+      }
+   }
+   else
+      bgColor == "transparent";
+
+   if (bgColor == "transparent")
+   {
+      var slideClass = getClassList(slide);
+
+      for (var i = 0; i < backgrounds.length; i++)
+      {
+         background = backgrounds[i];
+
+         var bgClass = getClassList(background);
+
+         if (matchingBackground(slideClass, bgClass))
+         {
+            background.style.display = "block";
+            background.style.visibility = "visible";
+         }
+         else
+         {
+            background.style.display = "none";
+            background.style.visibility = "hidden";
+         }
+      }
+   }
+   else // forcibly hide all backgrounds
+      hideBackgrounds();
+}
+
+function hideBackgrounds()
+{
+   for (var i = 0; i < backgrounds.length; i++)
+   {
+      background = backgrounds[i];
+      background.style.display = "none";
+      background.style.visibility = "hidden";
+   }
+}
+
+// compare classes for slide and background
+function matchingBackground(slideClass, bgClass)
+{
+   if (bgClass == "background")
+      return true;
+
+   // define pattern as regular expression
+   var pattern = /\w+/g;
+
+   // check for matches and place result in array
+   var result = slideClass.match(pattern);
+
+   // now check if desired name is present for background
+   for (var i = 0; i < result.length; i++)
+   {
+      if (hasToken(bgClass, result[i]))
+         return true;
+   }
+
+   return false;
+}
+
+// left to right traversal of root's content
+function nextNode(root, node)
+{
+   if (node == null)
+      return root.firstChild;
+
+   if (node.firstChild)
+      return node.firstChild;
+
+   if (node.nextSibling)
+      return node.nextSibling;
+
+   for (;;)
+   {
+      node = node.parentNode;
+
+      if (!node || node == root)
+         break;
+
+      if (node && node.nextSibling)
+         return node.nextSibling;
+   }
+
+   return null;
+}
+
+// right to left traversal of root's content
+function previousNode(root, node)
+{
+   if (node == null)
+   {
+      node = root.lastChild;
+
+      if (node)
+      {
+         while (node.lastChild)
+            node = node.lastChild;
+      }
+
+      return node;
+   }
+
+   if (node.previousSibling)
+   {
+      node = node.previousSibling;
+
+      while (node.lastChild)
+         node = node.lastChild;
+
+      return node;
+   }
+
+   if (node.parentNode != root)
+      return node.parentNode;
+
+   return null;
+}
+
+// HTML elements that can be used with class="incremental"
+// note that you can also put the class on containers like
+// up, ol, dl, and div to make their contents appear
+// incrementally. Upper case is used since this is what
+// browsers report for HTML node names (text/html).
+function incrementalElementList()
+{
+   var inclist = new Array();
+   inclist["P"] = true;
+   inclist["PRE"] = true;
+   inclist["LI"] = true;
+   inclist["BLOCKQUOTE"] = true;
+   inclist["DT"] = true;
+   inclist["DD"] = true;
+   inclist["H2"] = true;
+   inclist["H3"] = true;
+   inclist["H4"] = true;
+   inclist["H5"] = true;
+   inclist["H6"] = true;
+   inclist["SPAN"] = true;
+   inclist["ADDRESS"] = true;
+   inclist["TABLE"] = true;
+   inclist["TR"] = true;
+   inclist["TH"] = true;
+   inclist["TD"] = true;
+   inclist["IMG"] = true;
+   inclist["OBJECT"] = true;
+   return inclist;
+}
+
+function nextIncrementalItem(node)
+{
+   var slide = slides[slidenum];
+
+   for (;;)
+   {
+      node = nextNode(slide, node);
+
+      if (node == null || node.parentNode == null)
+         break;
+
+      if (node.nodeType == 1)  // ELEMENT
+      {
+         if (node.nodeName == "BR")
+           continue;
+
+         if (hasClass(node, "incremental")
+             && okayForIncremental[node.nodeName])
+            return node;
+
+         if (hasClass(node.parentNode, "incremental")
+             && !hasClass(node, "non-incremental"))
+            return node;
+      }
+   }
+
+   return node;
+}
+
+function previousIncrementalItem(node)
+{
+   var slide = slides[slidenum];
+
+   for (;;)
+   {
+      node = previousNode(slide, node);
+
+      if (node == null || node.parentNode == null)
+         break;
+
+      if (node.nodeType == 1)
+      {
+         if (node.nodeName == "BR")
+           continue;
+
+         if (hasClass(node, "incremental")
+             && okayForIncremental[node.nodeName])
+            return node;
+
+         if (hasClass(node.parentNode, "incremental")
+             && !hasClass(node, "non-incremental"))
+            return node;
+      }
+   }
+
+   return node;
+}
+
+// set visibility for all elements on current slide with
+// a parent element with attribute class="incremental"
+function setVisibilityAllIncremental(value)
+{
+   var node = nextIncrementalItem(null);
+
+   while (node)
+   {
+      node.style.visibility = value;
+      node = nextIncrementalItem(node);
+   }
+}
+
+// reveal the next hidden item on the slide
+// node is null or the node that was last revealed
+function revealNextItem(node)
+{
+   node = nextIncrementalItem(node);
+
+   if (node && node.nodeType == 1)  // an element
+      node.style.visibility = "visible";
+
+   return node;
+}
+
+
+// exact inverse of revealNextItem(node)
+function hidePreviousItem(node)
+{
+   if (node && node.nodeType == 1)  // an element
+      node.style.visibility = "hidden";
+
+   return previousIncrementalItem(node);
+}
+
+
+/* set click handlers on all anchors */
+function patchAnchors()
+{
+   var anchors = document.body.getElementsByTagName("a");
+
+   for (var i = 0; i < anchors.length; ++i)
+   {
+      anchors[i].onclick = clickedAnchor;
+   }
+}
+
+function clickedAnchor(e)
+{
+   if (!e)
+      var e = window.event;
+
+   // compare this.href with location.href
+   // for link to another slide in this doc
+
+   if (pageAddress(this.href) == pageAddress(location.href))
+   {
+      // yes, so find new slide number
+      var newslidenum = findSlideNumber(this.href);
+
+      if (newslidenum != slidenum)
+      {
+         slide = slides[slidenum];
+         hideSlide(slide);
+         slidenum = newslidenum;
+         slide = slides[slidenum];
+         showSlide(slide);
+         setLocation();
+      }
+   }
+   else if (this.target == null)
+      location.href = this.href;
+
+   this.blur();
+   stopPropagation(e);
+}
+
+function pageAddress(uri)
+{
+   var i = uri.indexOf("#");
+
+   if (i < 0)
+     i = uri.indexOf("%23");
+
+   // check if anchor is entire page
+
+   if (i < 0)
+      return uri;  // yes
+
+   return uri.substr(0, i);
+}
+
+function showSlideNumber()
+{
+   slideNumElement.innerHTML = "slide".localize() + " " +
+           (slidenum + 1) + "/" + slides.length;
+}
+
+// every 200mS check if the location has been changed as a
+// result of the user activating the Back button/menu item
+// doesn't work for Opera < 9.5
+function checkLocation()
+{
+  var hash = location.hash;
+
+  if (slidenum > 0 && (hash == "" || hash == "#"))
+    gotoSlide(0);
+  else if (hash.length > 2 && hash != "#("+(slidenum+1)+")")
+  {
+    var num = parseInt(location.hash.substr(2));
+
+    if (!isNaN(num))
+      gotoSlide(num-1);
+  }
+}
+
+// this doesn't push location onto history stack for IE
+// for which a hidden iframe hack is needed: load page into
+// the iframe with script that set's parent's location.hash
+// but that won't work for standalone use unless we can
+// create the page dynamically via a javascript: URL
+function setLocation()
+{
+   var uri = pageAddress(location.href);
+   var hash = "#(" + (slidenum+1) + ")";
+
+   if (slidenum >= 0)
+      uri = uri + hash;
+
+   if (ie && !ie8)
+      pushHash(hash);
+
+   if (uri != location.href /*&& !khtml */)
+      location.href = uri;
+
+   if (khtml)
+      hash = "(" + (slidenum+1) + ")";
+
+   if (!ie && location.hash != hash && location.hash != "")
+     location.hash = hash;
+
+   document.title = title + " (" + (slidenum+1) + ")";
+   showSlideNumber();
+}
+
+// only used for IE6 and IE7
+function onFrameLoaded(hash)
+{
+  location.hash = hash;
+  var uri = pageAddress(location.href);
+  location.href = uri + hash;
+}
+
+// history hack with thanks to Bertrand Le Roy
+function pushHash(hash)
+{
+  if (hash == "") hash = "#(1)";
+  window.location.hash = hash;
+  var doc = document.getElementById("historyFrame").contentWindow.document;
+  doc.open("javascript:'<html></html>'");
+  doc.write("<html><head><script type=\"text/javascript\">parent.onFrameLoaded('"+
+    (hash) + "');</script></head><body>hello mum</body></html>");
+  doc.close();
+}
+
+// find current slide based upon location
+// first find target anchor and then look
+// for associated div element enclosing it
+// finally map that to slide number
+function findSlideNumber(uri)
+{
+   // first get anchor from page location
+
+   var i = uri.indexOf("#");
+
+   // check if anchor is entire page
+
+   if (i < 0)
+      return 0;  // yes
+
+   var anchor = unescape(uri.substr(i+1));
+
+   // now use anchor as XML ID to find target
+   var target = document.getElementById(anchor);
+
+   if (!target)
+   {
+      // does anchor look like "(2)" for slide 2 ??
+      // where first slide is (1)
+      var re = /\((\d)+\)/;
+
+      if (anchor.match(re))
+      {
+         var num = parseInt(anchor.substring(1, anchor.length-1));
+
+         if (num > slides.length)
+            num = 1;
+
+         if (--num < 0)
+            num = 0;
+
+         return num;
+      }
+
+      // accept [2] for backwards compatibility
+      re = /\[(\d)+\]/;
+
+      if (anchor.match(re))
+      {
+         var num = parseInt(anchor.substring(1, anchor.length-1));
+
+         if (num > slides.length)
+            num = 1;
+
+         if (--num < 0)
+            num = 0;
+
+         return num;
+      }
+
+      // oh dear unknown anchor
+      return 0;
+   }
+
+   // search for enclosing slide
+
+   while (true)
+   {
+      // browser coerces html elements to uppercase!
+      if (target.nodeName.toLowerCase() == "div" &&
+            hasClass(target, "slide"))
+      {
+         // found the slide element
+         break;
+      }
+
+      // otherwise try parent element if any
+
+      target = target.parentNode;
+
+      if (!target)
+      {
+         return 0;   // no luck!
+      }
+   };
+
+   for (i = 0; i < slides.length; ++i)
+   {
+      if (slides[i] == target)
+         return i;  // success
+   }
+
+   // oh dear still no luck
+   return 0;
+}
+
+// find slide name from first h1 element
+// default to document title + slide number
+function slideName(index)
+{
+   var name = null;
+   var slide = slides[index];
+
+   var heading = findHeading(slide);
+
+   if (heading)
+     name = extractText(heading);
+
+   if (!name)
+     name = title + "(" + (index + 1) + ")";
+
+   name.replace(/\&/g, "&amp;");
+   name.replace(/\</g, "&lt;");
+   name.replace(/\>/g, "&gt;");
+
+   return name;
+}
+
+// find first h1 element in DOM tree
+function findHeading(node)
+{  if (!node || node.nodeType != 1)
+    return null;
+
+  if (node.nodeName == "H1" || node.nodeName == "h1")
+    return node;
+
+  var child = node.firstChild;
+
+  while (child)
+  {
+    node = findHeading(child);
+
+    if (node)
+      return node;
+
+    child = child.nextSibling;
+  }
+
+  return null;
+}
+
+// recursively extract text from DOM tree
+function extractText(node)
+{
+  if (!node)
+    return "";
+
+  // text nodes
+  if (node.nodeType == 3)
+    return node.nodeValue;
+
+  // elements
+  if (node.nodeType == 1)
+  {
+    node = node.firstChild;
+    var text = "";
+
+    while (node)
+    {
+      text = text + extractText(node);
+      node = node.nextSibling;
+    }
+
+    return text;
+  }
+
+  return "";
+}
+
+
+// find copyright text from meta element
+function findCopyright()
+{
+   var name, content;
+   var meta = document.getElementsByTagName("meta");
+
+   for (var i = 0; i < meta.length; ++i)
+   {
+      name = meta[i].getAttribute("name");
+      content = meta[i].getAttribute("content");
+
+      if (name == "copyright")
+         return content;
+   }
+
+   return null;
+}
+
+function findSizeAdjust()
+{
+   var name, content, offset;
+   var meta = document.getElementsByTagName("meta");
+
+   for (var i = 0; i < meta.length; ++i)
+   {
+      name = meta[i].getAttribute("name");
+      content = meta[i].getAttribute("content");
+
+      if (name == "font-size-adjustment")
+         return 1 * content;
+   }
+
+   return 1;
+}
+
+function addToolbar()
+{
+   var slideCounter, page;
+
+   var toolbar = createElement("div");
+   toolbar.setAttribute("class", "toolbar");
+
+   if (ns_pos) // a reasonably behaved browser
+   {
+      var right = document.createElement("div");
+      right.setAttribute("style", "float: right; text-align: right");
+
+      slideCounter = document.createElement("div")
+      slideCounter.innerHTML = "slide".localize() + " n/m";
+      right.appendChild(slideCounter);
+      toolbar.appendChild(right);
+
+      var left = document.createElement("div");
+      left.setAttribute("style", "text-align: left");
+
+      // global end of slide indicator
+      eos = document.createElement("span");
+      eos.innerHTML = "* ";
+      left.appendChild(eos);
+
+      var help = document.createElement("a");
+      help.setAttribute("href", helpPage);
+      help.setAttribute("title", helpText.localize());
+      help.innerHTML = "help?".localize();
+      left.appendChild(help);
+      helpAnchor = help;  // save for focus hack
+
+      var gap1 = document.createTextNode(" ");
+      left.appendChild(gap1);
+
+      var contents = document.createElement("a");
+      contents.setAttribute("href", "javascript:toggleTableOfContents()");
+      contents.setAttribute("title", "table of contents".localize());
+      contents.innerHTML = "contents?".localize();
+      left.appendChild(contents);
+
+      var gap2 = document.createTextNode(" ");
+      left.appendChild(gap2);
+
+      var start = document.createElement("a");
+      start.setAttribute("href", "javascript:firstSlide()");
+      start.setAttribute("title", "restart presentation".localize());
+      start.innerHTML = "restart?".localize();
+//    start.setAttribute("href", "javascript:printSlides()");
+//    start.setAttribute("title", "print all slides".localize());
+//    start.innerHTML = "print!".localize();
+      left.appendChild(start);
+
+      var copyright = findCopyright();
+
+      if (copyright)
+      {
+         var span = document.createElement("span");
+         span.innerHTML = copyright;
+         span.style.color = "black";
+         span.style.marginLeft = "4em";
+         left.appendChild(span);
+      }
+
+      toolbar.appendChild(left);
+   }
+   else // IE so need to work around its poor CSS support
+   {
+      toolbar.style.position = (ie7 ? "fixed" : "absolute");
+      toolbar.style.zIndex = "200";
+      toolbar.style.width = "99.9%";
+      toolbar.style.height = "1.2em";
+      toolbar.style.top = "auto";
+      toolbar.style.bottom = "0";
+      toolbar.style.left = "0";
+      toolbar.style.right = "0";
+      toolbar.style.textAlign = "left";
+      toolbar.style.fontSize = "60%";
+      toolbar.style.color = "red";
+      toolbar.borderWidth = 0;
+      toolbar.style.background = "rgb(240,240,240)";
+
+      // would like to have help text left aligned
+      // and page counter right aligned, floating
+      // div's don't work, so instead use nested
+      // absolutely positioned div's.
+
+      var sp = document.createElement("span");
+      sp.innerHTML = "&nbsp;&nbsp;*&nbsp;";
+      toolbar.appendChild(sp);
+      eos = sp;  // end of slide indicator
+
+      var help = document.createElement("a");
+      help.setAttribute("href", helpPage);
+      help.setAttribute("title", helpText.localize());
+      help.innerHTML = "help?".localize();
+      toolbar.appendChild(help);
+      helpAnchor = help;  // save for focus hack
+
+      var gap1 = document.createTextNode(" ");
+      toolbar.appendChild(gap1);
+
+      var contents = document.createElement("a");
+      contents.setAttribute("href", "javascript:toggleTableOfContents()");
+      contents.setAttribute("title", "table of contents".localize());
+      contents.innerHTML = "contents?".localize();
+      toolbar.appendChild(contents);
+
+      var gap2 = document.createTextNode(" ");
+      toolbar.appendChild(gap2);
+
+      var start = document.createElement("a");
+      start.setAttribute("href", "javascript:firstSlide()");
+      start.setAttribute("title", "restart presentation".localize());
+      start.innerHTML = "restart?".localize();
+//    start.setAttribute("href", "javascript:printSlides()");
+//    start.setAttribute("title", "print all slides".localize());
+//    start.innerHTML = "print!".localize();
+      toolbar.appendChild(start);
+
+      var copyright = findCopyright();
+
+      if (copyright)
+      {
+         var span = document.createElement("span");
+         span.innerHTML = copyright;
+         span.style.color = "black";
+         span.style.marginLeft = "2em";
+         toolbar.appendChild(span);
+      }
+
+      slideCounter = document.createElement("div")
+      slideCounter.style.position = "absolute";
+      slideCounter.style.width = "auto"; //"20%";
+      slideCounter.style.height = "1.2em";
+      slideCounter.style.top = "auto";
+      slideCounter.style.bottom = 0;
+      slideCounter.style.right = "0";
+      slideCounter.style.textAlign = "right";
+      slideCounter.style.color = "red";
+      slideCounter.style.background = "rgb(240,240,240)";
+
+      slideCounter.innerHTML = "slide".localize() + " n/m";
+      toolbar.appendChild(slideCounter);
+   }
+
+   // ensure that click isn't passed through to the page
+   toolbar.onclick = stopPropagation;
+   document.body.appendChild(toolbar);
+   slideNumElement = slideCounter;
+   setEosStatus(false);
+
+   return toolbar;
+}
+
+function isShownToc()
+{
+  if (toc && toc.style.visible == "visible")
+    return true;
+
+  return false;
+}
+
+function showTableOfContents()
+{
+  if (toc)
+  {
+    if (toc.style.visibility != "visible")
+    {
+      toc.style.visibility = "visible";
+      toc.style.display = "block";
+      toc.focus();
+
+      if (ie7 && slidenum == 0)
+        setTimeout("ieHack()", 100);
+    }
+    else
+      hideTableOfContents();
+  }
+}
+
+function hideTableOfContents()
+{
+  if (toc && toc.style.visibility != "hidden")
+  {
+    toc.style.visibility = "hidden";
+    toc.style.display = "none";
+
+    try
+    {
+       if (!opera)
+         helpAnchor.focus();
+    }
+    catch (e)
+    {
+    }
+  }
+}
+
+function toggleTableOfContents()
+{
+  if (toc)
+  {
+     if (toc.style.visible != "visible")
+       showTableOfContents();
+     else
+       hideTableOfContents();
+  }
+}
+
+// called on clicking toc entry
+function gotoEntry(e)
+{
+   var target;
+
+   if (!e)
+      var e = window.event;
+
+   if (e.target)
+      target = e.target;
+   else if (e.srcElement)
+      target = e.srcElement;
+
+   // work around Safari bug
+   if (target.nodeType == 3)
+      target = target.parentNode;
+
+   if (target && target.nodeType == 1)
+   {
+     var uri = target.getAttribute("href");
+
+     if (uri)
+     {
+        //alert("going to " + uri);
+        var slide = slides[slidenum];
+        hideSlide(slide);
+        slidenum = findSlideNumber(uri);
+        slide = slides[slidenum];
+        lastShown = null;
+        setLocation();
+        setVisibilityAllIncremental("hidden");
+        setEosStatus(!nextIncrementalItem(lastShown));
+        showSlide(slide);
+        //target.focus();
+
+        try
+        {
+           if (!opera)
+             helpAnchor.focus();
+        }
+        catch (e)
+        {
+        }
+     }
+   }
+
+   hideTableOfContents(e);
+   if (ie7) ieHack();
+   stopPropagation(e);
+   return cancel(e);
+}
+
+// called onkeydown for toc entry
+function gotoTocEntry(event)
+{
+  var key;
+
+  if (!event)
+    var event = window.event;
+
+  // kludge around NS/IE differences 
+  if (window.event)
+    key = window.event.keyCode;
+  else if (event.which)
+    key = event.which;
+  else
+    return true; // Yikes! unknown browser
+
+  // ignore event if key value is zero
+  // as for alt on Opera and Konqueror
+  if (!key)
+     return true;
+
+  // check for concurrent control/command/alt key
+  // but are these only present on mouse events?
+
+  if (event.ctrlKey || event.altKey)
+     return true;
+
+  if (key == 13)
+  {
+    var uri = this.getAttribute("href");
+
+    if (uri)
+    {
+      //alert("going to " + uri);
+      var slide = slides[slidenum];
+      hideSlide(slide);
+      slidenum = findSlideNumber(uri);
+      slide = slides[slidenum];
+      lastShown = null;
+      setLocation();
+      setVisibilityAllIncremental("hidden");
+      setEosStatus(!nextIncrementalItem(lastShown));
+      showSlide(slide);
+      //target.focus();
+
+      try
+      {
+         if (!opera)
+           helpAnchor.focus();
+      }
+      catch (e)
+      {
+      }
+    }
+
+    hideTableOfContents();
+    if (ie7) ieHack();
+    return cancel(event);
+  }
+
+  if (key == 40 && this.next)
+  {
+    this.next.focus();
+    return cancel(event);
+  }
+
+  if (key == 38 && this.previous)
+  {
+    this.previous.focus();
+    return cancel(event);
+  }
+
+  return true;
+}
+
+function isTitleSlide(slide)
+{
+   return hasClass(slide, "title");
+}
+
+// create div element with links to each slide
+function tableOfContents()
+{
+  var toc = document.createElement("div");
+  addClass(toc, "toc");
+  //toc.setAttribute("tabindex", "0");
+
+  var heading = document.createElement("div");
+  addClass(heading, "toc-heading");
+  heading.innerHTML = "Table of Contents".localize();
+
+  heading.style.textAlign = "center";
+  heading.style.width = "100%";
+  heading.style.margin = "0";
+  heading.style.marginBottom = "1em";
+  heading.style.borderBottomStyle = "solid";
+  heading.style.borderBottomColor = "rgb(180,180,180)";
+  heading.style.borderBottomWidth = "1px";
+
+  toc.appendChild(heading);
+  var previous = null;
+
+  for (var i = 0; i < slides.length; ++i)
+  {
+    var title = hasClass(slides[i], "title");
+    var num = document.createTextNode((i + 1) + ". ");
+
+    toc.appendChild(num);
+
+    var a = document.createElement("a");
+    a.setAttribute("href", "#(" + (i+1) + ")");
+
+    if (title)
+      addClass(a, "titleslide");
+
+    var name = document.createTextNode(slideName(i));
+    a.appendChild(name);
+    a.onclick = gotoEntry;
+    a.onkeydown = gotoTocEntry;
+    a.previous = previous;
+
+    if (previous)
+      previous.next = a;
+
+    toc.appendChild(a);
+
+    if (i == 0)
+      toc.first = a;
+
+    if (i < slides.length - 1)
+    {
+      var br = document.createElement("br");
+      toc.appendChild(br);
+    }
+
+    previous = a;
+  }
+
+  toc.focus = function () {
+    if (this.first)
+      this.first.focus();
+  }
+
+  toc.onmouseup = mouseButtonUp;
+
+  toc.onclick = function (e) {
+    e||(e=window.event);
+
+    if (selectedTextLen <= 0)
+       hideTableOfContents();
+
+    stopPropagation(e);
+    
+    if (e.cancel != undefined)
+      e.cancel = true;
+      
+    if (e.returnValue != undefined)
+      e.returnValue = false;
+      
+    return false;
+  };
+
+  toc.style.position = "absolute";
+  toc.style.zIndex = "300";
+  toc.style.width = "60%";
+  toc.style.maxWidth = "30em";
+  toc.style.height = "30em";
+  toc.style.overflow = "auto";
+  toc.style.top = "auto";
+  toc.style.right = "auto";
+  toc.style.left = "4em";
+  toc.style.bottom = "4em";
+  toc.style.padding = "1em";
+  toc.style.background = "rgb(240,240,240)";
+  toc.style.borderStyle = "solid";
+  toc.style.borderWidth = "2px";
+  toc.style.fontSize = "60%";
+
+  document.body.insertBefore(toc, document.body.firstChild);
+  return toc;
+}
+
+function replaceByNonBreakingSpace(str)
+{
+   for (var i = 0; i < str.length; ++i)
+      str[i] = 160;
+}
+
+
+function initOutliner()
+{
+  var items = document.getElementsByTagName("LI");
+
+  for (var i = 0; i < items.length; ++i)
+  {
+     var target = items[i];
+
+     if (!hasClass(target.parentNode, "outline"))
+        continue;
+
+     target.onclick = outlineClick;
+
+     if (!ns_pos)
+     {
+        target.onmouseover = hoverOutline;
+        target.onmouseout = unhoverOutline;
+     }
+
+     if (foldable(target))
+     {
+       target.foldable = true;
+       target.onfocus = function () {outline = this;};
+       target.onblur = function () {outline = null;};
+
+       if (!target.getAttribute("tabindex"))
+         target.setAttribute("tabindex", "0");
+
+       if (hasClass(target, "expand"))
+         unfold(target);
+       else
+         fold(target);
+     }
+     else
+     {
+       addClass(target, "nofold");
+       target.visible = true;
+       target.foldable = false;
+     }
+  }
+}
+
+function foldable(item)
+{
+   if (!item || item.nodeType != 1)
+      return false;
+
+   var node = item.firstChild;
+
+   while (node)
+   {
+     if (node.nodeType == 1 && isBlock(node))
+       return true;
+
+      node = node.nextSibling;
+   }
+
+   return false;
+}
+
+function fold(item)
+{
+  if (item)
+  {
+    removeClass(item, "unfolded");
+    addClass(item, "folded");
+  }
+
+  var node = item ? item.firstChild : null;
+
+  while (node)
+  {
+    if (node.nodeType == 1 && isBlock(node)) // element
+    {
+      // note that getElementStyle won't work for Safari 1.3
+      node.display = getElementStyle(node, "display", "display");
+      node.style.display = "none";
+      node.style.visibility = "hidden";
+    }
+
+    node = node.nextSibling;
+  }
+
+  item.visible = false;
+}
+
+function unfold(item)
+{
+   if (item)
+   {
+     addClass(item, "unfolded");
+     removeClass(item, "folded");
+   }
+
+  var node = item ? item.firstChild : null;
+
+  while (node)
+  {
+    if (node.nodeType == 1 && isBlock(node)) // element
+    {
+      // with fallback for Safari, see above
+      node.style.display = (node.display ? node.display : "block");
+      node.style.visibility = "visible";
+    }
+
+    node = node.nextSibling;
+  }
+
+  item.visible = true;
+}
+
+function outlineClick(e)
+{
+   var rightclick = false;
+   var target;
+
+   if (!e)
+      var e = window.event;
+
+   if (e.target)
+      target = e.target;
+   else if (e.srcElement)
+      target = e.srcElement;
+
+   // work around Safari bug
+   if (target.nodeType == 3)
+      target = target.parentNode;
+
+   while (target && target.visible == undefined)
+      target = target.parentNode;
+
+   if (!target)
+      return true;
+
+   if (e.which)
+      rightclick = (e.which == 3);
+   else if (e.button)
+      rightclick = (e.button == 2);
+
+   if (!rightclick && target.visible != undefined)
+   {
+      if (target.foldable)
+      {
+         if (target.visible)
+           fold(target);
+         else
+           unfold(target);
+      }
+
+      stopPropagation(e);
+      e.cancel = true;
+      e.returnValue = false;
+   }
+
+   return false;
+}
+
+function hoverOutline(e)
+{
+   var target;
+
+   if (!e)
+      var e = window.event;
+
+   if (e.target)
+      target = e.target;
+   else if (e.srcElement)
+      target = e.srcElement;
+
+   // work around Safari bug
+   if (target.nodeType == 3)
+      target = target.parentNode;
+
+   while (target && target.visible == undefined)
+      target = target.parentNode;
+
+   if (target && target.foldable)
+      target.style.cursor = "pointer";
+
+   return true;
+}
+
+function unhoverOutline(e)
+{
+   var target;
+
+   if (!e)
+      var e = window.event;
+
+   if (e.target)
+      target = e.target;
+   else if (e.srcElement)
+      target = e.srcElement;
+
+   // work around Safari bug
+   if (target.nodeType == 3)
+      target = target.parentNode;
+
+   while (target && target.visible == undefined)
+      target = target.parentNode;
+
+   if (target)
+     target.style.cursor = "default";
+
+   return true;
+}
+
+
+function stopPropagation(e)
+{
+   if (window.event)
+   {
+      window.event.cancelBubble = true;
+      //window.event.returnValue = false;
+   }
+   else if (e)
+   {
+      e.cancelBubble = true;
+      e.stopPropagation();
+      //e.preventDefault();
+   }
+}
+
+/* can't rely on display since we set that to none to hide things */
+function isBlock(elem)
+{
+   var tag = elem.nodeName;
+
+   return tag == "OL" || tag == "UL" || tag == "P" ||
+          tag == "LI" || tag == "TABLE" || tag == "PRE" ||
+          tag == "H1" || tag == "H2" || tag == "H3" ||
+          tag == "H4" || tag == "H5" || tag == "H6" ||
+          tag == "BLOCKQUOTE" || tag == "ADDRESS"; 
+}
+
+function getElementStyle(elem, IEStyleProp, CSSStyleProp)
+{
+   if (elem.currentStyle)
+   {
+      return elem.currentStyle[IEStyleProp];
+   }
+   else if (window.getComputedStyle)
+   {
+      var compStyle = window.getComputedStyle(elem, "");
+      return compStyle.getPropertyValue(CSSStyleProp);
+   }
+   return "";
+}
+
+// works with text/html and text/xhtml+xml with thanks to Simon Willison
+function createElement(element)
+{
+   if (typeof document.createElementNS != 'undefined')
+   {
+      return document.createElementNS('http://www.w3.org/1999/xhtml', element);
+   }
+
+   if (typeof document.createElement != 'undefined')
+   {
+      return document.createElement(element);
+   }
+
+   return false;
+}
+
+// designed to work with both text/html and text/xhtml+xml
+function getElementsByTagName(name)
+{
+   if (typeof document.getElementsByTagNameNS != 'undefined')
+   {
+      return document.getElementsByTagNameNS('http://www.w3.org/1999/xhtml', name);
+   }
+
+   if (typeof document.getElementsByTagName != 'undefined')
+   {
+      return document.getElementsByTagName(name);
+   }
+
+   return null;
+}
+
+/*
+// clean alternative to innerHTML method, but on IE6
+// it doesn't work with named entities like &nbsp;
+// which need to be replaced by numeric entities
+function insertText(element, text)
+{
+   try
+   {
+     element.textContent = text;  // DOM3 only
+   }
+   catch (e)
+   {
+      if (element.firstChild)
+      {
+         // remove current children
+         while (element.firstChild)
+            element.removeChild(element.firstChild);
+      }
+
+      element.appendChild(document.createTextNode(text));
+   }
+}
+
+// as above, but as method of all element nodes
+// doesn't work in IE6 which doesn't allow you to
+// add methods to the HTMLElement prototype
+if (HTMLElement != undefined)
+{
+  HTMLElement.prototype.insertText = function(text) {
+    var element = this;
+
+    try
+    {
+      element.textContent = text;  // DOM3 only
+    }
+    catch (e)
+    {
+      if (element.firstChild)
+      {
+         // remove current children
+         while (element.firstChild)
+           element.removeChild(element.firstChild);
+      }
+
+      element.appendChild(document.createTextNode(text));
+    }
+  };
+}
+*/
+
+function getSelectedText()
+{
+  try
+  {
+    if (window.getSelection)
+      return window.getSelection().toString();
+
+    if (document.getSelection)
+      return document.getSelection().toString();
+
+    if (document.selection)
+      return document.selection.createRange().text;
+  }
+  catch (e)
+  {
+    return "";
+  }
+  return "";
+}
+