/* 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, ">");
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;
},
// 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
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