# HG changeset patch # User kent # Date 1266308721 -32400 # Node ID 67544736317e059c81980a2baccf1fff6edf5c81 # Parent 064c4bd99db0d09a75ea52f24f2d093b2fbe4414 add slides for recital. diff -r 064c4bd99db0 -r 67544736317e recital-slide/figures/call-return.png Binary file recital-slide/figures/call-return.png has changed diff -r 064c4bd99db0 -r 67544736317e recital-slide/figures/continuation.png Binary file recital-slide/figures/continuation.png has changed diff -r 064c4bd99db0 -r 67544736317e recital-slide/figures/gcc-flow.png Binary file recital-slide/figures/gcc-flow.png has changed diff -r 064c4bd99db0 -r 67544736317e recital-slide/figures/gcc-flow2.png Binary file recital-slide/figures/gcc-flow2.png has changed diff -r 064c4bd99db0 -r 67544736317e recital-slide/figures/gcc-repository.png Binary file recital-slide/figures/gcc-repository.png has changed diff -r 064c4bd99db0 -r 67544736317e recital-slide/figures/tailcall.png Binary file recital-slide/figures/tailcall.png has changed diff -r 064c4bd99db0 -r 67544736317e recital-slide/figures/tailcallstack.png Binary file recital-slide/figures/tailcallstack.png has changed diff -r 064c4bd99db0 -r 67544736317e recital-slide/slide.css --- /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; +} diff -r 064c4bd99db0 -r 67544736317e recital-slide/slide.html --- /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 @@ + + + + + Continuation based C + + + + + + + + + + + + +
+ +
+
+ +
+ + + + +
+ +
+

組み込み向け言語Continuation based CのGCC上の実装

+

+ 与儀健人 (並列信頼研究室) + <kent@cr.ie.u-ryukyu.ac.jp> +

+ + +
+ +
+

研究の背景

+ +

なにが問題になるのか?

+ +
+ +
+

研究目的

+

+ 状態遷移記述をベースとした、より細かい単位でのプログラミングを実現する +

+ +

条件

+ +
+ +
+

Continuation based Cの提案

+

継続を基本とする記述言語CbC

+ +

+
+ +
+

コードセグメントと軽量継続の記述

+
+typedef code (*NEXT)(int);
+int main(int argc, char **argv) {
+  int i;
+  i = atoi(argv[1]);
+  goto factor(i, print_fact);
+}
+code factor(int x, NEXT next) {
+  goto factor0(1, x, next);
+}
+code factor0(int prod,int x,NEXT next) {
+  if (x >= 1) {
+    goto factor0(prod*x, x-1, next);
+  } else {
+    goto (*next)(prod);
+  }
+}
+code print_fact(int value) {
+  printf("factorial = %d\n", value);
+  exit(0);
+} 
+

実際のプログラム記述は?

+ +
+ +
+

これまでのCbC

+

+
+
2000
+
micro-cをベースとしたコンパイラの完成
+ x86, PowerPC, ARM, MIPS. +
+
2002
+
CbCを用いた分散計算
+
2005
+
CbCを用いたプログラム分割手法
+
2006
+
CbCによるSPUマシンのシミュレータ
+
2007
+
時相論理をベースとしたCbCプログラムの検証
+
2008
+
GCCをベースとしたコンパイラが開発される
+
2010
+
GCCベースコンパイラを実用レベルに拡張
+
+
+ +
+

本研究での取り組み

+

取り組み

+
+
First
+
GCCにて実用レベルのCbCプログラムを動作可能にする +
    +
  • 軽量継続の実装、これまでの制限の除去
  • +
  • x86アーキテクチャにて高速化を行った
  • +
  • PowerPCアーキテクチャでの間接継続の追加
  • +
+
+
Second
+
C言語との相互利用を可能にした
+
Third
+
ソースコードメンテナンス性の向上
+
+
+ + + +
+

GNU コンパイラコレクション (GCC)

+
+

GCCでのコンパイルの流れ

