source: presentations/trunk/Tools/Javascript/slidy.js@ 318

Last change on this file since 318 was 274, checked in by garnier, 17 years ago

remodelage

  • Property svn:executable set to *
File size: 63.6 KB
Line 
1/* slidy.js
2
3 Copyright (c) 2005 W3C (MIT, ERCIM, Keio), All Rights Reserved.
4 W3C liability, trademark, document use and software licensing
5 rules apply, see:
6
7 http://www.w3.org/Consortium/Legal/copyright-documents
8 http://www.w3.org/Consortium/Legal/copyright-software
9*/
10
11var ns_pos = (typeof window.pageYOffset!='undefined');
12var khtml = ((navigator.userAgent).indexOf("KHTML") >= 0 ? true : false);
13var opera = ((navigator.userAgent).indexOf("Opera") >= 0 ? true : false);
14
15window.onload = startup; // equivalent to onload on body element
16
17// IE only event handlers to ensure all slides are printed
18// I don't yet know how to emulate these for other browsers
19window.onbeforeprint = beforePrint;
20window.onafterprint = afterPrint;
21
22// hack to hide slides while loading
23setTimeout(hideAll, 50);
24
25function hideAll()
26{
27 if (document.body)
28 document.body.style.visibility = "hidden";
29 else
30 setTimeout(hideAll, 50);
31}
32
33var slidenum = 0; // integer slide count: 0, 1, 2, ...
34var slides; // set to array of slide div's
35var slideNumElement; // element containing slide number
36var notes; // set to array of handout div's
37var backgrounds; // set to array of background div's
38var toolbar; // element containing toolbar
39var title; // document title
40var lastShown = null; // last incrementally shown item
41var lastExpand = null;// last expanded item
42var eos = null; // span element for end of slide indicator
43var toc = null; // table of contents
44var outline = null; // outline element with the focus
45var selectedTextLen; // length of drag selection on document
46
47var viewAll = 0; // 1 to view all slides + handouts
48var wantToolbar = 1; // 0 if toolbar isn't wanted
49var mouseClickEnabled = true; // enables left click for next slide
50var scrollhack = 0; // IE work around for position: fixed
51
52var helpAnchor; // used for keyboard focus hack in showToolbar()
53var helpPage = "http://www.w3.org/Talks/Tools/Slidy/help.html";
54var helpText = "Navigate with mouse click, space bar, Cursor Left/Right, " +
55 "or Pg Up and Pg Dn. Use S and B to change font size.";
56
57var sizeIndex = 0;
58var sizeAdjustment = 0;
59var sizes = new Array("10pt", "12pt", "14pt", "16pt", "18pt", "20pt",
60 "22pt", "24pt", "26pt", "28pt", "30pt", "32pt");
61
62var okayForIncremental = incrementalElementList();
63
64// needed for efficient resizing
65var lastWidth = 0;
66var lastHeight = 0;
67
68// Needed for cross browser support for relative width/height on
69// object elements. The work around is to save width/height attributes
70// and then to recompute absolute width/height dimensions on resizing
71var objects;
72
73// updated to language specified by html file
74var lang = "en";
75
76var localize = {};
77
78// for each language there is an associative array
79var strings_es = {
80 "slide":"pág.",
81 "help?":"Ayuda",
82 "contents?":"Índice",
83 "table of contents":"tabla de contenidos",
84 "Table of Contents":"Tabla de Contenidos",
85 "restart presentation":"Reiniciar presentación",
86 "restart?":"Inicio"
87 };
88
89strings_es[helpText] =
90 "Utilice el ratón, barra espaciadora, teclas Izda/Dhca, " +
91 "o Re pág y Av pág. Use S y B para cambiar el tamaño de fuente.";
92
93var strings_nl = {
94 "slide":"pagina",
95 "help?":"Help?",
96 "contents?":"Inhoud?",
97 "table of contents":"inhoudsoverzicht",
98 "Table of Contents":"Inhoudsoverzicht",
99 "restart presentation":"herstart presentatie",
100 "restart?":"Herstart?"
101 };
102
103var strings_fr = {
104 "slide":"page",
105 "help?":"Aide?",
106 "contents?":"Contenu?",
107 "table of contents":"sommaire",
108 "Table of Contents":"Sommaire",
109 "restart presentation":"aller au début",
110 "restart?":"Début?"
111 };
112
113strings_fr[helpText] =
114 "Naviguer avec la souris, la barre espace, le curseur gauche/droite " +
115 "ou Page up/Page Down. Utiliser S et B pour changer le taille de police.";
116
117var strings_de = {
118 "slide":"Seite",
119 "help?":"Hilfe",
120 "contents?":"Übersicht",
121 "table of contents":"Inhaltsverzeichnis",
122 "Table of Contents":"Inhaltsverzeichnis",
123 "restart presentation":"PrÀsentation neu starten",
124 "restart?":"Neustart"
125 };
126
127strings_de[helpText] =
128 "Benutzen Sie die Maus, Leerschlag, die Cursortasten links/rechts" +
129 "oder Page up/Page Down zum Wechseln der Seiten und S und B fÌr die Schriftgrösse.";
130
131var strings_pl = {
132 "slide":"slajd",
133 "help?":"pomoc?",
134 "contents?":"spis treści?",
135 "table of contents":"spis treści",
136 "Table of Contents":"Spis Treści",
137 "restart presentation":"Restartuj prezentację",
138 "restart?":"restart?"
139 };
140
141strings_pl[helpText] =
142 "Zmieniaj slajdy klikają
143c myszą
144, naciskają
145c spację, strzałki lewo/prawo" +
146 "lub PgUp / PgDn. UÅŒyj klawiszy S i B, aby zmienić rozmiar czczionki.";
147
148// each such language array is declared in the localize array
149// used indirectly as in help.innerHTML = "help".localize();
150var localize = {
151 "es":strings_es,
152 "nl":strings_nl,
153 "de":strings_de,
154 "pl":strings_pl,
155 "fr":strings_fr
156 };
157
158/* general initialization */
159function startup()
160{
161 // find human language from html element
162 // for use in localizing strings
163 lang = document.body.parentNode.getAttribute("lang");
164
165 if (!lang)
166 lang = document.body.parentNode.getAttribute("xml:lang");
167
168 if (!lang)
169 lang = "en";
170
171 document.body.style.visibility = "visible";
172 title = document.title;
173 toolbar = addToolbar();
174 wrapImplicitSlides();
175 slides = collectSlides();
176 notes = collectNotes();
177 objects = document.body.getElementsByTagName("object");
178 backgrounds = collectBackgrounds();
179 patchAnchors();
180 slidenum = findSlideNumber(location.href);
181 window.offscreenbuffering = true;
182 sizeAdjustment = findSizeAdjust();
183 hideImageToolbar(); // suppress IE image toolbar popup
184 initOutliner(); // activate fold/unfold support
185
186 if (slides.length > 0)
187 {
188 var slide = slides[slidenum];
189 slide.style.position = "absolute";
190
191 if (slidenum > 0)
192 {
193 setVisibilityAllIncremental("visible");
194 lastShown = previousIncrementalItem(null);
195 setEosStatus(true);
196 }
197 else
198 {
199 lastShown = null;
200 setVisibilityAllIncremental("hidden");
201 setEosStatus(!nextIncrementalItem(lastShown));
202 }
203
204 setLocation();
205 }
206
207 toc = tableOfContents();
208 hideTableOfContents();
209
210 // bind event handlers
211 document.onclick = mouseButtonClick;
212 document.onmouseup = mouseButtonUp;
213 document.onkeydown = keyDown;
214 window.onresize = resized;
215 window.onscroll = scrolled;
216 singleSlideView();
217
218 setLocation();
219 resized();
220 showToolbar();
221}
222
223// add localize method to all strings for use
224// as in help.innerHTML = "help".localize();
225String.prototype.localize = function()
226{
227 if (this == "")
228 return this;
229
230 // try full language code, e.g. en-US
231 var s, lookup = localize[lang];
232
233 if (lookup)
234 {
235 s = lookup[this];
236
237 if (s)
238 return s;
239 }
240
241 // try en if undefined for en-US
242 var lg = lang.split("-");
243
244 if (lg.length > 1)
245 {
246 lookup = localize[lg[0]];
247
248 if (lookup)
249 {
250 s = lookup[this];
251
252 if (s)
253 return s;
254 }
255 }
256
257 // otherwise string as is
258 return this;
259}
260
261// suppress IE's image toolbar pop up
262function hideImageToolbar()
263{
264 if (!ns_pos)
265 {
266 var images = document.getElementsByTagName("IMG");
267
268 for (var i = 0; i < images.length; ++i)
269 images[i].setAttribute("galleryimg", "no");
270 }
271}
272
273// hack to persuade IE to compute correct document height
274// as needed for simulating fixed positioning of toolbar
275function ieHack()
276{
277 window.resizeBy(0,-1);
278 window.resizeBy(0, 1);
279}
280
281// Firefox reload SVG bug work around
282function reload(e)
283{
284 if (!e)
285 var e = window.event;
286
287 hideBackgrounds();
288 setTimeout("document.reload();", 100);
289
290 stopPropagation(e);
291 e.cancel = true;
292 e.returnValue = false;
293
294 return false;
295}
296
297// Safari and Konqueror don't yet support getComputedStyle()
298// and they always reload page when location.href is updated
299function isKHTML()
300{
301 var agent = navigator.userAgent;
302 return (agent.indexOf("KHTML") >= 0 ? true : false);
303}
304
305function resized()
306{
307 var width = 0;
308
309 if ( typeof( window.innerWidth ) == 'number' )
310 width = window.innerWidth; // Non IE browser
311 else if (document.documentElement && document.documentElement.clientWidth)
312 width = document.documentElement.clientWidth; // IE6
313 else if (document.body && document.body.clientWidth)
314 width = document.body.clientWidth; // IE4
315
316 var height = 0;
317
318 if ( typeof( window.innerHeight ) == 'number' )
319 height = window.innerHeight; // Non IE browser
320 else if (document.documentElement && document.documentElement.clientHeight)
321 height = document.documentElement.clientHeight; // IE6
322 else if (document.body && document.body.clientHeight)
323 height = document.body.clientHeight; // IE4
324
325 // IE fires onresize even when only font size is changed!
326 // so we do a check to avoid blocking < and > actions
327 if (width != lastWidth || height != lastHeight)
328 {
329 if (width >= 1100)
330 sizeIndex = 5; // 4
331 else if (width >= 1000)
332 sizeIndex = 4; // 3
333 else if (width >= 800)
334 sizeIndex = 3; // 2
335 else if (width >= 600)
336 sizeIndex = 2; // 1
337 else if (width)
338 sizeIndex = 0;
339
340 // add in font size adjustment from meta element e.g.
341 // <meta name="font-size-adjustment" content="-2" />
342 // useful when slides have too much content ;-)
343
344 if (0 <= sizeIndex + sizeAdjustment &&
345 sizeIndex + sizeAdjustment < sizes.length)
346 sizeIndex = sizeIndex + sizeAdjustment;
347
348 // enables cross browser use of relative width/height
349 // on object elements for use with SVG and Flash media
350 adjustObjectDimensions(width, height);
351
352 document.body.style.fontSize = sizes[sizeIndex];
353
354 lastWidth = width;
355 lastHeight = height;
356
357 // force reflow to work around Mozilla bug
358 //if (ns_pos)
359 {
360 var slide = slides[slidenum];
361 hideSlide(slide);
362 showSlide(slide);
363 }
364
365 // force correct positioning of toolbar
366 refreshToolbar(200);
367 }
368}
369
370function scrolled()
371{
372 if (toolbar && !ns_pos)
373 {
374 // hide toolbar
375 toolbar.style.display = "none";
376
377 // make it reappear later
378 if (scrollhack == 0 && !viewAll)
379 {
380 setTimeout(showToolbar, 1000);
381 scrollhack = 1;
382 }
383 }
384}
385
386// used to ensure IE refreshes toolbar in correct position
387function refreshToolbar(interval)
388{
389 hideToolbar();
390 setTimeout(showToolbar, interval);
391}
392
393// restores toolbar after short delay
394function showToolbar()
395{
396 if (wantToolbar)
397 {
398 if (!ns_pos)
399 {
400 // adjust position to allow for scrolling
401 var xoffset = scrollXOffset();
402 toolbar.style.left = xoffset;
403 toolbar.style.right = xoffset;
404
405 // determine vertical scroll offset
406 //var yoffset = scrollYOffset();
407
408 // bottom is doc height - window height - scroll offset
409 //var bottom = documentHeight() - lastHeight - yoffset
410
411 //if (yoffset > 0 || documentHeight() > lastHeight)
412 // bottom += 16; // allow for height of scrollbar
413
414 toolbar.style.bottom = 0; //bottom;
415 }
416
417 toolbar.style.display = "block";
418 toolbar.style.visibility = "visible";
419 }
420
421 scrollhack = 0;
422
423
424 // set the keyboard focus to the help link on the
425 // toolbar to ensure that document has the focus
426 // IE doesn't always work with window.focus()
427 // and this hack has benefit of Enter for help
428
429 try
430 {
431 if (!opera)
432 helpAnchor.focus();
433 }
434 catch (e)
435 {
436 }
437}
438
439function test()
440{
441 var s = "docH: " + documentHeight() +
442 " winH: " + lastHeight +
443 " yoffset: " + scrollYOffset() +
444 " toolbot: " + (documentHeight() - lastHeight - scrollYOffset());
445
446 //alert(s);
447
448 var slide = slides[slidenum];
449 // IE getAttribute requires "class" to be "className"
450 var name = ns_pos ? "class" : "className";
451 var style = (slide.currentStyle ? slide.currentStyle["backgroundColor"] :
452 document.defaultView.getComputedStyle(slide, '').getPropertyValue("background-color"));
453 alert("class='" + slide.getAttribute(name) + "' backgroundColor: " + style);
454}
455
456function hideToolbar()
457{
458 toolbar.style.display = "none";
459 toolbar.style.visibility = "hidden";
460 window.focus();
461}
462
463// invoked via F key
464function toggleToolbar()
465{
466 if (!viewAll)
467 {
468 if (toolbar.style.display == "none")
469 {
470 toolbar.style.display = "block";
471 toolbar.style.visibility = "visible";
472 wantToolbar = 1;
473 }
474 else
475 {
476 toolbar.style.display = "none";
477 toolbar.style.visibility = "hidden";
478 wantToolbar = 0;
479 }
480 }
481}
482
483function scrollXOffset()
484{
485 if (window.pageXOffset)
486 return self.pageXOffset;
487
488 if (document.documentElement &&
489 document.documentElement.scrollLeft)
490 return document.documentElement.scrollLeft;
491
492 if (document.body)
493 return document.body.scrollLeft;
494
495 return 0;
496}
497
498
499function scrollYOffset()
500{
501 if (window.pageYOffset)
502 return self.pageYOffset;
503
504 if (document.documentElement &&
505 document.documentElement.scrollTop)
506 return document.documentElement.scrollTop;
507
508 if (document.body)
509 return document.body.scrollTop;
510
511 return 0;
512}
513
514// looking for a way to determine height of slide content
515// the slide itself is set to the height of the window
516function optimizeFontSize()
517{
518 var slide = slides[slidenum];
519
520 //var dh = documentHeight(); //getDocHeight(document);
521 var dh = slide.scrollHeight;
522 var wh = getWindowHeight();
523 var u = 100 * dh / wh;
524
525 alert("window utilization = " + u + "% (doc "
526 + dh + " win " + wh + ")");
527}
528
529function getDocHeight(doc) // from document object
530{
531 if (!doc)
532 doc = document;
533
534 if (doc && doc.body && doc.body.offsetHeight)
535 return doc.body.offsetHeight; // ns/gecko syntax
536
537 if (doc && doc.body && doc.body.scrollHeight)
538 return doc.body.scrollHeight;
539
540 alert("couldn't determine document height");
541}
542
543function getWindowHeight()
544{
545 if ( typeof( window.innerHeight ) == 'number' )
546 return window.innerHeight; // Non IE browser
547
548 if (document.documentElement && document.documentElement.clientHeight)
549 return document.documentElement.clientHeight; // IE6
550
551 if (document.body && document.body.clientHeight)
552 return document.body.clientHeight; // IE4
553}
554
555
556
557function documentHeight()
558{
559 var sh, oh;
560
561 sh = document.body.scrollHeight;
562 oh = document.body.offsetHeight;
563
564 if (sh && oh)
565 {
566 return (sh > oh ? sh : oh);
567 }
568
569 // no idea!
570 return 0;
571}
572
573function smaller()
574{
575 if (sizeIndex > 0)
576 {
577 --sizeIndex;
578 }
579
580 toolbar.style.display = "none";
581 document.body.style.fontSize = sizes[sizeIndex];
582 var slide = slides[slidenum];
583 hideSlide(slide);
584 showSlide(slide);
585 setTimeout(showToolbar, 300);
586}
587
588function bigger()
589{
590 if (sizeIndex < sizes.length - 1)
591 {
592 ++sizeIndex;
593 }
594
595 toolbar.style.display = "none";
596 document.body.style.fontSize = sizes[sizeIndex];
597 var slide = slides[slidenum];
598 hideSlide(slide);
599 showSlide(slide);
600 setTimeout(showToolbar, 300);
601}
602
603// enables cross browser use of relative width/height
604// on object elements for use with SVG and Flash media
605// with thanks to Ivan Herman for the suggestion
606function adjustObjectDimensions(width, height)
607{
608 for( var i = 0; i < objects.length; i++ )
609 {
610 var obj = objects[i];
611 var mimeType = obj.getAttribute("type");
612
613 if (mimeType == "image/svg+xml" || mimeType == "application/x-shockwave-flash")
614 {
615 if ( !obj.initialWidth )
616 obj.initialWidth = obj.getAttribute("width");
617
618 if ( !obj.initialHeight )
619 obj.initialHeight = obj.getAttribute("height");
620
621 if ( obj.initialWidth && obj.initialWidth.charAt(obj.initialWidth.length-1) == "%" )
622 {
623 var w = parseInt(obj.initialWidth.slice(0, obj.initialWidth.length-1));
624 var newW = width * (w/100.0);
625 obj.setAttribute("width",newW);
626 }
627
628 if ( obj.initialHeight && obj.initialHeight.charAt(obj.initialHeight.length-1) == "%" )
629 {
630 var h = parseInt(obj.initialHeight.slice(0, obj.initialHeight.length-1));
631 var newH = height * (h/100.0);
632 obj.setAttribute("height", newH);
633 }
634 }
635 }
636}
637
638function cancel(event)
639{
640 if (event)
641 {
642 event.cancel = true;
643 event.returnValue = false;
644
645 if (event.preventDefault)
646 event.preventDefault();
647 }
648
649 return false;
650}
651
652// See e.g. http://www.quirksmode.org/js/events/keys.html for keycodes
653function keyDown(event)
654{
655 var key;
656
657 if (!event)
658 var event = window.event;
659
660 // kludge around NS/IE differences
661 if (window.event)
662 key = window.event.keyCode;
663 else if (event.which)
664 key = event.which;
665 else
666 return true; // Yikes! unknown browser
667
668 // ignore event if key value is zero
669 // as for alt on Opera and Konqueror
670 if (!key)
671 return true;
672
673 // check for concurrent control/command/alt key
674 // but are these only present on mouse events?
675
676 if (event.ctrlKey || event.altKey)
677 return true;
678
679 // uncommnt this line to ckeck the remote control
680 // alert("event key :" + key);
681
682 // dismiss table of contents if visible
683 if (isShownToc() && key != 9 && key != 16 && key != 38 && key != 40)
684 {
685 hideTableOfContents();
686
687 if (key == 27 || key == 84 || key == 67)
688 return cancel(event);
689 }
690 else if (key == 32) // space bar
691 {
692 nextSlide(true);
693 return cancel(event);
694 }
695 else if (key == 37 || key == 33) // Left arrow
696 {
697 previousSlide(!event.shiftKey);
698 return cancel(event);
699 }
700 else if ( key == 38) // PageUp : also left arrow of remote control
701 {
702 lastExpand = expandPreviousFoldNext();
703 if (lastExpand == null) {
704 previousSlide(!event.shiftKey);
705 }
706 return cancel(event);
707 }
708 else if (key == 40) // PageDown : also right arrow of remote control
709 {
710 lastExpand = foldPreviousExpandNext();
711 if (lastExpand == null) {
712 nextSlide(!event.shiftKey);
713 }
714 return cancel(event);
715 }
716 else if (key == 36) // Home
717 {
718 firstSlide();
719 return cancel(event);
720 }
721 else if (key == 35) // End
722 {
723 lastSlide();
724 return cancel(event);
725 }
726 else if (key == 39 || key == 34) // Right arrow
727 {
728 nextSlide(!event.shiftKey);
729 return cancel(event);
730 }
731 else if (key == 13) // Enter
732 {
733 if (outline)
734 {
735 if (outline.visible)
736 fold(outline);
737 else
738 unfold(outline);
739
740 return cancel(event);
741 }
742 }
743 else if (key == 188) // < for smaller fonts
744 {
745 smaller();
746 return cancel(event);
747 }
748 else if (key == 190) // > for larger fonts
749 {
750 bigger();
751 return cancel(event);
752 }
753 else if (key == 189 || key == 109) // - for smaller fonts
754 {
755 smaller();
756 return cancel(event);
757 }
758 else if (key == 187 || key == 191 || key == 107) // = + for larger fonts
759 {
760 bigger();
761 return cancel(event);
762 }
763 else if (key == 83) // S for smaller fonts
764 {
765 smaller();
766 return cancel(event);
767 }
768 else if (key == 66) // B for larger fonts
769 {
770 bigger();
771 return cancel(event);
772 }
773 else if (key == 90) // Z for last slide
774 {
775 lastSlide();
776 return cancel(event);
777 }
778 else if (key == 70) // F for toggle toolbar
779 {
780 toggleToolbar();
781 return cancel(event);
782 }
783 else if (key == 65) // A for toggle view single/all slides
784 {
785 toggleView();
786 return cancel(event);
787 }
788 else if (key == 75) // toggle action of left click for next page
789 {
790 mouseClickEnabled = !mouseClickEnabled;
791 alert((mouseClickEnabled ? "enabled" : "disabled") + " mouse click advance");
792 return cancel(event);
793 }
794 else if (key == 84 || key == 67) // T or C for table of contents
795 {
796 if (toc)
797 showTableOfContents();
798
799 return cancel(event);
800 }
801 else if (key == 72) // H for help
802 {
803 window.location = helpPage;
804 return cancel(event);
805 }
806
807 //else if (key == 93) // Windows menu key
808 //alert("lastShown is " + lastShown);
809 //else alert("key code is "+ key);
810
811
812 return true;
813}
814
815// make note of length of selected text
816// as this evaluates to zero in click event
817function mouseButtonUp(e)
818{
819 selectedTextLen = getSelectedText().length;
820}
821
822// right mouse button click is reserved for context menus
823// it is more reliable to detect rightclick than leftclick
824function mouseButtonClick(e)
825{
826 var rightclick = false;
827 var leftclick = false;
828 var middleclick = false;
829 var target;
830
831 if (!e)
832 var e = window.event;
833
834 if (e.target)
835 target = e.target;
836 else if (e.srcElement)
837 target = e.srcElement;
838
839 // work around Safari bug
840 if (target.nodeType == 3)
841 target = target.parentNode;
842
843 if (e.which) // all browsers except IE
844 {
845 leftclick = (e.which == 1);
846 middleclick = (e.which == 2);
847 rightclick = (e.which == 3);
848 }
849 else if (e.button)
850 {
851 // Konqueror gives 1 for left, 4 for middle
852 // IE6 gives 0 for left and not 1 as I expected
853
854 if (e.button == 4)
855 middleclick = true;
856
857 // all browsers agree on 2 for right button
858 rightclick = (e.button == 2);
859 }
860 else leftclick = true;
861
862 // dismiss table of contents
863 hideTableOfContents();
864
865 if (selectedTextLen > 0)
866 {
867 stopPropagation(e);
868 e.cancel = true;
869 e.returnValue = false;
870 return false;
871 }
872
873 // check if target is something that probably want's clicks
874 // e.g. embed, object, input, textarea, select, option
875
876 if (mouseClickEnabled && leftclick &&
877 target.nodeName != "EMBED" &&
878 target.nodeName != "OBJECT" &&
879 target.nodeName != "INPUT" &&
880 target.nodeName != "TEXTAREA" &&
881 target.nodeName != "SELECT" &&
882 target.nodeName != "OPTION")
883 {
884 nextSlide(true);
885 stopPropagation(e);
886 e.cancel = true;
887 e.returnValue = false;
888 }
889}
890
891function previousSlide(incremental)
892{
893 if (!viewAll)
894 {
895 var slide;
896
897 if ((incremental || slidenum == 0) && lastShown != null)
898 {
899 lastShown = hidePreviousItem(lastShown);
900 setEosStatus(false);
901 }
902 else if (slidenum > 0)
903 {
904 slide = slides[slidenum];
905 hideSlide(slide);
906
907 slidenum = slidenum - 1;
908 slide = slides[slidenum];
909 setVisibilityAllIncremental("visible");
910 lastShown = previousIncrementalItem(null);
911 setEosStatus(true);
912 showSlide(slide);
913 }
914
915 setLocation();
916
917 if (!ns_pos)
918 refreshToolbar(200);
919 }
920}
921
922function nextSlide(incremental)
923{
924 if (!viewAll)
925 {
926 var slide, last = lastShown;
927 lastExpand= null;
928
929 if (incremental || slidenum == slides.length - 1)
930 lastShown = revealNextItem(lastShown);
931
932 if ((!incremental || lastShown == null) && slidenum < slides.length - 1)
933 {
934 slide = slides[slidenum];
935 hideSlide(slide);
936
937 slidenum = slidenum + 1;
938 slide = slides[slidenum];
939 lastShown = null;
940 setVisibilityAllIncremental("hidden");
941 showSlide(slide);
942 }
943 else if (!lastShown)
944 {
945 if (last && incremental)
946 lastShown = last;
947 }
948
949 setLocation();
950
951 setEosStatus(!nextIncrementalItem(lastShown));
952
953 if (!ns_pos)
954 refreshToolbar(200);
955 }
956}
957
958// to first slide with nothing revealed
959// i.e. state at start of presentation
960function firstSlide()
961{
962 if (!viewAll)
963 {
964 var slide;
965
966 if (slidenum != 0)
967 {
968 slide = slides[slidenum];
969 hideSlide(slide);
970
971 slidenum = 0;
972 slide = slides[slidenum];
973 lastShown = null;
974 lastExpand= null;
975 setVisibilityAllIncremental("hidden");
976 showSlide(slide);
977 }
978
979 setEosStatus(!nextIncrementalItem(lastShown));
980 setLocation();
981 }
982}
983
984
985// to last slide with everything revealed
986// i.e. state at end of presentation
987function lastSlide()
988{
989 if (!viewAll)
990 {
991 var slide;
992
993 lastShown = null; //revealNextItem(lastShown);
994 lastExpand= null;
995
996 if (lastShown == null && slidenum < slides.length - 1)
997 {
998 slide = slides[slidenum];
999 hideSlide(slide);
1000 slidenum = slides.length - 1;
1001 slide = slides[slidenum];
1002 setVisibilityAllIncremental("visible");
1003 lastShown = previousIncrementalItem(null);
1004 showSlide(slide);
1005 }
1006 else
1007 {
1008 setVisibilityAllIncremental("visible");
1009 lastShown = previousIncrementalItem(null);
1010 }
1011
1012 setEosStatus(true);
1013 setLocation();
1014 }
1015}
1016
1017function setEosStatus(state)
1018{
1019 if (eos)
1020 eos.style.color = (state ? "rgb(240,240,240)" : "red");
1021}
1022
1023function showSlide(slide)
1024{
1025 syncBackground(slide);
1026 window.scrollTo(0,0);
1027 slide.style.visibility = "visible";
1028 slide.style.display = "block";
1029}
1030
1031function hideSlide(slide)
1032{
1033 slide.style.visibility = "hidden";
1034 slide.style.display = "none";
1035}
1036
1037function beforePrint()
1038{
1039 showAllSlides();
1040 hideToolbar();
1041}
1042
1043function afterPrint()
1044{
1045 if (!viewAll)
1046 {
1047 singleSlideView();
1048 showToolbar();
1049 }
1050}
1051
1052function printSlides()
1053{
1054 beforePrint();
1055 window.print();
1056 afterPrint();
1057}
1058
1059function toggleView()
1060{
1061 if (viewAll)
1062 {
1063 initOutliner();
1064 singleSlideView();
1065 showToolbar();
1066 viewAll = 0;
1067 }
1068 else
1069 {
1070 expandAll();
1071 showAllSlides();
1072 hideToolbar();
1073 viewAll = 1;
1074 }
1075}
1076
1077// prepare for printing
1078function showAllSlides()
1079{
1080 var slide;
1081
1082 for (var i = 0; i < slides.length; ++i)
1083 {
1084 slide = slides[i];
1085
1086 slide.style.position = "relative";
1087 slide.style.borderTopStyle = "solid";
1088 slide.style.borderTopWidth = "thin";
1089 slide.style.borderTopColor = "black";
1090
1091 try {
1092 if (i == 0)
1093 slide.style.pageBreakBefore = "avoid";
1094 else
1095 slide.style.pageBreakBefore = "always";
1096 }
1097 catch (e)
1098 {
1099 //do nothing
1100 }
1101
1102 setVisibilityAllIncremental("visible");
1103 showSlide(slide);
1104 }
1105
1106 var note;
1107
1108 for (var i = 0; i < notes.length; ++i)
1109 {
1110 showSlide(notes[i]);
1111 }
1112
1113 // no easy way to render background under each slide
1114 // without duplicating the background divs for each slide
1115 // therefore hide backgrounds to avoid messing up slides
1116 hideBackgrounds();
1117}
1118
1119// restore after printing
1120function singleSlideView()
1121{
1122 var slide;
1123
1124 for (var i = 0; i < slides.length; ++i)
1125 {
1126 slide = slides[i];
1127
1128 slide.style.position = "absolute";
1129
1130 if (i == slidenum)
1131 {
1132 slide.style.borderStyle = "none";
1133 showSlide(slide);
1134 }
1135 else
1136 {
1137 slide.style.borderStyle = "none";
1138 hideSlide(slide);
1139 }
1140 }
1141
1142 setVisibilityAllIncremental("visible");
1143 lastShown = previousIncrementalItem(null);
1144 lastExpand= null;
1145
1146 var note;
1147
1148 for (var i = 0; i < notes.length; ++i)
1149 {
1150 hideSlide(notes[i]);
1151 }
1152}
1153
1154// the string str is a whitespace separated list of tokens
1155// test if str contains a particular token, e.g. "slide"
1156function hasToken(str, token)
1157{
1158 if (str)
1159 {
1160 // define pattern as regular expression
1161 var pattern = /\w+/g;
1162
1163 // check for matches
1164 // place result in array
1165 var result = str.match(pattern);
1166
1167 // now check if desired token is present
1168 for (var i = 0; i < result.length; i++)
1169 {
1170 if (result[i] == token)
1171 return true;
1172 }
1173 }
1174
1175 return false;
1176}
1177
1178function getClassList(element)
1179{
1180 if (typeof window.pageYOffset =='undefined')
1181 return element.getAttribute("className");
1182
1183 return element.getAttribute("class");
1184}
1185
1186function hasClass(element, name)
1187{
1188 var regexp = new RegExp("(^| )" + name + "\W*");
1189
1190 if (regexp.test(getClassList(element)))
1191 return true;
1192
1193 return false;
1194
1195}
1196
1197function removeClass(element, name)
1198{
1199 // IE getAttribute requires "class" to be "className"
1200 var clsname = ns_pos ? "class" : "className";
1201 var clsval = element.getAttribute(clsname);
1202
1203 var regexp = new RegExp("(^| )" + name + "\W*");
1204
1205 if (clsval)
1206 {
1207 clsval = clsval.replace(regexp, "");
1208 element.setAttribute(clsname, clsval);
1209 }
1210}
1211
1212function addClass(element, name)
1213{
1214 if (!hasClass(element, name))
1215 {
1216 // IE getAttribute requires "class" to be "className"
1217 var clsname = ns_pos ? "class" : "className";
1218 var clsval = element.getAttribute(clsname);
1219 element.setAttribute(clsname, (clsval ? clsval + " " + name : name));
1220 }
1221}
1222
1223// wysiwyg editors make it hard to use div elements
1224// e.g. amaya loses the div when you copy and paste
1225// this function wraps div elements around implicit
1226// slides which start with an h1 element and continue
1227// up to the next heading or div element
1228function wrapImplicitSlides()
1229{
1230 var i, heading, node, next, div;
1231 var headings = document.getElementsByTagName("h1");
1232
1233 if (!headings)
1234 return;
1235
1236 for (i = 0; i < headings.length; ++i)
1237 {
1238 heading = headings[i];
1239
1240 if (heading.parentNode != document.body)
1241 continue;
1242
1243 node = heading.nextSibling;
1244
1245 div = document.createElement("div");
1246 div.setAttribute((ns_pos ? "class" : "className"), "slide");
1247 document.body.replaceChild(div, heading);
1248 div.appendChild(heading);
1249
1250 while (node)
1251 {
1252 if (node.nodeType == 1 && // an element
1253 (node.nodeName == "H1" ||
1254 node.nodeName == "h1" ||
1255 node.nodeName == "DIV" ||
1256 node.nodeName == "div"))
1257 break;
1258
1259 next = node.nextSibling;
1260 node = document.body.removeChild(node);
1261 div.appendChild(node);
1262 node = next;
1263 }
1264 }
1265}
1266
1267// return new array of all slides
1268function collectSlides()
1269{
1270 var slides = new Array();
1271 var divs = document.body.getElementsByTagName("div");
1272
1273 for (var i = 0; i < divs.length; ++i)
1274 {
1275 div = divs.item(i);
1276
1277 if (hasClass(div, "slide"))
1278 {
1279 // add slide to collection
1280 slides[slides.length] = div;
1281
1282 // hide each slide as it is found
1283 div.style.display = "none";
1284 div.style.visibility = "hidden";
1285
1286 // add dummy <br/> at end for scrolling hack
1287 var node1 = document.createElement("br");
1288 div.appendChild(node1);
1289 var node2 = document.createElement("br");
1290 div.appendChild(node2);
1291 }
1292 else if (hasClass(div, "background"))
1293 { // work around for Firefox SVG reload bug
1294 // which otherwise replaces 1st SVG graphic with 2nd
1295 div.style.display = "block";
1296 }
1297 }
1298
1299 return slides;
1300}
1301
1302// return new array of all <div class="handout">
1303function collectNotes()
1304{
1305 var notes = new Array();
1306 var divs = document.body.getElementsByTagName("div");
1307
1308 for (var i = 0; i < divs.length; ++i)
1309 {
1310 div = divs.item(i);
1311
1312 if (hasClass(div, "handout"))
1313 {
1314 // add slide to collection
1315 notes[notes.length] = div;
1316
1317 // hide handout notes as they are found
1318 div.style.display = "none";
1319 div.style.visibility = "hidden";
1320 }
1321 }
1322
1323 return notes;
1324}
1325
1326// return new array of all <div class="background">
1327// including named backgrounds e.g. class="background titlepage"
1328function collectBackgrounds()
1329{
1330 var backgrounds = new Array();
1331 var divs = document.body.getElementsByTagName("div");
1332
1333 for (var i = 0; i < divs.length; ++i)
1334 {
1335 div = divs.item(i);
1336
1337 if (hasClass(div, "background"))
1338 {
1339 // add slide to collection
1340 backgrounds[backgrounds.length] = div;
1341
1342 // hide named backgrounds as they are found
1343 // e.g. class="background epilog"
1344 if (getClassList(div) != "background")
1345 {
1346 div.style.display = "none";
1347 div.style.visibility = "hidden";
1348 }
1349 }
1350 }
1351
1352 return backgrounds;
1353}
1354
1355// show just the backgrounds pertinent to this slide
1356function syncBackground(slide)
1357{
1358 var background;
1359 var bgColor;
1360
1361 if (slide.currentStyle)
1362 bgColor = slide.currentStyle["backgroundColor"];
1363 else if (document.defaultView)
1364 {
1365 var styles = document.defaultView.getComputedStyle(slide,null);
1366
1367 if (styles)
1368 bgColor = styles.getPropertyValue("background-color");
1369 else // broken implementation probably due Safari or Konqueror
1370 {
1371 //alert("defective implementation of getComputedStyle()");
1372 bgColor = "transparent";
1373 }
1374 }
1375 else
1376 bgColor == "transparent";
1377
1378 if (bgColor == "transparent")
1379 {
1380 var slideClass = getClassList(slide);
1381
1382 for (var i = 0; i < backgrounds.length; i++)
1383 {
1384 background = backgrounds[i];
1385
1386 var bgClass = getClassList(background);
1387
1388 if (matchingBackground(slideClass, bgClass))
1389 {
1390 background.style.display = "block";
1391 background.style.visibility = "visible";
1392 }
1393 else
1394 {
1395 background.style.display = "none";
1396 background.style.visibility = "hidden";
1397 }
1398 }
1399 }
1400 else // forcibly hide all backgrounds
1401 hideBackgrounds();
1402}
1403
1404function hideBackgrounds()
1405{
1406 for (var i = 0; i < backgrounds.length; i++)
1407 {
1408 background = backgrounds[i];
1409 background.style.display = "none";
1410 background.style.visibility = "hidden";
1411 }
1412}
1413
1414// compare classes for slide and background
1415function matchingBackground(slideClass, bgClass)
1416{
1417 if (bgClass == "background")
1418 return true;
1419
1420 // define pattern as regular expression
1421 var pattern = /\w+/g;
1422
1423 // check for matches and place result in array
1424 var result = slideClass.match(pattern);
1425
1426 // now check if desired name is present for background
1427 for (var i = 0; i < result.length; i++)
1428 {
1429 if (hasToken(bgClass, result[i]))
1430 return true;
1431 }
1432
1433 return false;
1434}
1435
1436// left to right traversal of root's content
1437function nextNode(root, node)
1438{
1439 if (node == null)
1440 return root.firstChild;
1441
1442 if (node.firstChild)
1443 return node.firstChild;
1444
1445 if (node.nextSibling)
1446 return node.nextSibling;
1447
1448 for (;;)
1449 {
1450 node = node.parentNode;
1451
1452 if (!node || node == root)
1453 break;
1454
1455 if (node && node.nextSibling)
1456 return node.nextSibling;
1457 }
1458
1459 return null;
1460}
1461
1462// right to left traversal of root's content
1463function previousNode(root, node)
1464{
1465 if (node == null)
1466 {
1467 node = root.lastChild;
1468
1469 if (node)
1470 {
1471 while (node.lastChild)
1472 node = node.lastChild;
1473 }
1474
1475 return node;
1476 }
1477
1478 if (node.previousSibling)
1479 {
1480 node = node.previousSibling;
1481
1482 while (node.lastChild)
1483 node = node.lastChild;
1484
1485 return node;
1486 }
1487
1488 if (node.parentNode != root)
1489 return node.parentNode;
1490
1491 return null;
1492}
1493
1494// HTML elements that can be used with class="incremental"
1495// note that you can also put the class on containers like
1496// up, ol, dl, and div to make their contents appear
1497// incrementally. Upper case is used since this is what
1498// browsers report for HTML node names (text/html).
1499function incrementalElementList()
1500{
1501 var inclist = new Array();
1502 inclist["P"] = true;
1503 inclist["PRE"] = true;
1504 inclist["LI"] = true;
1505 inclist["BLOCKQUOTE"] = true;
1506 inclist["DT"] = true;
1507 inclist["DD"] = true;
1508 inclist["H2"] = true;
1509 inclist["H3"] = true;
1510 inclist["H4"] = true;
1511 inclist["H5"] = true;
1512 inclist["H6"] = true;
1513 inclist["SPAN"] = true;
1514 inclist["ADDRESS"] = true;
1515 inclist["TABLE"] = true;
1516 inclist["TR"] = true;
1517 inclist["TH"] = true;
1518 inclist["TD"] = true;
1519 inclist["IMG"] = true;
1520 inclist["OBJECT"] = true;
1521 return inclist;
1522}
1523
1524function nextIncrementalItem(node)
1525{
1526 var slide = slides[slidenum];
1527
1528 for (;;)
1529 {
1530 node = nextNode(slide, node);
1531
1532 if (node == null || node.parentNode == null)
1533 break;
1534
1535 if (node.nodeType == 1) // ELEMENT
1536 {
1537 if (node.nodeName == "BR")
1538 continue;
1539
1540 if (hasClass(node, "incremental")
1541 && okayForIncremental[node.nodeName])
1542 return node;
1543
1544 if (hasClass(node.parentNode, "incremental")
1545 && !hasClass(node, "non-incremental"))
1546 return node;
1547 }
1548 }
1549
1550 return node;
1551}
1552
1553function previousIncrementalItem(node)
1554{
1555 var slide = slides[slidenum];
1556
1557 for (;;)
1558 {
1559 node = previousNode(slide, node);
1560
1561 if (node == null || node.parentNode == null)
1562 break;
1563
1564 if (node.nodeType == 1)
1565 {
1566 if (node.nodeName == "BR")
1567 continue;
1568
1569 if (hasClass(node, "incremental")
1570 && okayForIncremental[node.nodeName])
1571 return node;
1572
1573 if (hasClass(node.parentNode, "incremental")
1574 && !hasClass(node, "non-incremental"))
1575 return node;
1576 }
1577 }
1578
1579 return node;
1580}
1581
1582// set visibility for all elements on current slide with
1583// a parent element with attribute class="incremental"
1584function setVisibilityAllIncremental(value)
1585{
1586 var node = nextIncrementalItem(null);
1587
1588 while (node)
1589 {
1590 node.style.visibility = value;
1591 node = nextIncrementalItem(node);
1592 }
1593}
1594
1595// reveal the next hidden item on the slide
1596// node is null or the node that was last revealed
1597function revealNextItem(node)
1598{
1599 node = nextIncrementalItem(node);
1600
1601 if (node && node.nodeType == 1) // an element
1602 node.style.visibility = "visible";
1603
1604 return node;
1605}
1606
1607
1608// exact inverse of revealNextItem(node)
1609function hidePreviousItem(node)
1610{
1611 if (node && node.nodeType == 1) // an element
1612 node.style.visibility = "hidden";
1613
1614 return previousIncrementalItem(node);
1615}
1616
1617
1618/* set click handlers on all anchors */
1619function patchAnchors()
1620{
1621 var anchors = document.body.getElementsByTagName("a");
1622
1623 for (var i = 0; i < anchors.length; ++i)
1624 {
1625 anchors[i].onclick = clickedAnchor;
1626 }
1627}
1628
1629function clickedAnchor(e)
1630{
1631 if (!e)
1632 var e = window.event;
1633
1634 // compare this.href with location.href
1635 // for link to another slide in this doc
1636
1637 if (pageAddress(this.href) == pageAddress(location.href))
1638 {
1639 // yes, so find new slide number
1640 var newslidenum = findSlideNumber(this.href);
1641
1642 if (newslidenum != slidenum)
1643 {
1644 slide = slides[slidenum];
1645 hideSlide(slide);
1646 slidenum = newslidenum;
1647 slide = slides[slidenum];
1648 showSlide(slide);
1649 setLocation();
1650 }
1651 }
1652 else if (this.target == null)
1653 location.href = this.href;
1654
1655 this.blur();
1656 stopPropagation(e);
1657}
1658
1659function pageAddress(uri)
1660{
1661 var i = uri.indexOf("#");
1662
1663 // check if anchor is entire page
1664
1665 if (i < 0)
1666 return uri; // yes
1667
1668 return uri.substr(0, i);
1669}
1670
1671function showSlideNumber()
1672{
1673 slideNumElement.innerHTML = "slide".localize() + " " +
1674 (slidenum + 1) + "/" + slides.length;
1675}
1676
1677function setLocation()
1678{
1679 var uri = pageAddress(location.href);
1680
1681 if (slidenum > 0)
1682 uri = uri + "#(" + (slidenum+1) + ")";
1683
1684 if (uri != location.href && !khtml)
1685 location.href = uri;
1686
1687 document.title = title + " (" + (slidenum+1) + ")";
1688 //document.title = (slidenum+1) + ") " + slideName(slidenum);
1689
1690 showSlideNumber();
1691}
1692
1693// find current slide based upon location
1694// first find target anchor and then look
1695// for associated div element enclosing it
1696// finally map that to slide number
1697function findSlideNumber(uri)
1698{
1699 // first get anchor from page location
1700
1701 var i = uri.indexOf("#");
1702
1703 // check if anchor is entire page
1704
1705 if (i < 0)
1706 return 0; // yes
1707
1708 var anchor = uri.substr(i+1);
1709
1710 // now use anchor as XML ID to find target
1711 var target = document.getElementById(anchor);
1712
1713 if (!target)
1714 {
1715 // does anchor look like "(2)" for slide 2 ??
1716 // where first slide is (1)
1717 var re = /\((\d)+\)/;
1718
1719 if (anchor.match(re))
1720 {
1721 var num = parseInt(anchor.substring(1, anchor.length-1));
1722
1723 if (num > slides.length)
1724 num = 1;
1725
1726 if (--num < 0)
1727 num = 0;
1728
1729 return num;
1730 }
1731
1732 // accept [2] for backwards compatibility
1733 re = /\[(\d)+\]/;
1734
1735 if (anchor.match(re))
1736 {
1737 var num = parseInt(anchor.substring(1, anchor.length-1));
1738
1739 if (num > slides.length)
1740 num = 1;
1741
1742 if (--num < 0)
1743 num = 0;
1744
1745 return num;
1746 }
1747
1748 // oh dear unknown anchor
1749 return 0;
1750 }
1751
1752 // search for enclosing slide
1753
1754 while (true)
1755 {
1756 // browser coerces html elements to uppercase!
1757 if (target.nodeName.toLowerCase() == "div" &&
1758 hasClass(target, "slide"))
1759 {
1760 // found the slide element
1761 break;
1762 }
1763
1764 // otherwise try parent element if any
1765
1766 target = target.parentNode;
1767
1768 if (!target)
1769 {
1770 return 0; // no luck!
1771 }
1772 };
1773
1774 for (i = 0; i < slides.length; ++i)
1775 {
1776 if (slides[i] == target)
1777 return i; // success
1778 }
1779
1780 // oh dear still no luck
1781 return 0;
1782}
1783
1784// find slide name from first h1 element
1785// default to document title + slide number
1786function slideName(index)
1787{
1788 var name = null;
1789 var slide = slides[index];
1790
1791 var heading = findHeading(slide);
1792
1793 if (heading)
1794 name = extractText(heading);
1795
1796 if (!name)
1797 name = title + "(" + (index + 1) + ")";
1798
1799 name.replace(/\&/g, "&amp;");
1800 name.replace(/\</g, "&lt;");
1801 name.replace(/\>/g, "&gt;");
1802
1803 return name;
1804}
1805
1806// find first h1 element in DOM tree
1807function findHeading(node)
1808{
1809 if (!node || node.nodeType != 1)
1810 return null;
1811
1812 if (node.nodeName == "H1" || node.nodeName == "h1")
1813 return node;
1814
1815 var child = node.firstChild;
1816
1817 while (child)
1818 {
1819 node = findHeading(child);
1820
1821 if (node)
1822 return node;
1823
1824 child = child.nextSibling;
1825 }
1826
1827 return null;
1828}
1829
1830// recursively extract text from DOM tree
1831function extractText(node)
1832{
1833 if (!node)
1834 return "";
1835
1836 // text nodes
1837 if (node.nodeType == 3)
1838 return node.nodeValue;
1839
1840 // elements
1841 if (node.nodeType == 1)
1842 {
1843 node = node.firstChild;
1844 var text = "";
1845
1846 while (node)
1847 {
1848 text = text + extractText(node);
1849 node = node.nextSibling;
1850 }
1851
1852 return text;
1853 }
1854
1855 return "";
1856}
1857
1858
1859// find copyright text from meta element
1860function findCopyright()
1861{
1862 var name, content;
1863 var meta = document.getElementsByTagName("meta");
1864
1865 for (var i = 0; i < meta.length; ++i)
1866 {
1867 name = meta[i].getAttribute("name");
1868 content = meta[i].getAttribute("content");
1869
1870 if (name == "copyright")
1871 return content;
1872 }
1873
1874 return null;
1875}
1876
1877function findSizeAdjust()
1878{
1879 var name, content, offset;
1880 var meta = document.getElementsByTagName("meta");
1881
1882 for (var i = 0; i < meta.length; ++i)
1883 {
1884 name = meta[i].getAttribute("name");
1885 content = meta[i].getAttribute("content");
1886
1887 if (name == "font-size-adjustment")
1888 return 1 * content;
1889 }
1890
1891 return 0;
1892}
1893
1894function addToolbar()
1895{
1896 var slideCounter, page;
1897
1898 var toolbar = createElement("div");
1899 toolbar.setAttribute("class", "toolbar");
1900
1901 if (ns_pos) // a reasonably behaved browser
1902 {
1903 var right = document.createElement("div");
1904 right.setAttribute("style", "float: right; text-align: right");
1905
1906 slideCounter = document.createElement("div")
1907 slideCounter.innerHTML = "slide".localize() + " n/m";
1908 right.appendChild(slideCounter);
1909 toolbar.appendChild(right);
1910
1911 var left = document.createElement("div");
1912 left.setAttribute("style", "text-align: left");
1913
1914 // global end of slide indicator
1915 eos = document.createElement("span");
1916 eos.innerHTML = "* ";
1917 left.appendChild(eos);
1918
1919 var help = document.createElement("a");
1920 help.setAttribute("href", helpPage);
1921 help.setAttribute("title", helpText.localize());
1922 help.innerHTML = "help?".localize();
1923 left.appendChild(help);
1924 helpAnchor = help; // save for focus hack
1925
1926 var gap1 = document.createTextNode(" ");
1927 left.appendChild(gap1);
1928
1929 var contents = document.createElement("a");
1930 contents.setAttribute("href", "javascript:toggleTableOfContents()");
1931 contents.setAttribute("title", "table of contents".localize());
1932 contents.innerHTML = "contents?".localize();
1933 left.appendChild(contents);
1934
1935 var gap2 = document.createTextNode(" ");
1936 left.appendChild(gap2);
1937
1938 var i = location.href.indexOf("#");
1939
1940 // check if anchor is entire page
1941
1942 if (i > 0)
1943 page = location.href.substr(0, i);
1944 else
1945 page = location.href;
1946
1947 var start = document.createElement("a");
1948 start.setAttribute("href", page);
1949 start.setAttribute("title", "restart presentation".localize());
1950 start.innerHTML = "restart?".localize();
1951// start.setAttribute("href", "javascript:printSlides()");
1952// start.setAttribute("title", "print all slides".localize());
1953// start.innerHTML = "print!".localize();
1954 left.appendChild(start);
1955
1956 var copyright = findCopyright();
1957
1958 if (copyright)
1959 {
1960 var span = document.createElement("span");
1961 span.innerHTML = copyright;
1962 span.style.color = "black";
1963 span.style.marginLeft = "4em";
1964 left.appendChild(span);
1965 }
1966
1967 toolbar.appendChild(left);
1968 }
1969 else // IE so need to work around its poor CSS support
1970 {
1971 toolbar.style.position = "absolute";
1972 toolbar.style.zIndex = "200";
1973 toolbar.style.width = "100%";
1974 toolbar.style.height = "1.2em";
1975 toolbar.style.top = "auto";
1976 toolbar.style.bottom = "0";
1977 toolbar.style.left = "0";
1978 toolbar.style.right = "0";
1979 toolbar.style.textAlign = "left";
1980 toolbar.style.fontSize = "60%";
1981 toolbar.style.color = "red";
1982 toolbar.borderWidth = 0;
1983 toolbar.style.background = "rgb(240,240,240)";
1984
1985 // would like to have help text left aligned
1986 // and page counter right aligned, floating
1987 // div's don't work, so instead use nested
1988 // absolutely positioned div's.
1989
1990 var sp = document.createElement("span");
1991 sp.innerHTML = "&nbsp;&nbsp;*&nbsp;";
1992 toolbar.appendChild(sp);
1993 eos = sp; // end of slide indicator
1994
1995 var help = document.createElement("a");
1996 help.setAttribute("href", helpPage);
1997 help.setAttribute("title", helpText.localize());
1998 help.innerHTML = "help?".localize();
1999 toolbar.appendChild(help);
2000 helpAnchor = help; // save for focus hack
2001
2002 var gap1 = document.createTextNode(" ");
2003 toolbar.appendChild(gap1);
2004
2005 var contents = document.createElement("a");
2006 contents.setAttribute("href", "javascript:toggleTableOfContents()");
2007 contents.setAttribute("title", "table of contents".localize());
2008 contents.innerHTML = "contents?".localize();
2009 toolbar.appendChild(contents);
2010
2011 var gap2 = document.createTextNode(" ");
2012 toolbar.appendChild(gap2);
2013
2014 var i = location.href.indexOf("#");
2015
2016 // check if anchor is entire page
2017
2018 if (i > 0)
2019 page = location.href.substr(0, i);
2020 else
2021 page = location.href;
2022
2023 var start = document.createElement("a");
2024 start.setAttribute("href", page);
2025 start.setAttribute("title", "restart presentation".localize());
2026 start.innerHTML = "restart?".localize();
2027// start.setAttribute("href", "javascript:printSlides()");
2028// start.setAttribute("title", "print all slides".localize());
2029// start.innerHTML = "print!".localize();
2030 toolbar.appendChild(start);
2031
2032 var copyright = findCopyright();
2033
2034 if (copyright)
2035 {
2036 var span = document.createElement("span");
2037 span.innerHTML = copyright;
2038 span.style.color = "black";
2039 span.style.marginLeft = "2em";
2040 toolbar.appendChild(span);
2041 }
2042
2043 slideCounter = document.createElement("div")
2044 slideCounter.style.position = "absolute";
2045 slideCounter.style.width = "auto"; //"20%";
2046 slideCounter.style.height = "1.2em";
2047 slideCounter.style.top = "auto";
2048 slideCounter.style.bottom = 0;
2049 slideCounter.style.right = "0";
2050 slideCounter.style.textAlign = "right";
2051 slideCounter.style.color = "red";
2052 slideCounter.style.background = "rgb(240,240,240)";
2053
2054 slideCounter.innerHTML = "slide".localize() + " n/m";
2055 toolbar.appendChild(slideCounter);
2056 }
2057
2058 // ensure that click isn't passed through to the page
2059 toolbar.onclick = stopPropagation;
2060 document.body.appendChild(toolbar);
2061 slideNumElement = slideCounter;
2062 setEosStatus(false);
2063
2064 return toolbar;
2065}
2066
2067function isShownToc()
2068{
2069 if (toc && toc.style.visible == "visible")
2070 return true;
2071
2072 return false;
2073}
2074
2075function showTableOfContents()
2076{
2077 if (toc)
2078 {
2079 if (toc.style.visible != "visible")
2080 {
2081 toc.style.visible = "visible";
2082 toc.style.display = "block";
2083 toc.focus();
2084 }
2085 else
2086 hideTableOfContents();
2087 }
2088}
2089
2090function hideTableOfContents()
2091{
2092 if (toc)
2093 {
2094 toc.style.visible = "hidden";
2095 toc.style.display = "none";
2096
2097 try
2098 {
2099 if (!opera)
2100 helpAnchor.focus();
2101 }
2102 catch (e)
2103 {
2104 }
2105 }
2106}
2107
2108function toggleTableOfContents()
2109{
2110 if (toc)
2111 {
2112 if (toc.style.visible != "visible")
2113 showTableOfContents();
2114 else
2115 hideTableOfContents();
2116 }
2117}
2118
2119// called on clicking toc entry
2120function gotoEntry(e)
2121{
2122 var target;
2123
2124 if (!e)
2125 var e = window.event;
2126
2127 if (e.target)
2128 target = e.target;
2129 else if (e.srcElement)
2130 target = e.srcElement;
2131
2132 // work around Safari bug
2133 if (target.nodeType == 3)
2134 target = target.parentNode;
2135
2136 if (target && target.nodeType == 1)
2137 {
2138 var uri = target.getAttribute("href");
2139
2140 if (uri)
2141 {
2142 //alert("going to " + uri);
2143 var slide = slides[slidenum];
2144 hideSlide(slide);
2145 slidenum = findSlideNumber(uri);
2146 slide = slides[slidenum];
2147 lastShown = null;
2148 lastExpand= null;
2149 setLocation();
2150 setVisibilityAllIncremental("hidden");
2151 setEosStatus(!nextIncrementalItem(lastShown));
2152 showSlide(slide);
2153 //target.focus();
2154
2155 try
2156 {
2157 if (!opera)
2158 helpAnchor.focus();
2159 }
2160 catch (e)
2161 {
2162 }
2163 }
2164 }
2165
2166 hideTableOfContents(e);
2167 stopPropagation(e);
2168 return cancel(e);
2169}
2170
2171// called onkeydown for toc entry
2172function gotoTocEntry(event)
2173{
2174 var key;
2175
2176 if (!event)
2177 var event = window.event;
2178
2179 // kludge around NS/IE differences
2180 if (window.event)
2181 key = window.event.keyCode;
2182 else if (event.which)
2183 key = event.which;
2184 else
2185 return true; // Yikes! unknown browser
2186
2187 // ignore event if key value is zero
2188 // as for alt on Opera and Konqueror
2189 if (!key)
2190 return true;
2191
2192 // check for concurrent control/command/alt key
2193 // but are these only present on mouse events?
2194
2195 if (event.ctrlKey || event.altKey)
2196 return true;
2197
2198 if (key == 13)
2199 {
2200 var uri = this.getAttribute("href");
2201
2202 if (uri)
2203 {
2204 //alert("going to " + uri);
2205 var slide = slides[slidenum];
2206 hideSlide(slide);
2207 slidenum = findSlideNumber(uri);
2208 slide = slides[slidenum];
2209 lastShown = null;
2210 lastExpand= null;
2211 setLocation();
2212 setVisibilityAllIncremental("hidden");
2213 setEosStatus(!nextIncrementalItem(lastShown));
2214 showSlide(slide);
2215 //target.focus();
2216
2217 try
2218 {
2219 if (!opera)
2220 helpAnchor.focus();
2221 }
2222 catch (e)
2223 {
2224 }
2225 }
2226
2227 hideTableOfContents();
2228 return cancel(event);
2229 }
2230
2231 if (key == 40 && this.next)
2232 {
2233 this.next.focus();
2234 return cancel(event);
2235 }
2236
2237 if (key == 38 && this.previous)
2238 {
2239 this.previous.focus();
2240 return cancel(event);
2241 }
2242
2243 return true;
2244}
2245
2246// create div element with links to each slide
2247function tableOfContents()
2248{
2249 var toc = document.createElement("div");
2250 addClass(toc, "toc");
2251 //toc.setAttribute("tabindex", "0");
2252
2253 var heading = document.createElement("div");
2254 addClass(heading, "toc-heading");
2255 heading.innerHTML = "Table of Contents".localize();
2256
2257 heading.style.textAlign = "center";
2258 heading.style.width = "100%";
2259 heading.style.margin = "0";
2260 heading.style.marginBottom = "1em";
2261 heading.style.borderBottomStyle = "solid";
2262 heading.style.borderBottomColor = "rgb(180,180,180)";
2263 heading.style.borderBottomWidth = "1px";
2264
2265 toc.appendChild(heading);
2266 var previous = null;
2267
2268 for (var i = 0; i < slides.length; ++i)
2269 {
2270 var num = document.createTextNode((i + 1) + ". ");
2271 toc.appendChild(num);
2272
2273 var a = document.createElement("a");
2274 a.setAttribute("href", "#(" + (i+1) + ")");
2275
2276 var name = document.createTextNode(slideName(i));
2277 a.appendChild(name);
2278 a.onclick = gotoEntry;
2279 a.onkeydown = gotoTocEntry;
2280 a.previous = previous;
2281
2282 if (previous)
2283 previous.next = a;
2284
2285 toc.appendChild(a);
2286
2287 if (i == 0)
2288 toc.first = a;
2289
2290 if (i < slides.length - 1)
2291 {
2292 var br = document.createElement("br");
2293 toc.appendChild(br);
2294 }
2295
2296 previous = a;
2297 }
2298
2299 toc.focus = function () {
2300 if (this.first)
2301 this.first.focus();
2302 }
2303
2304 toc.onclick = function (e) {
2305 hideTableOfContents();
2306 stopPropagation(e);
2307 e.cancel = true;
2308 e.returnValue = false;
2309 return false;
2310 };
2311
2312 toc.style.position = "absolute";
2313 toc.style.zIndex = "300";
2314 toc.style.width = "60%";
2315 toc.style.maxWidth = "30em";
2316 toc.style.height = "30em";
2317 toc.style.overflow = "auto";
2318 toc.style.top = "auto";
2319 toc.style.right = "auto";
2320 toc.style.left = "4em";
2321 toc.style.bottom = "4em";
2322 toc.style.padding = "1em";
2323 toc.style.background = "rgb(240,240,240)";
2324 toc.style.borderStyle = "solid";
2325 toc.style.borderWidth = "2px";
2326 toc.style.fontSize = "60%";
2327
2328 document.body.insertBefore(toc, document.body.firstChild);
2329 return toc;
2330}
2331
2332function replaceByNonBreakingSpace(str)
2333{
2334 for (var i = 0; i < str.length; ++i)
2335 str[i] = 160;
2336}
2337
2338
2339function initOutliner()
2340{
2341 var items = document.getElementsByTagName("LI");
2342
2343 for (var i = 0; i < items.length; ++i)
2344 {
2345 var target = items[i];
2346
2347 if (!hasClass(target.parentNode, "outline"))
2348 continue;
2349
2350 target.onclick = outlineClick;
2351
2352 if (!ns_pos)
2353 {
2354 target.onmouseover = hoverOutline;
2355 target.onmouseout = unhoverOutline;
2356 }
2357
2358 if (foldable(target))
2359 {
2360 target.foldable = true;
2361 target.onfocus = function () {outline = this;};
2362 target.onblur = function () {outline = null;};
2363
2364 if (!target.getAttribute("tabindex"))
2365 target.setAttribute("tabindex", "0");
2366
2367 if (hasClass(target, "expand"))
2368 unfold(target);
2369 else
2370 fold(target);
2371 }
2372 else
2373 {
2374 addClass(target, "nofold");
2375 target.visible = true;
2376 target.foldable = false;
2377 }
2378 }
2379}
2380
2381/*
2382 If their is no item expand :
2383 - Expand the next fold item
2384 - lastExpand = this item
2385 If one item is already expand :
2386 - Fold this item if it is foldable
2387 - lastExpand = this item
2388*/
2389function foldPreviousExpandNext ()
2390{
2391 var slide = slides[slidenum];
2392 var state ="noExpandedItem"; //noExpandedItem | foundLastExpandFolded | foundLastExpandUnfold
2393 var firstOutline = null;
2394 node = nextNode(slide, null);
2395
2396 for (;;)
2397 {
2398 node = nextNode(slide, node);
2399
2400 if (node == null || node.parentNode == null)
2401 break;
2402
2403 if (node.nodeType == 1)
2404 {
2405 if (hasClass(node.parentNode, "outline"))
2406 {
2407 if (node == lastExpand) {
2408 if (node.visible)
2409 state = "foundLastExpandUnfold";
2410 else
2411 state = "foundLastExpandFold";
2412 }
2413 if (node.visible) {
2414 fold(node);
2415 }
2416 else if ((node != lastExpand) && (state == "foundLastExpandFold")) {
2417 unfold(node);
2418 return node;
2419 } else if (firstOutline == null) {
2420 firstOutline = node;
2421 }
2422 }
2423 }
2424 }
2425 if ((state == "noExpandedItem") && (firstOutline != null)) {
2426 unfold(firstOutline);
2427 return firstOutline;
2428 } else if (state == "foundLastExpandUnfold") {
2429 return lastExpand;
2430 } else {
2431 return null;
2432 }
2433}
2434
2435/*
2436 If their is no item expand :
2437 - Expand the previous fold item
2438 - lastExpand = this item
2439 If one item is already expand :
2440 - Fold this item if it is foldable
2441 - lastExpand = this item
2442*/
2443function expandPreviousFoldNext ()
2444{
2445 var slide = slides[slidenum];
2446 var state ="noExpandedItem"; //noExpandedItem | foundLastExpandFolded | foundLastExpandUnfold
2447 var firstOutline = null;
2448 node = previousNode(slide, null);
2449
2450 for (;;)
2451 {
2452 node = previousNode(slide, node);
2453
2454 if (node == null || node.parentNode == null)
2455 break;
2456
2457 if (node.nodeType == 1)
2458 {
2459 if (hasClass(node.parentNode, "outline"))
2460 {
2461 if (node == lastExpand) {
2462 if (node.visible)
2463 state = "foundLastExpandUnfold";
2464 else
2465 state = "foundLastExpandFold";
2466 }
2467 if (node.visible) {
2468 fold(node);
2469 }
2470 else if ((node != lastExpand) && (state == "foundLastExpandFold")) {
2471 unfold(node);
2472 return node;
2473 } else if (firstOutline == null) {
2474 firstOutline = node;
2475 }
2476 }
2477 }
2478 }
2479 if ((state == "noExpandedItem") && (firstOutline != null)) {
2480 unfold(firstOutline);
2481 return firstOutline;
2482 } else if (state == "foundLastExpandUnfold") {
2483 return lastExpand;
2484 } else {
2485 return null;
2486 }
2487}
2488
2489/*
2490 Expand all the foldable items of slides
2491 */
2492
2493function expandAll()
2494{
2495 var items = document.getElementsByTagName("LI");
2496
2497 for (var i = 0; i < items.length; ++i)
2498 {
2499 var target = items[i];
2500
2501 if (!hasClass(target.parentNode, "outline"))
2502 continue;
2503
2504 target.onclick = outlineClick;
2505
2506 if (!ns_pos)
2507 {
2508 target.onmouseover = hoverOutline;
2509 target.onmouseout = unhoverOutline;
2510 }
2511
2512 if (foldable(target))
2513 {
2514 target.foldable = true;
2515 target.onfocus = function () {outline = this;};
2516 target.onblur = function () {outline = null;};
2517
2518 if (!target.getAttribute("tabindex"))
2519 target.setAttribute("tabindex", "0");
2520
2521 unfold(target);
2522 }
2523 else
2524 {
2525 addClass(target, "nofold");
2526 target.visible = true;
2527 target.foldable = false;
2528 }
2529 }
2530}
2531
2532function foldable(item)
2533{
2534 if (!item || item.nodeType != 1)
2535 return false;
2536
2537 var node = item.firstChild;
2538
2539 while (node)
2540 {
2541 if (node.nodeType == 1 && isBlock(node))
2542 return true;
2543
2544 node = node.nextSibling;
2545 }
2546
2547 return false;
2548}
2549
2550function fold(item)
2551{
2552 if (item)
2553 {
2554 removeClass(item, "unfolded");
2555 addClass(item, "folded");
2556 }
2557
2558 var node = item ? item.firstChild : null;
2559
2560 while (node)
2561 {
2562 if (node.nodeType == 1 && isBlock(node)) // element
2563 {
2564 // note that getElementStyle won't work for Safari 1.3
2565 node.display = getElementStyle(node, "display", "display");
2566 node.style.display = "none";
2567 node.style.visibility = "hidden";
2568 }
2569
2570 node = node.nextSibling;
2571 }
2572
2573 item.visible = false;
2574}
2575
2576function unfold(item)
2577{
2578 if (item)
2579 {
2580 addClass(item, "unfolded");
2581 removeClass(item, "folded");
2582 }
2583
2584 var node = item ? item.firstChild : null;
2585
2586 while (node)
2587 {
2588 if (node.nodeType == 1 && isBlock(node)) // element
2589 {
2590 // with fallback for Safari, see above
2591 node.style.display = (node.display ? node.display : "block");
2592 node.style.visibility = "visible";
2593 }
2594
2595 node = node.nextSibling;
2596 }
2597
2598 item.visible = true;
2599}
2600
2601function outlineClick(e)
2602{
2603 var rightclick = false;
2604 var target;
2605
2606 if (!e)
2607 var e = window.event;
2608
2609 if (e.target)
2610 target = e.target;
2611 else if (e.srcElement)
2612 target = e.srcElement;
2613
2614 // work around Safari bug
2615 if (target.nodeType == 3)
2616 target = target.parentNode;
2617
2618 while (target && target.visible == undefined)
2619 target = target.parentNode;
2620
2621 if (!target)
2622 return true;
2623
2624 if (e.which)
2625 rightclick = (e.which == 3);
2626 else if (e.button)
2627 rightclick = (e.button == 2);
2628
2629 if (!rightclick && target.visible != undefined)
2630 {
2631 if (target.foldable)
2632 {
2633 if (target.visible)
2634 fold(target);
2635 else
2636 unfold(target);
2637 }
2638
2639 stopPropagation(e);
2640 e.cancel = true;
2641 e.returnValue = false;
2642 }
2643
2644 return false;
2645}
2646
2647function hoverOutline(e)
2648{
2649 var target;
2650
2651 if (!e)
2652 var e = window.event;
2653
2654 if (e.target)
2655 target = e.target;
2656 else if (e.srcElement)
2657 target = e.srcElement;
2658
2659 // work around Safari bug
2660 if (target.nodeType == 3)
2661 target = target.parentNode;
2662
2663 while (target && target.visible == undefined)
2664 target = target.parentNode;
2665
2666 if (target && target.foldable)
2667 target.style.cursor = "pointer";
2668
2669 return true;
2670}
2671
2672function unhoverOutline(e)
2673{
2674 var target;
2675
2676 if (!e)
2677 var e = window.event;
2678
2679 if (e.target)
2680 target = e.target;
2681 else if (e.srcElement)
2682 target = e.srcElement;
2683
2684 // work around Safari bug
2685 if (target.nodeType == 3)
2686 target = target.parentNode;
2687
2688 while (target && target.visible == undefined)
2689 target = target.parentNode;
2690
2691 if (target)
2692 target.style.cursor = "default";
2693
2694 return true;
2695}
2696
2697
2698function stopPropagation(e)
2699{
2700 if (window.event)
2701 {
2702 window.event.cancelBubble = true;
2703 //window.event.returnValue = false;
2704 }
2705 else if (e)
2706 {
2707 e.cancelBubble = true;
2708 e.stopPropagation();
2709 //e.preventDefault();
2710 }
2711}
2712
2713/* can't rely on display since we set that to none to hide things */
2714function isBlock(elem)
2715{
2716 var tag = elem.nodeName;
2717
2718 return tag == "OL" || tag == "UL" || tag == "P" ||
2719 tag == "LI" || tag == "TABLE" || tag == "PRE" ||
2720 tag == "H1" || tag == "H2" || tag == "H3" ||
2721 tag == "H4" || tag == "H5" || tag == "H6" ||
2722 tag == "BLOCKQUOTE" || tag == "ADDRESS";
2723}
2724
2725function getElementStyle(elem, IEStyleProp, CSSStyleProp)
2726{
2727 if (elem.currentStyle)
2728 {
2729 return elem.currentStyle[IEStyleProp];
2730 }
2731 else if (window.getComputedStyle)
2732 {
2733 var compStyle = window.getComputedStyle(elem, "");
2734 return compStyle.getPropertyValue(CSSStyleProp);
2735 }
2736 return "";
2737}
2738
2739// works with text/html and text/xhtml+xml with thanks to Simon Willison
2740function createElement(element)
2741{
2742 if (typeof document.createElementNS != 'undefined')
2743 {
2744 return document.createElementNS('http://www.w3.org/1999/xhtml', element);
2745 }
2746
2747 if (typeof document.createElement != 'undefined')
2748 {
2749 return document.createElement(element);
2750 }
2751
2752 return false;
2753}
2754
2755// designed to work with both text/html and text/xhtml+xml
2756function getElementsByTagName(name)
2757{
2758 if (typeof document.getElementsByTagNameNS != 'undefined')
2759 {
2760 return document.getElementsByTagNameNS('http://www.w3.org/1999/xhtml', name);
2761 }
2762
2763 if (typeof document.getElementsByTagName != 'undefined')
2764 {
2765 return document.getElementsByTagName(name);
2766 }
2767
2768 return null;
2769}
2770
2771/*
2772// clean alternative to innerHTML method, but on IE6
2773// it doesn't work with named entities like &nbsp;
2774// which need to be replaced by numeric entities
2775function insertText(element, text)
2776{
2777 try
2778 {
2779 element.textContent = text; // DOM3 only
2780 }
2781 catch (e)
2782 {
2783 if (element.firstChild)
2784 {
2785 // remove current children
2786 while (element.firstChild)
2787 element.removeChild(element.firstChild);
2788 }
2789
2790 element.appendChild(document.createTextNode(text));
2791 }
2792}
2793
2794// as above, but as method of all element nodes
2795// doesn't work in IE6 which doesn't allow you to
2796// add methods to the HTMLElement prototype
2797if (HTMLElement != undefined)
2798{
2799 HTMLElement.prototype.insertText = function(text) {
2800 var element = this;
2801
2802 try
2803 {
2804 element.textContent = text; // DOM3 only
2805 }
2806 catch (e)
2807 {
2808 if (element.firstChild)
2809 {
2810 // remove current children
2811 while (element.firstChild)
2812 element.removeChild(element.firstChild);
2813 }
2814
2815 element.appendChild(document.createTextNode(text));
2816 }
2817 };
2818}
2819*/
2820
2821function getSelectedText()
2822{
2823 try
2824 {
2825 if (window.getSelection)
2826 return window.getSelection().toString();
2827
2828 if (document.getSelection)
2829 return document.getSelection().toString();
2830
2831 if (document.selection)
2832 return document.selection.createRange().text;
2833 }
2834 catch (e)
2835 {
2836 return "";
2837 }
2838 return "";
2839}
Note: See TracBrowser for help on using the repository browser.