Last commit for src/scripts/slidy.js: 98688d40f725c0e5eeddeefa9d8a8a42a0cc6b10

Fix some HTML validation issues, a=chris

Chris Pollett [2023-01-06 08:Jan:th]
Fix some HTML validation issues, a=chris
/* slidy.js
    Copyright (c) 2005-2013 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
    Defines single name "w3c_slidy" in global namespace
    Adds event handlers without trampling on any others
*/
// 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),
    android: ((navigator.userAgent).indexOf("Android") >= 0 ? true : false),
    ie: (typeof document.all != "undefined" && !this.opera),
    // data for swipe and double tap detection on touch screens
    last_tap: 0,
    prev_tap: 0,
    start_x: 0,
    start_y: 0,
    delta_x: 0,
    delta_y: 0,
    // 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
    observers: [], // list of observer functions
    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: [],
    up_link: null,
    // 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 (this.body && !w3c_slidy.initialized) {
            this.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 () {
        this.body = document.body;
        if (typeof setDisplay === "function") {
            setDisplay("top-container", false);
        }
        this.body.style.visibility = "visible";
        this.init_localization();
        this.add_toolbar();
        this.wrap_implicit_slides();
        this.collect_slides();
        this.collect_notes();
        this.collect_backgrounds();
        this.objects = this.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;
        this.keyboardless = (this.ipad||this.iphone||this.android);
        if (this.keyboardless) {
            w3c_slidy.remove_class(w3c_slidy.toolbar, "hidden")
            this.want_toolbar = 0;
        }
        // work around for opera bug
        this.is_xhtml = (this.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(this.body, "click", this.mouse_button_click);
            this.add_listener(this.body, "mousedown",
                this.mouse_button_down);
        }
        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);
        this.add_listener(document, "gesturechange", function () {
            return false;
        });
        this.attach_touch_handers(this.slides);
        this.single_slide_view();
        this.resized();
        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 = this.localize("Table of Contents");
        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_key_down;
            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(true);
            }
            w3c_slidy.stop_propagation(e);
            if (e.cancel != undefined) {
                e.cancel = true;
            }
            if (e.returnValue != undefined) {
                e.returnValue = false;
            }
            return false;
        };
        this.body.insertBefore(toc, this.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();
    },
    hide_table_of_contents: function (focus) {
        w3c_slidy.add_class(w3c_slidy.toc, "hidden");
        if (focus && !w3c_slidy.opera &&
            !w3c_slidy.has_class(w3c_slidy.toc, "hidden")) {
            w3c_slidy.set_focus();
        }
    },
    toggle_table_of_contents: function () {
        if (w3c_slidy.is_shown_toc()) {
            w3c_slidy.hide_table_of_contents(true);
        } 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) {
                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);
                try {
                    if (!w3c_slidy.opera) {
                        w3c_slidy.set_focus();
                    }
                } catch (e) {
                }
            }
        }
        w3c_slidy.hide_table_of_contents(true);
        w3c_slidy.stop_propagation(e);
        return w3c_slidy.cancel(e);
    },
    // called onkeydown for toc entry
    toc_key_down: 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) {
                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);
                try {
                    if (!w3c_slidy.opera) {
                        w3c_slidy.set_focus();
                    }
                } catch (e) {
                }
            }
            w3c_slidy.hide_table_of_contents(true);
            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;
    },
    touchstart: function (e) {
        // a double touch often starts with a
        // single touch due to fingers touching
        // down at slightly different times
        // thus avoid calling preventDefault here
        this.prev_tap = this.last_tap;
        this.last_tap = (new Date).getTime();
        var tap_delay = this.last_tap - this.prev_tap;
        if (tap_delay <= 200) {
            // double tap
        }
        var touch = e.touches[0];
        this.pageX = touch.pageX;
        this.pageY = touch.pageY;
        this.screenX = touch.screenX;
        this.screenY = touch.screenY;
        this.clientX = touch.clientX;
        this.clientY = touch.clientY;
        this.delta_x = this.delta_y = 0;
    },
    touchmove: function (e)
    {
        // override native gestures for single touch
        if (e.touches.length > 1) {
            return;
        }
        e.preventDefault();
        var touch = e.touches[0];
        this.delta_x = touch.pageX - this.pageX;
        this.delta_y = touch.pageY - this.pageY;
    },
    touchend: function (e) {
        // default behavior for multi-touch
        if (e.touches.length > 1) {
            return;
        }
        var delay = (new Date).getTime() - this.last_tap;
        var dx = this.delta_x;
        var dy = this.delta_y;
        var abs_dx = Math.abs(dx);
        var abs_dy = Math.abs(dy);
        if (delay < 500 && (abs_dx > 100 || abs_dy > 100)) {
            if (abs_dx > 0.5 * abs_dy) {
                e.preventDefault();
                if (dx < 0) {
                    w3c_slidy.next_slide(true);
                } else {
                    w3c_slidy.previous_slide(true);
                }
            } else if (abs_dy > 2 * abs_dx) {
                e.preventDefault();
                w3c_slidy.toggle_table_of_contents();
            }
        }
    },
    // ### 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(this.body, "single_slide");
        this.set_visibility_all_incremental("visible");
    },
    // restore after printing  ### OBSOLETE ###
    single_slide_view: function () {
        this.add_class(this.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) {
    },
    // 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, "&amp;");
        name.replace(/\</g, "&lt;");
        name.replace(/\>/g, "&gt;");
        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;
            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 = "swipe left to move to next slide";
        } else {
          p1.innerHTML = "Space, Right Arrow or swipe left to move to " +
            "next slide, click help below for more details";
        }
        this.add_listener(prompt, "click", function (e) {
            this.body.removeChild(prompt);
            w3c_slidy.stop_propagation(e);
            if (e.cancel != undefined) {
                e.cancel = true;
            }
            if (e.returnValue != undefined) {
                e.returnValue = false;
            }
            return false;
        });
        this.body.appendChild(prompt);
        this.initial_prompt = prompt;
        setTimeout(function() {
            w3c_slidy.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
        var right = this.create_element("div");
        right.setAttribute("style", "float: right; text-align: right");
        counter = this.create_element("span")
        counter.innerHTML = this.localize("slide") + " 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 hamburger = this.create_element("a");
        hamburger.setAttribute("href", "javascript:toggleOptions()");
        hamburger.innerHTML = "&equiv;";
        left.appendChild(hamburger);
        this.spacer = this.create_element("span");
        this.spacer.innerHTML = "&nbsp; ";
        left.appendChild(this.spacer);
        var help = this.create_element("a");
        help.setAttribute("href", this.help_page);
        help.setAttribute("title", this.localize(this.help_text));
        help.innerHTML = this.localize("help?");
        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", this.localize("table of contents"));
        contents.innerHTML = this.localize("contents?");
        left.appendChild(contents);
        var gap2 = document.createTextNode(" ");
        left.appendChild(gap2);
        var links = document.getElementsByTagName("link");
        for (var i = 0; i < links.length; i++) {
            if (links[i].getAttribute("rel") == "prev" ) {
                w3c_slidy.up_link = links[i].getAttribute("href");
            }
        }
        if (w3c_slidy.up_link) {
            var contents = this.create_element("a");
            contents.setAttribute("href", w3c_slidy.up_link);
            contents.setAttribute("title",
                this.localize("Pre-slideshow link"));
            contents.innerHTML = this.localize("up?");
            left.appendChild(contents);
        }
        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);
         // 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);
        this.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 != this.body) {
                continue;
            }
            node = heading.nextSibling;
            div = document.createElement("div");
            this.add_class(div, "slide");
            this.body.replaceChild(div, heading);
            div.appendChild(heading);
            while (node) {
                if (node.nodeType == 1) { // an element
                    if (node.nodeName == "H1" || node.nodeName == "h1") {
                        break;
                    }
                    if (node.nodeName == "DIV" || node.nodeName == "div") {
                        if (this.has_class(node, "slide")) {
                            break;
                        }
                        if (this.has_class(node, "handout")) {
                            break;
                        }
                    }
                }
                next = node.nextSibling;
                node = this.body.removeChild(node);
                div.appendChild(node);
                node = next;
            }
        }
    },
    attach_touch_handers: function(slides) {
        var i, slide;
        for (i = 0; i < slides.length; ++i) {
            slide = slides[i];
            this.add_listener(slide, "touchstart", this.touchstart);
            this.add_listener(slide, "touchmove", this.touchmove);
            this.add_listener(slide, "touchend", this.touchend);
        }
    },
    // return new array of all slides
    collect_slides: function () {
        var slides = new Array();
        var divs = this.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 = this.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 = this.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 {
                w3c_slidy.stop_propagation(event);
            }
            this.blur();
            self.disable_slide_click = true;
        };
        var anchors = this.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 +
            w3c_slidy.localize("slide") + " " +
            (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
    // ### use history.pushState if available
    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 (typeof(history.pushState) != "undefined") {
            document.title = w3c_slidy.title + " (" +
                (w3c_slidy.slide_number+1) + ")";
            history.pushState(0, document.title, hash);
            w3c_slidy.show_slide_number();
            w3c_slidy.notify_observers();
            return;
        }
        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();
        w3c_slidy.notify_observers();
    },
    notify_observers: function () {
        var slide = this.slides[this.slide_number];
        for (var i = 0; i < this.observers.length; ++i) {
            this.observers[i](this.slide_number+1,
                this.find_heading(slide).innerText, location.href);
        }
    },
    add_observer: function (observer) {
        for (var i = 0; i < this.observers.length; ++i) {
            if (observer == this.observers[i]) {
                return;
            }
        }
        this.observers.push(observer);
    },
    remove_observer: function (o)
    {
        for (var i = 0; i < this.observers.length; ++i) {
            if (observer == this.observers[i]) {
                this.observers.splice(i,1);
                break;
            }
        }
    },
    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);
    },
    // 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>'");
        doc.write("<html><head><script>" +
            "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;
            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) {
        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);
        this.remove_class(slide, "hidden");
    },
    hide_slide: function (slide) {
        this.add_class(slide, "hidden");
    },
    set_focus: function (element) {
        if (element) {
            element.focus();
        } else {
            w3c_slidy.help_anchor.focus();
            setTimeout(function() {
                w3c_slidy.help_anchor.blur();
            }, 1);
        }
    },
    // 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
                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;
        width = window.innerWidth;  // Non IE browser
        var height = 0;
        height = window.innerHeight;  // Non IE browser
        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 (this.body.style.fontSize !=
                w3c_slidy.sizes[w3c_slidy.size_index]) {
                this.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.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();
    },
    refresh_toolbar: function (interval) {
        if (!w3c_slidy.ns_pos) {
            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;
                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.set_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 (this.body) {
            return this.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 (this.body) {
            return this.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 () {
        return window.innerHeight;
    },
    document_height: function () {
        var sh, oh;
        sh = this.body.scrollHeight;
        oh = this.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";
        this.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";
        this.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, target, tag;
        w3c_slidy.key_wanted = true;
        if (!event) {
            event = window.event;
        }
        // kludge around NS/IE differences
        if (window.event) {
          key = window.event.keyCode;
          target = window.event.srcElement;
        } else if (event.which) {
            key = event.which;
            target = event.target;
        } else {
            return true; // Yikes! unknown browser
        }
        // ignore event if key value is zero
        // as for alt on Opera and Konqueror
        if (!key) {
           return true;
        }
        // avoid interfering with keystroke
        // behavior for non-slidy chrome elements
        if (!w3c_slidy.slidy_chrome(target) &&
            w3c_slidy.special_element(target)) {
            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(true);
            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(w3c_slidy.localize(alert_msg));
            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 if (key == 85 && w3c_slidy.up_link) { //U for up link
            window.location = w3c_slidy.up_link;
            return w3c_slidy.cancel(event);
        }
        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;
    },
    mouse_button_down: function (e) {
        w3c_slidy.selected_text_len = w3c_slidy.get_selected_text().length;
        w3c_slidy.mouse_x = e.clientX;
        w3c_slidy.mouse_y = e.clientY;
    },
    // right mouse button click is reserved for context menus
    // it is more reliable to detect rightclick than leftclick
    mouse_button_click: function (e) {
        if (!e) {
            var e = window.event;
        }
        if (Math.abs(e.clientX -w3c_slidy.mouse_x) +
            Math.abs(e.clientY -w3c_slidy.mouse_y) > 10) {
            return true;
        }
        if (w3c_slidy.selected_text_len > 0) {
            return true;
        }
        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;
        }
        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(false);
        // 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 &&
            !w3c_slidy.special_element(target) &&
            !target.onclick) {
            w3c_slidy.next_slide(true);
            w3c_slidy.stop_propagation(e);
            e.cancel = true;
            e.returnValue = false;
            return false;
        }
        return true;
    },
    special_element: function (element) {
        if (this.has_class(element, "non-interactive")) {
            return false;
        }
        var tag = element.nodeName.toLowerCase();
        return element.onkeydown ||
            element.onclick ||
            tag == "a" ||
            tag == "embed" ||
            tag == "object" ||
            tag == "video" ||
            tag == "audio" ||
            tag == "svg" ||
            tag == "canvas" ||
            tag == "input" ||
            tag == "textarea" ||
            tag == "select" ||
            tag == "option";
    },
    slidy_chrome: function (el) {
        while (el) {
            if (el == w3c_slidy.toc ||
                el == w3c_slidy.toolbar ||
                w3c_slidy.has_class(el, "outline")) {
                return true;
            }
            el = el.parentNode;
        }
        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 == "dl" ||
           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
    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.",
        strings: { },
    localize: function (src) {
        if (src == "") {
            return src;
        }
        // try full language code, e.g. en-US
        var s, lookup = w3c_slidy.strings[w3c_slidy.lang];
        if (lookup) {
            s = lookup[src];
            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.strings[lg[0]];
            if (lookup) {
                s = lookup[src];
                if (s) {
                    return s;
                }
            }
        }
        // otherwise string as is
        return src;
    },
    init_localization: function () {
        var i18n = w3c_slidy;
        var help_text = w3c_slidy.help_text;
        // each such language array is declared in the localize array
        // this is used as in  w3c_slidy.localize("foo");
        this.strings = {
            "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
        },
        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";
        }
    }
};
// 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);
ViewGit