+
    +
  • フロントエンド
  • +
  • ミドルエンド
  • +
  • バックエンド
  • +
+
+ +
+ +
+

GNU コンパイラコレクション (GCC)

+
+

GCCでのコンパイルの流れ

+
    +
  • フロントエンド
  • +
  • ミドルエンド
  • +
  • バックエンド
  • +
+
+ +
+ + +
+

First: 軽量継続の実装

+

軽量継続を実装するには?

+ +

末尾呼出をGCCに強制させる必要がある

+
+ +
+

First: 軽量継続の実装

+

末尾呼出ってなに?

+ + +
+ +
+

First: 軽量継続の実装

+

末尾呼出ってなに?

+ + +

この末尾呼出(TCE)を強制して軽量継続を実装!

+
+ +
+

First: x86における高速化

+

軽量継続は実装されたが、やはりmicro-cに比べると遅い

+ +

fastcallの導入

+ +
+ +
+

First: x86における高速化

+

fastcallの強制

+ +

これで軽量継続制御が高速化される!

+
+ +
+

First: CbCコンパイラ実装の評価

+

CbCGCCとmicro-cで性能の比較

+ +

実行環境

+ +
+ +
+

First: 性能評価(速度比較) vs.旧ver

+

速度測定結果(単位:秒)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
新CbCGCC旧CbCGCC
最適化無し最適化有り最適化無し最適化有り
x86/OS X5.9072.4344.6683.048
x86/Linux5.7152.4014.5252.851
+

評価

+ +
+ + +
+

First: 性能評価(速度比較)

+

速度測定結果(単位:秒)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
最適化なしのGCC最適化付きのGCCmicro-c
x86/OS X5.9012.4342.857
x86/Linux5.7322.4012.254
ppc/OS X14.8752.1464.811
ppc/Linux19.7933.9556.454
ppc/PS339.1765.87411.121
+

結果(micro-cとの比較)

+ +

この違いはどこから?

+ +
+ + +
+

Second: Cとの相互利用

+

なぜそれが必要か

+ +

環境付き継続の導入

+ +
+ +
+

Second: Cとの相互利用

+
+typedef code (*NEXT)(int);
+int main(int argc, char **argv) {
+  int i,a;
+  i = atoi(argv[1]);
+  a = factor(i);
+  printf("%d! = %d\n", a);
+}
+int factor(int x) {
+  NEXT ret = __return;
+  goto factor0(1, x, ret);
+}
+code
+factor0(int prod,int x,NEXT next) {
+  if (x >= 1) {
+    goto factor0(prod*x, x-1, next);
+  } else {
+    goto (*next)(prod);
+  }
+}
+
+

環境付き継続の使用例

+ +
+ +
+

Second: Cとの相互利用

+

内部関数を用いた実装

+ +
int factor(int x) {
+   int retval;
+
+   code __return(int val) {
+      retval = val;
+      goto label;
+   }
+   if (0) {
+     label:
+      return retval;
+   }
+
+   NEXT ret = __return;
+   goto factor0(1, x, ret);
+} 
+
+ + +
+

Second: Cとの相互利用・評価

+

この取り組みにより

+ +
+ + + +
+

まとめ

+

本研究での取り組み

+
+
First
+
CbCGCCにて実用レベルのCbCプログラムが動作可能となった +
    +
  • 軽量継続における引数順序の制限を取り除いた
  • +
  • PowerPCでの間接継続の制限を取り除いた
  • +
  • x86アーキテクチャにて高速化を行った
  • +
+
+
Second
+
Cとの相互利用性の向上
+
Third
+
ソースコードメンテナンス性の向上
+
+
+ +
+

まとめ

+

本研究での成果

+
+
成果1
+
CbCGCCがCとの相互利用も含むCbCのフルセットとして利用可能になった +
成果2
+
CbCが多数のアーキテクチャに対応 +
    +
  • 20以上のアーキテクチャ
  • +
  • 特に64bitのx86, SPUがうれしい
  • +
