@@ -0,0 +1,321 @@
1+/* This work is licensed under Creative Commons GNU LGPL License.
2+
3+ License: http://creativecommons.org/licenses/LGPL/2.1/
4+
5+ Author: Stefan Goessner/2005-2006
6+ Web: http://goessner.net/
7+*/
8+var Slideous = {
9+ version: 1.0,
10+ // == user customisable ===
11+ clickables: { a: true, button: true, img: true, input: true, object: true, textarea: true, select: true, option: true },
12+ incrementables: { blockquote: { filter: "self, parent" },
13+ dd: { filter: "self, parent" },
14+ dt: { filter: "self, parent" },
15+ h2: { filter: "self, parent" },
16+ h3: { filter: "self, parent" },
17+ h4: { filter: "self, parent" },
18+ h5: { filter: "self, parent" },
19+ h6: { filter: "self, parent" },
20+ li: { filter: "self, parent" },
21+ p: { filter: "self" },
22+ pre: { filter: "self" },
23+ img: { filter: "self, parent" },
24+ object: { filter: "self, parent" },
25+ table: { filter: "self, parent" },
26+ td: { filter: "self, parent" },
27+ th: { filter: "self, parent" },
28+ tr: { filter: "parent, grandparent" }
29+ },
30+ autoincrementables: { ol: true, ul: true, dl: true },
31+ autoincrement: false,
32+ statusbar: true,
33+ navbuttons: { incfontbutton: function(){Slideous.changefontsize(+Slideous.fontdelta);},
34+ decfontbutton: function(){Slideous.changefontsize(-Slideous.fontdelta);},
35+ contentbutton: function(){Slideous.gotoslide(Slideous.tocidx(), true, true);},
36+ homebutton: function(){Slideous.gotoslide(1, true, true);},
37+ prevslidebutton: function(){Slideous.previous(false);},
38+ previtembutton: function(){Slideous.previous(true);},
39+ nextitembutton: function(){Slideous.next(true);},
40+ nextslidebutton: function(){Slideous.next(false);},
41+ endbutton: function(){Slideous.gotoslide(Slideous.count,true,true);} },
42+ fontsize: 125, // in percent, corresponding to body.font-size in css file
43+ fontdelta: 5, // increase/decrease fontsize by this value
44+ mousesensitive: true,
45+ tocidx: 0,
46+ tocitems: { toc: "<li><a href=\"#s{\$slideidx}\">{\$slidetitle}</a></li>",
47+ tocbox: "<option value=\"#s{\$slideidx}\" title=\"{\$slidetitle}\">{\$slidetitle}</option>" },
48+ keydown: function(evt) {
49+ evt = evt || window.event;
50+ var key = evt.keyCode || evt.which;
51+ if (key && !evt.ctrlKey && !evt.altKey) {
52+ switch (key) {
53+ case 33: // page up ... previous slide
54+ Slideous.previous(false); evt.cancel = !Slideous.showall; break;
55+ case 37: // left arrow ... previous item
56+ Slideous.previous(true); evt.cancel = !Slideous.showall; break;
57+ case 32: // space bar
58+ case 39: // right arrow
59+ Slideous.next(true); evt.cancel = !Slideous.showall; break;
60+ case 13: // carriage return ... next slide
61+ case 34: // page down
62+ Slideous.next(false); evt.cancel = !Slideous.showall; break;
63+ case 35: // end ... last slide (not recognised by opera)
64+ Slideous.gotoslide(Slideous.count, true, true); evt.cancel = !Slideous.showall; break;
65+ case 36: // home ... first slide (not recognised by opera)
66+ Slideous.gotoslide(1, true, true); evt.cancel = !Slideous.showall; break;
67+ case 65: // A ... show All
68+ case 80: // P ... Print mode
69+ Slideous.toggleshowall(!Slideous.showall); evt.cancel = true; break;
70+ case 67: // C ... goto contents
71+ Slideous.gotoslide(Slideous.tocidx, true, true); evt.cancel = true; break;
72+ case 77: // M ... toggle mouse sensitivity
73+ Slideous.mousenavigation(Slideous.mousesensitive = !Slideous.mousesensitive); evt.cancel = true; break;
74+ case 83: // S ... toggle statusbar
75+ Slideous.togglestatusbar(); evt.cancel = true; break;
76+ case 61: // + ... increase fontsize
77+ case 107:
78+ Slideous.changefontsize(+Slideous.fontdelta); evt.cancel = true; break;
79+ case 109: // - ... decrease fontsize
80+ Slideous.changefontsize(-Slideous.fontdelta); evt.cancel = true; break;
81+ default: break;
82+ }
83+ if (evt.cancel) evt.returnValue = false;
84+ }
85+ return !evt.cancel;
86+ },
87+
88+ // == program logic ===
89+ count: 0, // # of slides ..
90+ curidx: 0, // current slide index ..
91+ mousedownpos: null, // last mouse down position ..
92+ contentselected: false, // indicates content selection ..
93+ showall: true,
94+ init: function() {
95+ Slideous.curidx = 1;
96+ Slideous.importproperties();
97+ Slideous.registerslides();
98+ document.body.innerHTML = Slideous.injectproperties(document.body.innerHTML);
99+ Slideous.buildtocs();
100+ Slideous.registeranchors();
101+ Slideous.toggleshowall(false);
102+ Slideous.updatestatus();
103+ document.body.style.fontSize = Slideous.fontsize+"%";
104+ document.getElementById("s1").style.display = "block";
105+ document.onkeydown = Slideous.keydown;
106+ Slideous.mousenavigation(Slideous.mousesensitive);
107+ Slideous.registerbuttons();
108+ if (window.location.hash)
109+ Slideous.gotoslide(window.location.hash.substr(2), true, true);
110+ },
111+ registerslides: function() {
112+ var div = document.getElementsByTagName("div");
113+ Slideous.count = 0;
114+ for (var i in div)
115+ if (Slideous.hasclass(div[i], "slide"))
116+ div[i].setAttribute("id", "s"+(++Slideous.count));
117+ },
118+ registeranchors: function() {
119+ var a = document.getElementsByTagName("a"),
120+ loc = (window.location.hostname+window.location.pathname).replace(/\\/g, "/");
121+ for (var i in a) {
122+ if (a[i].href && a[i].href.indexOf(loc) >= 0 && a[i].href.lastIndexOf("#") >= 0) {
123+ a[i].href = "javascript:Slideous.gotoslide(" + a[i].href.substr(a[i].href.lastIndexOf("#")+2)+",true,true)";
124+ }
125+ }
126+ },
127+ registerbuttons: function() {
128+ var button;
129+ for (var b in Slideous.navbuttons)
130+ if (button = document.getElementById(b))
131+ button.onclick = Slideous.navbuttons[b];
132+ },
133+ importproperties: function() { // from html meta section ..
134+ var meta = document.getElementsByTagName("meta"), elem;
135+ for (var i in meta)
136+ if (meta[i].attributes && meta[i].attributes["name"] && meta[i].attributes["name"].value in Slideous)
137+ switch (typeof(Slideous[meta[i].attributes["name"].value])) {
138+ case "number": Slideous[meta[i].attributes["name"].value] = parseInt(meta[i].attributes["content"].value); break;
139+ case "boolean": Slideous[meta[i].attributes["name"].value] = meta[i].attributes["content"].value == "true" ? true : false; break;
140+ default: Slideous[meta[i].attributes["name"].value] = meta[i].attributes["content"].value; break;
141+ }
142+ },
143+ injectproperties: function(str) {
144+ var meta = document.getElementsByTagName("meta"), elem;
145+ for (var i in meta) {
146+ if (meta[i].attributes && meta[i].attributes["name"])
147+ str = str.replace(new RegExp("{\\$"+meta[i].attributes["name"].value+"}","g"), meta[i].attributes["content"].value);
148+ }
149+ return str = str.replace(/{\$generator}/g, "Slideous")
150+ .replace(/{\$version}/g, Slideous.version)
151+ .replace(/{\$title}/g, document.title)
152+ .replace(/{\$slidecount}/g, Slideous.count);
153+ },
154+ buildtocs: function() {
155+ var toc = document.getElementById("toc"), list = "",
156+ tocbox = document.getElementById("tocbox");
157+ if (toc) {
158+ for (var i=0; i<Slideous.count; i++)
159+ list += Slideous.tocitems.toc.replace(/{\$slideidx}/g, i+1).replace(/{\$slidetitle}/, document.getElementById("s"+(i+1)).getElementsByTagName("h1")[0].innerHTML);
160+ toc.innerHTML = list;
161+ while (toc && !Slideous.hasclass(toc, "slide")) toc = toc.parentNode;
162+ if (toc) Slideous.tocidx = toc.getAttribute("id").substr(1);
163+ }
164+ if (tocbox) {
165+ tocbox.innerHTML = "";
166+ for (var i=0; i<Slideous.count; i++)
167+ tocbox.options[tocbox.length] = new Option((i+1)+". "+document.getElementById("s"+(i+1)).getElementsByTagName("h1")[0].innerHTML, "#s"+(i+1));
168+ tocbox.onchange = function() { Slideous.gotoslide(this.selectedIndex+1, true, true); };
169+ }
170+ },
171+ next: function(deep) {
172+ if (!Slideous.showall) {
173+ var slide = document.getElementById("s"+Slideous.curidx),
174+ item = Slideous.firstitem(slide, Slideous.isitemhidden);
175+ if (deep) { // next item
176+ if (item)
177+ Slideous.displayitem(item, true);
178+ else
179+ Slideous.gotoslide(Slideous.curidx+1, false, false);
180+ }
181+ else if (item) // complete slide ..
182+ while (item = Slideous.firstitem(slide, Slideous.isitemhidden))
183+ Slideous.displayitem(item, true);
184+ else // next slide
185+ Slideous.gotoslide(Slideous.curidx+1, true, false);
186+ Slideous.updatestatus();
187+ }
188+ },
189+ previous: function(deep) {
190+ if (!Slideous.showall) {
191+ var slide = document.getElementById("s"+Slideous.curidx);
192+ if (deep) {
193+ var item = Slideous.lastitem(slide, Slideous.isitemvisible);
194+ if (item)
195+ Slideous.displayitem(item, false);
196+ else
197+ Slideous.gotoslide(Slideous.curidx-1, true, false);
198+ }
199+ else
200+ Slideous.gotoslide(Slideous.curidx-1, true, false);
201+ Slideous.updatestatus();
202+ }
203+ },
204+ gotoslide: function(i, showitems, updatestatus) {
205+ if (!Slideous.showall && i > 0 && i <= Slideous.count && i != Slideous.curidx) {
206+ document.getElementById("s"+Slideous.curidx).style.display = "none";
207+ var slide = document.getElementById("s"+(Slideous.curidx=i)), item;
208+ while (item = Slideous.firstitem(slide, showitems ? Slideous.isitemhidden : Slideous.isitemvisible))
209+ Slideous.displayitem(item, showitems);
210+ slide.style.display = "block";
211+ if (updatestatus)
212+ Slideous.updatestatus();
213+ }
214+ },
215+ firstitem: function(root, filter) {
216+ var found = filter(root);
217+ for (var node=root.firstChild; node!=null && !found; node = node.nextSibling)
218+ found = Slideous.firstitem(node, filter);
219+ return found;
220+ },
221+ lastitem: function(root, filter) {
222+ var found = null;
223+ for (var node=root.lastChild; node!=null && !found; node = node.previousSibling)
224+ found = Slideous.lastitem(node, filter);
225+ return found || filter(root);
226+ },
227+ isitem: function(node, visible) {
228+ var nodename;
229+ return node && node.nodeType == 1 // elements only ..
230+ && (nodename=node.nodeName.toLowerCase()) in Slideous.incrementables
231+ && ( Slideous.incrementables[nodename].filter.match("\\bself\\b") && (Slideous.hasclass(node, "incremental") || (Slideous.autoincrement && nodename in Slideous.autoincrementables))
232+ || Slideous.incrementables[nodename].filter.match("\\bparent\\b") && (Slideous.hasclass(node.parentNode, "incremental") || (Slideous.autoincrement && node.parentNode.nodeName.toLowerCase() in Slideous.autoincrementables))
233+ || Slideous.incrementables[nodename].filter.match("\\bgrandparent\\b") && (Slideous.hasclass(node.parentNode.parentNode, "incremental") || (Slideous.autoincrement && node.parentNode.parentNode.nodeName.toLowerCase() in Slideous.autoincrementables))
234+ )
235+ && (visible ? (node.style.visibility != "hidden")
236+ : (node.style.visibility == "hidden"))
237+ ? node : null;
238+ },
239+ isitemvisible: function(node) { return Slideous.isitem(node, true); },
240+ isitemhidden: function(node) { return Slideous.isitem(node, false); },
241+ displayitem: function(item, show) {
242+ if (item) item.style.visibility = (show ? "visible" : "hidden");
243+ },
244+ updatestatus: function() {
245+ if (Slideous.statusbar) {
246+ var eos = document.getElementById("eos"),
247+ idx = document.getElementById("slideidx"),
248+ tocbox = document.getElementById("tocbox");
249+ if (eos)
250+ eos.style.visibility = Slideous.firstitem(document.getElementById("s"+Slideous.curidx), Slideous.isitemhidden) != null
251+ ? "visible" : "hidden";
252+ if (idx)
253+ idx.innerHTML = Slideous.curidx;
254+ if (tocbox)
255+ tocbox.selectedIndex = Slideous.curidx-1;
256+ }
257+ },
258+ changefontsize: function(delta) {
259+ document.body.style.fontSize = (Slideous.fontsize+=delta)+"%";
260+ },
261+ togglestatusbar: function() {
262+ document.getElementById("statusbar").style.display = (Slideous.statusbar = !Slideous.statusbar) ? "block" : "none";
263+ },
264+ toggleshowall: function(showall) {
265+ var slide, item;
266+ for (var i=0; i<Slideous.count; i++) {
267+ slide = document.getElementById("s"+(i+1));
268+ slide.style.display = showall ? "block" : "none";
269+ while (item = Slideous.firstitem(slide, showall ? Slideous.isitemhidden : Slideous.isitemvisible))
270+ Slideous.displayitem(item, showall);
271+ var divs = slide.getElementsByTagName("div");
272+ for (var j in divs)
273+ if (Slideous.hasclass(divs[j], "handout"))
274+ divs[j].style.display = showall ? "block" : "none";
275+ }
276+ if (!showall)
277+ document.getElementById("s"+Slideous.curidx).style.display = "block";
278+ if (Slideous.statusbar)
279+ document.getElementById("statusbar").style.display = showall ? "none" : "block";
280+ Slideous.showall = showall;
281+ },
282+ hasclass: function(elem, classname) {
283+ var classattr = null;
284+ return (classattr=(elem.attributes && elem.attributes["class"]))
285+ && classattr.nodeValue.match("\\b"+classname+"\\b");
286+ },
287+ selectedcontent: function() {
288+ return window.getSelection ? window.getSelection().toString()
289+ : document.getSelection ? document.getSelection()
290+ : document.selection ? document.selection.createRange().text
291+ : "";
292+ },
293+ mousenavigation: function(on) {
294+ if (on) {
295+ document.onmousedown = Slideous.mousedown;
296+ document.onmouseup = Slideous.mouseup;
297+ }
298+ else
299+ document.onmousedown = document.onmouseup = null;
300+ },
301+ mousepos: function(e) {
302+ return e.pageX ? {x: e.pageX, y: e.pageY}
303+ : {x: e.x+document.body.scrollLeft, y: e.y+document.body.scrollTop};
304+ },
305+ mousedown: function(evt) {
306+ evt = evt||window.event;
307+ Slideous.mousedownpos = Slideous.mousepos(evt);
308+ Slideous.contentselected = !!Slideous.selectedcontent() || ((evt.target || evt.srcElement).nodeName.toLowerCase() in Slideous.clickables);
309+ return true;
310+ },
311+ mouseup: function(evt) {
312+ evt = evt||window.event;
313+ var pos = Slideous.mousepos(evt);
314+ if (pos.x == Slideous.mousedownpos.x && pos.y == Slideous.mousedownpos.y && !Slideous.contentselected) {
315+ Slideous.next(true);
316+ return evt.returnValue = !(evt.cancel = true);
317+ }
318+ return false;
319+ }
320+};
321+window.onload = Slideous.init;