diff options
Diffstat (limited to '2018/ttcn-eh2018')
| -rw-r--r-- | 2018/ttcn-eh2018/ttcn3.adoc | 587 | ||||
| -rw-r--r-- | 2018/ttcn-eh2018/ttcn3.html | 5126 | ||||
| -rw-r--r-- | 2018/ttcn-eh2018/ttcn3__1.png | bin | 0 -> 40149 bytes | 
3 files changed, 5713 insertions, 0 deletions
diff --git a/2018/ttcn-eh2018/ttcn3.adoc b/2018/ttcn-eh2018/ttcn3.adoc new file mode 100644 index 0000000..83df14c --- /dev/null +++ b/2018/ttcn-eh2018/ttcn3.adoc @@ -0,0 +1,587 @@ +TTCN-3 and Eclipse TITAN for testing protocol stacks +==================================================== +:author:	Harald Welte <laforge@gnumonks.org> +:copyright:	2017-2018 by Harald Welte (License: CC-BY-SA) +:backend:	slidy +:max-width:	45em + +== Protocol Testing + +Important for: + +* conformance to specification +* ensuring interoperability +* network security +* regression testing +* performance + +== Protocol Testing + +No standard methodology, language, approach, tool + +* testing implementation against itself +** works only for symmetric protocols +** wouldn't cover lots of problems +* testing against wireshark +** wireshark often way more tolerant than spec +* custom implementation +** in Python (e.g. using scapy) +** in Erlang (good binary encoder/decoder) or other languages +* specific tools like packetdrill + +== Protocol Testing + +Personal story: During past years, + +* I implemented tons of [telecom] protocols / stacks at Osmocom.org +* I was looking for better tools to help [automatic] testing +** primarily functional testing (correctness / conformance) +** not so much performance testing +* I figured Ideal test tool would... +** allow very productive and expressive way to describe encoding/decoding +** allow very convenient pattern matching on incoming messages +** allow exchange of messages asynchronously with implementation under test +* I stumbled on TTCN-3 occasionally and investigated + +== The TTCN-3 Language + +* domain-specific language *just* for protocol conformance tests +* TTCN history back to 1983 (!), TTCN-3 since 2000 +* used extensively in classic telecom sector (Ericsson, Nokia, etc.) +* ETSI developed and published abstract test suites in TTCN-3 for +** IPv6, SIP, DIAMETER, ePassports, Digital Mobiel Radio, 6LoWPAN +* Other bodies published test suites for +** CoAP, MQTT, MOST, AUTOSAR + +But: Until 2015, only proprietary tools / compilers :( + +== Eclipse TITAN + +* After TTCN-3 specification in 2000, Ericsson internally develops TTCN-3 toolchain +* adopted for many Ericsson-internal testing of all kinds of products +* proprietary software with commercial licenses +* 300,000 lines of Java + 1.6 Million lines of C++ +* Released as Open Source as "Eclipse TITAN" in 2015 +** Not just TTCN-3 compiler, but also extensive documentations and many protocol modules, test ports as well as Eclipse IDE, Log file viewer/visualizer, etc. +* `eclipse-titan` part of standard Debian / Ubuntu archive, only one apt-get away + +Great, we can finally use TTCN-3 in FOSS! + +== Eclipse TITAN compiler workflow + +[graphviz] +---- +digraph G { +  progra [label="Human Developer"]; +  ttcn3 [label="TTCN-3 source (ATS)"]; +  cpp [label="Generated C++ source"]; +  exec [label="Binary Executable (ETS)"]; +  tps [label="Other C++ sources, as needed"]; +  progra -> ttcn3 [label="writes code"]; +  ttcn3 -> cpp [label="ttcn3_compiler"]; +  cpp -> exec [label="GNU gcc / g++"]; +  tps -> cpp +} +---- + +* TITAN actually _compiles_ into executable binaries, it is not using a VM or scripting +** ATS: Abstract Test Suite (source code) +** ETS: Executable Test Suite (executable code) + +== TTCN-3 Language Features (with TITAN) + +* comprehensive type system +* parametric templates +* variety of encoders/decoders +* automatic / comprehensive logging framework +* powerful program control statements +* built-in notion of tests cases, test suites, verdicts, ... +* runtime / executor for parallel test components + aggregating results + +== TTCN-3 Basic Types + +* Simple basic types such as `integer`, `float`, `boolen` +* Basic string types such as `bitstring`, `octetstring`, `hexstring`, `charstring` (IA5) and `universal charstring` (UCS-4). +* Structured Types `record`, `set`, `record of`, `set of` +* Verdict type `verdicttype` +** can have either value `none`, `pass`, `inconc`, `fail`,  or `error` +** verdict can only _deteriorate_ (`pass` -> `fail`) but never improve (`error` -> `pass`) +** every test case implicitly has a verdict, no need to explicitly declare a variable of `verdicttype` + +== TTCN-3 Structured Types + +A structured type is an abstract type comprised of other types, whcih can be nested. +An example for a `record` type (similar to a C-language `struct`) is shown below + +-------- +type record MyMessageType { +  integer field1 optional<1>, +  charstring field2, +  boolean field3 +}; +-------- +<1> optional members may be present or not + +== TTCN-3 Union Type + +A union expresses a set of alternative types of which one alternative must be chosen. + +-------- +type union MyMessageUnion { +  integer field1, +  charstring field2, +}; +-------- + +Difference to C-language union: `ischosen()` can be used to learn which of the union members is +chosen/defined! + + +== Not-used and omit + +* until a variable or field of structured type is assigned, it is _unbound_ +* whenever a _value_ is expected, TTCN-3 runtime will create an error for _unbound_ +* in case of absence of optional fields, explicit `omit` value must be assigned! + +== Sub-typing + +Sub-typing can be used to further constrain a given type.  Typical examples include constrained number ranges, +and string patterns + +-------- +type integer MyIntRange (1..100); +type integer MyIntRange8 (0..infinity); +type charstring MyCharRange (”k”..”w"); +type charstring SideType (”left”, ”right”); +type integer MyIntListRange (1..5,7,9); +type record length(0..10) of integer RecOfInt; +type charstring CrLfTermStrin (pattern ”*\r\n”); +-------- + +== Templates + +* Matching incoming messages against some kind of specification is one of the most common tasks in testing protocols +** some expected fields are static (message type) +** some expected fields are known (source address) +** some fields are chosen by sender (some identifier) +** some fields we don't care (optional headers that may or may not be present) +* TTCN-3 Templates provide elegant solution for this, avoiding any explicit code to be written +** templates can even be parametric, i.e. they can be instantiated with "arguments" +* templates can also be used for sending messages, if they are fully specified/qualified + +== Templates + +-------- +// Value list template +template charstring tr_SingleABorC := (”A”, ”B”, ”C”); +-------- + +-------- +// Value range +template float tr_NearPi := (3.14 .. 3.15); +template integer tr_FitsToOneByte := (0 .. 255); +template integer tr_GreaterThanZero := (1 .. infinity); +-------- + +-------- +// Intermixed value list and range matching +template integer tr_Intermixed := ((0..127), 200, 255); +-------- + +== Matching inside values + +-------- +// Using any element matching inside a bitstring value +// Last 2 bits can be '0' or '1' +template bitstring tr_AnyBSValue := ’101101??’B; +-------- + +-------- +// Matches charstrings with the first character "a" +// and the last one "z" +template charstring tr_0 := pattern "a*z"; +-------- + +* more capabilities using `complement`, `ifpresent`, `subset`, `superset`, `permutation` constructs not +  covered here + + +== Parametric Templates + +See below for an example of a parametric template: + +-------- +type record MyMessageType { +  integer field1 optional, +  charstring field2, +  boolean field3 +}; + +template MyMessageType trMyTemplte(boolean pl_param) := { +  field1 : = ?, // present, but any value +  field2 : = (”B”, ”O”, ”Q”) , +  field3 := pl_param +}; +-------- + +The built-in `match()` function can be used to check if a given value matches a given template.  Some TTCN-3 +statements such as `receive()` have built-in capabilities for template matching, avoiding even the explicit +call of `match()` in many cases. + +== Template Hierarchy + +Using modified templates, one can build a hierarchy of templates: From the specific to the unspecific + +---- +template MyMsgType t_MyMsgAny := { +  msg_type := ?, +  foo := bar +}; + +template MyMsgType t_MyMsg23 modifies t_MyMsgAny := { +  msg_type := 23, +}; +---- +where + +* _t_MyMsgAny_ matches a message with any message type and "foo=bar", while +* _t_MMyMsg23_ matches only those that have "foo=bar" and "msg_type=23" + +== Encoders/Decoders + +* type system, templates, matching are all nice and great, but we need to get data from wire format into +  TTCN-3 abstract types +* TTTCN-3 specifies importing of formal schema definitios, such as ASN.1, IDL, XSD (XML) and JSON +* TITAN has additional codecs for those (many) protocols that lack formal syntax +** `raw` codec for binary protocols (e.g. GTP) +** `text` codec for text based protocols (e.g. HTTP, MGCP, IMAP, ...) +* codecs allow you to _express/describe_ the format (declarative programming) rather than the usual imperative approach + +== TITAN raw codec: UDP Example + +How to express an UDP header using TITAN raw codec + +-------- +type integer LIN2_BO_LAST (0..65535) with { +  variant ”FIELDLENGTH(16), COMP(nosign), BYTEORDER(last)” +}; +type record UDP_header { +  LIN2_BO_LAST srcport, +  LIN2_BO_LAST dstport, +  LIN2_BO_LAST len, +  LIN2_BO_LAST cksum +} with { variant ”FIELDORDER(msb)” }; +type record UDP packet { +  UDP_header header +  octetstring payload +} with { +  variant (header) ”LENGTHTO(header, payload), LENGTHINDEX(len)” +}; +-------- + +== TITAN raw codec: GTP Example + +How to express an GTP header using TITAN raw codec + +-------- +type record GRE_Header { +  BIT1 csum_present, +  BIT1 rt_present, +  BIT1 key_present, +  ... +  OCT2 protocol_type, +  OCT2 checksum optional, +  OCT2 offset optional, +  OCT4 key otional, +  ... +} with { +  variant (checksum) "PRESENCE(csum_present='1', rt_present='1'B)" +  variant (offset) "PRESENCE(csum_present='1'B, rt_present='1'B)" +  variant (key) "PRESENCE(key_present='1'B)" +} +-------- + + +== TITAN text codec: MGCP Example + +-------- +type charstring MgcpVerb ("EPCF", "CRCX", "MDCX", "DLCX", "RQNT", "NTFY", +                          "AUEP", "AUCX", "RSIP") with { +  variant "TEXT_CODING(,convert=upper_case,,case_insensitive)" +}; +type charstring MgcpTransId     (pattern "\d#(1,9)"); +type charstring MgcpEndpoint    (pattern "*@*"); +type charstring MgcpVersion     (pattern "\d.\d") with { +  variant "BEGIN('MGCP ')" +}; +type record MgcpCommandLine { +        MgcpVerb        verb, +        MgcpTransId     trans_id, +        MgcpEndpoint    ep, +        MgcpVersion     ver +} with { +        variant "SEPARATOR(' ', '[\t ]+')" +        variant "END('\r\n', '([\r\n])|(\r\n)')" +}; +-------- + +== Program Control Statements + +* `if` / `else` like in C +* `select` statement similar to C `switch` +* `for`, `while`, `do-while` loops like in C +* `goto` and `label` +* `break` and `continue` like in C + +== Abstract Communications Operations + +* TTCN-3 test suites communicate with _implementation under test_ through abstract TestPorts +** TestPorts can be implemented in TTCN-3 or C++ and linked in +** TestPorts must be _connected_ before using send/receive operaitons +** TITAN provides TestPorts for e.g. packet socket, IP/UDP/TCP/SCTP socket, ... +* `<port>.send(<ValueRef>)` performs non-blocking send +** Literal value, constant, variable, specific value template, ... +* `<port>.receive(<TemplateRef>)` or `<port>.receive` performs blocking receive +** literal value, constant, variable, template (with matching!), inline template + +'... but if receive blocks, how can we wait for any of N events? + +== Program Control and Behavior + +* program statements are executed in order +* blocking statements block the execution of the component +* occurrence of unexpected event may cause infinite blocking + +---- +// x must be the first on queue P, y the second +P.receive(x); // Blocks until x appears on top of queue P +P.receive(y); // Blocks until y appears on top of queue P +// When y arrives first then P.receive(x) blocks -> error +---- + +This is what leads to the `alt` statement: +`alt` declares a seto alternatives covering all events, which + +* can happen: expected messages, timeouts, ... +* must not happen: unexpected faulty messages, no message received, ... +* all alternatives inside `alt` are blocking operations + +== The `alt` statement + +---- +P.send(req) +T.start; +// ... +alt { +[] P.receive(resp)  { /* actions to do and exit alt */ } +[] any port.receive { /* handle unexpected event */ } +[] T.timeout        { /* handle timer expiry and exit */ } +} +---- + +* [] is guard condition enables or disables the alternative +** usually empty `[]` equals `[true]` +** can contain a condition like `[x > 0]` +** very good for e.g. state machines to activate some alternatives only in certain states while others may +occur in any state + + +== The `alt` and `repeat` statements + +The `repeat` statement + +* takes a new snapshot and re-evaluates the alt statement +* can appear as last statement in statement blocks of statements + +---- +P.send(req) +T.start; +alt { +  [] P.receive(resp)       { /* actions to do and exit alt */ } +  [] P.receive(keep_alive) { /* handle keep alive message */ +                             repeat } +  [] any port.receive      { /* handle unexpected event */ } +  [] T.timeout             { /* handle timer expiry and exit */ } +} +---- + +== The `interleave` statement + +The `interleave` statement + +* enforces N matching events happen exactly once +* permits any order! + +---- +interleave { +  [] P.receive(1) { /* actions, optional */ } +  [] Q.receive(4) { /* actions, optional */ } +  [] R.receive(6) { /* actions, optional */ } +} +---- + +== The `altstep` construct + +* collection of set of common/shared `alt`  +** such as responding to a keep-alive PING, depending on protocol +* invoked in-line, inside `alt` statements or _activated as default_ + +Definition of an `altstep` for PING/PONG handling and guard timer: + +---- +altstep my_altstep() { +  [] P.receive(tr_PING) { P.send(ts_PONG); } +  [] T_guard.timeout { setverdict(fail, "Guard timer timed out"); } +} +---- + +== The `altstep` construct + +Explicit Usage of the `my_altstep` defined on previous slide: + +---- +P.send(foo) +alt { +  [] P.receive(bar) +  [] my_altstep() +} +---- + +this dynamically adds the alternatives from `my_altstep` at the given location +and priority of the above `alt`, ensuring that one doesn't have to repeat +ping/pong handling as well as global guard timer timeout handling in every `alt` +or every single test case all over again. + + +== default `altstep` activation + +* in previous slide, `altstep` invocation was explicit +* some behavior is so universal, that you want to activate it _always_ + +---- +altstep as_pingpong() { +  [] P.receive(tr_PING) { P.send(ts_PONG); } +} +... +var default d1 := activate(as_pingpong()); +... /* code executing with as_pingpong activated */ +deactivate(d1); +---- + +* all alt-statements implicitly have as_pingpong active +* but also all _stand-alone receive statements_ ! + +== TTCN-3 modules + +TTCN-3 code is written in _modules_ + +* a test suite consists of one or more modules +* a module contains _module definitions_ and an optional _control part_ +** _parameters_ (automatically configurable via config file) +** definition of _data types_, _constants_, _templates_ +** definition of _communications ports_ +** definition of _test components_, _functions_ _altsteps_ and _test cases_ +** _control part_ determines default order/execution of test cases +* modules can import from each other (think in python terms) + +== Examples + +Let's have a look at some real-world examples and do a bit of a walk-through +before continuing with the slides... + +== Logging + +* TITAN runtime contains extensive logging framework +* config file determines log level for various different subsystems +** e.g. any encode, decode, receive, transmit operations logged +** timer starts, expirations +** any changes to test case verdict +* explicit logging from code by use of `log()` built-in function +* `ttcn3_logformat` tool for pretty-printing log files +* `ttcn3_logmerge` tool for merging/splicing multiple logs +* log plugins e.g. for generating JUnit-XML available +** facilitates easy reporting / integration to Jenkins or other CI + +== Logging + +Log file format example: + +---- +// abstract data type before encode +13:30:41.243536 Sent on GTPC to system @GTP_CodecPort.Gtp1cUnitdata : { peer := { connId := 1, remName := "127.0.23.1", remPort := 2123 }, gtpc := { pn_bit := '0'B, s_bit := '1'B, e_bit := '0'B, spare := '0'B, pt := '1'B, version := '001'B, messageType := '01'O, lengthf := 0, teid := '00000000'O, opt_part := { sequenceNumber := '3AAC'O, npduNumber := '00'O, nextExtHeader := '00'O, gTPC_extensionHeader_List := omit }, gtpc_pdu := { echoRequest := { private_extension_gtpc := omit } } } } + +// 'msg' contains encoded binary data actually sent via socket +13:30:41.243799 Outgoing message was mapped to @IPL4asp_Types.ASP_SendTo : { connId := 1, remName := "127.0.23.1", remPort := 2123, proto := { udp := { } }, msg := '32010004000000003AAC0000'O } +---- + +== Logging + +The same log file lines if run through `ttcn3_logformat` + +---- +13:30:41.243536 Sent on GTPC to system @GTP_CodecPort.Gtp1cUnitdata : { +    peer := { +        connId := 1, +        remName := "127.0.23.1", +        remPort := 2123 +    }, +    gtpc := { +        pn_bit := '0'B, +        s_bit := '1'B, +        e_bit := '0'B, +        spare := '0'B, +        pt := '1'B, +        version := '001'B, +        messageType := '01'O, +        lengthf := 0, +        teid := '00000000'O, +        opt_part := { +            sequenceNumber := '3AAC'O, +            npduNumber := '00'O, +            nextExtHeader := '00'O, +            gTPC_extensionHeader_List := omit +        }, +        gtpc_pdu := { +            echoRequest := { +                private_extension_gtpc := omit +            } +        } +    } +} +13:30:41.243799 Outgoing message was mapped to @IPL4asp_Types.ASP_SendTo : { +    connId := 1, +    remName := "127.0.23.1", +    remPort := 2123, +    proto := { +        udp := { } +    }, +    msg := '32010004000000003AAC0000'O +} +---- + + +== Existing TITAN Source + +* Protocol encoding/decoding +** BSSAP+, BSSGP, BSSMAP, CoAP, DSS1, DUA, EAP, GRE, GTP, HTTP, ISUP, LLC, M2PA, M2UA, MQTT, MongoDB, NDP, NS, +   NTAF, ROSE, SCTP, SDP, SNDCP, STOMP, STUN, SUA, TLS, WTP, DNS, IP, SMPP, SNMP, IKEv2, DHCP, PPP, RTP, TCP, +   UDP, XMPP, DHCPv6, SMTP, ICMP, RTSP, ICMPv6, DIAMETER, FrameRelay, ProtoBuff, IUA, L2TP, M3UA, MIME, +   WebSocket, H.248, IMAP, IPsec, SRTP, MSRP, ICAP, RADIUS +* Protocol Emulation +** M3UA, SCCP, SUA +* Test Ports +** GPIO, MTP3, Serial, SocketCAN, SCTP, SIP, HTTP, Telnet, UDP, pcap file, pipe, SQL, TCP, SUNRPC, SSH, STDINOUT, sockets, LDAP + + +== Further Reading + +* Ericsson TTCN-3 tutorial http://www.ttcn-3.org/files/TTCN3_P.pdf +* An Introduction to TTCN-3, 2nd Edition <http://www.wiley.com/go/willcock_TTCN-3_2e> +* Modules https://github.com/eclipse +* More Modules http://git.eclipse.org/ +* Debian https://packages.debian.org/search?keywords=eclipse-titan +* Ubuntu https://packages.ubuntu.com/search?keywords=eclipse-titan + +== EOF + +End of File diff --git a/2018/ttcn-eh2018/ttcn3.html b/2018/ttcn-eh2018/ttcn3.html new file mode 100644 index 0000000..115b179 --- /dev/null +++ b/2018/ttcn-eh2018/ttcn3.html @@ -0,0 +1,5126 @@ +<?xml version="1.0" encoding="UTF-8"?>
 +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 +<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
 +<head>
 +<title>TTCN-3 and Eclipse TITAN for testing protocol stacks</title>
 +<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />
 +<meta name="copyright" content="Copyright © 2017-2018 by Harald Welte (License: CC-BY-SA)" />
 +<meta name="generator" content="AsciiDoc 8.6.10" />
 +<style type="text/css">
 +/* Shared CSS for AsciiDoc xhtml11 and html5 backends */
 +
 +/* Default font. */
 +body {
 +  font-family: Georgia,serif;
 +}
 +
 +/* Title font. */
 +h1, h2, h3, h4, h5, h6,
 +div.title, caption.title,
 +thead, p.table.header,
 +#toctitle,
 +#author, #revnumber, #revdate, #revremark,
 +#footer {
 +  font-family: Arial,Helvetica,sans-serif;
 +}
 +
 +body {
 +  margin: 1em 5% 1em 5%;
 +}
 +
 +a {
 +  color: blue;
 +  text-decoration: underline;
 +}
 +a:visited {
 +  color: fuchsia;
 +}
 +
 +em {
 +  font-style: italic;
 +  color: navy;
 +}
 +
 +strong {
 +  font-weight: bold;
 +  color: #083194;
 +}
 +
 +h1, h2, h3, h4, h5, h6 {
 +  color: #527bbd;
 +  margin-top: 1.2em;
 +  margin-bottom: 0.5em;
 +  line-height: 1.3;
 +}
 +
 +h1, h2, h3 {
 +  border-bottom: 2px solid silver;
 +}
 +h2 {
 +  padding-top: 0.5em;
 +}
 +h3 {
 +  float: left;
 +}
 +h3 + * {
 +  clear: left;
 +}
 +h5 {
 +  font-size: 1.0em;
 +}
 +
 +div.sectionbody {
 +  margin-left: 0;
 +}
 +
 +hr {
 +  border: 1px solid silver;
 +}
 +
 +p {
 +  margin-top: 0.5em;
 +  margin-bottom: 0.5em;
 +}
 +
 +ul, ol, li > p {
 +  margin-top: 0;
 +}
 +ul > li     { color: #aaa; }
 +ul > li > * { color: black; }
 +
 +.monospaced, code, pre {
 +  font-family: "Courier New", Courier, monospace;
 +  font-size: inherit;
 +  color: navy;
 +  padding: 0;
 +  margin: 0;
 +}
 +pre {
 +  white-space: pre-wrap;
 +}
 +
 +#author {
 +  color: #527bbd;
 +  font-weight: bold;
 +  font-size: 1.1em;
 +}
 +#email {
 +}
 +#revnumber, #revdate, #revremark {
 +}
 +
 +#footer {
 +  font-size: small;
 +  border-top: 2px solid silver;
 +  padding-top: 0.5em;
 +  margin-top: 4.0em;
 +}
 +#footer-text {
 +  float: left;
 +  padding-bottom: 0.5em;
 +}
 +#footer-badges {
 +  float: right;
 +  padding-bottom: 0.5em;
 +}
 +
 +#preamble {
 +  margin-top: 1.5em;
 +  margin-bottom: 1.5em;
 +}
 +div.imageblock, div.exampleblock, div.verseblock,
 +div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
 +div.admonitionblock {
 +  margin-top: 1.0em;
 +  margin-bottom: 1.5em;
 +}
 +div.admonitionblock {
 +  margin-top: 2.0em;
 +  margin-bottom: 2.0em;
 +  margin-right: 10%;
 +  color: #606060;
 +}
 +
 +div.content { /* Block element content. */
 +  padding: 0;
 +}
 +
 +/* Block element titles. */
 +div.title, caption.title {
 +  color: #527bbd;
 +  font-weight: bold;
 +  text-align: left;
 +  margin-top: 1.0em;
 +  margin-bottom: 0.5em;
 +}
 +div.title + * {
 +  margin-top: 0;
 +}
 +
 +td div.title:first-child {
 +  margin-top: 0.0em;
 +}
 +div.content div.title:first-child {
 +  margin-top: 0.0em;
 +}
 +div.content + div.title {
 +  margin-top: 0.0em;
 +}
 +
 +div.sidebarblock > div.content {
 +  background: #ffffee;
 +  border: 1px solid #dddddd;
 +  border-left: 4px solid #f0f0f0;
 +  padding: 0.5em;
 +}
 +
 +div.listingblock > div.content {
 +  border: 1px solid #dddddd;
 +  border-left: 5px solid #f0f0f0;
 +  background: #f8f8f8;
 +  padding: 0.5em;
 +}
 +
 +div.quoteblock, div.verseblock {
 +  padding-left: 1.0em;
 +  margin-left: 1.0em;
 +  margin-right: 10%;
 +  border-left: 5px solid #f0f0f0;
 +  color: #888;
 +}
 +
 +div.quoteblock > div.attribution {
 +  padding-top: 0.5em;
 +  text-align: right;
 +}
 +
 +div.verseblock > pre.content {
 +  font-family: inherit;
 +  font-size: inherit;
 +}
 +div.verseblock > div.attribution {
 +  padding-top: 0.75em;
 +  text-align: left;
 +}
 +/* DEPRECATED: Pre version 8.2.7 verse style literal block. */
 +div.verseblock + div.attribution {
 +  text-align: left;
 +}
 +
 +div.admonitionblock .icon {
 +  vertical-align: top;
 +  font-size: 1.1em;
 +  font-weight: bold;
 +  text-decoration: underline;
 +  color: #527bbd;
 +  padding-right: 0.5em;
 +}
 +div.admonitionblock td.content {
 +  padding-left: 0.5em;
 +  border-left: 3px solid #dddddd;
 +}
 +
 +div.exampleblock > div.content {
 +  border-left: 3px solid #dddddd;
 +  padding-left: 0.5em;
 +}
 +
 +div.imageblock div.content { padding-left: 0; }
 +span.image img { border-style: none; vertical-align: text-bottom; }
 +a.image:visited { color: white; }
 +
 +dl {
 +  margin-top: 0.8em;
 +  margin-bottom: 0.8em;
 +}
 +dt {
 +  margin-top: 0.5em;
 +  margin-bottom: 0;
 +  font-style: normal;
 +  color: navy;
 +}
 +dd > *:first-child {
 +  margin-top: 0.1em;
 +}
 +
 +ul, ol {
 +    list-style-position: outside;
 +}
 +ol.arabic {
 +  list-style-type: decimal;
 +}
 +ol.loweralpha {
 +  list-style-type: lower-alpha;
 +}
 +ol.upperalpha {
 +  list-style-type: upper-alpha;
 +}
 +ol.lowerroman {
 +  list-style-type: lower-roman;
 +}
 +ol.upperroman {
 +  list-style-type: upper-roman;
 +}
 +
 +div.compact ul, div.compact ol,
 +div.compact p, div.compact p,
 +div.compact div, div.compact div {
 +  margin-top: 0.1em;
 +  margin-bottom: 0.1em;
 +}
 +
 +tfoot {
 +  font-weight: bold;
 +}
 +td > div.verse {
 +  white-space: pre;
 +}
 +
 +div.hdlist {
 +  margin-top: 0.8em;
 +  margin-bottom: 0.8em;
 +}
 +div.hdlist tr {
 +  padding-bottom: 15px;
 +}
 +dt.hdlist1.strong, td.hdlist1.strong {
 +  font-weight: bold;
 +}
 +td.hdlist1 {
 +  vertical-align: top;
 +  font-style: normal;
 +  padding-right: 0.8em;
 +  color: navy;
 +}
 +td.hdlist2 {
 +  vertical-align: top;
 +}
 +div.hdlist.compact tr {
 +  margin: 0;
 +  padding-bottom: 0;
 +}
 +
 +.comment {
 +  background: yellow;
 +}
 +
 +.footnote, .footnoteref {
 +  font-size: 0.8em;
 +}
 +
 +span.footnote, span.footnoteref {
 +  vertical-align: super;
 +}
 +
 +#footnotes {
 +  margin: 20px 0 20px 0;
 +  padding: 7px 0 0 0;
 +}
 +
 +#footnotes div.footnote {
 +  margin: 0 0 5px 0;
 +}
 +
 +#footnotes hr {
 +  border: none;
 +  border-top: 1px solid silver;
 +  height: 1px;
 +  text-align: left;
 +  margin-left: 0;
 +  width: 20%;
 +  min-width: 100px;
 +}
 +
 +div.colist td {
 +  padding-right: 0.5em;
 +  padding-bottom: 0.3em;
 +  vertical-align: top;
 +}
 +div.colist td img {
 +  margin-top: 0.3em;
 +}
 +
 +@media print {
 +  #footer-badges { display: none; }
 +}
 +
 +#toc {
 +  margin-bottom: 2.5em;
 +}
 +
 +#toctitle {
 +  color: #527bbd;
 +  font-size: 1.1em;
 +  font-weight: bold;
 +  margin-top: 1.0em;
 +  margin-bottom: 0.1em;
 +}
 +
 +div.toclevel0, div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
 +  margin-top: 0;
 +  margin-bottom: 0;
 +}
 +div.toclevel2 {
 +  margin-left: 2em;
 +  font-size: 0.9em;
 +}
 +div.toclevel3 {
 +  margin-left: 4em;
 +  font-size: 0.9em;
 +}
 +div.toclevel4 {
 +  margin-left: 6em;
 +  font-size: 0.9em;
 +}
 +
 +span.aqua { color: aqua; }
 +span.black { color: black; }
 +span.blue { color: blue; }
 +span.fuchsia { color: fuchsia; }
 +span.gray { color: gray; }
 +span.green { color: green; }
 +span.lime { color: lime; }
 +span.maroon { color: maroon; }
 +span.navy { color: navy; }
 +span.olive { color: olive; }
 +span.purple { color: purple; }
 +span.red { color: red; }
 +span.silver { color: silver; }
 +span.teal { color: teal; }
 +span.white { color: white; }
 +span.yellow { color: yellow; }
 +
 +span.aqua-background { background: aqua; }
 +span.black-background { background: black; }
 +span.blue-background { background: blue; }
 +span.fuchsia-background { background: fuchsia; }
 +span.gray-background { background: gray; }
 +span.green-background { background: green; }
 +span.lime-background { background: lime; }
 +span.maroon-background { background: maroon; }
 +span.navy-background { background: navy; }
 +span.olive-background { background: olive; }
 +span.purple-background { background: purple; }
 +span.red-background { background: red; }
 +span.silver-background { background: silver; }
 +span.teal-background { background: teal; }
 +span.white-background { background: white; }
 +span.yellow-background { background: yellow; }
 +
 +span.big { font-size: 2em; }
 +span.small { font-size: 0.6em; }
 +
 +span.underline { text-decoration: underline; }
 +span.overline { text-decoration: overline; }
 +span.line-through { text-decoration: line-through; }
 +
 +div.unbreakable { page-break-inside: avoid; }
 +
 +
 +/*
 + * xhtml11 specific
 + *
 + * */
 +
 +div.tableblock {
 +  margin-top: 1.0em;
 +  margin-bottom: 1.5em;
 +}
 +div.tableblock > table {
 +  border: 3px solid #527bbd;
 +}
 +thead, p.table.header {
 +  font-weight: bold;
 +  color: #527bbd;
 +}
 +p.table {
 +  margin-top: 0;
 +}
 +/* Because the table frame attribute is overriden by CSS in most browsers. */
 +div.tableblock > table[frame="void"] {
 +  border-style: none;
 +}
 +div.tableblock > table[frame="hsides"] {
 +  border-left-style: none;
 +  border-right-style: none;
 +}
 +div.tableblock > table[frame="vsides"] {
 +  border-top-style: none;
 +  border-bottom-style: none;
 +}
 +
 +
 +/*
 + * html5 specific
 + *
 + * */
 +
 +table.tableblock {
 +  margin-top: 1.0em;
 +  margin-bottom: 1.5em;
 +}
 +thead, p.tableblock.header {
 +  font-weight: bold;
 +  color: #527bbd;
 +}
 +p.tableblock {
 +  margin-top: 0;
 +}
 +table.tableblock {
 +  border-width: 3px;
 +  border-spacing: 0px;
 +  border-style: solid;
 +  border-color: #527bbd;
 +  border-collapse: collapse;
 +}
 +th.tableblock, td.tableblock {
 +  border-width: 1px;
 +  padding: 4px;
 +  border-style: solid;
 +  border-color: #527bbd;
 +}
 +
 +table.tableblock.frame-topbot {
 +  border-left-style: hidden;
 +  border-right-style: hidden;
 +}
 +table.tableblock.frame-sides {
 +  border-top-style: hidden;
 +  border-bottom-style: hidden;
 +}
 +table.tableblock.frame-none {
 +  border-style: hidden;
 +}
 +
 +th.tableblock.halign-left, td.tableblock.halign-left {
 +  text-align: left;
 +}
 +th.tableblock.halign-center, td.tableblock.halign-center {
 +  text-align: center;
 +}
 +th.tableblock.halign-right, td.tableblock.halign-right {
 +  text-align: right;
 +}
 +
 +th.tableblock.valign-top, td.tableblock.valign-top {
 +  vertical-align: top;
 +}
 +th.tableblock.valign-middle, td.tableblock.valign-middle {
 +  vertical-align: middle;
 +}
 +th.tableblock.valign-bottom, td.tableblock.valign-bottom {
 +  vertical-align: bottom;
 +}
 +
 +
 +/*
 + * manpage specific
 + *
 + * */
 +
 +body.manpage h1 {
 +  padding-top: 0.5em;
 +  padding-bottom: 0.5em;
 +  border-top: 2px solid silver;
 +  border-bottom: 2px solid silver;
 +}
 +body.manpage h2 {
 +  border-style: none;
 +}
 +body.manpage div.sectionbody {
 +  margin-left: 3em;
 +}
 +
 +@media print {
 +  body.manpage div#toc { display: none; }
 +}
 +/* slidy.css
 +
 +   Copyright (c) 2005-2010 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
 +*/
 +
 +/*
 +   SJR: 2010-09-29: Modified for AsciiDoc slidy backend.
 +   Mostly just commented out stuff that is handled by AsciiDoc's CSS files.
 +*/
 +
 +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;
 +}
 +
 +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-color: rgb(240,240,240);
 +  border-top: solid 1px rgb(180,180,180);
 +}
 +
 +div.toolbar span.copyright {
 +  color: black;
 +  margin-left: 0.5em;
 +}
 +
 +div.initial_prompt {
 +  position: absolute;
 +  z-index: 1000;
 +  bottom: 1.2em;
 +  width: 90%;
 +  background-color: rgb(200,200,200);
 +  opacity: 0.35;
 +  background-color: rgb(200,200,200, 0.35);
 +  cursor: pointer;
 +}
 +
 +div.initial_prompt p.help {
 +  text-align: center;
 +}
 +
 +div.initial_prompt p.close {
 +  text-align: right;
 +  font-style: italic;
 +}
 +
 +div.slidy_toc {
 +  position: absolute;
 +  z-index: 300;
 +  width: 60%;
 +  max-width: 30em;
 +  height: 30em;
 +  overflow: auto;
 +  top: auto;
 +  right: auto;
 +  left: 4em;
 +  bottom: 4em;
 +  padding: 1em;
 +  background: rgb(240,240,240);
 +  border-style: solid;
 +  border-width: 2px;
 +  font-size: 60%;
 +}
 +
 +div.slidy_toc .toc_heading {
 +  text-align: center;
 +  width: 100%;
 +  margin: 0;
 +  margin-bottom: 1em;
 +  border-bottom-style: solid;
 +  border-bottom-color: rgb(180,180,180);
 +  border-bottom-width: 1px;
 +}
 +
 +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;
 +  clear: both;
 +  top: 0;
 +  bottom: 0;
 +  left: 0;
 +  right: 0;
 +  line-height: 120%;
 +  background-color: transparent;
 +}
 +
 +div.background {
 +  display: none;
 +}
 +
 +div.handout {
 +  margin-left: 20px;
 +  margin-right: 20px;
 +}
 +
 +div.slide.titlepage {
 +  text-align: center;
 +}
 +
 +div.slide.titlepage.h1 {
 +  padding-top: 10%;
 +}
 +
 +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; }
 +
 +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(../graphics/nofold-dim.gif) no-repeat 0px 0.5em;
 +}
 +ol.outline li.unfolded {
 +  padding: 0 0 0 20px;
 +  background: transparent url(../graphics/fold-dim.gif) no-repeat 0px 0.5em;
 +}
 +ol.outline li.folded {
 +  padding: 0 0 0 20px;
 +  background: transparent url(../graphics/unfold-dim.gif) no-repeat 0px 0.5em;
 +}
 +ol.outline li.unfolded:hover {
 +  padding: 0 0 0 20px;
 +  background: transparent url(../graphics/fold.gif) no-repeat 0px 0.5em;
 +}
 +ol.outline li.folded:hover {
 +  padding: 0 0 0 20px;
 +  background: transparent url(../graphics/unfold.gif) no-repeat 0px 0.5em;
 +}
 +
 +ul.outline li.nofold {
 +  padding: 0 0 0 20px;
 +  background: transparent url(../graphics/nofold-dim.gif) no-repeat 0px 0.5em;
 +}
 +ul.outline li.unfolded {
 +  padding: 0 0 0 20px;
 +  background: transparent url(../graphics/fold-dim.gif) no-repeat 0px 0.5em;
 +}
 +ul.outline li.folded {
 +  padding: 0 0 0 20px;
 +  background: transparent url(../graphics/unfold-dim.gif) no-repeat 0px 0.5em;
 +}
 +ul.outline li.unfolded:hover {
 +  padding: 0 0 0 20px;
 +  background: transparent url(../graphics/fold.gif) no-repeat 0px 0.5em;
 +}
 +ul.outline li.folded:hover {
 +  padding: 0 0 0 20px;
 +  background: transparent url(../graphics/unfold.gif) no-repeat 0px 0.5em;
 +}
 +
 +/* for slides with class "title" in table of contents */
 +a.titleslide { font-weight: bold; font-style: italic }
 +
 +/*
 + hide images for work around for save as bug
 + where browsers fail to save images used by CSS
 +*/
 +img.hidden { display: none; visibility: hidden }
 +div.initial_prompt { display: none; visibility: hidden }
 +
 +  div.slide {
 +     visibility: visible;
 +     position: inherit;
 +  }
 +  div.handout {
 +     border-top-style: solid;
 +     border-top-width: thin;
 +     border-top-color: black;
 +  }
 +
 +@media screen {
 +  .hidden { display: none; visibility: visible }
 +
 +  div.slide.hidden { display: block; visibility: visible }
 +  div.handout.hidden { display: block; visibility: visible }
 +  div.background { display: none; visibility: hidden }
 +  body.single_slide div.initial_prompt { display: block; visibility: visible }
 +  body.single_slide div.background { display: block; visibility: visible }
 +  body.single_slide div.background.hidden { display: none; visibility: hidden }
 +  body.single_slide .invisible { visibility: hidden }
 +  body.single_slide .hidden { display: none; visibility: hidden }
 +  body.single_slide div.slide { position: absolute }
 +  body.single_slide div.handout { display: none; visibility: hidden }
 +}
 +
 +@media print {
 +  .hidden { display: block; visibility: visible }
 +
 +/*
 +  div.slide pre { font-size: 60%; padding-left: 0.5em; }
 +*/
 +  div.toolbar { display: none; visibility: hidden; }
 +  div.slidy_toc { display: none; visibility: hidden; }
 +  div.background { display: none; visibility: hidden; }
 +  div.slide { page-break-before: always }
 +  /* :first-child isn't reliable for print media */
 +  div.slide.first-slide { page-break-before: avoid }
 +}
 +
 +
 +/* SJR: AsciiDoc slidy backend tweaks */
 +
 +ol, ul {
 +  margin: 0.8em 1.5em 0.8em 1.8em;
 +}
 +li > ul, li > ol {
 +  margin-top: 0.5em;
 +}
 +
 +.outline > li.folded,
 +.outline > li.unfolded {
 +  color: #527bbd;
 +}
 +ul > li{ color: #aaa; }
 +ul > li > *, ol > li > * { color: black; }
 +
 +li {
 +  margin-top: 0.5em;
 +  margin-bottom: 0.5em;
 +}
 +
 +
 +</style>
 +<script type="text/javascript">
 +/*<![CDATA[*/
 +/* slidy.js
 +
 +   Copyright (c) 2005-2010 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
 +*/
 +
 +// the slidy object implementation
 +var w3c_slidy = {
 +  // classify which kind of browser we're running under
 +  ns_pos: (typeof window.pageYOffset!='undefined'),
 +  khtml: ((navigator.userAgent).indexOf("KHTML") >= 0 ? true : false),
 +  opera: ((navigator.userAgent).indexOf("Opera") >= 0 ? true : false),
 +  ipad: ((navigator.userAgent).indexOf("iPad") >= 0 ? true : false),
 +  iphone: ((navigator.userAgent).indexOf("iPhone") >= 0 ? true : false),
 +  ie: (typeof document.all != "undefined" && !this.opera),
 +  ie6: (!this.ns_pos && navigator.userAgent.indexOf("MSIE 6") != -1),
 +  ie7: (!this.ns_pos && navigator.userAgent.indexOf("MSIE 7") != -1),
 +  ie8: (!this.ns_pos && navigator.userAgent.indexOf("MSIE 8") != -1),
 +  ie9: (!this.ns_pos && navigator.userAgent.indexOf("MSIE 9") != -1),
 +  keyboardless: (this.ipad || this.iphone),
 +
 +  // are we running as XHTML? (doesn't work on Opera)
 +  is_xhtml: /xml/.test(document.contentType),
 +
 +  slide_number: 0, // integer slide count: 0, 1, 2, ...
 +  slide_number_element: null, // element containing slide number
 +  slides: [], // set to array of slide div's
 +  notes: [], // set to array of handout div's
 +  backgrounds: [], // set to array of background div's
 +  toolbar: null, // element containing toolbar
 +  title: null, // document title
 +  last_shown: null, // last incrementally shown item
 +  eos: null,  // span element for end of slide indicator
 +  toc: null, // table of contents
 +  outline: null, // outline element with the focus
 +  selected_text_len: 0, // length of drag selection on document
 +  view_all: 0,  // 1 to view all slides + handouts
 +  want_toolbar: true,  // user preference to show/hide toolbar
 +  mouse_click_enabled: true, // enables left click for next slide
 +  scroll_hack: 0, // IE work around for position: fixed
 +  disable_slide_click: false,  // used by clicked anchors
 +
 +  lang: "en", // updated to language specified by html file
 +
 +  help_anchor: null, // used for keyboard focus hack in showToolbar()
 +  help_page: "http://www.w3.org/Talks/Tools/Slidy2/help/help.html",
 +  help_text: "Navigate with mouse click, space bar, Cursor Left/Right, " +
 +             "or Pg Up and Pg Dn. Use S and B to change font size.",
 +
 +  size_index: 0,
 +  size_adjustment: 0,
 +  sizes:  new Array("10pt", "12pt", "14pt", "16pt", "18pt", "20pt",
 +                    "22pt", "24pt", "26pt", "28pt", "30pt", "32pt"),
 +
 +  // needed for efficient resizing
 +  last_width: 0,
 +  last_height: 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
 +   objects: [],
 +
 +  // attach initialiation event handlers
 +  set_up: function () {
 +    var init = function() { w3c_slidy.init(); };
 +    if (typeof window.addEventListener != "undefined")
 +      window.addEventListener("load", init, false);
 +    else
 +      window.attachEvent("onload", init);
 +  },
 +
 +  hide_slides: function () {
 +    if (document.body && !w3c_slidy.initialized)
 +      document.body.style.visibility = "hidden";
 +    else
 +      setTimeout(w3c_slidy.hide_slides, 50);
 +  },
 +
 +  // hack to persuade IE to compute correct document height
 +  // as needed for simulating fixed positioning of toolbar
 +  ie_hack: function () {
 +    window.resizeBy(0,-1);
 +    window.resizeBy(0, 1);
 +  },
 +
 +  init: function () {
 +    //alert("slidy starting test 10");
 +    document.body.style.visibility = "visible";
 +    w3c_slidy_i18n.init();
 +    this.add_toolbar();
 +    this.wrap_implicit_slides();
 +    this.collect_slides();
 +    this.collect_notes();
 +    this.collect_backgrounds();
 +    this.objects = document.body.getElementsByTagName("object");
 +    this.patch_anchors();
 +    this.slide_number = this.find_slide_number(location.href);
 +    window.offscreenbuffering = true;
 +    this.size_adjustment = this.find_size_adjust();
 +    this.time_left = this.find_duration();
 +    this.hide_image_toolbar();  // suppress IE image toolbar popup
 +    this.init_outliner();  // activate fold/unfold support
 +    this.title = document.title;
 +
 +    // work around for opera bug
 +    this.is_xhtml = (document.body.tagName == "BODY" ? false : true);
 +
 +    if (this.slides.length > 0)
 +    {
 +      var slide = this.slides[this.slide_number];
 +
 +      if (this.slide_number > 0)
 +      {
 +        this.set_visibility_all_incremental("visible");
 +        this.last_shown = this.previous_incremental_item(null);
 +        this.set_eos_status(true);
 +      }
 +      else
 +      {
 +        this.last_shown = null;
 +        this.set_visibility_all_incremental("hidden");
 +        this.set_eos_status(!this.next_incremental_item(this.last_shown));
 +      }
 +
 +      this.set_location();
 +      this.add_class(this.slides[0], "first-slide");
 +      w3c_slidy.show_slide(slide);
 +    }
 +
 +    this.toc = this.table_of_contents();
 +
 +    this.add_initial_prompt();
 +
 +    // bind event handlers without interfering with custom page scripts
 +    // Tap events behave too weirdly to support clicks reliably on
 +    // iPhone and iPad, so exclude these from click handler
 +
 +    if (!this.keyboardless)
 +      this.add_listener(document.body, "click", this.mouse_button_click);
 +
 +    this.add_listener(document, "keydown", this.key_down);
 +    this.add_listener(document, "keypress", this.key_press);
 +    this.add_listener(window, "resize", this.resized);
 +    this.add_listener(window, "scroll", this.scrolled);
 +    this.add_listener(window, "unload", this.unloaded);
 +
 +    if (!document.body.onclick)
 +      document.body.onclick = function () { };
 +
 +    this.single_slide_view();
 +
 +    //this.set_location();
 +
 +    this.resized();
 +
 +    if (this.ie7)
 +      setTimeout(w3c_slidy.ie_hack, 100);
 +
 +    this.show_toolbar();
 +
 +    // for back button detection
 +    setInterval(function () { w3c_slidy.check_location(); }, 200);
 +    w3c_slidy.initialized = true;
 +  },
 +
 +  // create div element with links to each slide
 +  table_of_contents: function () {
 +    var toc = this.create_element("div");
 +    this.add_class(toc, "slidy_toc hidden");
 +    //toc.setAttribute("tabindex", "0");
 +
 +    var heading = this.create_element("div");
 +    this.add_class(heading, "toc-heading");
 +    heading.innerHTML = "Table of Contents".localize();
 +
 +    toc.appendChild(heading);
 +    var previous = null;
 +
 +    for (var i = 0; i < this.slides.length; ++i)
 +    {
 +      var title = this.has_class(this.slides[i], "title");
 +      var num = document.createTextNode((i + 1) + ". ");
 +
 +      toc.appendChild(num);
 +
 +      var a = this.create_element("a");
 +      a.setAttribute("href", "#(" + (i+1) + ")");
 +
 +      if (title)
 +        this.add_class(a, "titleslide");
 +
 +      var name = document.createTextNode(this.slide_name(i));
 +      a.appendChild(name);
 +      a.onclick = w3c_slidy.toc_click;
 +      a.onkeydown = w3c_slidy.toc_keydown;
 +      a.previous = previous;
 +
 +      if (previous)
 +        previous.next = a;
 +
 +      toc.appendChild(a);
 +
 +      if (i == 0)
 +        toc.first = a;
 +
 +      if (i < this.slides.length - 1)
 +      {
 +        var br = this.create_element("br");
 +        toc.appendChild(br);
 +      }
 +
 +      previous = a;
 +    }
 +
 +    toc.focus = function () {
 +      if (this.first)
 +        this.first.focus();
 +    }
 +
 +    toc.onmouseup = w3c_slidy.mouse_button_up;
 +
 +    toc.onclick = function (e) {
 +      e||(e=window.event);
 +
 +      if (w3c_slidy.selected_text_len <= 0)
 +         w3c_slidy.hide_table_of_contents();
 +
 +      w3c_slidy.stop_propagation(e);
 +
 +      if (e.cancel != undefined)
 +        e.cancel = true;
 +
 +      if (e.returnValue != undefined)
 +        e.returnValue = false;
 +
 +      return false;
 +    };
 +
 +    document.body.insertBefore(toc, document.body.firstChild);
 +    return toc;
 +  },
 +
 +  is_shown_toc: function () {
 +    return !w3c_slidy.has_class(w3c_slidy.toc, "hidden");
 +  },
 +
 +  show_table_of_contents: function () {
 +    w3c_slidy.remove_class(w3c_slidy.toc, "hidden");
 +    var toc = w3c_slidy.toc;
 +    toc.focus();
 +
 +    if (w3c_slidy.ie7 && w3c_slidy.slide_number == 0)
 +      setTimeout(w3c_slidy.ie_hack, 100);
 +  },
 +
 +  hide_table_of_contents: function () {
 +    w3c_slidy.add_class(w3c_slidy.toc, "hidden");
 +
 +    if (!w3c_slidy.opera)
 +      w3c_slidy.help_anchor.focus();
 +  },
 +
 +  toggle_table_of_contents: function () {
 +    if (w3c_slidy.is_shown_toc())
 +      w3c_slidy.hide_table_of_contents();
 +    else
 +      w3c_slidy.show_table_of_contents();
 +  },
 +
 +  // called on clicking toc entry
 +  toc_click: function (e) {
 +    if (!e)
 +      e = window.event;
 +
 +    var target = w3c_slidy.get_target(e);
 +
 +    if (target && target.nodeType == 1)
 +    {
 +      var uri = target.getAttribute("href");
 +
 +      if (uri)
 +      {
 +        //alert("going to " + uri);
 +        var slide = w3c_slidy.slides[w3c_slidy.slide_number];
 +        w3c_slidy.hide_slide(slide);
 +        w3c_slidy.slide_number = w3c_slidy.find_slide_number(uri);
 +        slide = w3c_slidy.slides[w3c_slidy.slide_number];
 +        w3c_slidy.last_shown = null;
 +        w3c_slidy.set_location();
 +        w3c_slidy.set_visibility_all_incremental("hidden");
 +        w3c_slidy.set_eos_status(!w3c_slidy.next_incremental_item(w3c_slidy.last_shown));
 +        w3c_slidy.show_slide(slide);
 +        //target.focus();
 +
 +        try
 +        {
 +          if (!w3c_slidy.opera)
 +            w3c_slidy.help_anchor.focus();
 +        }
 +        catch (e)
 +        {
 +        }
 +      }
 +    }
 +
 +    w3c_slidy.hide_table_of_contents(e);
 +    if (w3c_slidy.ie7) w3c_slidy.ie_hack();
 +    w3c_slidy.stop_propagation(e);
 +    return w3c_slidy.cancel(e);
 +  },
 +
 +  // called onkeydown for toc entry
 +  toc_keydown: function (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 = w3c_slidy.slides[w3c_slidy.slide_number];
 +        w3c_slidy.hide_slide(slide);
 +        w3c_slidy.slide_number = w3c_slidy.find_slide_number(uri);
 +        slide = w3c_slidy.slides[w3c_slidy.slide_number];
 +        w3c_slidy.last_shown = null;
 +        w3c_slidy.set_location();
 +        w3c_slidy.set_visibility_all_incremental("hidden");
 +        w3c_slidy.set_eos_status(!w3c_slidy.next_incremental_item(w3c_slidy.last_shown));
 +        w3c_slidy.show_slide(slide);
 +        //target.focus();
 +
 +        try
 +        {
 +          if (!w3c_slidy.opera)
 +            w3c_slidy.help_anchor.focus();
 +        }
 +        catch (e)
 +        {
 +        }
 +      }
 +
 +      w3c_slidy.hide_table_of_contents();
 +
 +      if (self.ie7)
 +       w3c_slidy.ie_hack();
 +
 +      return w3c_slidy.cancel(event);
 +    }
 +
 +    if (key == 40 && this.next)
 +    {
 +      this.next.focus();
 +      return w3c_slidy.cancel(event);
 +    }
 +
 +    if (key == 38 && this.previous)
 +    {
 +      this.previous.focus();
 +      return w3c_slidy.cancel(event);
 +    }
 +
 +    return true;
 +  },
 +
 +
 +  // ### OBSOLETE ###
 +  before_print: function () {
 +    this.show_all_slides();
 +    this.hide_toolbar();
 +    alert("before print");
 +  },
 +
 +  // ### OBSOLETE ###
 +  after_print: function () {
 +    if (!this.view_all)
 +    {
 +      this.single_slide_view();
 +      this.show_toolbar();
 +    }
 +    alert("after print");
 +  },
 +
 +  // ### OBSOLETE ###
 +  print_slides: function () {
 +    this.before_print();
 +    window.print();
 +    this.after_print();
 +  },
 +
 +  // ### OBSOLETE ?? ###
 +  toggle_view: function () {
 +    if (this.view_all)
 +    {
 +      this.single_slide_view();
 +      this.show_toolbar();
 +      this.view_all = 0;
 +    }
 +    else
 +    {
 +      this.show_all_slides();
 +      this.hide_toolbar();
 +      this.view_all = 1;
 +    }
 +  },
 +
 +  // prepare for printing  ### OBSOLETE ###
 +  show_all_slides: function () {
 +    this.remove_class(document.body, "single_slide");
 +    this.set_visibility_all_incremental("visible");
 +  },
 +
 +  // restore after printing  ### OBSOLETE ###
 +  single_slide_view: function () {
 +    this.add_class(document.body, "single_slide");
 +    this.set_visibility_all_incremental("visible");
 +    this.last_shown = this.previous_incremental_item(null);
 +  },
 +
 +  // suppress IE's image toolbar pop up
 +  hide_image_toolbar: function () {
 +    if (!this.ns_pos)
 +    {
 +      var images = document.getElementsByTagName("IMG");
 +
 +      for (var i = 0; i < images.length; ++i)
 +        images[i].setAttribute("galleryimg", "no");
 +    }
 +  },
 +
 +  unloaded: function (e) {
 +    //alert("unloaded");
 +  },
 +
 +  // Safari and Konqueror don't yet support getComputedStyle()
 +  // and they always reload page when location.href is updated
 +  is_KHTML: function () {
 +    var agent = navigator.userAgent;
 +    return (agent.indexOf("KHTML") >= 0 ? true : false);
 +  },
 +
 +  // find slide name from first h1 element
 +  // default to document title + slide number
 +  slide_name: function (index) {
 +    var name = null;
 +    var slide = this.slides[index];
 +
 +    var heading = this.find_heading(slide);
 +
 +    if (heading)
 +      name = this.extract_text(heading);
 +
 +    if (!name)
 +      name = this.title + "(" + (index + 1) + ")";
 +
 +    name.replace(/\&/g, "&");
 +    name.replace(/\</g, "<");
 +    name.replace(/\>/g, ">");
 +
 +    return name;
 +  },
 +
 +  // find first h1 element in DOM tree
 +  find_heading: function (node) {
 +    if (!node || node.nodeType != 1)
 +      return null;
 +
 +    if (node.nodeName == "H1" || node.nodeName == "h1")
 +      return node;
 +
 +    var child = node.firstChild;
 +
 +    while (child)
 +    {
 +      node = this.find_heading(child);
 +
 +      if (node)
 +        return node;
 +
 +      child = child.nextSibling;
 +    }
 +
 +    return null;
 +  },
 +
 +  // recursively extract text from DOM tree
 +  extract_text: function (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 + this.extract_text(node);
 +        node = node.nextSibling;
 +      }
 +
 +      return text;
 +    }
 +
 +    return "";
 +  },
 +
 +  // find copyright text from meta element
 +  find_copyright: function () {
 +    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;
 +  },
 +
 +  find_size_adjust: function () {
 +    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;
 +  },
 +
 +  // <meta name="duration" content="20" />  for 20 minutes
 +  find_duration: function () {
 +    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 == "duration")
 +        return 60000 * content;
 +    }
 +
 +    return null;
 +  },
 +
 +  replace_by_non_breaking_space: function (str) {
 +    for (var i = 0; i < str.length; ++i)
 +      str[i] = 160;
 +  },
 +
 +  // ### CHECK ME ### is use of "li" okay for text/html?
 +  // for XHTML do we also need to specify namespace?
 +  init_outliner: function () {
 +    var items = document.getElementsByTagName("li");
 +
 +    for (var i = 0; i < items.length; ++i)
 +    {
 +      var target = items[i];
 +
 +      if (!this.has_class(target.parentNode, "outline"))
 +        continue;
 +
 +      target.onclick = this.outline_click;
 +/* ### more work needed for IE6
 +      if (!this.ns_pos)
 +      {
 +        target.onmouseover = this.hover_outline;
 +        target.onmouseout = this.unhover_outline;
 +      }
 +*/
 +      if (this.foldable(target))
 +      {
 +        target.foldable = true;
 +        target.onfocus = function () {w3c_slidy.outline = this;};
 +        target.onblur = function () {w3c_slidy.outline = null;};
 +
 +        if (!target.getAttribute("tabindex"))
 +          target.setAttribute("tabindex", "0");
 +
 +        if (this.has_class(target, "expand"))
 +          this.unfold(target);
 +        else
 +          this.fold(target);
 +      }
 +      else
 +      {
 +        this.add_class(target, "nofold");
 +        target.visible = true;
 +        target.foldable = false;
 +      }
 +    }
 +  },
 +
 +  foldable: function (item) {
 +    if (!item || item.nodeType != 1)
 +      return false;
 +
 +    var node = item.firstChild;
 +
 +    while (node)
 +    {
 +      if (node.nodeType == 1 && this.is_block(node))
 +        return true;
 +
 +      node = node.nextSibling;
 +    }
 +
 +    return false;
 +  },
 +
 +  // ### CHECK ME ### switch to add/remove "hidden" class
 +  fold: function (item) {
 +    if (item)
 +    {
 +      this.remove_class(item, "unfolded");
 +      this.add_class(item, "folded");
 +    }
 +
 +    var node = item ? item.firstChild : null;
 +
 +    while (node)
 +    {
 +      if (node.nodeType == 1 && this.is_block(node)) // element
 +      {
 +         w3c_slidy.add_class(node, "hidden");
 +      }
 +
 +      node = node.nextSibling;
 +    }
 +
 +    item.visible = false;
 +  },
 +
 +  // ### CHECK ME ### switch to add/remove "hidden" class
 +  unfold: function (item) {
 +    if (item)
 +    {
 +      this.add_class(item, "unfolded");
 +      this.remove_class(item, "folded");
 +    }
 +
 +    var node = item ? item.firstChild : null;
 +
 +    while (node)
 +    {
 +      if (node.nodeType == 1 && this.is_block(node)) // element
 +      {
 +        w3c_slidy.remove_class(node, "hidden");
 +      }
 +
 +      node = node.nextSibling;
 +    }
 +
 +    item.visible = true;
 +  },
 +
 +  outline_click: function (e) {
 +    if (!e)
 +      e = window.event;
 +
 +    var rightclick = false;
 +    var target = w3c_slidy.get_target(e);
 +
 +    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)
 +          w3c_slidy.fold(target);
 +        else
 +          w3c_slidy.unfold(target);
 +      }
 +
 +      w3c_slidy.stop_propagation(e);
 +      e.cancel = true;
 +      e.returnValue = false;
 +    }
 +
 +    return false;
 +  },
 +
 +  add_initial_prompt: function () {
 +    var prompt = this.create_element("div");
 +    prompt.setAttribute("class", "initial_prompt");
 +
 +    var p1 = this.create_element("p");
 +    prompt.appendChild(p1);
 +    p1.setAttribute("class", "help");
 +
 +    if (this.keyboardless)
 +      p1.innerHTML = "Tap footer to move to next slide";
 +    else
 +      p1.innerHTML = "Space or Right Arrow to move to next " +
 +                     "slide, click help below for more details";
 +
 +    this.add_listener(prompt, "click", function (e) {
 +      document.body.removeChild(prompt);
 +      w3c_slidy.stop_propagation(e);
 +
 +      if (e.cancel != undefined)
 +        e.cancel = true;
 +
 +      if (e.returnValue != undefined)
 +        e.returnValue = false;
 +
 +      return false;
 +    });
 +
 +    document.body.appendChild(prompt);
 +    this.initial_prompt = prompt;
 +    setTimeout(function() {document.body.removeChild(prompt);}, 5000);
 +  },
 +
 +  add_toolbar: function () {
 +    var counter, page;
 +
 +     this.toolbar = this.create_element("div");
 +     this.toolbar.setAttribute("class", "toolbar");
 +
 +     // a reasonably behaved browser
 +     if (this.ns_pos || !this.ie6)
 +     {
 +       var right = this.create_element("div");
 +       right.setAttribute("style", "float: right; text-align: right");
 +
 +       counter = this.create_element("span")
 +       counter.innerHTML = "slide".localize() + " n/m";
 +       right.appendChild(counter);
 +       this.toolbar.appendChild(right);
 +
 +       var left = this.create_element("div");
 +       left.setAttribute("style", "text-align: left");
 +
 +       // global end of slide indicator
 +       this.eos = this.create_element("span");
 +       this.eos.innerHTML = "* ";
 +       left.appendChild(this.eos);
 +
 +       var help = this.create_element("a");
 +       help.setAttribute("href", this.help_page);
 +       help.setAttribute("title", this.help_text.localize());
 +       help.innerHTML = "help?".localize();
 +       left.appendChild(help);
 +       this.help_anchor = help;  // save for focus hack
 +
 +       var gap1 = document.createTextNode(" ");
 +       left.appendChild(gap1);
 +
 +       var contents = this.create_element("a");
 +       contents.setAttribute("href", "javascript:w3c_slidy.toggle_table_of_contents()");
 +       contents.setAttribute("title", "table of contents".localize());
 +       contents.innerHTML = "contents?".localize();
 +       left.appendChild(contents);
 +
 +       var gap2 = document.createTextNode(" ");
 +       left.appendChild(gap2);
 +
 +       var copyright = this.find_copyright();
 +
 +       if (copyright)
 +       {
 +         var span = this.create_element("span");
 +         span.className = "copyright";
 +         span.innerHTML = copyright;
 +         left.appendChild(span);
 +       }
 +
 +       this.toolbar.setAttribute("tabindex", "0");
 +       this.toolbar.appendChild(left);
 +     }
 +     else // IE6 so need to work around its poor CSS support
 +     {
 +       this.toolbar.style.position = (this.ie7 ? "fixed" : "absolute");
 +       this.toolbar.style.zIndex = "200";
 +       this.toolbar.style.width = "99.9%";
 +       this.toolbar.style.height = "1.2em";
 +       this.toolbar.style.top = "auto";
 +       this.toolbar.style.bottom = "0";
 +       this.toolbar.style.left = "0";
 +       this.toolbar.style.right = "0";
 +       this.toolbar.style.textAlign = "left";
 +       this.toolbar.style.fontSize = "60%";
 +       this.toolbar.style.color = "red";
 +       this.toolbar.borderWidth = 0;
 +       this.toolbar.className = "toolbar";
 +       this.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 = this.create_element("span");
 +       sp.innerHTML = "  * ";
 +       this.toolbar.appendChild(sp);
 +       this.eos = sp;  // end of slide indicator
 +
 +       var help = this.create_element("a");
 +       help.setAttribute("href", this.help_page);
 +       help.setAttribute("title", this.help_text.localize());
 +       help.innerHTML = "help?".localize();
 +       this.toolbar.appendChild(help);
 +       this.help_anchor = help;  // save for focus hack
 +
 +       var gap1 = document.createTextNode(" ");
 +       this.toolbar.appendChild(gap1);
 +
 +       var contents = this.create_element("a");
 +       contents.setAttribute("href", "javascript:toggleTableOfContents()");
 +       contents.setAttribute("title", "table of contents".localize());
 +       contents.innerHTML = "contents?".localize();
 +       this.toolbar.appendChild(contents);
 +
 +       var gap2 = document.createTextNode(" ");
 +       this.toolbar.appendChild(gap2);
 +
 +       var copyright = this.find_copyright();
 +
 +       if (copyright)
 +       {
 +         var span = this.create_element("span");
 +         span.innerHTML = copyright;
 +         span.style.color = "black";
 +         span.style.marginLeft = "0.5em";
 +         this.toolbar.appendChild(span);
 +       }
 +
 +       counter = this.create_element("div")
 +       counter.style.position = "absolute";
 +       counter.style.width = "auto"; //"20%";
 +       counter.style.height = "1.2em";
 +       counter.style.top = "auto";
 +       counter.style.bottom = 0;
 +       counter.style.right = "0";
 +       counter.style.textAlign = "right";
 +       counter.style.color = "red";
 +       counter.style.background = "rgb(240,240,240)";
 +
 +       counter.innerHTML = "slide".localize() + " n/m";
 +       this.toolbar.appendChild(counter);
 +     }
 +
 +     // ensure that click isn't passed through to the page
 +     this.toolbar.onclick =
 +         function (e) {
 +           if (!e)
 +             e = window.event;
 +
 +           var target = e.target;
 +
 +           if (!target && e.srcElement)
 +             target = e.srcElement;
 +
 +           // work around Safari bug
 +           if (target && target.nodeType == 3)
 +             target = target.parentNode;
 +
 +           w3c_slidy.stop_propagation(e);
 +
 +           if (target && target.nodeName.toLowerCase() != "a")
 +             w3c_slidy.mouse_button_click(e);
 +         };
 +
 +     this.slide_number_element = counter;
 +     this.set_eos_status(false);
 +     document.body.appendChild(this.toolbar);
 +  },
 +
 +  // 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
 +  wrap_implicit_slides: function () {
 +    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");
 +      this.add_class(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
 +  collect_slides: function () {
 +    var slides = new Array();
 +    var divs = document.body.getElementsByTagName("div");
 +
 +    for (var i = 0; i < divs.length; ++i)
 +    {
 +      div = divs.item(i);
 +
 +      if (this.has_class(div, "slide"))
 +      {
 +        // add slide to collection
 +        slides[slides.length] = div;
 +
 +        // hide each slide as it is found
 +        this.add_class(div, "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 (this.has_class(div, "background"))
 +      {  // work around for Firefox SVG reload bug
 +        // which otherwise replaces 1st SVG graphic with 2nd
 +        div.style.display = "block";
 +      }
 +    }
 +
 +    this.slides = slides;
 +  },
 +
 +  // return new array of all <div class="handout">
 +  collect_notes: function () {
 +    var notes = new Array();
 +    var divs = document.body.getElementsByTagName("div");
 +
 +    for (var i = 0; i < divs.length; ++i)
 +    {
 +      div = divs.item(i);
 +
 +      if (this.has_class(div, "handout"))
 +      {
 +        // add note to collection
 +        notes[notes.length] = div;
 +
 +        // and hide it
 +        this.add_class(div, "hidden");
 +      }
 +    }
 +
 +    this.notes = notes;
 +  },
 +
 +  // return new array of all <div class="background">
 +  // including named backgrounds e.g. class="background titlepage"
 +  collect_backgrounds: function () {
 +    var backgrounds = new Array();
 +    var divs = document.body.getElementsByTagName("div");
 +
 +    for (var i = 0; i < divs.length; ++i)
 +    {
 +      div = divs.item(i);
 +
 +      if (this.has_class(div, "background"))
 +      {
 +        // add background to collection
 +        backgrounds[backgrounds.length] = div;
 +
 +        // and hide it
 +        this.add_class(div, "hidden");
 +      }
 +    }
 +
 +    this.backgrounds = backgrounds;
 +  },
 +
 +  // set click handlers on all anchors
 +  patch_anchors: function () {
 +    var self = w3c_slidy;
 +    var handler = function (event) {
 +      // compare this.href with location.href
 +      // for link to another slide in this doc
 +
 +      if (self.page_address(this.href) == self.page_address(location.href))
 +      {
 +        // yes, so find new slide number
 +        var newslidenum = self.find_slide_number(this.href);
 +
 +        if (newslidenum != self.slide_number)
 +        {
 +          var slide = self.slides[self.slide_number];
 +          self.hide_slide(slide);
 +          self.slide_number = newslidenum;
 +          slide = self.slides[self.slide_number];
 +          self.show_slide(slide);
 +          self.set_location();
 +        }
 +      }
 +      else if (this.target == null)
 +        location.href = this.href;
 +
 +      this.blur();
 +      self.disable_slide_click = true;
 +    };
 +
 +    var anchors = document.body.getElementsByTagName("a");
 +
 +    for (var i = 0; i < anchors.length; ++i)
 +    {
 +      if (window.addEventListener)
 +        anchors[i].addEventListener("click", handler, false);
 +      else
 +        anchors[i].attachEvent("onclick", handler);
 +    }
 +  },
 +
 +  // ### CHECK ME ### see which functions are invoked via setTimeout
 +  // either directly or indirectly for use of w3c_slidy vs this
 +  show_slide_number: function () {
 +    var timer = w3c_slidy.get_timer();
 +    w3c_slidy.slide_number_element.innerHTML = timer + "slide".localize() + " " +
 +           (w3c_slidy.slide_number + 1) + "/" + w3c_slidy.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
 +  check_location: function () {
 +    var hash = location.hash;
 +
 +    if (w3c_slidy.slide_number > 0 && (hash == "" || hash == "#"))
 +      w3c_slidy.goto_slide(0);
 +    else if (hash.length > 2 && hash != "#("+(w3c_slidy.slide_number+1)+")")
 +    {
 +      var num = parseInt(location.hash.substr(2));
 +
 +      if (!isNaN(num))
 +        w3c_slidy.goto_slide(num-1);
 +    }
 +
 +    if (w3c_slidy.time_left && w3c_slidy.slide_number > 0)
 +    {
 +      w3c_slidy.show_slide_number();
 +
 +      if (w3c_slidy.time_left > 0)
 +        w3c_slidy.time_left -= 200;
 +    }
 +  },
 +
 +  get_timer: function () {
 +    var timer = "";
 +    if (w3c_slidy.time_left)
 +    {
 +      var mins, secs;
 +      secs = Math.floor(w3c_slidy.time_left/1000);
 +      mins = Math.floor(secs / 60);
 +      secs = secs % 60;
 +      timer = (mins ? mins+"m" : "") + secs + "s ";
 +    }
 +
 +    return timer;
 +  },
 +
 +  // 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
 +  set_location: function () {
 +     var uri = w3c_slidy.page_address(location.href);
 +     var hash = "#(" + (w3c_slidy.slide_number+1) + ")";
 +
 +     if (w3c_slidy.slide_number >= 0)
 +       uri = uri + hash;
 +
 +     if (w3c_slidy.ie && !w3c_slidy.ie8)
 +       w3c_slidy.push_hash(hash);
 +
 +     if (uri != location.href) // && !khtml
 +        location.href = uri;
 +
 +     if (this.khtml)
 +        hash = "(" + (w3c_slidy.slide_number+1) + ")";
 +
 +     if (!this.ie && location.hash != hash && location.hash != "")
 +       location.hash = hash;
 +
 +     document.title = w3c_slidy.title + " (" + (w3c_slidy.slide_number+1) + ")";
 +     w3c_slidy.show_slide_number();
 +  },
 +
 +  page_address: function (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);
 +  },
 +
 +  // only used for IE6 and IE7
 +  on_frame_loaded: function (hash) {
 +    location.hash = hash;
 +    var uri = w3c_slidy.page_address(location.href);
 +    location.href = uri + hash;
 +  },
 +
 +  // history hack with thanks to Bertrand Le Roy
 +  push_hash: function (hash) {
 +    if (hash == "") hash = "#(1)";
 +      window.location.hash = hash;
 +
 +    var doc = document.getElementById("historyFrame").contentWindow.document;
 +    doc.open("javascript:'<html></html>'");
 +    // PWL modified this string literal to break the close script tag
 +    // which otherwise gets parsed when incorporated
 +    doc.write("<html><head><script type=\"text/javascript\">window.parent.w3c_slidy.on_frame_loaded('"+
 +      (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
 +  find_slide_number: function (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 > this.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 > this.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" &&
 +            this.has_class(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;
 +  },
 +
 +  previous_slide: function (incremental) {
 +    if (!w3c_slidy.view_all)
 +    {
 +      var slide;
 +
 +      if ((incremental || w3c_slidy.slide_number == 0) && w3c_slidy.last_shown != null)
 +      {
 +        w3c_slidy.last_shown = w3c_slidy.hide_previous_item(w3c_slidy.last_shown);
 +        w3c_slidy.set_eos_status(false);
 +      }
 +      else if (w3c_slidy.slide_number > 0)
 +      {
 +        slide = w3c_slidy.slides[w3c_slidy.slide_number];
 +        w3c_slidy.hide_slide(slide);
 +
 +        w3c_slidy.slide_number = w3c_slidy.slide_number - 1;
 +        slide = w3c_slidy.slides[w3c_slidy.slide_number];
 +        w3c_slidy.set_visibility_all_incremental("visible");
 +        w3c_slidy.last_shown = w3c_slidy.previous_incremental_item(null);
 +        w3c_slidy.set_eos_status(true);
 +        w3c_slidy.show_slide(slide);
 +      }
 +
 +      w3c_slidy.set_location();
 +
 +      if (!w3c_slidy.ns_pos)
 +        w3c_slidy.refresh_toolbar(200);
 +    }
 +  },
 +
 +  next_slide: function (incremental) {
 +    if (!w3c_slidy.view_all)
 +    {
 +      var slide, last = w3c_slidy.last_shown;
 +
 +      if (incremental || w3c_slidy.slide_number == w3c_slidy.slides.length - 1)
 +         w3c_slidy.last_shown = w3c_slidy.reveal_next_item(w3c_slidy.last_shown);
 +
 +      if ((!incremental || w3c_slidy.last_shown == null) &&
 +             w3c_slidy.slide_number < w3c_slidy.slides.length - 1)
 +      {
 +         slide = w3c_slidy.slides[w3c_slidy.slide_number];
 +         w3c_slidy.hide_slide(slide);
 +
 +         w3c_slidy.slide_number = w3c_slidy.slide_number + 1;
 +         slide = w3c_slidy.slides[w3c_slidy.slide_number];
 +         w3c_slidy.last_shown = null;
 +         w3c_slidy.set_visibility_all_incremental("hidden");
 +         w3c_slidy.show_slide(slide);
 +      }
 +      else if (!w3c_slidy.last_shown)
 +      {
 +         if (last && incremental)
 +           w3c_slidy.last_shown = last;
 +      }
 +
 +      w3c_slidy.set_location();
 +
 +      w3c_slidy.set_eos_status(!w3c_slidy.next_incremental_item(w3c_slidy.last_shown));
 +
 +      if (!w3c_slidy.ns_pos)
 +         w3c_slidy.refresh_toolbar(200);
 +     }
 +  },
 +
 +  // to first slide with nothing revealed
 +  // i.e. state at start of presentation
 +  first_slide: function () {
 +     if (!w3c_slidy.view_all)
 +     {
 +       var slide;
 +
 +       if (w3c_slidy.slide_number != 0)
 +       {
 +         slide = w3c_slidy.slides[w3c_slidy.slide_number];
 +         w3c_slidy.hide_slide(slide);
 +
 +         w3c_slidy.slide_number = 0;
 +         slide = w3c_slidy.slides[w3c_slidy.slide_number];
 +         w3c_slidy.last_shown = null;
 +         w3c_slidy.set_visibility_all_incremental("hidden");
 +         w3c_slidy.show_slide(slide);
 +       }
 +
 +       w3c_slidy.set_eos_status(
 +         !w3c_slidy.next_incremental_item(w3c_slidy.last_shown));
 +       w3c_slidy.set_location();
 +     }
 +  },
 +
 +  // goto last slide with everything revealed
 +  // i.e. state at end of presentation
 +  last_slide: function () {
 +    if (!w3c_slidy.view_all)
 +    {
 +      var slide;
 +
 +      w3c_slidy.last_shown = null; //revealNextItem(lastShown);
 +
 +      if (w3c_slidy.last_shown == null &&
 +          w3c_slidy.slide_number < w3c_slidy.slides.length - 1)
 +      {
 +         slide = w3c_slidy.slides[w3c_slidy.slide_number];
 +         w3c_slidy.hide_slide(slide);
 +         w3c_slidy.slide_number = w3c_slidy.slides.length - 1;
 +         slide = w3c_slidy.slides[w3c_slidy.slide_number];
 +         w3c_slidy.set_visibility_all_incremental("visible");
 +         w3c_slidy.last_shown = w3c_slidy.previous_incremental_item(null);
 +
 +         w3c_slidy.show_slide(slide);
 +      }
 +      else
 +      {
 +         w3c_slidy.set_visibility_all_incremental("visible");
 +         w3c_slidy.last_shown = w3c_slidy.previous_incremental_item(null);
 +      }
 +
 +      w3c_slidy.set_eos_status(true);
 +      w3c_slidy.set_location();
 +    }
 +  },
 +
 +
 +  // ### check this and consider add/remove class
 +  set_eos_status: function (state) {
 +    if (this.eos)
 +      this.eos.style.color = (state ? "rgb(240,240,240)" : "red");
 +  },
 +
 +  // first slide is 0
 +  goto_slide: function (num) {
 +    //alert("going to slide " + (num+1));
 +    var slide = w3c_slidy.slides[w3c_slidy.slide_number];
 +    w3c_slidy.hide_slide(slide);
 +    w3c_slidy.slide_number = num;
 +    slide = w3c_slidy.slides[w3c_slidy.slide_number];
 +    w3c_slidy.last_shown = null;
 +    w3c_slidy.set_visibility_all_incremental("hidden");
 +    w3c_slidy.set_eos_status(!w3c_slidy.next_incremental_item(w3c_slidy.last_shown));
 +    document.title = w3c_slidy.title + " (" + (w3c_slidy.slide_number+1) + ")";
 +    w3c_slidy.show_slide(slide);
 +    w3c_slidy.show_slide_number();
 +  },
 +
 +
 +  show_slide: function (slide) {
 +    this.sync_background(slide);
 +    window.scrollTo(0,0);
 +    this.remove_class(slide, "hidden");
 +  },
 +
 +  hide_slide: function (slide) {
 +    this.add_class(slide, "hidden");
 +  },
 +
 +  // show just the backgrounds pertinent to this slide
 +  // when slide background-color is transparent
 +  // this should now work with rgba color values
 +  sync_background: function (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" ||
 +        bgColor.indexOf("rgba") >= 0 ||
 +        bgColor.indexOf("opacity") >= 0)
 +    {
 +      var slideClass = this.get_class_list(slide);
 +
 +      for (var i = 0; i < this.backgrounds.length; i++)
 +      {
 +        background = this.backgrounds[i];
 +
 +        var bgClass = this.get_class_list(background);
 +
 +        if (this.matching_background(slideClass, bgClass))
 +          this.remove_class(background, "hidden");
 +        else
 +          this.add_class(background, "hidden");
 +      }
 +    }
 +    else // forcibly hide all backgrounds
 +      this.hide_backgrounds();
 +  },
 +
 +  hide_backgrounds: function () {
 +    for (var i = 0; i < this.backgrounds.length; i++)
 +    {
 +      background = this.backgrounds[i];
 +      this.add_class(background, "hidden");
 +    }
 +  },
 +
 +  // compare classes for slide and background
 +  matching_background: function (slideClass, bgClass) {
 +    var i, count, pattern, result;
 +
 +    // define pattern as regular expression
 +    pattern = /\w+/g;
 +
 +    // check background class names
 +    result = bgClass.match(pattern);
 +
 +    for (i = count = 0; i < result.length; i++)
 +    {
 +      if (result[i] == "hidden")
 +        continue;
 +
 +      if (result[i] == "background")
 +	continue;
 +
 +      ++count;
 +    }
 +
 +    if (count == 0)  // default match
 +      return true;
 +
 +    // check for matches and place result in array
 +    result = slideClass.match(pattern);
 +
 +    // now check if desired name is present for background
 +    for (i = count = 0; i < result.length; i++)
 +    {
 +      if (result[i] == "hidden")
 +        continue;
 +
 +      if (this.has_token(bgClass, result[i]))
 +        return true;
 +    }
 +
 +    return false;
 +  },
 +
 +  resized: function () {
 +     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 != w3c_slidy.last_width || height != w3c_slidy.last_height)
 +     {
 +       if (width >= 1100)
 +         w3c_slidy.size_index = 5;    // 4
 +       else if (width >= 1000)
 +         w3c_slidy.size_index = 4;    // 3
 +       else if (width >= 800)
 +         w3c_slidy.size_index = 3;    // 2
 +       else if (width >= 600)
 +         w3c_slidy.size_index = 2;    // 1
 +       else if (width)
 +         w3c_slidy.size_index = 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 <= w3c_slidy.size_index + w3c_slidy.size_adjustment &&
 +             w3c_slidy.size_index + w3c_slidy.size_adjustment < w3c_slidy.sizes.length)
 +         w3c_slidy.size_index = w3c_slidy.size_index + w3c_slidy.size_adjustment;
 +
 +       // enables cross browser use of relative width/height
 +       // on object elements for use with SVG and Flash media
 +       w3c_slidy.adjust_object_dimensions(width, height);
 +
 +       if (document.body.style.fontSize != w3c_slidy.sizes[w3c_slidy.size_index])
 +       {
 +         document.body.style.fontSize = w3c_slidy.sizes[w3c_slidy.size_index];
 +       }
 +
 +       w3c_slidy.last_width = width;
 +       w3c_slidy.last_height = height;
 +
 +       // force reflow to work around Mozilla bug
 +       if (w3c_slidy.ns_pos)
 +       {
 +         var slide = w3c_slidy.slides[w3c_slidy.slide_number];
 +         w3c_slidy.hide_slide(slide);
 +         w3c_slidy.show_slide(slide);
 +       }
 +
 +       // force correct positioning of toolbar
 +       w3c_slidy.refresh_toolbar(200);
 +     }
 +  },
 +
 +  scrolled: function () {
 +    if (w3c_slidy.toolbar && !w3c_slidy.ns_pos && !w3c_slidy.ie7)
 +    {
 +      w3c_slidy.hack_offset = w3c_slidy.scroll_x_offset();
 +      // hide toolbar
 +      w3c_slidy.toolbar.style.display = "none";
 +
 +      // make it reappear later
 +      if (w3c_slidy.scrollhack == 0 && !w3c_slidy.view_all)
 +      {
 +        setTimeout(function () {w3c_slidy.show_toolbar(); }, 1000);
 +        w3c_slidy.scrollhack = 1;
 +      }
 +    }
 +  },
 +
 +  hide_toolbar: function () {
 +    w3c_slidy.add_class(w3c_slidy.toolbar, "hidden");
 +    window.focus();
 +  },
 +
 +  // used to ensure IE refreshes toolbar in correct position
 +  refresh_toolbar: function (interval) {
 +    if (!w3c_slidy.ns_pos && !w3c_slidy.ie7)
 +    {
 +      w3c_slidy.hide_toolbar();
 +      setTimeout(function () {w3c_slidy.show_toolbar(); }, interval);
 +    }
 +  },
 +
 +  // restores toolbar after short delay
 +  show_toolbar: function () {
 +    if (w3c_slidy.want_toolbar)
 +    {
 +      w3c_slidy.toolbar.style.display = "block";
 +
 +      if (!w3c_slidy.ns_pos)
 +      {
 +        // adjust position to allow for scrolling
 +        var xoffset = w3c_slidy.scroll_x_offset();
 +        w3c_slidy.toolbar.style.left = xoffset;
 +        w3c_slidy.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
 +
 +        w3c_slidy.toolbar.style.bottom = 0; //bottom;
 +      }
 +
 +      w3c_slidy.remove_class(w3c_slidy.toolbar, "hidden");
 +    }
 +
 +    w3c_slidy.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 (!w3c_slidy.opera)
 +        w3c_slidy.help_anchor.focus();
 +    }
 +    catch (e)
 +    {
 +    }
 +  },
 +
 +// invoked via F key
 +  toggle_toolbar: function () {
 +    if (!w3c_slidy.view_all)
 +    {
 +      if (w3c_slidy.has_class(w3c_slidy.toolbar, "hidden"))
 +      {
 +        w3c_slidy.remove_class(w3c_slidy.toolbar, "hidden")
 +        w3c_slidy.want_toolbar = 1;
 +      }
 +      else
 +      {
 +        w3c_slidy.add_class(w3c_slidy.toolbar, "hidden")
 +        w3c_slidy.want_toolbar = 0;
 +      }
 +    }
 +  },
 +
 +  scroll_x_offset: function () {
 +    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;
 +  },
 +
 +  scroll_y_offset: function () {
 +    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
 +  optimize_font_size: function () {
 +    var slide = w3c_slidy.slides[w3c_slidy.slide_number];
 +
 +    //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 + ")");
 +  },
 +
 +  // from document object
 +  get_doc_height: function (doc) {
 +    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");
 +  },
 +
 +  get_window_height: function () {
 +    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
 +  },
 +
 +  document_height: function () {
 +    var sh, oh;
 +
 +    sh = document.body.scrollHeight;
 +    oh = document.body.offsetHeight;
 +
 +    if (sh && oh)
 +    {
 +      return (sh > oh ? sh : oh);
 +    }
 +
 +    // no idea!
 +    return 0;
 +  },
 +
 +  smaller: function () {
 +    if (w3c_slidy.size_index > 0)
 +    {
 +      --w3c_slidy.size_index;
 +    }
 +
 +    w3c_slidy.toolbar.style.display = "none";
 +    document.body.style.fontSize = w3c_slidy.sizes[w3c_slidy.size_index];
 +    var slide = w3c_slidy.slides[w3c_slidy.slide_number];
 +    w3c_slidy.hide_slide(slide);
 +    w3c_slidy.show_slide(slide);
 +    setTimeout(function () {w3c_slidy.show_toolbar(); }, 50);
 +  },
 +
 +  bigger: function () {
 +    if (w3c_slidy.size_index < w3c_slidy.sizes.length - 1)
 +    {
 +      ++w3c_slidy.size_index;
 +    }
 +
 +    w3c_slidy.toolbar.style.display = "none";
 +    document.body.style.fontSize = w3c_slidy.sizes[w3c_slidy.size_index];
 +    var slide = w3c_slidy.slides[w3c_slidy.slide_number];
 +    w3c_slidy.hide_slide(slide);
 +    w3c_slidy.show_slide(slide);
 +    setTimeout(function () {w3c_slidy.show_toolbar(); }, 50);
 +  },
 +
 +  // 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
 +  adjust_object_dimensions: function (width, height) {
 +    for( var i = 0; i < w3c_slidy.objects.length; i++ )
 +    {
 +      var obj = this.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);
 +        }
 +      }
 +    }
 +  },
 +
 +  // needed for Opera to inhibit default behavior
 +  // since Opera delivers keyPress even if keyDown
 +  // was cancelled
 +  key_press: function (event) {
 +    if (!event)
 +      event = window.event;
 +
 +    if (!w3c_slidy.key_wanted)
 +      return w3c_slidy.cancel(event);
 +
 +    return true;
 +  },
 +
 +  //  See e.g. http://www.quirksmode.org/js/events/keys.html for keycodes
 +  key_down: function (event) {
 +    var key;
 +
 +    w3c_slidy.key_wanted = true;
 +
 +    if (!event)
 +      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 (w3c_slidy.is_shown_toc() && key != 9 && key != 16 && key != 38 && key != 40)
 +    {
 +      w3c_slidy.hide_table_of_contents();
 +
 +      if (key == 27 || key == 84 || key == 67)
 +        return w3c_slidy.cancel(event);
 +    }
 +
 +    if (key == 34) // Page Down
 +    {
 +      if (w3c_slidy.view_all)
 +        return true;
 +
 +      w3c_slidy.next_slide(false);
 +      return w3c_slidy.cancel(event);
 +    }
 +    else if (key == 33) // Page Up
 +    {
 +      if (w3c_slidy.view_all)
 +        return true;
 +
 +      w3c_slidy.previous_slide(false);
 +      return w3c_slidy.cancel(event);
 +    }
 +    else if (key == 32) // space bar
 +    {
 +      w3c_slidy.next_slide(true);
 +      return w3c_slidy.cancel(event);
 +    }
 +    else if (key == 37) // Left arrow
 +    {
 +      w3c_slidy.previous_slide(!event.shiftKey);
 +      return w3c_slidy.cancel(event);
 +    }
 +    else if (key == 36) // Home
 +    {
 +      w3c_slidy.first_slide();
 +      return w3c_slidy.cancel(event);
 +    }
 +    else if (key == 35) // End
 +    {
 +      w3c_slidy.last_slide();
 +      return w3c_slidy.cancel(event);
 +    }
 +    else if (key == 39) // Right arrow
 +    {
 +      w3c_slidy.next_slide(!event.shiftKey);
 +      return w3c_slidy.cancel(event);
 +    }
 +    else if (key == 13) // Enter
 +    {
 +      if (w3c_slidy.outline)
 +      {
 +        if (w3c_slidy.outline.visible)
 +          w3c_slidy.fold(w3c_slidy.outline);
 +        else
 +          w3c_slidy.unfold(w3c_slidy.outline);
 +
 +       return w3c_slidy.cancel(event);
 +      }
 +    }
 +    else if (key == 188)  // < for smaller fonts
 +    {
 +      w3c_slidy.smaller();
 +      return w3c_slidy.cancel(event);
 +    }
 +    else if (key == 190)  // > for larger fonts
 +    {
 +      w3c_slidy.bigger();
 +      return w3c_slidy.cancel(event);
 +    }
 +    else if (key == 189 || key == 109)  // - for smaller fonts
 +    {
 +      w3c_slidy.smaller();
 +      return w3c_slidy.cancel(event);
 +    }
 +    else if (key == 187 || key == 191 || key == 107)  // = +  for larger fonts
 +    {
 +      w3c_slidy.bigger();
 +      return w3c_slidy.cancel(event);
 +    }
 +    else if (key == 83)  // S for smaller fonts
 +    {
 +      w3c_slidy.smaller();
 +      return w3c_slidy.cancel(event);
 +    }
 +    else if (key == 66)  // B for larger fonts
 +    {
 +      w3c_slidy.bigger();
 +      return w3c_slidy.cancel(event);
 +    }
 +    else if (key == 90)  // Z for last slide
 +    {
 +      w3c_slidy.last_slide();
 +      return w3c_slidy.cancel(event);
 +    }
 +    else if (key == 70)  // F for toggle toolbar
 +    {
 +      w3c_slidy.toggle_toolbar();
 +      return w3c_slidy.cancel(event);
 +    }
 +    else if (key == 65)  // A for toggle view single/all slides
 +    {
 +      w3c_slidy.toggle_view();
 +      return w3c_slidy.cancel(event);
 +    }
 +    else if (key == 75)  // toggle action of left click for next page
 +    {
 +      w3c_slidy.mouse_click_enabled = !w3c_slidy.mouse_click_enabled;
 +      var alert_msg = (w3c_slidy.mouse_click_enabled ?
 +                "enabled" : "disabled") +  " mouse click advance";
 +
 +      alert(alert_msg.localize());
 +      return w3c_slidy.cancel(event);
 +    }
 +    else if (key == 84 || key == 67)  // T or C for table of contents
 +    {
 +      if (w3c_slidy.toc)
 +        w3c_slidy.toggle_table_of_contents();
 +
 +      return w3c_slidy.cancel(event);
 +    }
 +    else if (key == 72) // H for help
 +    {
 +      window.location = w3c_slidy.help_page;
 +      return w3c_slidy.cancel(event);
 +    }
 +    //else alert("key code is "+ key);
 +
 +    return true;
 +  },
 +
 +  // safe for both text/html and application/xhtml+xml
 +  create_element: function (name) {
 +    if (this.xhtml && (typeof document.createElementNS != 'undefined'))
 +      return document.createElementNS("http://www.w3.org/1999/xhtml", name)
 +
 +    return document.createElement(name);
 +  },
 +
 +  get_element_style: function (elem, IEStyleProp, CSSStyleProp) {
 +    if (elem.currentStyle)
 +    {
 +      return elem.currentStyle[IEStyleProp];
 +    }
 +    else if (window.getComputedStyle)
 +    {
 +      var compStyle = window.getComputedStyle(elem, "");
 +      return compStyle.getPropertyValue(CSSStyleProp);
 +    }
 +    return "";
 +  },
 +
 +  // the string str is a whitespace separated list of tokens
 +  // test if str contains a particular token, e.g. "slide"
 +  has_token: function (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;
 +  },
 +
 +  get_class_list: function (element) {
 +    if (typeof element.className != 'undefined')
 +      return element.className;
 +
 +    return element.getAttribute("class");
 +  },
 +
 +  has_class: function (element, name) {
 +    if (element.nodeType != 1)
 +      return false;
 +
 +    var regexp = new RegExp("(^| )" + name + "\W*");
 +
 +    if (typeof element.className != 'undefined')
 +      return regexp.test(element.className);
 +
 +    return regexp.test(element.getAttribute("class"));
 +  },
 +
 +  remove_class: function (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
 +    {
 +      clsval = element.getAttribute("class");
 +
 +      if (clsval)
 +      {
 +        clsval = clsval.replace(regexp, "");
 +        element.setAttribute("class", clsval);
 +      }
 +    }
 +  },
 +
 +  add_class: function (element, name) {
 +    if (!this.has_class(element, name))
 +    {
 +      if (typeof element.className != 'undefined')
 +        element.className += " " + name;
 +      else
 +      {
 +        var clsval = element.getAttribute("class");
 +        clsval = clsval ? clsval + " " + name : name;
 +        element.setAttribute("class", clsval);
 +      }
 +    }
 +  },
 +
 +  // 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).
 +  incremental_elements: null,
 +  okay_for_incremental: function (name) {
 +    if (!this.incremental_elements)
 +    {
 +      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;
 +      this.incremental_elements = inclist;
 +    }
 +    return this.incremental_elements[name.toLowerCase()];
 +  },
 +
 +  next_incremental_item: function (node) {
 +    var br = this.is_xhtml ? "br" : "BR";
 +    var slide = w3c_slidy.slides[w3c_slidy.slide_number];
 +
 +    for (;;)
 +    {
 +      node = w3c_slidy.next_node(slide, node);
 +
 +      if (node == null || node.parentNode == null)
 +        break;
 +
 +      if (node.nodeType == 1)  // ELEMENT
 +      {
 +        if (node.nodeName == br)
 +          continue;
 +
 +        if (w3c_slidy.has_class(node, "incremental")
 +             && w3c_slidy.okay_for_incremental(node.nodeName))
 +          return node;
 +
 +        if (w3c_slidy.has_class(node.parentNode, "incremental")
 +             && !w3c_slidy.has_class(node, "non-incremental"))
 +          return node;
 +      }
 +    }
 +
 +    return node;
 +  },
 +
 +  previous_incremental_item: function (node) {
 +    var br = this.is_xhtml ? "br" : "BR";
 +    var slide = w3c_slidy.slides[w3c_slidy.slide_number];
 +
 +    for (;;)
 +    {
 +      node = w3c_slidy.previous_node(slide, node);
 +
 +      if (node == null || node.parentNode == null)
 +        break;
 +
 +      if (node.nodeType == 1)
 +      {
 +        if (node.nodeName == br)
 +          continue;
 +
 +        if (w3c_slidy.has_class(node, "incremental")
 +             && w3c_slidy.okay_for_incremental(node.nodeName))
 +          return node;
 +
 +        if (w3c_slidy.has_class(node.parentNode, "incremental")
 +             && !w3c_slidy.has_class(node, "non-incremental"))
 +          return node;
 +      }
 +    }
 +
 +    return node;
 +  },
 +
 +  // set visibility for all elements on current slide with
 +  // a parent element with attribute class="incremental"
 +  set_visibility_all_incremental: function (value) {
 +    var node = this.next_incremental_item(null);
 +
 +    if (value == "hidden")
 +    {
 +      while (node)
 +      {
 +        w3c_slidy.add_class(node, "invisible");
 +        node = w3c_slidy.next_incremental_item(node);
 +      }
 +    }
 +    else // value == "visible"
 +    {
 +      while (node)
 +      {
 +        w3c_slidy.remove_class(node, "invisible");
 +        node = w3c_slidy.next_incremental_item(node);
 +      }
 +    }
 +  },
 +
 +  // reveal the next hidden item on the slide
 +  // node is null or the node that was last revealed
 +  reveal_next_item: function (node) {
 +    node = w3c_slidy.next_incremental_item(node);
 +
 +    if (node && node.nodeType == 1)  // an element
 +      w3c_slidy.remove_class(node, "invisible");
 +
 +    return node;
 +  },
 +
 +  // exact inverse of revealNextItem(node)
 +  hide_previous_item: function (node) {
 +    if (node && node.nodeType == 1)  // an element
 +      w3c_slidy.add_class(node, "invisible");
 +
 +    return this.previous_incremental_item(node);
 +  },
 +
 +  // left to right traversal of root's content
 +  next_node: function (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
 +  previous_node: function (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;
 +  },
 +
 +  previous_sibling_element: function (el) {
 +    el = el.previousSibling;
 +
 +    while (el && el.nodeType != 1)
 +      el = el.previousSibling;
 +
 +    return el;
 +  },
 +
 +  next_sibling_element: function (el) {
 +    el = el.nextSibling;
 +
 +    while (el && el.nodeType != 1)
 +      el = el.nextSibling;
 +
 +    return el;
 +  },
 +
 +  first_child_element: function (el) {
 +    var node;
 +
 +    for (node = el.firstChild; node; node = node.nextSibling)
 +    {
 +      if (node.nodeType == 1)
 +        break;
 +    }
 +
 +    return node;
 +  },
 +
 +  first_tag: function (element, tag) {
 +    var node;
 +
 +    if (!this.is_xhtml)
 +      tag = tag.toUpperCase();
 +
 +    for (node = element.firstChild; node; node = node.nextSibling)
 +    {
 +      if (node.nodeType == 1 && node.nodeName == tag)
 +        break;
 +    }
 +
 +    return node;
 +  },
 +
 +  hide_selection: function () {
 +    if (window.getSelection) // Firefox, Chromium, Safari, Opera
 +    {
 +      var selection = window.getSelection();
 +
 +      if (selection.rangeCount > 0)
 +      {
 +        var range = selection.getRangeAt(0);
 +        range.collapse (false);
 +      }
 +    }
 +    else // Internet Explorer
 +    {
 +      var textRange = document.selection.createRange ();
 +      textRange.collapse (false);
 +    }
 +  },
 +
 +  get_selected_text: function () {
 +    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 "";
 +  },
 +
 +  // make note of length of selected text
 +  // as this evaluates to zero in click event
 +  mouse_button_up: function (e) {
 +    w3c_slidy.selected_text_len = w3c_slidy.get_selected_text().length;
 +  },
 +
 +  // right mouse button click is reserved for context menus
 +  // it is more reliable to detect rightclick than leftclick
 +  mouse_button_click: function (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("you clicked over a " + target.nodeName + " element\n" +
 +    "w3c_slidy.mouse_click_enabled = " + w3c_slidy.mouse_click_enabled + "\n" +
 +    "leftclick = " + leftclick + "\n" +
 +    "selected text length = " + w3c_slidy.selected_text_len);
 +    //alert("selected text length = " + w3c_slidy.selected_text_len);
 +*/
 +    if (w3c_slidy.selected_text_len > 0)
 +    {
 +      w3c_slidy.stop_propagation(e);
 +      e.cancel = true;
 +      e.returnValue = false;
 +      return false;
 +    }
 +
 +    // dismiss table of contents
 +    w3c_slidy.hide_table_of_contents();
 +
 +    // check if target is something that probably want's clicks
 +    // e.g. a, embed, object, input, textarea, select, option
 +    var tag = target.nodeName.toLowerCase();
 +
 +    if (w3c_slidy.mouse_click_enabled && leftclick &&
 +        tag != "a" &&
 +        tag != "embed" &&
 +        tag != "object" &&
 +        tag != "video" &&
 +        tag != "input" &&
 +        tag != "textarea" &&
 +        tag != "select" &&
 +        tag != "option" &&
 +        !target.onclick)
 +    {
 +      w3c_slidy.next_slide(true);
 +      w3c_slidy.stop_propagation(e);
 +      e.cancel = true;
 +      e.returnValue = false;
 +      return false;
 +    }
 +  },
 +
 +  get_key: function (e)
 +  {
 +    var key;
 +
 +    // kludge around NS/IE differences
 +    if (typeof window.event != "undefined")
 +      key = window.event.keyCode;
 +    else if (e.which)
 +      key = e.which;
 +
 +    return key;
 +  },
 +
 +  get_target: function (e) {
 +    var target;
 +
 +    if (!e)
 +      e = window.event;
 +
 +    if (e.target)
 +      target = e.target;
 +    else if (e.srcElement)
 +      target = e.srcElement;
 +
 +    if (target.nodeType != 1)
 +      target = target.parentNode;
 +
 +    return target;
 +  },
 +
 +  // does display property provide correct defaults?
 +  is_block: function (elem) {
 +    var tag = elem.nodeName.toLowerCase();
 +
 +    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";
 +  },
 +
 +  add_listener: function (element, event, handler) {
 +    if (window.addEventListener)
 +      element.addEventListener(event, handler, false);
 +    else
 +      element.attachEvent("on"+event, handler);
 +  },
 +
 +  // used to prevent event propagation from field controls
 +  stop_propagation: function (event) {
 +    event = event ? event : window.event;
 +    event.cancelBubble = true;  // for IE
 +
 +    if (event.stopPropagation)
 +      event.stopPropagation();
 +
 +    return true;
 +  },
 +
 +  cancel: function (event) {
 +    if (event)
 +    {
 +       event.cancel = true;
 +       event.returnValue = false;
 +
 +      if (event.preventDefault)
 +        event.preventDefault();
 +    }
 +
 +    w3c_slidy.key_wanted = false;
 +    return false;
 +  }
 +};
 +
 +// for each language define an associative array
 +// and also the help text which is longer
 +
 +var w3c_slidy_i18n = {
 +  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"
 +  },
 +  help_es:
 +    "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.",
 +
 +  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"
 +  },
 +  help_ca:
 +    "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.",
 +
 +  strings_cs: {
 +    "slide":"snímek",
 +    "help?":"nápověda",
 +    "contents?":"obsah",
 +    "table of contents":"obsah prezentace",
 +    "Table of Contents":"Obsah prezentace",
 +    "restart presentation":"znovu spustit prezentaci",
 +    "restart?":"restart"
 +  },
 +  help_cs:
 +    "Prezentaci můžete procházet pomocí kliknutí myši, mezerníku, " +
 +    "šipek vlevo a vpravo nebo kláves PageUp a PageDown. Písmo se " +
 +    "dá zvětšit a zmenšit pomocí kláves B a S.",
 +
 +  strings_nl: {
 +    "slide":"pagina",
 +    "help?":"Help?",
 +    "contents?":"Inhoud?",
 +    "table of contents":"inhoudsopgave",
 +    "Table of Contents":"Inhoudsopgave",
 +    "restart presentation":"herstart presentatie",
 +    "restart?":"Herstart?"
 +  },
 +  help_nl:
 +     "Navigeer d.m.v. het muis, spatiebar, Links/Rechts toetsen, " +
 +     "of PgUp en PgDn. Gebruik S en B om de karaktergrootte te veranderen.",
 +
 +  strings_de: {
 +    "slide":"Seite",
 +    "help?":"Hilfe",
 +    "contents?":"Übersicht",
 +    "table of contents":"Inhaltsverzeichnis",
 +    "Table of Contents":"Inhaltsverzeichnis",
 +    "restart presentation":"Präsentation neu starten",
 +    "restart?":"Neustart"
 +  },
 +  help_de:
 +    "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.",
 +
 +  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?"
 +  },
 +  help_pl:
 +    "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.",
 +
 +  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"
 +  },
 +  help_fr:
 +    "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.",
 +
 +  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"
 +  },
 +  help_hu:
 +    "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.",
 +
 +  strings_it: {
 +    "slide":"pag.",
 +    "help?":"Aiuto",
 +    "contents?":"Indice",
 +    "table of contents":"indice",
 +    "Table of Contents":"Indice",
 +    "restart presentation":"Ricominciare la presentazione",
 +    "restart?":"Inizio"
 +  },
 +  help_it:
 +    "Navigare con mouse, barra spazio, frecce sinistra/destra o " +
 +    "PgUp e PgDn. Usare S e B per cambiare la dimensione dei caratteri.",
 +
 +  strings_el: {
 +    "slide":"σελίδα",
 +    "help?":"βοήθεια;",
 +    "contents?":"περιεχόμενα;",
 +    "table of contents":"πίνακας περιεχομένων",
 +    "Table of Contents":"Πίνακας Περιεχομένων",
 +    "restart presentation":"επανεκκίνηση παρουσίασης",
 +    "restart?":"επανεκκίνηση;"
 +  },
 +  help_el:
 +    "Πλοηγηθείτε με το κλίκ του ποντικιού, το space, τα βέλη αριστερά/δεξιά, " +
 +    "ή Page Up και Page Down. Χρησιμοποιήστε τα πλήκτρα S και B για να αλλάξετε " +
 +    "το μέγεθος της γραμματοσειράς.",
 +
 +  strings_ja: {
 +    "slide":"スライド",
 +    "help?":"ヘルプ",
 +    "contents?":"目次",
 +    "table of contents":"目次を表示",
 +    "Table of Contents":"目次",
 +    "restart presentation":"最初から再生",
 +    "restart?":"最初から"
 +  },
 +  help_ja:
 +     "マウス左クリック ・ スペース ・ 左右キー " +
 +     "または Page Up ・ Page Downで操作, S ・ Bでフォントサイズ変更",
 +
 +  strings_zh: {
 +    "slide":"幻灯片",
 +    "help?":"帮助?",
 +    "contents?":"内容?",
 +    "table of contents":"目录",
 +    "Table of Contents":"目录",
 +    "restart presentation":"重新启动展示",
 +    "restart?":"重新启动?"
 +  },
 +  help_zh:
 +    "用鼠标点击, 空格条, 左右箭头, Pg Up 和 Pg Dn 导航. " +
 +    "用 S, B 改变字体大小.",
 +
 +  strings_ru: {
 +    "slide":"слайд",
 +    "help?":"помощь?",
 +    "contents?":"содержание?",
 +    "table of contents":"оглавление",
 +    "Table of Contents":"Оглавление",
 +    "restart presentation":"перезапустить презентацию",
 +    "restart?":"перезапуск?"
 +  },
 +  help_ru:
 +    "Перемещайтесь кликая мышкой, используя клавишу пробел, стрелки" +
 +    "влево/вправо или Pg Up и Pg Dn. Клавиши S и B меняют размер шрифта.",
 +
 +  strings_sv: {
 +    "slide":"sida",
 +    "help?":"hjälp",
 +    "contents?":"innehåll",
 +    "table of contents":"innehållsförteckning",
 +    "Table of Contents":"Innehållsförteckning",
 +    "restart presentation":"visa presentationen från början",
 +    "restart?":"börja om"
 +  },
 +  help_sv:
 +    "Bläddra med ett klick med vänstra musknappen, mellanslagstangenten, " +
 +    "vänster- och högerpiltangenterna eller tangenterna Pg Up, Pg Dn. " +
 +    "Använd tangenterna S och B för att ändra textens storlek.",
 +
 +// each such language array is declared in the localize array
 +// which is set on string prototype and used as in "foo".localize();
 +  localize: {
 +    "es":this.strings_es,
 +    "ca":this.strings_ca,
 +    "cs":this.strings_cs,
 +    "nl":this.strings_nl,
 +    "de":this.strings_de,
 +    "pl":this.strings_pl,
 +    "fr":this.strings_fr,
 +    "hu":this.strings_hu,
 +    "it":this.strings_it,
 +    "el":this.strings_el,
 +    "jp":this.strings_ja,
 +    "zh":this.strings_zh,
 +    "ru":this.strings_ru,
 +    "sv":this.strings_sv
 +  },
 +
 +  init: function () {
 +    var i18n = w3c_slidy_i18n;
 +    var help_text = w3c_slidy.help_text;
 +    i18n.strings_es[help_text] = i18n.help_es;
 +    i18n.strings_ca[help_text] = i18n.help_ca;
 +    i18n.strings_cs[help_text] = i18n.help_cs;
 +    i18n.strings_nl[help_text] = i18n.help_nl;
 +    i18n.strings_de[help_text] = i18n.help_de;
 +    i18n.strings_pl[help_text] = i18n.help_pl;
 +    i18n.strings_fr[help_text] = i18n.help_fr;
 +    i18n.strings_hu[help_text] = i18n.help_hu;
 +    i18n.strings_it[help_text] = i18n.help_it;
 +    i18n.strings_el[help_text] = i18n.help_el;
 +    i18n.strings_ja[help_text] = i18n.help_ja;
 +    i18n.strings_zh[help_text] = i18n.help_zh;
 +    i18n.strings_ru[help_text] = i18n.help_ru;
 +    i18n.strings_sv[help_text] = i18n.help_sv;
 +
 +    w3c_slidy.lang = document.body.parentNode.getAttribute("lang");
 +
 +    if (!w3c_slidy.lang)
 +      w3c_slidy.lang = document.body.parentNode.getAttribute("xml:lang");
 +
 +    if (!w3c_slidy.lang)
 +      w3c_slidy.lang = "en";
 +
 +    // add localize method to all strings
 +    // for use as in "contents".localize()
 +   String.prototype.localize = function() {
 +     if (this == "")
 +       return this;
 +
 +     // try full language code, e.g. en-US
 +     var s, lookup = w3c_slidy_i18n.localize[w3c_slidy.lang];
 +
 +     if (lookup)
 +     {
 +       s = lookup[this];
 +
 +       if (s)
 +        return s;
 +     }
 +
 +     // strip country code suffix, e.g.
 +     // try en if undefined for en-US
 +     var lg = w3c_slidy.lang.split("-");
 +
 +     if (lg.length > 1)
 +     {
 +       lookup = w3c_slidy_i18n.localize[lg[0]];
 +
 +       if (lookup)
 +       {
 +         s = lookup[this];
 +
 +         if (s)
 +          return s;
 +       }
 +     }
 +
 +     // otherwise string as is
 +     return this;
 +   };
 +  }
 +};
 +
 +// hack for back button behavior
 +if (w3c_slidy.ie6 || w3c_slidy.ie7)
 +{
 +  document.write("<iframe id='historyFrame' " +
 +  "src='javascript:\"<html"+"></"+"html>\"' " +
 +  "height='1' width='1' " +
 +  "style='position:absolute;left:-800px'></iframe>");
 +}
 +
 +// attach event listeners for initialization
 +w3c_slidy.set_up();
 +
 +// hide the slides as soon as body element is available
 +// to reduce annoying screen mess before the onload event
 +setTimeout(w3c_slidy.hide_slides, 50);
 +
 +/*]]>*/
 +</script>
 +</head>
 +<body class="article" style="max-width:45em">
 +<div id="header" class="slide">
 +<h1>TTCN-3 and Eclipse TITAN for testing protocol stacks</h1>
 +<span id="author">Harald Welte <laforge@gnumonks.org></span><br />
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_protocol_testing">Protocol Testing</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<div class="paragraph"><p>Important for:</p></div>
 +<ul class="">
 +<li>
 +<span>
 +conformance to specification
 +</span>
 +</li>
 +<li>
 +<span>
 +ensuring interoperability
 +</span>
 +</li>
 +<li>
 +<span>
 +network security
 +</span>
 +</li>
 +<li>
 +<span>
 +regression testing
 +</span>
 +</li>
 +<li>
 +<span>
 +performance
 +</span>
 +</li>
 +</ul>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_protocol_testing_2">Protocol Testing</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<div class="paragraph"><p>No standard methodology, language, approach, tool</p></div>
 +<ul class="">
 +<li>
 +<span>
 +testing implementation against itself
 +</span>
 +<ul class="">
 +<li>
 +<span>
 +works only for symmetric protocols
 +</span>
 +</li>
 +<li>
 +<span>
 +wouldn’t cover lots of problems
 +</span>
 +</li>
 +</ul>
 +</li>
 +<li>
 +<span>
 +testing against wireshark
 +</span>
 +<ul class="">
 +<li>
 +<span>
 +wireshark often way more tolerant than spec
 +</span>
 +</li>
 +</ul>
 +</li>
 +<li>
 +<span>
 +custom implementation
 +</span>
 +<ul class="">
 +<li>
 +<span>
 +in Python (e.g. using scapy)
 +</span>
 +</li>
 +<li>
 +<span>
 +in Erlang (good binary encoder/decoder) or other languages
 +</span>
 +</li>
 +</ul>
 +</li>
 +<li>
 +<span>
 +specific tools like packetdrill
 +</span>
 +</li>
 +</ul>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_protocol_testing_3">Protocol Testing</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<div class="paragraph"><p>Personal story: During past years,</p></div>
 +<ul class="">
 +<li>
 +<span>
 +I implemented tons of [telecom] protocols / stacks at Osmocom.org
 +</span>
 +</li>
 +<li>
 +<span>
 +I was looking for better tools to help [automatic] testing
 +</span>
 +<ul class="">
 +<li>
 +<span>
 +primarily functional testing (correctness / conformance)
 +</span>
 +</li>
 +<li>
 +<span>
 +not so much performance testing
 +</span>
 +</li>
 +</ul>
 +</li>
 +<li>
 +<span>
 +I figured Ideal test tool would…
 +</span>
 +<ul class="">
 +<li>
 +<span>
 +allow very productive and expressive way to describe encoding/decoding
 +</span>
 +</li>
 +<li>
 +<span>
 +allow very convenient pattern matching on incoming messages
 +</span>
 +</li>
 +<li>
 +<span>
 +allow exchange of messages asynchronously with implementation under test
 +</span>
 +</li>
 +</ul>
 +</li>
 +<li>
 +<span>
 +I stumbled on TTCN-3 occasionally and investigated
 +</span>
 +</li>
 +</ul>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_the_ttcn_3_language">The TTCN-3 Language</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<ul class="">
 +<li>
 +<span>
 +domain-specific language <strong>just</strong> for protocol conformance tests
 +</span>
 +</li>
 +<li>
 +<span>
 +TTCN history back to 1983 (!), TTCN-3 since 2000
 +</span>
 +</li>
 +<li>
 +<span>
 +used extensively in classic telecom sector (Ericsson, Nokia, etc.)
 +</span>
 +</li>
 +<li>
 +<span>
 +ETSI developed and published abstract test suites in TTCN-3 for
 +</span>
 +<ul class="">
 +<li>
 +<span>
 +IPv6, SIP, DIAMETER, ePassports, Digital Mobiel Radio, 6LoWPAN
 +</span>
 +</li>
 +</ul>
 +</li>
 +<li>
 +<span>
 +Other bodies published test suites for
 +</span>
 +<ul class="">
 +<li>
 +<span>
 +CoAP, MQTT, MOST, AUTOSAR
 +</span>
 +</li>
 +</ul>
 +</li>
 +</ul>
 +<div class="paragraph"><p>But: Until 2015, only proprietary tools / compilers :(</p></div>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_eclipse_titan">Eclipse TITAN</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<ul class="">
 +<li>
 +<span>
 +After TTCN-3 specification in 2000, Ericsson internally develops TTCN-3 toolchain
 +</span>
 +</li>
 +<li>
 +<span>
 +adopted for many Ericsson-internal testing of all kinds of products
 +</span>
 +</li>
 +<li>
 +<span>
 +proprietary software with commercial licenses
 +</span>
 +</li>
 +<li>
 +<span>
 +300,000 lines of Java + 1.6 Million lines of C++
 +</span>
 +</li>
 +<li>
 +<span>
 +Released as Open Source as "Eclipse TITAN" in 2015
 +</span>
 +<ul class="">
 +<li>
 +<span>
 +Not just TTCN-3 compiler, but also extensive documentations and many protocol modules, test ports as well as Eclipse IDE, Log file viewer/visualizer, etc.
 +</span>
 +</li>
 +</ul>
 +</li>
 +<li>
 +<span>
 +<code>eclipse-titan</code> part of standard Debian / Ubuntu archive, only one apt-get away
 +</span>
 +</li>
 +</ul>
 +<div class="paragraph"><p>Great, we can finally use TTCN-3 in FOSS!</p></div>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_eclipse_titan_compiler_workflow">Eclipse TITAN compiler workflow</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<div class="imageblock graphviz">
 +<div class="content">
 +<img src="ttcn3__1.png" alt="ttcn3__1.png" />
 +</div>
 +</div>
 +<ul class="">
 +<li>
 +<span>
 +TITAN actually <em>compiles</em> into executable binaries, it is not using a VM or scripting
 +</span>
 +<ul class="">
 +<li>
 +<span>
 +ATS: Abstract Test Suite (source code)
 +</span>
 +</li>
 +<li>
 +<span>
 +ETS: Executable Test Suite (executable code)
 +</span>
 +</li>
 +</ul>
 +</li>
 +</ul>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_ttcn_3_language_features_with_titan">TTCN-3 Language Features (with TITAN)</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<ul class="">
 +<li>
 +<span>
 +comprehensive type system
 +</span>
 +</li>
 +<li>
 +<span>
 +parametric templates
 +</span>
 +</li>
 +<li>
 +<span>
 +variety of encoders/decoders
 +</span>
 +</li>
 +<li>
 +<span>
 +automatic / comprehensive logging framework
 +</span>
 +</li>
 +<li>
 +<span>
 +powerful program control statements
 +</span>
 +</li>
 +<li>
 +<span>
 +built-in notion of tests cases, test suites, verdicts, …
 +</span>
 +</li>
 +<li>
 +<span>
 +runtime / executor for parallel test components + aggregating results
 +</span>
 +</li>
 +</ul>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_ttcn_3_basic_types">TTCN-3 Basic Types</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<ul class="">
 +<li>
 +<span>
 +Simple basic types such as <code>integer</code>, <code>float</code>, <code>boolen</code>
 +</span>
 +</li>
 +<li>
 +<span>
 +Basic string types such as <code>bitstring</code>, <code>octetstring</code>, <code>hexstring</code>, <code>charstring</code> (IA5) and <code>universal charstring</code> (UCS-4).
 +</span>
 +</li>
 +<li>
 +<span>
 +Structured Types <code>record</code>, <code>set</code>, <code>record of</code>, <code>set of</code>
 +</span>
 +</li>
 +<li>
 +<span>
 +Verdict type <code>verdicttype</code>
 +</span>
 +<ul class="">
 +<li>
 +<span>
 +can have either value <code>none</code>, <code>pass</code>, <code>inconc</code>, <code>fail</code>,  or <code>error</code>
 +</span>
 +</li>
 +<li>
 +<span>
 +verdict can only <em>deteriorate</em> (<code>pass</code> → <code>fail</code>) but never improve (<code>error</code> → <code>pass</code>)
 +</span>
 +</li>
 +<li>
 +<span>
 +every test case implicitly has a verdict, no need to explicitly declare a variable of <code>verdicttype</code>
 +</span>
 +</li>
 +</ul>
 +</li>
 +</ul>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_ttcn_3_structured_types">TTCN-3 Structured Types</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<div class="paragraph"><p>A structured type is an abstract type comprised of other types, whcih can be nested.
 +An example for a <code>record</code> type (similar to a C-language <code>struct</code>) is shown below</p></div>
 +<div class="listingblock">
 +<div class="content">
 +<pre><code>type record MyMessageType {
 +  integer field1 optional<b><1></b>,
 +  charstring field2,
 +  boolean field3
 +};</code></pre>
 +</div></div>
 +<div class="colist arabic"><ol>
 +<li>
 +<p>
 +optional members may be present or not
 +</p>
 +</li>
 +</ol></div>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_ttcn_3_union_type">TTCN-3 Union Type</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<div class="paragraph"><p>A union expresses a set of alternative types of which one alternative must be chosen.</p></div>
 +<div class="listingblock">
 +<div class="content">
 +<pre><code>type union MyMessageUnion {
 +  integer field1,
 +  charstring field2,
 +};</code></pre>
 +</div></div>
 +<div class="paragraph"><p>Difference to C-language union: <code>ischosen()</code> can be used to learn which of the union members is
 +chosen/defined!</p></div>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_not_used_and_omit">Not-used and omit</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<ul class="">
 +<li>
 +<span>
 +until a variable or field of structured type is assigned, it is <em>unbound</em>
 +</span>
 +</li>
 +<li>
 +<span>
 +whenever a <em>value</em> is expected, TTCN-3 runtime will create an error for <em>unbound</em>
 +</span>
 +</li>
 +<li>
 +<span>
 +in case of absence of optional fields, explicit <code>omit</code> value must be assigned!
 +</span>
 +</li>
 +</ul>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_sub_typing">Sub-typing</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<div class="paragraph"><p>Sub-typing can be used to further constrain a given type.  Typical examples include constrained number ranges,
 +and string patterns</p></div>
 +<div class="listingblock">
 +<div class="content">
 +<pre><code>type integer MyIntRange (1..100);
 +type integer MyIntRange8 (0..infinity);
 +type charstring MyCharRange (”k”..”w");
 +type charstring SideType (”left”, ”right”);
 +type integer MyIntListRange (1..5,7,9);
 +type record length(0..10) of integer RecOfInt;
 +type charstring CrLfTermStrin (pattern ”*\r\n”);</code></pre>
 +</div></div>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_templates">Templates</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<ul class="">
 +<li>
 +<span>
 +Matching incoming messages against some kind of specification is one of the most common tasks in testing protocols
 +</span>
 +<ul class="">
 +<li>
 +<span>
 +some expected fields are static (message type)
 +</span>
 +</li>
 +<li>
 +<span>
 +some expected fields are known (source address)
 +</span>
 +</li>
 +<li>
 +<span>
 +some fields are chosen by sender (some identifier)
 +</span>
 +</li>
 +<li>
 +<span>
 +some fields we don’t care (optional headers that may or may not be present)
 +</span>
 +</li>
 +</ul>
 +</li>
 +<li>
 +<span>
 +TTCN-3 Templates provide elegant solution for this, avoiding any explicit code to be written
 +</span>
 +<ul class="">
 +<li>
 +<span>
 +templates can even be parametric, i.e. they can be instantiated with "arguments"
 +</span>
 +</li>
 +</ul>
 +</li>
 +<li>
 +<span>
 +templates can also be used for sending messages, if they are fully specified/qualified
 +</span>
 +</li>
 +</ul>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_templates_2">Templates</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<div class="listingblock">
 +<div class="content">
 +<pre><code>// Value list template
 +template charstring tr_SingleABorC := (”A”, ”B”, ”C”);</code></pre>
 +</div></div>
 +<div class="listingblock">
 +<div class="content">
 +<pre><code>// Value range
 +template float tr_NearPi := (3.14 .. 3.15);
 +template integer tr_FitsToOneByte := (0 .. 255);
 +template integer tr_GreaterThanZero := (1 .. infinity);</code></pre>
 +</div></div>
 +<div class="listingblock">
 +<div class="content">
 +<pre><code>// Intermixed value list and range matching
 +template integer tr_Intermixed := ((0..127), 200, 255);</code></pre>
 +</div></div>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_matching_inside_values">Matching inside values</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<div class="listingblock">
 +<div class="content">
 +<pre><code>// Using any element matching inside a bitstring value
 +// Last 2 bits can be '0' or '1'
 +template bitstring tr_AnyBSValue := ’101101??’B;</code></pre>
 +</div></div>
 +<div class="listingblock">
 +<div class="content">
 +<pre><code>// Matches charstrings with the first character "a"
 +// and the last one "z"
 +template charstring tr_0 := pattern "a*z";</code></pre>
 +</div></div>
 +<ul class="">
 +<li>
 +<span>
 +more capabilities using <code>complement</code>, <code>ifpresent</code>, <code>subset</code>, <code>superset</code>, <code>permutation</code> constructs not
 +  covered here
 +</span>
 +</li>
 +</ul>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_parametric_templates">Parametric Templates</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<div class="paragraph"><p>See below for an example of a parametric template:</p></div>
 +<div class="listingblock">
 +<div class="content">
 +<pre><code>type record MyMessageType {
 +  integer field1 optional,
 +  charstring field2,
 +  boolean field3
 +};
 +
 +template MyMessageType trMyTemplte(boolean pl_param) := {
 +  field1 : = ?, // present, but any value
 +  field2 : = (”B”, ”O”, ”Q”) ,
 +  field3 := pl_param
 +};</code></pre>
 +</div></div>
 +<div class="paragraph"><p>The built-in <code>match()</code> function can be used to check if a given value matches a given template.  Some TTCN-3
 +statements such as <code>receive()</code> have built-in capabilities for template matching, avoiding even the explicit
 +call of <code>match()</code> in many cases.</p></div>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_template_hierarchy">Template Hierarchy</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<div class="paragraph"><p>Using modified templates, one can build a hierarchy of templates: From the specific to the unspecific</p></div>
 +<div class="listingblock">
 +<div class="content">
 +<pre><code>template MyMsgType t_MyMsgAny := {
 +  msg_type := ?,
 +  foo := bar
 +};
 +
 +template MyMsgType t_MyMsg23 modifies t_MyMsgAny := {
 +  msg_type := 23,
 +};</code></pre>
 +</div></div>
 +<div class="paragraph"><p>where</p></div>
 +<ul class="">
 +<li>
 +<span>
 +<em>t_MyMsgAny</em> matches a message with any message type and "foo=bar", while
 +</span>
 +</li>
 +<li>
 +<span>
 +<em>t_MMyMsg23</em> matches only those that have "foo=bar" and "msg_type=23"
 +</span>
 +</li>
 +</ul>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_encoders_decoders">Encoders/Decoders</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<ul class="">
 +<li>
 +<span>
 +type system, templates, matching are all nice and great, but we need to get data from wire format into
 +  TTCN-3 abstract types
 +</span>
 +</li>
 +<li>
 +<span>
 +TTTCN-3 specifies importing of formal schema definitios, such as ASN.1, IDL, XSD (XML) and JSON
 +</span>
 +</li>
 +<li>
 +<span>
 +TITAN has additional codecs for those (many) protocols that lack formal syntax
 +</span>
 +<ul class="">
 +<li>
 +<span>
 +<code>raw</code> codec for binary protocols (e.g. GTP)
 +</span>
 +</li>
 +<li>
 +<span>
 +<code>text</code> codec for text based protocols (e.g. HTTP, MGCP, IMAP, …)
 +</span>
 +</li>
 +</ul>
 +</li>
 +<li>
 +<span>
 +codecs allow you to <em>express/describe</em> the format (declarative programming) rather than the usual imperative approach
 +</span>
 +</li>
 +</ul>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_titan_raw_codec_udp_example">TITAN raw codec: UDP Example</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<div class="paragraph"><p>How to express an UDP header using TITAN raw codec</p></div>
 +<div class="listingblock">
 +<div class="content">
 +<pre><code>type integer LIN2_BO_LAST (0..65535) with {
 +  variant ”FIELDLENGTH(16), COMP(nosign), BYTEORDER(last)”
 +};
 +type record UDP_header {
 +  LIN2_BO_LAST srcport,
 +  LIN2_BO_LAST dstport,
 +  LIN2_BO_LAST len,
 +  LIN2_BO_LAST cksum
 +} with { variant ”FIELDORDER(msb)” };
 +type record UDP packet {
 +  UDP_header header
 +  octetstring payload
 +} with {
 +  variant (header) ”LENGTHTO(header, payload), LENGTHINDEX(len)”
 +};</code></pre>
 +</div></div>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_titan_raw_codec_gtp_example">TITAN raw codec: GTP Example</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<div class="paragraph"><p>How to express an GTP header using TITAN raw codec</p></div>
 +<div class="listingblock">
 +<div class="content">
 +<pre><code>type record GRE_Header {
 +  BIT1 csum_present,
 +  BIT1 rt_present,
 +  BIT1 key_present,
 +  ...
 +  OCT2 protocol_type,
 +  OCT2 checksum optional,
 +  OCT2 offset optional,
 +  OCT4 key otional,
 +  ...
 +} with {
 +  variant (checksum) "PRESENCE(csum_present='1', rt_present='1'B)"
 +  variant (offset) "PRESENCE(csum_present='1'B, rt_present='1'B)"
 +  variant (key) "PRESENCE(key_present='1'B)"
 +}</code></pre>
 +</div></div>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_titan_text_codec_mgcp_example">TITAN text codec: MGCP Example</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<div class="listingblock">
 +<div class="content">
 +<pre><code>type charstring MgcpVerb ("EPCF", "CRCX", "MDCX", "DLCX", "RQNT", "NTFY",
 +                          "AUEP", "AUCX", "RSIP") with {
 +  variant "TEXT_CODING(,convert=upper_case,,case_insensitive)"
 +};
 +type charstring MgcpTransId     (pattern "\d#(1,9)");
 +type charstring MgcpEndpoint    (pattern "*@*");
 +type charstring MgcpVersion     (pattern "\d.\d") with {
 +  variant "BEGIN('MGCP ')"
 +};
 +type record MgcpCommandLine {
 +        MgcpVerb        verb,
 +        MgcpTransId     trans_id,
 +        MgcpEndpoint    ep,
 +        MgcpVersion     ver
 +} with {
 +        variant "SEPARATOR(' ', '[\t ]+')"
 +        variant "END('\r\n', '([\r\n])|(\r\n)')"
 +};</code></pre>
 +</div></div>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_program_control_statements">Program Control Statements</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<ul class="">
 +<li>
 +<span>
 +<code>if</code> / <code>else</code> like in C
 +</span>
 +</li>
 +<li>
 +<span>
 +<code>select</code> statement similar to C <code>switch</code>
 +</span>
 +</li>
 +<li>
 +<span>
 +<code>for</code>, <code>while</code>, <code>do-while</code> loops like in C
 +</span>
 +</li>
 +<li>
 +<span>
 +<code>goto</code> and <code>label</code>
 +</span>
 +</li>
 +<li>
 +<span>
 +<code>break</code> and <code>continue</code> like in C
 +</span>
 +</li>
 +</ul>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_abstract_communications_operations">Abstract Communications Operations</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<ul class="">
 +<li>
 +<span>
 +TTCN-3 test suites communicate with <em>implementation under test</em> through abstract TestPorts
 +</span>
 +<ul class="">
 +<li>
 +<span>
 +TestPorts can be implemented in TTCN-3 or C++ and linked in
 +</span>
 +</li>
 +<li>
 +<span>
 +TestPorts must be <em>connected</em> before using send/receive operaitons
 +</span>
 +</li>
 +<li>
 +<span>
 +TITAN provides TestPorts for e.g. packet socket, IP/UDP/TCP/SCTP socket, …
 +</span>
 +</li>
 +</ul>
 +</li>
 +<li>
 +<span>
 +<code><port>.send(<ValueRef>)</code> performs non-blocking send
 +</span>
 +<ul class="">
 +<li>
 +<span>
 +Literal value, constant, variable, specific value template, …
 +</span>
 +</li>
 +</ul>
 +</li>
 +<li>
 +<span>
 +<code><port>.receive(<TemplateRef>)</code> or <code><port>.receive</code> performs blocking receive
 +</span>
 +<ul class="">
 +<li>
 +<span>
 +literal value, constant, variable, template (with matching!), inline template
 +</span>
 +</li>
 +</ul>
 +</li>
 +</ul>
 +<div class="paragraph"><p>'… but if receive blocks, how can we wait for any of N events?</p></div>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_program_control_and_behavior">Program Control and Behavior</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<ul class="">
 +<li>
 +<span>
 +program statements are executed in order
 +</span>
 +</li>
 +<li>
 +<span>
 +blocking statements block the execution of the component
 +</span>
 +</li>
 +<li>
 +<span>
 +occurrence of unexpected event may cause infinite blocking
 +</span>
 +</li>
 +</ul>
 +<div class="listingblock">
 +<div class="content">
 +<pre><code>// x must be the first on queue P, y the second
 +P.receive(x); // Blocks until x appears on top of queue P
 +P.receive(y); // Blocks until y appears on top of queue P
 +// When y arrives first then P.receive(x) blocks -> error</code></pre>
 +</div></div>
 +<div class="paragraph"><p>This is what leads to the <code>alt</code> statement:
 +<code>alt</code> declares a seto alternatives covering all events, which</p></div>
 +<ul class="">
 +<li>
 +<span>
 +can happen: expected messages, timeouts, …
 +</span>
 +</li>
 +<li>
 +<span>
 +must not happen: unexpected faulty messages, no message received, …
 +</span>
 +</li>
 +<li>
 +<span>
 +all alternatives inside <code>alt</code> are blocking operations
 +</span>
 +</li>
 +</ul>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_the_code_alt_code_statement">The <code>alt</code> statement</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<div class="listingblock">
 +<div class="content">
 +<pre><code>P.send(req)
 +T.start;
 +// ...
 +alt {
 +[] P.receive(resp)  { /* actions to do and exit alt */ }
 +[] any port.receive { /* handle unexpected event */ }
 +[] T.timeout        { /* handle timer expiry and exit */ }
 +}</code></pre>
 +</div></div>
 +<ul class="">
 +<li>
 +<span>
 +[] is guard condition enables or disables the alternative
 +</span>
 +<ul class="">
 +<li>
 +<span>
 +usually empty <code>[]</code> equals <code>[true]</code>
 +</span>
 +</li>
 +<li>
 +<span>
 +can contain a condition like <code>[x > 0]</code>
 +</span>
 +</li>
 +<li>
 +<span>
 +very good for e.g. state machines to activate some alternatives only in certain states while others may
 +occur in any state
 +</span>
 +</li>
 +</ul>
 +</li>
 +</ul>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_the_code_alt_code_and_code_repeat_code_statements">The <code>alt</code> and <code>repeat</code> statements</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<div class="paragraph"><p>The <code>repeat</code> statement</p></div>
 +<ul class="">
 +<li>
 +<span>
 +takes a new snapshot and re-evaluates the alt statement
 +</span>
 +</li>
 +<li>
 +<span>
 +can appear as last statement in statement blocks of statements
 +</span>
 +</li>
 +</ul>
 +<div class="listingblock">
 +<div class="content">
 +<pre><code>P.send(req)
 +T.start;
 +alt {
 +  [] P.receive(resp)       { /* actions to do and exit alt */ }
 +  [] P.receive(keep_alive) { /* handle keep alive message */
 +                             repeat }
 +  [] any port.receive      { /* handle unexpected event */ }
 +  [] T.timeout             { /* handle timer expiry and exit */ }
 +}</code></pre>
 +</div></div>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_the_code_interleave_code_statement">The <code>interleave</code> statement</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<div class="paragraph"><p>The <code>interleave</code> statement</p></div>
 +<ul class="">
 +<li>
 +<span>
 +enforces N matching events happen exactly once
 +</span>
 +</li>
 +<li>
 +<span>
 +permits any order!
 +</span>
 +</li>
 +</ul>
 +<div class="listingblock">
 +<div class="content">
 +<pre><code>interleave {
 +  [] P.receive(1) { /* actions, optional */ }
 +  [] Q.receive(4) { /* actions, optional */ }
 +  [] R.receive(6) { /* actions, optional */ }
 +}</code></pre>
 +</div></div>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_the_code_altstep_code_construct">The <code>altstep</code> construct</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<ul class="">
 +<li>
 +<span>
 +collection of set of common/shared <code>alt</code>
 +</span>
 +<ul class="">
 +<li>
 +<span>
 +such as responding to a keep-alive PING, depending on protocol
 +</span>
 +</li>
 +</ul>
 +</li>
 +<li>
 +<span>
 +invoked in-line, inside <code>alt</code> statements or <em>activated as default</em>
 +</span>
 +</li>
 +</ul>
 +<div class="paragraph"><p>Definition of an <code>altstep</code> for PING/PONG handling and guard timer:</p></div>
 +<div class="listingblock">
 +<div class="content">
 +<pre><code>altstep my_altstep() {
 +  [] P.receive(tr_PING) { P.send(ts_PONG); }
 +  [] T_guard.timeout { setverdict(fail, "Guard timer timed out"); }
 +}</code></pre>
 +</div></div>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_the_code_altstep_code_construct_2">The <code>altstep</code> construct</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<div class="paragraph"><p>Explicit Usage of the <code>my_altstep</code> defined on previous slide:</p></div>
 +<div class="listingblock">
 +<div class="content">
 +<pre><code>P.send(foo)
 +alt {
 +  [] P.receive(bar)
 +  [] my_altstep()
 +}</code></pre>
 +</div></div>
 +<div class="paragraph"><p>this dynamically adds the alternatives from <code>my_altstep</code> at the given location
 +and priority of the above <code>alt</code>, ensuring that one doesn’t have to repeat
 +ping/pong handling as well as global guard timer timeout handling in every <code>alt</code>
 +or every single test case all over again.</p></div>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_default_code_altstep_code_activation">default <code>altstep</code> activation</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<ul class="">
 +<li>
 +<span>
 +in previous slide, <code>altstep</code> invocation was explicit
 +</span>
 +</li>
 +<li>
 +<span>
 +some behavior is so universal, that you want to activate it <em>always</em>
 +</span>
 +</li>
 +</ul>
 +<div class="listingblock">
 +<div class="content">
 +<pre><code>altstep as_pingpong() {
 +  [] P.receive(tr_PING) { P.send(ts_PONG); }
 +}
 +...
 +var default d1 := activate(as_pingpong());
 +... /* code executing with as_pingpong activated */
 +deactivate(d1);</code></pre>
 +</div></div>
 +<ul class="">
 +<li>
 +<span>
 +all alt-statements implicitly have as_pingpong active
 +</span>
 +</li>
 +<li>
 +<span>
 +but also all <em>stand-alone receive statements</em> !
 +</span>
 +</li>
 +</ul>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_ttcn_3_modules">TTCN-3 modules</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<div class="paragraph"><p>TTCN-3 code is written in <em>modules</em></p></div>
 +<ul class="">
 +<li>
 +<span>
 +a test suite consists of one or more modules
 +</span>
 +</li>
 +<li>
 +<span>
 +a module contains <em>module definitions</em> and an optional <em>control part</em>
 +</span>
 +<ul class="">
 +<li>
 +<span>
 +<em>parameters</em> (automatically configurable via config file)
 +</span>
 +</li>
 +<li>
 +<span>
 +definition of <em>data types</em>, <em>constants</em>, <em>templates</em>
 +</span>
 +</li>
 +<li>
 +<span>
 +definition of <em>communications ports</em>
 +</span>
 +</li>
 +<li>
 +<span>
 +definition of <em>test components</em>, <em>functions</em> <em>altsteps</em> and <em>test cases</em>
 +</span>
 +</li>
 +<li>
 +<span>
 +<em>control part</em> determines default order/execution of test cases
 +</span>
 +</li>
 +</ul>
 +</li>
 +<li>
 +<span>
 +modules can import from each other (think in python terms)
 +</span>
 +</li>
 +</ul>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_examples">Examples</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<div class="paragraph"><p>Let’s have a look at some real-world examples and do a bit of a walk-through
 +before continuing with the slides…</p></div>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_logging">Logging</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<ul class="">
 +<li>
 +<span>
 +TITAN runtime contains extensive logging framework
 +</span>
 +</li>
 +<li>
 +<span>
 +config file determines log level for various different subsystems
 +</span>
 +<ul class="">
 +<li>
 +<span>
 +e.g. any encode, decode, receive, transmit operations logged
 +</span>
 +</li>
 +<li>
 +<span>
 +timer starts, expirations
 +</span>
 +</li>
 +<li>
 +<span>
 +any changes to test case verdict
 +</span>
 +</li>
 +</ul>
 +</li>
 +<li>
 +<span>
 +explicit logging from code by use of <code>log()</code> built-in function
 +</span>
 +</li>
 +<li>
 +<span>
 +<code>ttcn3_logformat</code> tool for pretty-printing log files
 +</span>
 +</li>
 +<li>
 +<span>
 +<code>ttcn3_logmerge</code> tool for merging/splicing multiple logs
 +</span>
 +</li>
 +<li>
 +<span>
 +log plugins e.g. for generating JUnit-XML available
 +</span>
 +<ul class="">
 +<li>
 +<span>
 +facilitates easy reporting / integration to Jenkins or other CI
 +</span>
 +</li>
 +</ul>
 +</li>
 +</ul>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_logging_2">Logging</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<div class="paragraph"><p>Log file format example:</p></div>
 +<div class="listingblock">
 +<div class="content">
 +<pre><code>// abstract data type before encode
 +13:30:41.243536 Sent on GTPC to system @GTP_CodecPort.Gtp1cUnitdata : { peer := { connId := 1, remName := "127.0.23.1", remPort := 2123 }, gtpc := { pn_bit := '0'B, s_bit := '1'B, e_bit := '0'B, spare := '0'B, pt := '1'B, version := '001'B, messageType := '01'O, lengthf := 0, teid := '00000000'O, opt_part := { sequenceNumber := '3AAC'O, npduNumber := '00'O, nextExtHeader := '00'O, gTPC_extensionHeader_List := omit }, gtpc_pdu := { echoRequest := { private_extension_gtpc := omit } } } }
 +
 +// 'msg' contains encoded binary data actually sent via socket
 +13:30:41.243799 Outgoing message was mapped to @IPL4asp_Types.ASP_SendTo : { connId := 1, remName := "127.0.23.1", remPort := 2123, proto := { udp := { } }, msg := '32010004000000003AAC0000'O }</code></pre>
 +</div></div>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_logging_3">Logging</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<div class="paragraph"><p>The same log file lines if run through <code>ttcn3_logformat</code></p></div>
 +<div class="listingblock">
 +<div class="content">
 +<pre><code>13:30:41.243536 Sent on GTPC to system @GTP_CodecPort.Gtp1cUnitdata : {
 +    peer := {
 +        connId := 1,
 +        remName := "127.0.23.1",
 +        remPort := 2123
 +    },
 +    gtpc := {
 +        pn_bit := '0'B,
 +        s_bit := '1'B,
 +        e_bit := '0'B,
 +        spare := '0'B,
 +        pt := '1'B,
 +        version := '001'B,
 +        messageType := '01'O,
 +        lengthf := 0,
 +        teid := '00000000'O,
 +        opt_part := {
 +            sequenceNumber := '3AAC'O,
 +            npduNumber := '00'O,
 +            nextExtHeader := '00'O,
 +            gTPC_extensionHeader_List := omit
 +        },
 +        gtpc_pdu := {
 +            echoRequest := {
 +                private_extension_gtpc := omit
 +            }
 +        }
 +    }
 +}
 +13:30:41.243799 Outgoing message was mapped to @IPL4asp_Types.ASP_SendTo : {
 +    connId := 1,
 +    remName := "127.0.23.1",
 +    remPort := 2123,
 +    proto := {
 +        udp := { }
 +    },
 +    msg := '32010004000000003AAC0000'O
 +}</code></pre>
 +</div></div>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_existing_titan_source">Existing TITAN Source</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<ul class="">
 +<li>
 +<span>
 +Protocol encoding/decoding
 +</span>
 +<ul class="">
 +<li>
 +<span>
 +BSSAP+, BSSGP, BSSMAP, CoAP, DSS1, DUA, EAP, GRE, GTP, HTTP, ISUP, LLC, M2PA, M2UA, MQTT, MongoDB, NDP, NS,
 +   NTAF, ROSE, SCTP, SDP, SNDCP, STOMP, STUN, SUA, TLS, WTP, DNS, IP, SMPP, SNMP, IKEv2, DHCP, PPP, RTP, TCP,
 +   UDP, XMPP, DHCPv6, SMTP, ICMP, RTSP, ICMPv6, DIAMETER, FrameRelay, ProtoBuff, IUA, L2TP, M3UA, MIME,
 +   WebSocket, H.248, IMAP, IPsec, SRTP, MSRP, ICAP, RADIUS
 +</span>
 +</li>
 +</ul>
 +</li>
 +<li>
 +<span>
 +Protocol Emulation
 +</span>
 +<ul class="">
 +<li>
 +<span>
 +M3UA, SCCP, SUA
 +</span>
 +</li>
 +</ul>
 +</li>
 +<li>
 +<span>
 +Test Ports
 +</span>
 +<ul class="">
 +<li>
 +<span>
 +GPIO, MTP3, Serial, SocketCAN, SCTP, SIP, HTTP, Telnet, UDP, pcap file, pipe, SQL, TCP, SUNRPC, SSH, STDINOUT, sockets, LDAP
 +</span>
 +</li>
 +</ul>
 +</li>
 +</ul>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_further_reading">Further Reading</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<ul class="">
 +<li>
 +<span>
 +Ericsson TTCN-3 tutorial <a href="http://www.ttcn-3.org/files/TTCN3_P.pdf">http://www.ttcn-3.org/files/TTCN3_P.pdf</a>
 +</span>
 +</li>
 +<li>
 +<span>
 +An Introduction to TTCN-3, 2nd Edition <a href="http://www.wiley.com/go/willcock_TTCN-3_2e">http://www.wiley.com/go/willcock_TTCN-3_2e</a>
 +</span>
 +</li>
 +<li>
 +<span>
 +Modules <a href="https://github.com/eclipse">https://github.com/eclipse</a>
 +</span>
 +</li>
 +<li>
 +<span>
 +More Modules <a href="http://git.eclipse.org/">http://git.eclipse.org/</a>
 +</span>
 +</li>
 +<li>
 +<span>
 +Debian <a href="https://packages.debian.org/search?keywords=eclipse-titan">https://packages.debian.org/search?keywords=eclipse-titan</a>
 +</span>
 +</li>
 +<li>
 +<span>
 +Ubuntu <a href="https://packages.ubuntu.com/search?keywords=eclipse-titan">https://packages.ubuntu.com/search?keywords=eclipse-titan</a>
 +</span>
 +</li>
 +</ul>
 +</div>
 +</div>
 +<div class="sect1 slide">
 +<h1 id="_eof">EOF</h1>
 +<div class="sectionbody" style="max-width:45em">
 +<div class="paragraph"><p>End of File</p></div>
 +</div>
 +</div>
 +</body>
 +</html>
 diff --git a/2018/ttcn-eh2018/ttcn3__1.png b/2018/ttcn-eh2018/ttcn3__1.png Binary files differnew file mode 100644 index 0000000..bf0e691 --- /dev/null +++ b/2018/ttcn-eh2018/ttcn3__1.png  | 