+
成果3
+
CbCの高速化 +
    +
  • x86においてmicro-cと互角の速度を達成
  • +
  • PowerPCでは2倍の速度
  • +
+
+
+ +
+

今後の課題

+

+ +

CbC言語の今後

+ +
+ + +
+

おわり

+

ありがとうございました

+
+ + + + + + + + + + + + + + + + + + + + +
+

Continuation based C

+ +
+ + +
+

+

+ +
+
+

+

+ +
+ +
+

+

+ +
+ + +
+

First: PowerPCでの間接継続

+

+ +

+
+

+
+
+
+ +
+

継続制御での並列代入

+

+ 本当に最適化で余分なコードが消えるのか? +

+
+ 最適化しない場合 +
 _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
+
+
+
+ 最適化した場合 +

+_test:
+    mr r0,r3
+    mr r3,r4
+    mr r4,r5
+    mr r5,r0
+    b L_next$stub
+
+
+
+
    +
  • r3:=a, r4:=b, r5:=c
  • +
  • 最適化しないとload, storeが満載
  • +
  • 最適化すると無駄なload, store命令が消えている
  • +
  • 実際はr0を使って4命令で入れ替えられる!
  • +
+
+
+ + +
+

継続とはなんなのか?

+

継続

+ +

CbCでの軽量継続

+ +
+ +
+

コードセグメントと軽量継続の記述

+
+typedef code (*NEXT)(int);
+int main(int argc, char **argv) {
+  int i;
+  i = atoi(argv[1]);
+  goto factor(i, print_fact);
+}
+code factor(int x, NEXT next) {
+  goto factor0(1, x, next);
+}
+code factor0(int prod,int x,NEXT next) {
+  if (x >= 1) {
+    goto factor0(prod*x, x-1, next);
+  } else {
+    goto (*next)(prod);
+  }
+}
+code print_fact(int value) {
+  printf("factorial = %d\n", value);
+  exit(0);
+} 
+

実際のプログラム記述は?

+ +
+ +
+

Cとの比較について

+

quicksort例題をCと比較すると

+ +
+ + +
+

fastcall

+

実際の出力アセンブラ

+
+

fastcallにした場合

+
current:
+    subl    $12, %esp
+    movl    $30, 16(%esp)
+    movl    $20, %edx
+    movl    $10, %ecx
+    addl    $12, %esp
+    jmp     next
+
+
+
+

normalcallの場合

+
current:
+    pushl   %ebp
+    movl    %esp, %ebp
+    movl    $30, 16(%ebp)
+    movl    $20, 12(%ebp)
+    movl    $10, 8(%ebp)
+    leave
+    jmp     next
+
+
+
+ +
+ +
+

First: 性能評価(サイズ比較)

+

ファイルサイズの比較

+ +

結果

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CbCGCC
速度最適化
CbCGCC
サイズ最適化
micro-c
x86/OS X917691769172
x86/Linux575257525796
ppc/OS X8576857612664
ppc/Linux10068100689876
ppc/PS3696067288636
+

結果考察

+ +
+ +
+

並列代入

+

ある条件で末尾呼出が行われなくなる

+
    +
  1. 呼出先関数の全引数が占めるスタックサイズが、呼出元関数のそれより大きい場合 解決済み
  2. +
  3. 引数を順にスタックに格納すると、書き込み前のデータが上が着されてしまう場合
  4. +
+

問題となる例

+
code somesegment(int a, int b, int c) {
+  /∗ do something ∗/
+  goto nextsegment(b, c, a);
+}
+
+ +

次の様に構文木を変更する

+
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);
+}
+
+ +
+ + + + + + diff -r 064c4bd99db0 -r 67544736317e recital-slide/slidy.css --- /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 } diff -r 064c4bd99db0 -r 67544736317e recital-slide/slidy.js --- /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(""); + +// 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 +// 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. + // + // 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 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
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
+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
+// 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:''"); + doc.write("hello mum"); + 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, "&"); + name.replace(/\/g, ">"); + + 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 = "  * "; + 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   +// 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 ""; +} +