5626 lines
134 KiB
HTML
5626 lines
134 KiB
HTML
<?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>Running FOSS Cellular Networks on Linux</title>
|
||
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />
|
||
<meta name="copyright" content="Copyright © sysmocom - s.f.m.c. GmbH (License: CC-BY-SA)" />
|
||
<meta name="generator" content="AsciiDoc 8.6.9" />
|
||
<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>Running FOSS Cellular Networks on Linux</h1>
|
||
<span id="author">Harald Welte <laforge@gnumonks.org></span><br />
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_what_this_talk_is_about">What this talk is about</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<ul class=" incremental">
|
||
<li>
|
||
<span>
|
||
Implementing GSM/GPRS network elements as FOSS
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
Applied Protocol Archeology
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
Doing all of that on top of Linux (in userspace)
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
If you expeccted kernel stuff, you’ll be disappointed
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_running_your_own_internet_style_network">Running your own Internet-style network</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
use off-the-shelf hardware (x86, Ethernet card)
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
use any random Linux distribution
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
configure Linux kernel TCP/IP network stack
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
enjoy fancy features like netfilter/iproute2/tc
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
use apache/lighttpd/nginx on the server
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
use Firefox/chromium/konqueor/lynx on the client
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
do whatever modification/optimization on any part of the stack
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_running_your_own_gsm_network">Running your own GSM network</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<div class="paragraph"><p>Until 2009 the situation looked like this:</p></div>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
go to Ericsson/Huawei/ZTE/Nokia/Alcatel/…
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
spend lots of time convincing them that you’re an eligible customer
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
spend a six-digit figure for even the most basic full network
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
end up with black boxes you can neither study nor improve
|
||
</span>
|
||
<ul class=" incremental">
|
||
<li>
|
||
<span>
|
||
WTF?
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
I’ve grown up with FOSS and the Internet. I know a better world.
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_why_no_cellular_foss">Why no cellular FOSS?</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
both cellular (2G/3G/4G) and TCP/IP/HTTP protocol specs are publicly
|
||
available for decades. Can you believe it?
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
Internet protocol stacks have lots of FOSS implementations
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
cellular protocol stacks have no FOSS implementations for the
|
||
first almost 20 years of their existence?
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
it’s the classic conflict
|
||
</span>
|
||
<ul class=" incremental">
|
||
<li>
|
||
<span>
|
||
classic circuit-switched telco vs. the BBS community
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
ITU-T/OSI/ISO vs. Arpanet and TCP/IP
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_enter_osmocom">Enter Osmocom</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<div class="paragraph"><p>In 2008, some people started to write FOSS for GSM</p></div>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
to boldly go where no FOSS hacker has gone before
|
||
</span>
|
||
<ul class=" incremental">
|
||
<li>
|
||
<span>
|
||
where protocol stacks are deep
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
and acronyms are plentiful
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
we went from <code>bs11-abis</code> to <code>bsc_hack</code> to <em>OpenBSC</em>
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
many other related projects were created
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
finally leading to the <em>Osmocom</em> umbrella project
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_classic_gsm_network_architecture">Classic GSM network architecture</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<div class="imageblock">
|
||
<div class="content">
|
||
<img src="Gsm_structures.svg" alt="Gsm_structures.svg" width="850" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_gsm_acronyms_radio_access_network">GSM Acronyms, Radio Access Network</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<div class="dlist"><dl class="">
|
||
<dt class="hdlist1">
|
||
MS
|
||
</dt>
|
||
<dd>
|
||
<p>
|
||
Mobile Station (your phone)
|
||
</p>
|
||
</dd>
|
||
<dt class="hdlist1">
|
||
BTS
|
||
</dt>
|
||
<dd>
|
||
<p>
|
||
Base Transceiver Station, consists of 1..n TRX
|
||
</p>
|
||
</dd>
|
||
<dt class="hdlist1">
|
||
TRX
|
||
</dt>
|
||
<dd>
|
||
<p>
|
||
Transceiver for one radio channel, serves 8 TS
|
||
</p>
|
||
</dd>
|
||
<dt class="hdlist1">
|
||
TS
|
||
</dt>
|
||
<dd>
|
||
<p>
|
||
Timeslots in the GSM radio interface; each runs a specific combination of logical channels
|
||
</p>
|
||
</dd>
|
||
<dt class="hdlist1">
|
||
BSC
|
||
</dt>
|
||
<dd>
|
||
<p>
|
||
Base Station Controller
|
||
</p>
|
||
</dd>
|
||
</dl></div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_gsm_acronyms_core_network">GSM Acronyms, Core Network</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<div class="dlist"><dl class="">
|
||
<dt class="hdlist1">
|
||
MSC
|
||
</dt>
|
||
<dd>
|
||
<p>
|
||
Mobile Switching Center; Terminates MM + CC Sub-layers
|
||
</p>
|
||
</dd>
|
||
<dt class="hdlist1">
|
||
HLR
|
||
</dt>
|
||
<dd>
|
||
<p>
|
||
Home Location Register; Subscriber Database
|
||
</p>
|
||
</dd>
|
||
<dt class="hdlist1">
|
||
SMSC
|
||
</dt>
|
||
<dd>
|
||
<p>
|
||
SMS Service Center
|
||
</p>
|
||
</dd>
|
||
</dl></div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_gsm_acronyms_layer_2_3">GSM Acronyms, Layer 2 + 3</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<div class="dlist"><dl class="">
|
||
<dt class="hdlist1">
|
||
LAPDm
|
||
</dt>
|
||
<dd>
|
||
<p>
|
||
Link Access Protocol, D-Channel. Like LAPD in ISDN
|
||
</p>
|
||
</dd>
|
||
<dt class="hdlist1">
|
||
RR
|
||
</dt>
|
||
<dd>
|
||
<p>
|
||
Radio Resource (establish/release dedicated channels)
|
||
</p>
|
||
</dd>
|
||
<dt class="hdlist1">
|
||
MM
|
||
</dt>
|
||
<dd>
|
||
<p>
|
||
Mobility Management (registration, location, authentication)
|
||
</p>
|
||
</dd>
|
||
<dt class="hdlist1">
|
||
CC
|
||
</dt>
|
||
<dd>
|
||
<p>
|
||
Call Control (voice, circuit switched data, fax)
|
||
</p>
|
||
</dd>
|
||
<dt class="hdlist1">
|
||
CM
|
||
</dt>
|
||
<dd>
|
||
<p>
|
||
Connection Management
|
||
</p>
|
||
</dd>
|
||
</dl></div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_osmocom_gsm_components">Osmocom GSM components</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<div class="imageblock">
|
||
<div class="content">
|
||
<img src="osmocom-gsm.svg" alt="osmocom-gsm.svg" width="850" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_classic_gsm_network_as_digraph">Classic GSM network as digraph</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<div class="imageblock graphviz">
|
||
<div class="content">
|
||
<img src="running-foss-gsm__1.png" alt="running-foss-gsm__1.png" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_simplified_osmonitb_gsm_network">Simplified OsmoNITB GSM network</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<div class="imageblock graphviz">
|
||
<div class="content">
|
||
<img src="running-foss-gsm__2.png" alt="running-foss-gsm__2.png" />
|
||
</div>
|
||
</div>
|
||
<div class="paragraph"><p>which further reduces to the following minimal setup:</p></div>
|
||
<div class="imageblock graphviz">
|
||
<div class="content">
|
||
<img src="running-foss-gsm__3.png" alt="running-foss-gsm__3.png" />
|
||
</div>
|
||
</div>
|
||
<div class="paragraph"><p>So our minimal setup is a <em>Phone</em>, a <em>BTS</em> and <em>OsmoNITB</em>.</p></div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_which_bts_to_use">Which BTS to use?</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
Proprietary BTS of classic vendor
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
Siemens BS-11 is what we started with
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
Nokia, Ericsson, and others available 2nd hand
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
<em>OsmoBTS</em> software implementation, running with
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
Proprietary HW + PHY (DSP): <em>sysmoBTS</em>, or
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
General purpose SDR (like USRP) + <em>OsmoTRX</em>
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
<div class="paragraph"><p>We assume a sysmoBTS in the following tutorial</p></div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_osmobts_overview">OsmoBTS Overview</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<div class="imageblock">
|
||
<div class="content">
|
||
<img src="osmo-bts.svg" alt="osmo-bts.svg" />
|
||
</div>
|
||
</div>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
Implementation of GSM BTS
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
supports variety of hardware/PHY options
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
<code>osmo-bts-sysmo</code>: BTS family by sysmocom
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
<code>osmo-bts-trx</code>: Used with <em>OsmoTRX</em> + general-purpose SDR
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
<code>osmo-bts-octphy</code>: Octasic OCTBTS hardware / OCTSDR-2G PHY
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
<code>osmo-bts-litecell15</code>: Nutaq Litecell 1.5 hardware/PHY
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_configuring_osmocom_software">Configuring Osmocom software</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
all Osmo* GSM infrastructure programs share common architecture, as
|
||
defined by various libraries <em>libosmo{core,gsm,vty,abis,netif,…}</em>
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
part of this is configuration handling
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
interactive configuration via command line interface (<strong>vty</strong>), similar
|
||
to Cisco routers
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
based on a fork of the VTY code from Zebra/Quagga, now <em>libosmovty</em>
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
you can manually edit the config file,
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
or use <code>configure terminal</code> and interactively change it
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_configuring_osmobts">Configuring OsmoBTS</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
<em>OsmoBTS</em> in our example scenario runs on the embedded ARM/Linux system
|
||
inside the <em>sysmoBTS</em>
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
we access the <em>sysmoBTS</em> via serial console or ssh
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
we then edit the configuration file <code>/etc/osmocom/osmo-bts.cfg</code> as
|
||
described in the following slide
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_configuring_osmobts_2">Configuring OsmoBTS</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre><code>bts 0
|
||
band DCS1800 <b><1></b>
|
||
ipa unit-id 1801 0 <b><2></b>
|
||
oml remote-ip 192.168.100.11 <b><3></b></code></pre>
|
||
</div></div>
|
||
<div class="colist arabic"><ol>
|
||
<li>
|
||
<p>
|
||
the GSM frequency band in which the BTS operates
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
the unit-id by which this BTS identifies itself to the BSC
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
the IP address of the BSC (to establish the OML connection towards it)
|
||
</p>
|
||
</li>
|
||
</ol></div>
|
||
<div class="admonitionblock">
|
||
<table><tr>
|
||
<td class="icon">
|
||
<div class="title">Note</div>
|
||
</td>
|
||
<td class="content">All other configuration is downloaded by the BSC via OML. So most
|
||
BTS settings are configured in the BSC/NITB configuration file.</td>
|
||
</tr></table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_configuring_osmonitb">Configuring OsmoNITB</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
<em>OsmoNITB</em> is the <code>osmo-nitb</code> executable built from the <code>openbsc</code>
|
||
source tree / git repository
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
just your usual <code>git clone && autoreconf -fi && ./configure && make install</code>
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
(in reality, the <code>libosmo*</code> dependencies are required first…)
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
<em>OsmoNITB</em> runs on any Linux system, like your speakers' laptop
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
you can actually also run it on the ARM/Linux of the <em>sysmoBTS</em> itself,
|
||
having a literal <em>Network In The Box</em> with power as only external
|
||
dependency
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_configuring_osmonitb_2">Configuring OsmoNITB</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre><code>network
|
||
network country code 1 <b><1></b>
|
||
mobile network code 1 <b><2></b>
|
||
shot name Osmocom <b><3></b>
|
||
long name Osmocom
|
||
auth policy closed <b><4></b>
|
||
encryption a5 0 <b><5></b></code></pre>
|
||
</div></div>
|
||
<div class="colist arabic"><ol>
|
||
<li>
|
||
<p>
|
||
MCC (Country Code) e.g. 262 for Germany; 1 == Test
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
MNC (Network Code) e.g. mcc=262, mnc=02 == Vodafone; 1 == Test
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
Operator name to be sent to the phone <strong>after</strong> registration
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
Only accept subscribers (SIM cards) explicitly authorized in HLR
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
Use A5/0 (== no encryption)
|
||
</p>
|
||
</li>
|
||
</ol></div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_configuring_bts_in_osmonitb_bts">Configuring BTS in OsmoNITB (BTS)</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre><code>network
|
||
bts 0
|
||
type sysmobts <b><1></b>
|
||
band DCS1800 <b><2></b>
|
||
ms max power 33 <b><3></b>
|
||
periodic location update 6 <b><4></b>
|
||
ip.access unit_id 1801 0 <b><5></b>
|
||
codec-support fr hr efr amr <b><6></b></code></pre>
|
||
</div></div>
|
||
<div class="colist arabic"><ol>
|
||
<li>
|
||
<p>
|
||
type of the BTS that we use (must match BTS)
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
frequency band of the BTS (must match BTS)
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
maximum transmit power phones are permitted (33 dBm == 2W)
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
interval at which phones should send periodic location update (6 minutes)
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
Unit ID of the BTS (must match BTS)
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
Voice codecs supported by the BTS
|
||
</p>
|
||
</li>
|
||
</ol></div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_configuring_bts_in_osmonitb_trx">Configuring BTS in OsmoNITB (TRX)</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre><code>network
|
||
bts 0
|
||
trx 0
|
||
arfcn 871 <b><1></b>
|
||
max_power_red 0 <b><2></b>
|
||
timeslot 0
|
||
phys_chan_config CCCH+SDCCH4 <b><3></b>
|
||
timeslot 1
|
||
phys_chan_config TCH/F <b><4></b>
|
||
...
|
||
timeslot 7
|
||
phys_chan_config PDCH <b><5></b></code></pre>
|
||
</div></div>
|
||
<div class="colist arabic"><ol>
|
||
<li>
|
||
<p>
|
||
The RF channel number used by this TRX
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
The maximum power <strong>reduction</strong> in dBm. 0 = no reduction
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
Every BTS needs need one timeslot with a CCCH
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
We configure TS1 to TS6 as TCH/F for voice
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
We configure TS6 as PDCH for GPRS
|
||
</p>
|
||
</li>
|
||
</ol></div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_what_a_gsm_phone_does_after_power_up">What a GSM phone does after power-up</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
Check SIM card for last cell before switch-off
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
if that cell is found again, use that
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
if not, perform a netwok scan
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
try to find strong carriers, check if they contain BCCH
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
create a list of available cells + networks
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
if one of the networks MCC+MNC matches first digits of <em>IMSI</em>, this is
|
||
the home network, which has preference over others
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
perform <em>LOCATION UPDATE</em> (TYPE=IMSI ATTACH) procedure to network
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
when network sends <em>LOCATION UPDATE ACCEPT</em>, <strong>camp</strong> on that cell
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
<div class="paragraph"><p>→ let’s check if we can perform <em>LOCATION UPDATE</em> on our own network</p></div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_verifying_our_network">Verifying our network</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
look at stderr of <em>OsmoBTS</em> and <em>OsmoNITB</em>
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
<em>OsmoBTS</em> will terminate if Abis cannot be set-up
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
expected to be re-spawned by init / systemd
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
use MS to search for networks, try manual registration
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
observe registration attempts <code>logging level mm info</code>
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
<div class="paragraph"><p>→ should show <em>LOCATION UPDATE</em> request / reject / accept</p></div>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
use the VTY to explore system state (<code>show *</code>)
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
use the VTY to change subscriber parameters like extension number
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_exploring_your_gsm_networks_services">Exploring your GSM networks services</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
use <code>*#100#</code> from any registered MS to obtain own number
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
voice calls from mobile to mobile
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
SMS from mobile to mobile
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
SMS to/from external applications (via SMPP)
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
voice to/from external PBX (via MNCC)
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
explore the VTY interfaces of all network elements
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
send SMS from the command line
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
experiment with <em>silent call</em> feature
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
experiment with logging levels
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
use wireshark to investigate GSM protocols
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_using_the_vty">Using the VTY</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
The VTY can be used not only to configure, but also to interactively
|
||
explore the system status (<code>show</code> commands)
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
Every Osmo* program has its own telnet port
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
<div class="tableblock">
|
||
<table rules="all"
|
||
width="100%"
|
||
frame="border"
|
||
cellspacing="0" cellpadding="4">
|
||
<col width="50%" />
|
||
<col width="50%" />
|
||
<tbody>
|
||
<tr>
|
||
<td align="left" valign="top"><p class="table">Program</p></td>
|
||
<td align="left" valign="top"><p class="table">Telnet Port</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td align="left" valign="top"><p class="table">OsmoPCU</p></td>
|
||
<td align="left" valign="top"><p class="table">4240</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td align="left" valign="top"><p class="table">OsmoBTS</p></td>
|
||
<td align="left" valign="top"><p class="table">4241</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td align="left" valign="top"><p class="table">OsmoNITB</p></td>
|
||
<td align="left" valign="top"><p class="table">4242</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td align="left" valign="top"><p class="table">OsmoSGSN</p></td>
|
||
<td align="left" valign="top"><p class="table">4245</p></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
ports are bound to 127.0.0.1 by default
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
try tab-completion, <code>?</code> and <code>list</code> commands
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_using_the_vty_continued">Using the VTY (continued)</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
e.g. <code>show subsciber</code> to display data about subscriber:
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre><code>OpenBSC> show subscriber imsi 901700000003804
|
||
ID: 12, Authorized: 1
|
||
Extension: 3804
|
||
LAC: 0/0x0
|
||
IMSI: 901700000003804
|
||
TMSI: F2D4FA0A
|
||
Expiration Time: Mon, 07 Dec 2015 09:45:16 +0100
|
||
Paging: not paging Requests: 0
|
||
Use count: 1</code></pre>
|
||
</div></div>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
try <code>show bts</code>, <code>show trx</code>, <code>show lchan</code>, <code>show statistics</code>, …
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_extending_the_network_with_gprs">Extending the network with GPRS</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<div class="paragraph"><p>Now that GSM is working, up to the next challenge!</p></div>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
Classic GSM is circuit-switched only
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
Packet switched support introduced first with GPRS
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
GPRS adds new network elements (PCU, SGSN, GGSN)
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
tunnel for external packet networks like IP/Internet
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
tunnel terminates in MS and on GGSN
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_extending_the_network_with_gprs_support">Extending the network with GPRS support</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<div class="imageblock graphviz">
|
||
<div class="content">
|
||
<img src="running-foss-gsm__4.png" alt="running-foss-gsm__4.png" />
|
||
</div>
|
||
</div>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
<em>PCU</em>: Packet Control Unit. Runs RLC+MAC
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
<em>SGSN</em>: Serving GPRS Support Node (like VLR/MSC)
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
<em>GGSN</em>: Gateway GPRS Support Node (terminates tunnels)
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_gprs_signalling_basics">GPRS Signalling basics</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
GPRS Mobility Management (GMM)
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
just like GSM Mobility Management (MM)
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
<em>GPRS ATTACH</em>, <em>ROUTING AREA UPDATE</em>, <em>AUTHENTICATION</em>
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
GPRS Session Management (SM)
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
establishment, management and tear-down of packet data tunnels
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
independent from IP, but typically IP(v4) is used
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
<em>PDP Context</em> (Activation | Deactivation | Modification)
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_gprs_protocol_stack">GPRS Protocol Stack</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<div class="imageblock">
|
||
<div class="content">
|
||
<img src="gprs_user_stack.svg" alt="gprs_user_stack.svg" width="850" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_gprs_acronyms_protocol_stack">GPRS Acronyms, Protocol Stack</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
Layer 3
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
<em>SM</em>: Session Management (PDP contexts)
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
<em>GMM</em>: GPRS Mobility Management (like MM)
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
Layer 2
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
<em>MAC</em>: Medium Access Control
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
<em>LLC</em>: Link Layer Control (segmentation, compression, encryption)
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
<em>RLC</em>: Radio Link Control
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
<em>SNDCP</em>: Sub-Network Dependent Convergence Protocol
|
||
</span>
|
||
<ul class=" incremental">
|
||
<li>
|
||
<span>
|
||
Scotty to the bridge: <em>You have to re-modulate the sub-network dependent convergence protocols!</em>
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_simplified_osmonitb_network_with_gprs">Simplified OsmoNITB network with GPRS</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<div class="imageblock graphviz">
|
||
<div class="content">
|
||
<img src="running-foss-gsm__5.png" alt="running-foss-gsm__5.png" />
|
||
</div>
|
||
</div>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
<em>OsmoPCU</em> is co-located with <em>OsmoBTS</em>
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
connects over unix-domain PCU socket to BTS
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
<em>OsmoSGSN</em> can run on any Linux machine
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
<em>OpenGGSN</em> can run on any Linux machine
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
<code>tun</code> device is used for tunnel endpoints
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
circuit-switched and packet-switched networks are completely separate
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
<div class="paragraph"><p>We need to configure those additional components to provide GPRS
|
||
services.</p></div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_simplified_osmonitb_network_with_gprs_2">Simplified OsmoNITB network with GPRS</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<div class="imageblock">
|
||
<div class="content">
|
||
<img src="osmocom-gprs.svg" alt="osmocom-gprs.svg" width="750" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_configuring_osmopcu">Configuring OsmoPCU</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<div class="paragraph"><p>We assume we have obtained and compiled the <code>osmo-pcu</code> from
|
||
git://git.osmocom.org/osmo-pcu</p></div>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
<em>OsmoPCU</em> runs co-located with <em>OsmoBTS</em> to access/share the same PHY + Radio
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
<em>OsmoPCU</em> is primarily configured from <em>OsmoBTS</em>
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
<em>OsmoBTS</em> receives relevant config via A-bis OML
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
<em>OsmoNITB</em> sends those OML messages to OsmoBTS
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
we thus need to set the PCU configuration in the NITB config file!
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_bts_config_for_gprs_in_osmonitb">BTS config for GPRS (in OsmoNITB)</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre><code> bts 0
|
||
gprs mode gprs <b><1></b>
|
||
gprs nsei 1234 <b><2></b>
|
||
gprs nsvc 0 nsvci 1234 <b><3></b>
|
||
gprs nsvc 0 local udp port 23000 <b><4></b>
|
||
gprs nsvc 0 remote ip 192.168.1.11 <b><5></b>
|
||
gprs nsvc 0 remote udp port 23000 <b><6></b></code></pre>
|
||
</div></div>
|
||
<div class="colist arabic"><ol>
|
||
<li>
|
||
<p>
|
||
enable <code>gprs</code> or <code>egprs</code> mode
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
NSEI for the NS protocol layer (unique for each PCU in SGSN)
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
NSVCI for the NS protocol layer (unique for each PCU in SGSN)
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
UDP port on PCU side of Gb connection
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
IP address of SGSN side of Gb connection
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
UDP port on SGSN side of Gb connection
|
||
</p>
|
||
</li>
|
||
</ol></div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_configuring_osmosgsn_gb_and_gtp">Configuring OsmoSGSN (Gb and GTP)</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre><code>ns
|
||
encapsulation udp local-ip 192.168.100.11 <b><1></b>
|
||
encapsulation udp local-port 23000 <b><2></b>
|
||
sgsn
|
||
gtp local-ip 127.0.0.2 <b><3></b>
|
||
ggsn 0 remote-ip 127.0.0.1 <b><4></b>
|
||
ggsn 0 gtp-version 1 <b><5></b>
|
||
apn * ggsn 0 <b><6></b></code></pre>
|
||
</div></div>
|
||
<div class="colist arabic"><ol>
|
||
<li>
|
||
<p>
|
||
SGSN-local IP address for Gb connection from PCUs
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
SGSN-local UDP port number for Gb connection from PCUs
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
SGSN-local IP address for GTP connection to GGSN
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
remote IP address for GTP connection to GGSN
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
GTP protocol version for this GGSN
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
route all APN names to GGSN 0
|
||
</p>
|
||
</li>
|
||
</ol></div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_configuring_osmosgsn_subscribers">Configuring OsmoSGSN (subscribers)</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<div class="paragraph"><p><em>OsmoSGSN</em> (still) has no access to the <em>OsmoNITB</em> HLR, thus all IMSIs
|
||
permitted to use GPRS services need to be explicitly configured.</p></div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre><code>sgsn
|
||
auth-policy closed <b><1></b>
|
||
imsi-acl add 262778026147135 <b><2></b></code></pre>
|
||
</div></div>
|
||
<div class="colist arabic"><ol>
|
||
<li>
|
||
<p>
|
||
only allow explicitly authorized/white-listed subscribers
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
add given IMSI to the white-list of subscribers
|
||
</p>
|
||
</li>
|
||
</ol></div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_setting_up_openggsn">Setting up OpenGGSN</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<div class="paragraph"><p>In <code>ggsn.cfg</code> we need to set:</p></div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre><code>listen 172.0.0.1 <b><1></b>
|
||
net 10.23.24.0/24 <b><2></b>
|
||
dynip 10.23.42.0/24 <b><3></b>
|
||
pcodns1 8.8.8.8 <b><4></b></code></pre>
|
||
</div></div>
|
||
<div class="colist arabic"><ol>
|
||
<li>
|
||
<p>
|
||
IP address to bind GSN to.
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
network/mask of <code>tun</code> device
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
pool of dynamic IP addresses allocated to PDP contexts
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
IP address of DNS server (communicated to MS via signalling)
|
||
</p>
|
||
</li>
|
||
</ol></div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_testing_gprs">Testing GPRS</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
Check if <code>osmo-pcu</code>, <code>osmo-sgsn</code>, <code>openggsn</code> are running
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
Check if NS and BSSGP protocols are UNBLOCKED at SGSN
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
If not, check your NS/BSSGP configuration
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
Check for GPRS registration using <code>logging level mm info</code> in SGSN
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_osmocom_beyond_gsm_gprs_ran_nitb">Osmocom beyond GSM/GPRS RAN + NITB</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
Smalltalk implementation of SIGTRAN + TCAP/MAP
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
Erlang implementation of SIGTRAN + TCAP/MAP
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
Lots of special-purpose protocol mangling
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
<code>bsc-nat</code> to introduce NAT-like functionality on A (BSSAP/BSSMAP)
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
<code>mgw-nat</code> to transparently re-write MAP/ISUP/SCCP
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
GSMTAP pseudo-header for feeding non-IP protocols into wireshark
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
SIM card protocol tracer hardware + software
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
Lots of non-GSM projects from hardware to protocol stacks (TETRA, GMR, DECT, OP25)
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
check <a href="http://git.osmocom.org/">http://git.osmocom.org/</a> for full project list
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_so_8230_i_heard_about_openbts">So… I heard about OpenBTS?</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
OpenBTS is completely unrelated to the Osmocom stack
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
was independently developed by David Burgess & Harvind Simra
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
Kestrel Signal Processing → Range Networks
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
doesn’t follow GSM system architecture at all
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
no Abis, BSC, PCU, SGSN, GGSN
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
is a bridge of the GSM air interface (Um) to SIP
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
Osmocom follows classic GSM interfaces / system architecture
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
<em>OsmoTRX</em> forked <em>OpenBTS</em> SDR code to use <em>OsmoBTS</em> with SDR hardware
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_outlook_on_foss_2_75g_edge">Outlook on FOSS 2.75G (EDGE)</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
EDGE extends GPRS with higher data rates
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
8PSK instead of GMSK modulation
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
lots of new MAC/RLC features (larger windows, incremental redundancy)
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
No changes required in <em>OmsoSGSN</em> and <em>OsmoGGSN</em>
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
<em>OsmoPCU</em> is extended with EDGE support
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
First working minimal subset published last week
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_outlook_on_foss_3g_umts_wcdma">Outlook on FOSS 3G (UMTS/WCDMA)</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
UMTS very similar to GSM/GPRS in principle
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
still, almost every interface and protocol stack has changed
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
all elements have been renamed → more acronyms to learn
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
UMTS is ridiculously complex, particular PHY + Layer 2
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
however, control plane L3 (MM/CC/CM/SM/GMM) mostly the same
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
Implementing all of that from scratch is a long journey
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
We’ve already reached <em>Peak 3G</em>
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
Osmocom 3G support strategy
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
Implement Iu interface in NITB and SGSN
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
Implement HNB-GW to offer Iuh interface
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
Use existing femtocell / small cell hardware with proprietary PHY, RLC and MAC
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
Status: Started in October 2015, WIP. Overall completion > 50%.
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_outlook_on_foss_4g_lte">Outlook on FOSS 4G (LTE)</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
LTE has nothing in common with 2G/3G
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
various FOSS activities
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
<em>OpenAirInterface</em> has some code for a software eNodeB
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
but they switched from GPLv3 to <em>non-free</em> license :(
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
<em>srsLTE</em> (main focus on UE side, but large parts usable for eNodeB side)
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
<em>OpenLTE</em> is another active FOSS project
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
No Osmocom involvement so far
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
team is small, project scope of cellular infrastructure is gigantic
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
most customer funding currently still on GSM/GPRS/EDGE
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
if we’d start, we’d start implementing MME + S-GW and use existing LTE cells
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide">
|
||
<h1 id="_the_end">The End</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
so long, and thanks for all the fish
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
I hope you have questions!
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
have fun exploring mobile technologies using Osmocom
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
interested in working with more acronyms? Come join the project!
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
Check out <a href="http://openbsc.osmocom.org/">http://openbsc.osmocom.org/</a> and <a href="mailto:openbsc@lists.osmocom.org">openbsc@lists.osmocom.org</a>
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sect1 slide incremental">
|
||
<h1 id="_thanks_to">Thanks to</h1>
|
||
<div class="sectionbody" style="max-width:45em">
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
Pablo for running netdevconf and inviting me
|
||
</span>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
the entire Osmocom team for what they have achieved
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
notably Dieter Spaar, Holger Freyther, Andreas Eversberg, Sylvain Munaut
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<span>
|
||
last but not least: CEPT for making the GSM specs English
|
||
</span>
|
||
<ul class="">
|
||
<li>
|
||
<span>
|
||
(who’d want to read French specs anyway?)
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</body>
|
||
</html>
|