diff options
author | Alan Pearce | 2020-04-05 12:23:07 +0200 |
---|---|---|
committer | Alan Pearce | 2020-04-05 12:23:07 +0200 |
commit | 1ecfd131cfa4f1cd67ec3a7d396992f0b5e54582 (patch) | |
tree | b9f1b70e54002597af22efc5a44e3a1df71e94b9 | |
parent | b9b3c6e0a8a272f8bab072ca6d51677cae5cfb25 (diff) | |
download | website-1ecfd131cfa4f1cd67ec3a7d396992f0b5e54582.tar.xz website-1ecfd131cfa4f1cd67ec3a7d396992f0b5e54582.zip |
Remove presentation slides
-rw-r--r-- | static/talks/fp-js/index.html | 431 | ||||
-rw-r--r-- | static/talks/fp-js/index.org | 226 | ||||
-rw-r--r-- | static/talks/fp-js/s5-blank.html | 50 | ||||
-rw-r--r-- | static/talks/fp-js/ui/default/blank.gif | bin | 49 -> 0 bytes | |||
-rwxr-xr-x | static/talks/fp-js/ui/default/bodybg.gif | bin | 10119 -> 0 bytes | |||
-rw-r--r-- | static/talks/fp-js/ui/default/framing.css | 23 | ||||
-rw-r--r-- | static/talks/fp-js/ui/default/iepngfix.htc | 42 | ||||
-rw-r--r-- | static/talks/fp-js/ui/default/opera.css | 7 | ||||
-rw-r--r-- | static/talks/fp-js/ui/default/outline.css | 15 | ||||
-rw-r--r-- | static/talks/fp-js/ui/default/pretty.css | 86 | ||||
-rw-r--r-- | static/talks/fp-js/ui/default/print.css | 1 | ||||
-rw-r--r-- | static/talks/fp-js/ui/default/s5-core.css | 9 | ||||
-rw-r--r-- | static/talks/fp-js/ui/default/slides.css | 3 | ||||
-rw-r--r-- | static/talks/fp-js/ui/default/slides.js | 553 |
14 files changed, 0 insertions, 1446 deletions
diff --git a/static/talks/fp-js/index.html b/static/talks/fp-js/index.html deleted file mode 100644 index e90650c..0000000 --- a/static/talks/fp-js/index.html +++ /dev/null | |||
@@ -1,431 +0,0 @@ | |||
1 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" | ||
2 | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
3 | <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> | ||
4 | <head> | ||
5 | <!-- 2017-10-16 Mon 10:10 --> | ||
6 | <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> | ||
7 | <meta name="viewport" content="width=device-width, initial-scale=1" /> | ||
8 | <title>Functional Programming in JavaScript</title> | ||
9 | <meta name="generator" content="Org mode" /> | ||
10 | <meta name="author" content="Alan Pearce" /> | ||
11 | <meta name="version" content="S5 1.2a2" /> | ||
12 | <meta name='defaultView' content='slideshow' /> | ||
13 | <meta name='controlVis' content='hidden' /> | ||
14 | <!-- style sheet links --> | ||
15 | <link rel='stylesheet' href='ui/default/outline.css' type='text/css' media='screen' id='outlineStyle' /> | ||
16 | <link rel='stylesheet' href='ui/default/print.css' type='text/css' media='print' id='slidePrint' /> | ||
17 | <link rel='stylesheet' href='ui/default/opera.css' type='text/css' media='projection' id='operaFix' /> | ||
18 | <link rel='stylesheet' href='ui/default/slides.css' type='text/css' media='screen' id='slideProj' /> | ||
19 | <!-- S5 JS --> | ||
20 | <script src='ui/default/slides.js' type='text/javascript'></script> | ||
21 | |||
22 | |||
23 | </head> | ||
24 | <body> | ||
25 | <div class="layout"> | ||
26 | <div id="controls"><!-- no edit --></div> | ||
27 | <div id="currentSlide"><!-- no edit --></div> | ||
28 | <div id="header" class="status"> | ||
29 |   | ||
30 | </div> | ||
31 | |||
32 | <div id="footer" class="status"> | ||
33 | <h1>Alan Pearce - Functional Programming in JavaScript</h1> | ||
34 | </div> | ||
35 | |||
36 | </div> | ||
37 | <div id="content" class="presentation"> | ||
38 | <div id='title-slide' class='slide'> | ||
39 | <h1>Functional Programming in JavaScript</h1> | ||
40 | <h2></h2> | ||
41 | <h2>Alan Pearce</h2> | ||
42 | <h3><a href="mailto:alan@alanpearce.eu">alan@alanpearce.eu</a></h3> | ||
43 | <h4></h4> | ||
44 | </div> | ||
45 | <div id='table-of-contents' class='slide'> | ||
46 | <h1>Table of Contents</h1> | ||
47 | <div id="text-table-of-contents"> | ||
48 | <ul> | ||
49 | <li>1. Why?</li> | ||
50 | <li>2. Concepts</li> | ||
51 | <li>3. Further concepts</li> | ||
52 | <li>4. First-class functions</li> | ||
53 | <li>5. Higher-order functions</li> | ||
54 | <li>6. Higher-order functions (cont.)</li> | ||
55 | <li>7. Pure functions</li> | ||
56 | <li>8. Recursion</li> | ||
57 | <li>9. Partial application</li> | ||
58 | <li>10. Partial application (cont.)</li> | ||
59 | <li>11. Currying</li> | ||
60 | <li>12. Easy Currying</li> | ||
61 | <li>13. Practical Currying</li> | ||
62 | <li>14. Functional composition</li> | ||
63 | <li>15. Functional composition (cont.)</li> | ||
64 | <li>16. pipe</li> | ||
65 | <li>17. Point-free programming</li> | ||
66 | <li>18. Further Resources</li> | ||
67 | </ul> | ||
68 | </div> | ||
69 | </div> | ||
70 | |||
71 | <div id="outline-container-org05e10f9" class="outline-1 slide"> | ||
72 | <h1 id="org05e10f9"><span class="section-number-1">1</span> Why?</h1> | ||
73 | <div class="outline-text-1" id="text-1"> | ||
74 | <p> | ||
75 | Imperative programming is concerned with <b>how</b> | ||
76 | </p> | ||
77 | |||
78 | <p> | ||
79 | Functional programming is concerned with <b>what</b> | ||
80 | </p> | ||
81 | </div> | ||
82 | </div> | ||
83 | |||
84 | <div id="outline-container-org14fa5d9" class="outline-1 slide"> | ||
85 | <h1 id="org14fa5d9"><span class="section-number-1">2</span> Concepts</h1> | ||
86 | <div class="outline-text-1" id="text-2"> | ||
87 | <ul class="org-ul"> | ||
88 | <li>First-class Functions</li> | ||
89 | <li>Higher-order Functions</li> | ||
90 | <li>Recursion</li> | ||
91 | <li>Pure functions</li> | ||
92 | <li>Currying & Partial Application</li> | ||
93 | </ul> | ||
94 | </div> | ||
95 | </div> | ||
96 | |||
97 | <div id="outline-container-orgd930436" class="outline-1 slide"> | ||
98 | <h1 id="orgd930436"><span class="section-number-1">3</span> Further concepts</h1> | ||
99 | <div class="outline-text-1" id="text-3"> | ||
100 | <ul class="org-ul"> | ||
101 | <li>Lazy Evaluation</li> | ||
102 | <li>Types & Data Structures</li> | ||
103 | <li>Category Theory</li> | ||
104 | </ul> | ||
105 | </div> | ||
106 | </div> | ||
107 | |||
108 | <div id="outline-container-orgbd39891" class="outline-1 slide"> | ||
109 | <h1 id="orgbd39891"><span class="section-number-1">4</span> First-class functions</h1> | ||
110 | <div class="outline-text-1" id="text-4"> | ||
111 | <ul class="org-ul"> | ||
112 | <li>Are values</li> | ||
113 | <li>Have no restriction on their use</li> | ||
114 | <li>Enable the use of callback functions in JavaScript</li> | ||
115 | </ul> | ||
116 | |||
117 | <p> | ||
118 | |||
119 | </p> | ||
120 | |||
121 | <div class="org-src-container"> | ||
122 | <pre><code class="src src-js">var fn = function () { | ||
123 | return 2 | ||
124 | } | ||
125 | </code></pre> | ||
126 | </div> | ||
127 | </div> | ||
128 | </div> | ||
129 | |||
130 | |||
131 | <div id="outline-container-org8d1ee8a" class="outline-1 slide"> | ||
132 | <h1 id="org8d1ee8a"><span class="section-number-1">5</span> Higher-order functions</h1> | ||
133 | <div class="outline-text-1" id="text-5"> | ||
134 | <p> | ||
135 | Functions that operate on other functions are higher-order functions | ||
136 | </p> | ||
137 | |||
138 | <p> | ||
139 | |||
140 | </p> | ||
141 | |||
142 | <div class="org-src-container"> | ||
143 | <pre><code class="src src-js">var succ = function (x) { | ||
144 | return x + 1 | ||
145 | } | ||
146 | |||
147 | var arr = [1, 2, 3, 4] | ||
148 | |||
149 | arr.map(succ) | ||
150 | </code></pre> | ||
151 | </div> | ||
152 | |||
153 | <p> | ||
154 | Here, <code>Array.prototype.map</code> is the higher-order function | ||
155 | </p> | ||
156 | </div> | ||
157 | </div> | ||
158 | |||
159 | <div id="outline-container-org3bbf4ac" class="outline-1 slide"> | ||
160 | <h1 id="org3bbf4ac"><span class="section-number-1">6</span> Higher-order functions (cont.)</h1> | ||
161 | <div class="outline-text-1" id="text-6"> | ||
162 | <p> | ||
163 | Functions that return functions are also higher-order functions | ||
164 | </p> | ||
165 | |||
166 | <p> | ||
167 | |||
168 | </p> | ||
169 | |||
170 | <div class="org-src-container"> | ||
171 | <pre><code class="src src-js">function <span style="font-weight: bold;">adder</span> (n) { | ||
172 | return function (x) { | ||
173 | return n + x | ||
174 | } | ||
175 | } | ||
176 | |||
177 | var add1 = adder(1) | ||
178 | </code></pre> | ||
179 | </div> | ||
180 | |||
181 | <p> | ||
182 | <code>adder</code> is a higher-order function | ||
183 | </p> | ||
184 | </div> | ||
185 | </div> | ||
186 | |||
187 | <div id="outline-container-orgfb6eee2" class="outline-1 slide"> | ||
188 | <h1 id="orgfb6eee2"><span class="section-number-1">7</span> Pure functions</h1> | ||
189 | <div class="outline-text-1" id="text-7"> | ||
190 | <p> | ||
191 | Functions without side-effects | ||
192 | </p> | ||
193 | |||
194 | <div class="org-src-container"> | ||
195 | <pre><code class="src src-js">var succ = (x) => x + 1 | ||
196 | |||
197 | console.log(succ(succ(1))) | ||
198 | |||
199 | <span style="color: #b8b8b8; background-color: #f8f8f8; font-style: italic;">// </span><span style="color: #b8b8b8; background-color: #f8f8f8; font-style: italic;">could be optimised away by a compiler, e.g.:</span> | ||
200 | |||
201 | console.log(3) | ||
202 | </code></pre> | ||
203 | </div> | ||
204 | </div> | ||
205 | </div> | ||
206 | |||
207 | <div id="outline-container-org16e9966" class="outline-1 slide"> | ||
208 | <h1 id="org16e9966"><span class="section-number-1">8</span> Recursion</h1> | ||
209 | <div class="outline-text-1" id="text-8"> | ||
210 | <p> | ||
211 | Functions that call themselves | ||
212 | </p> | ||
213 | |||
214 | <div class="org-src-container"> | ||
215 | <pre><code class="src src-js">function <span style="font-weight: bold;">fibonacci</span> (n) { | ||
216 | switch (n) { | ||
217 | case 0: | ||
218 | case 1: | ||
219 | return 1 | ||
220 | default: | ||
221 | return fibonacci(n - 1) + fibonacci(n - 2) | ||
222 | } | ||
223 | } | ||
224 | </code></pre> | ||
225 | </div> | ||
226 | </div> | ||
227 | </div> | ||
228 | |||
229 | <div id="outline-container-org1b7b0af" class="outline-1 slide"> | ||
230 | <h1 id="org1b7b0af"><span class="section-number-1">9</span> Partial application</h1> | ||
231 | <div class="outline-text-1" id="text-9"> | ||
232 | <p> | ||
233 | The infamous <code>Function.prototype.bind</code> in JavaScript | ||
234 | </p> | ||
235 | |||
236 | <div class="org-src-container"> | ||
237 | <pre><code class="src src-js">function <span style="font-weight: bold;">add</span> (x, y) { | ||
238 | return x + y | ||
239 | } | ||
240 | |||
241 | var add1 = add.bind(add, 1) | ||
242 | |||
243 | add1(3) <span style="color: #b8b8b8; background-color: #f8f8f8; font-style: italic;">// </span><span style="color: #b8b8b8; background-color: #f8f8f8; font-style: italic;">= 4</span> | ||
244 | </code></pre> | ||
245 | </div> | ||
246 | </div> | ||
247 | </div> | ||
248 | |||
249 | <div id="outline-container-org796e6d3" class="outline-1 slide"> | ||
250 | <h1 id="org796e6d3"><span class="section-number-1">10</span> Partial application (cont.)</h1> | ||
251 | <div class="outline-text-1" id="text-10"> | ||
252 | <p> | ||
253 | After ES6 introduced arrow functions, partial application has become | ||
254 | more popular | ||
255 | </p> | ||
256 | |||
257 | <div class="org-src-container"> | ||
258 | <pre><code class="src src-js">var add = x => y => x + y | ||
259 | </code></pre> | ||
260 | </div> | ||
261 | </div> | ||
262 | </div> | ||
263 | |||
264 | <div id="outline-container-orgeb4d67b" class="outline-1 slide"> | ||
265 | <h1 id="orgeb4d67b"><span class="section-number-1">11</span> Currying</h1> | ||
266 | <div class="outline-text-1" id="text-11"> | ||
267 | <p> | ||
268 | Related to partial application, but more implicit and general | ||
269 | </p> | ||
270 | |||
271 | <p> | ||
272 | Translates <b><i>1</i> function of arity <i>n</i></b> to <b><i>n</i> functions of arity <i>1</i></b> | ||
273 | </p> | ||
274 | |||
275 | <div class="org-src-container"> | ||
276 | <pre><code class="src src-js">function <span style="font-weight: bold;">volume</span> (w, d, h) { | ||
277 | return w * d * h | ||
278 | } | ||
279 | |||
280 | var vol = curry(volume) | ||
281 | vol(10)(20)(30) | ||
282 | <span style="color: #b8b8b8; background-color: #f8f8f8; font-style: italic;">// </span><span style="color: #b8b8b8; background-color: #f8f8f8; font-style: italic;">is strictly equivalent to</span> | ||
283 | volume(10, 20, 30) | ||
284 | </code></pre> | ||
285 | </div> | ||
286 | </div> | ||
287 | </div> | ||
288 | |||
289 | <div id="outline-container-orgdfd8353" class="outline-1 slide"> | ||
290 | <h1 id="orgdfd8353"><span class="section-number-1">12</span> Easy Currying</h1> | ||
291 | <div class="outline-text-1" id="text-12"> | ||
292 | <p> | ||
293 | In order to make currying (and partial application) easier to use, | ||
294 | move the <b>most important</b> argument to a function to the end: | ||
295 | </p> | ||
296 | |||
297 | <div class="org-src-container"> | ||
298 | <pre><code class="src src-js">var badMap = (arr, fn) => arr.map(fn) | ||
299 | var goodMap = (fn, arr) => arr.map(fn) | ||
300 | var curriedBadMap = curry(badmap) | ||
301 | var curriedGoodMap = curry(goodMap) | ||
302 | |||
303 | var goodDoubleArray = goodMap(x => x * 2) | ||
304 | var badDoubleArray = badMap(_, x => x * 2) | ||
305 | </code></pre> | ||
306 | </div> | ||
307 | |||
308 | <p> | ||
309 | The bad version requires the curry function to support a magic | ||
310 | placeholder argument and doesn't look as clean. | ||
311 | </p> | ||
312 | </div> | ||
313 | </div> | ||
314 | |||
315 | <div id="outline-container-org30accd5" class="outline-1 slide"> | ||
316 | <h1 id="org30accd5"><span class="section-number-1">13</span> Practical Currying</h1> | ||
317 | <div class="outline-text-1" id="text-13"> | ||
318 | <p> | ||
319 | Currying is not automatic in JavaScript, as in other languages | ||
320 | </p> | ||
321 | |||
322 | <p> | ||
323 | External tools aren't (so far) able to statically analyse curried | ||
324 | functions | ||
325 | </p> | ||
326 | |||
327 | <p> | ||
328 | Solution: Don't expose curried functions | ||
329 | Instead, write functions as if currying were automatic | ||
330 | </p> | ||
331 | |||
332 | <p> | ||
333 | If consumers want to curry, they can. If they don't, their editor or | ||
334 | language server will show them the arguments | ||
335 | </p> | ||
336 | </div> | ||
337 | </div> | ||
338 | |||
339 | <div id="outline-container-org5f352dc" class="outline-1 slide"> | ||
340 | <h1 id="org5f352dc"><span class="section-number-1">14</span> Functional composition</h1> | ||
341 | <div class="outline-text-1" id="text-14"> | ||
342 | <p> | ||
343 | Creating functions from other functions | ||
344 | </p> | ||
345 | |||
346 | <p> | ||
347 | Usually provided by <code>compose</code> (right-to-left) and <code>pipe</code> (left-to-right) | ||
348 | </p> | ||
349 | |||
350 | <p> | ||
351 | A very simple definition of <code>compose</code> for only two functions would look like this | ||
352 | </p> | ||
353 | |||
354 | <div class="org-src-container"> | ||
355 | <pre><code class="src src-js">function <span style="font-weight: bold;">compose</span> (f, g) { | ||
356 | return function (...args) { | ||
357 | return f(g(...args)) | ||
358 | } | ||
359 | } | ||
360 | </code></pre> | ||
361 | </div> | ||
362 | </div> | ||
363 | </div> | ||
364 | |||
365 | <div id="outline-container-org0c2c4f9" class="outline-1 slide"> | ||
366 | <h1 id="org0c2c4f9"><span class="section-number-1">15</span> Functional composition (cont.)</h1> | ||
367 | <div class="outline-text-1" id="text-15"> | ||
368 | <div class="org-src-container"> | ||
369 | <pre><code class="src src-js">var plusOne = x => x + 1 | ||
370 | var timesTwo = x => x * 2 | ||
371 | |||
372 | var plusOneTimesTwo = compose(timesTwo, plusOne) | ||
373 | var timesTwoPlusOne = compose(plusOne, timesTwo) | ||
374 | |||
375 | nextDoubled(3) <span style="color: #b8b8b8; background-color: #f8f8f8; font-style: italic;">// </span><span style="color: #b8b8b8; background-color: #f8f8f8; font-style: italic;">= (3 + 1) * 2 = 8</span> | ||
376 | doubledPlusOne(3) <span style="color: #b8b8b8; background-color: #f8f8f8; font-style: italic;">// </span><span style="color: #b8b8b8; background-color: #f8f8f8; font-style: italic;">= (3 * 2) + 1 = 7</span> | ||
377 | </code></pre> | ||
378 | </div> | ||
379 | </div> | ||
380 | </div> | ||
381 | |||
382 | <div id="outline-container-orgddc61f6" class="outline-1 slide"> | ||
383 | <h1 id="orgddc61f6"><span class="section-number-1">16</span> pipe</h1> | ||
384 | <div class="outline-text-1" id="text-16"> | ||
385 | <p> | ||
386 | What about <code>pipe</code>? | ||
387 | </p> | ||
388 | |||
389 | <p> | ||
390 | <code>pipe</code> does the same thing, but runs the functions the other way around | ||
391 | </p> | ||
392 | |||
393 | <p> | ||
394 | <code>pipe(f, g)</code> is the same as <code>compose(g, f)</code> | ||
395 | </p> | ||
396 | </div> | ||
397 | </div> | ||
398 | |||
399 | <div id="outline-container-org7426c58" class="outline-1 slide"> | ||
400 | <h1 id="org7426c58"><span class="section-number-1">17</span> Point-free programming</h1> | ||
401 | <div class="outline-text-1" id="text-17"> | ||
402 | <p> | ||
403 | With currying and higher-order functions, we (often) don't need to declare function arguments | ||
404 | </p> | ||
405 | |||
406 | <div class="org-src-container"> | ||
407 | <pre><code class="src src-js">var modulo = a => b => b % a | ||
408 | var eq = a => b => a === b | ||
409 | |||
410 | var isEven = x => eq(0)(modulo(2)(x)) | ||
411 | var isEvenPointFree = compose(eq(0), modulo(2)) | ||
412 | </code></pre> | ||
413 | </div> | ||
414 | </div> | ||
415 | </div> | ||
416 | |||
417 | <div id="outline-container-org8868cfb" class="outline-1 slide"> | ||
418 | <h1 id="org8868cfb"><span class="section-number-1">18</span> Further Resources</h1> | ||
419 | <div class="outline-text-1" id="text-18"> | ||
420 | <ul class="org-ul"> | ||
421 | <li><a href="https://drboolean.gitbooks.io/mostly-adequate-guide/content/">Mostly adequate guide to FP (in javascript)</a></li> | ||
422 | <li><a href="http://ramdajs.com/">Ramda</a>, a general-purpose FP library</li> | ||
423 | <li><a href="https://sanctuary.js.org/">Sanctuary</a>, a JavaScript library for Haskellers</li> | ||
424 | </ul> | ||
425 | </div> | ||
426 | </div> | ||
427 | |||
428 | |||
429 | </div> | ||
430 | </body> | ||
431 | </html> | ||
diff --git a/static/talks/fp-js/index.org b/static/talks/fp-js/index.org deleted file mode 100644 index 8a4bf6c..0000000 --- a/static/talks/fp-js/index.org +++ /dev/null | |||
@@ -1,226 +0,0 @@ | |||
1 | #+TITLE: Functional Programming in JavaScript | ||
2 | #+PROPERTY: :html-toplevel-hlevel 1 | ||
3 | #+PROPERTY: :with-toc 0 | ||
4 | * Why? | ||
5 | |||
6 | Imperative programming is concerned with *how* | ||
7 | |||
8 | Functional programming is concerned with *what* | ||
9 | |||
10 | * Concepts | ||
11 | |||
12 | - First-class Functions | ||
13 | - Higher-order Functions | ||
14 | - Recursion | ||
15 | - Pure functions | ||
16 | - Currying & Partial Application | ||
17 | |||
18 | * Further concepts | ||
19 | |||
20 | - Lazy Evaluation | ||
21 | - Types & Data Structures | ||
22 | - Category Theory | ||
23 | |||
24 | * First-class functions | ||
25 | |||
26 | - Have no restriction on their use | ||
27 | - Are values | ||
28 | - Enable the use of callback functions in JavaScript | ||
29 | |||
30 | |||
31 | |||
32 | #+BEGIN_SRC js | ||
33 | const fn = function () { | ||
34 | return 2 | ||
35 | } | ||
36 | #+END_SRC | ||
37 | |||
38 | |||
39 | * Higher-order functions | ||
40 | |||
41 | Functions that operate on other functions are higher-order functions | ||
42 | |||
43 | |||
44 | |||
45 | #+BEGIN_SRC js | ||
46 | const succ = function (x) { | ||
47 | return x + 1 | ||
48 | } | ||
49 | |||
50 | const arr = [1, 2, 3, 4] | ||
51 | |||
52 | arr.map(succ) | ||
53 | #+END_SRC | ||
54 | |||
55 | Here, =Array.prototype.map= is the higher-order function | ||
56 | |||
57 | * Higher-order functions (cont.) | ||
58 | |||
59 | Functions that return functions are also higher-order functions | ||
60 | |||
61 | |||
62 | |||
63 | #+BEGIN_SRC js | ||
64 | function adder (n) { | ||
65 | return function (x) { | ||
66 | return n + x | ||
67 | } | ||
68 | } | ||
69 | |||
70 | const add1 = adder(1) | ||
71 | #+END_SRC | ||
72 | |||
73 | =adder= is a higher-order function | ||
74 | |||
75 | * Pure functions | ||
76 | |||
77 | Functions without side-effects | ||
78 | |||
79 | #+BEGIN_SRC js | ||
80 | const succ = (x) => x + 1 | ||
81 | |||
82 | console.log(succ(succ(1))) | ||
83 | |||
84 | // could be optimised away by a compiler, e.g.: | ||
85 | |||
86 | console.log(3) | ||
87 | #+END_SRC | ||
88 | |||
89 | * Recursion | ||
90 | |||
91 | Functions that call themselves | ||
92 | |||
93 | #+BEGIN_SRC js | ||
94 | function fibonacci (n) { | ||
95 | switch (n) { | ||
96 | case 0: | ||
97 | case 1: | ||
98 | return 1 | ||
99 | default: | ||
100 | return fibonacci(n - 1) + fibonacci(n - 2) | ||
101 | } | ||
102 | } | ||
103 | #+END_SRC | ||
104 | |||
105 | * Partial application | ||
106 | |||
107 | The infamous =Function.prototype.bind= in JavaScript | ||
108 | |||
109 | #+BEGIN_SRC js | ||
110 | function add (x, y) { | ||
111 | return x + y | ||
112 | } | ||
113 | |||
114 | const add1 = add.bind(add, 1) | ||
115 | |||
116 | add1(3) // = 4 | ||
117 | #+END_SRC | ||
118 | |||
119 | * Partial application (cont.) | ||
120 | |||
121 | After ES6 introduced arrow functions, partial application has become | ||
122 | more popular | ||
123 | |||
124 | #+BEGIN_SRC js | ||
125 | const add = x => y => x + y | ||
126 | #+END_SRC | ||
127 | |||
128 | * Currying | ||
129 | |||
130 | Related to partial application, but more implicit and general | ||
131 | |||
132 | Translates */1/ function of arity /n/* to */n/ functions of arity /1/* | ||
133 | |||
134 | #+BEGIN_SRC js | ||
135 | function volume (w, d, h) { | ||
136 | return w * d * h | ||
137 | } | ||
138 | |||
139 | const vol = curry(volume) | ||
140 | vol(10)(20)(30) | ||
141 | // is strictly equivalent to | ||
142 | volume(10, 20, 30) | ||
143 | #+END_SRC | ||
144 | |||
145 | * Easy Currying | ||
146 | |||
147 | In order to make currying (and partial application) easier to use, | ||
148 | move the *most important* argument to a function to the end: | ||
149 | |||
150 | #+BEGIN_SRC js | ||
151 | const badMap = (arr, fn) => arr.map(fn) | ||
152 | const goodMap = (fn, arr) => arr.map(fn) | ||
153 | const curriedBadMap = curry(badmap) | ||
154 | const curriedGoodMap = curry(goodMap) | ||
155 | |||
156 | const goodDoubleArray = goodMap(x => x * 2) | ||
157 | const badDoubleArray = badMap(_, x => x * 2) | ||
158 | #+END_SRC | ||
159 | |||
160 | The bad version requires the curry function to support a magic | ||
161 | placeholder argument and doesn't look as clean. | ||
162 | |||
163 | * Practical Currying | ||
164 | |||
165 | Currying is not automatic in JavaScript, as in other languages | ||
166 | |||
167 | External tools don't (currently) to statically analyse curried | ||
168 | functions | ||
169 | |||
170 | Solution: Don't expose curried functions | ||
171 | Instead, write functions as if currying were automatic | ||
172 | |||
173 | * Functional composition | ||
174 | |||
175 | Creating functions from other functions | ||
176 | |||
177 | Usually provided by =compose= (right-to-left) and =pipe= (left-to-right) | ||
178 | |||
179 | A very simple definition of =compose= for only two functions would look like this | ||
180 | |||
181 | #+BEGIN_SRC js | ||
182 | function compose (f, g) { | ||
183 | return function (...args) { | ||
184 | return f(g(...args)) | ||
185 | } | ||
186 | } | ||
187 | #+END_SRC | ||
188 | |||
189 | * Functional composition (cont.) | ||
190 | |||
191 | #+BEGIN_SRC js | ||
192 | const plusOne = x => x + 1 | ||
193 | const timesTwo = x => x * 2 | ||
194 | |||
195 | const plusOneTimesTwo = compose(timesTwo, plusOne) | ||
196 | const timesTwoPlusOne = compose(plusOne, timesTwo) | ||
197 | |||
198 | nextDoubled(3) // = (3 + 1) * 2 = 8 | ||
199 | timesTwoPlusOne(3) // = (3 * 2) + 1 = 7 | ||
200 | #+END_SRC | ||
201 | |||
202 | * pipe | ||
203 | |||
204 | What about =pipe=? | ||
205 | |||
206 | =pipe= does the same thing, but runs the functions the other way around | ||
207 | |||
208 | =pipe(f, g)= is the same as =compose(g, f)= | ||
209 | |||
210 | * Point-free programming | ||
211 | |||
212 | With currying and higher-order functions, we (often) don't need to declare function arguments | ||
213 | |||
214 | #+BEGIN_SRC js | ||
215 | const modulo = a => b => b % a | ||
216 | const eq = a => b => a === b | ||
217 | |||
218 | const isEven = x => eq(0)(modulo(2)(x)) | ||
219 | const isEvenPointFree = compose(eq(0), modulo(2)) | ||
220 | #+END_SRC | ||
221 | |||
222 | * Further Resources | ||
223 | |||
224 | - [[https://drboolean.gitbooks.io/mostly-adequate-guide/content/][Mostly adequate guide to FP (in javascript)]] | ||
225 | - [[http://ramdajs.com/][Ramda]], a general-purpose FP library | ||
226 | - [[https://sanctuary.js.org/][Sanctuary]], a JavaScript library for Haskellers | ||
diff --git a/static/talks/fp-js/s5-blank.html b/static/talks/fp-js/s5-blank.html deleted file mode 100644 index 0d126c7..0000000 --- a/static/talks/fp-js/s5-blank.html +++ /dev/null | |||
@@ -1,50 +0,0 @@ | |||
1 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" | ||
2 | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
3 | |||
4 | <html xmlns="http://www.w3.org/1999/xhtml"> | ||
5 | |||
6 | <head> | ||
7 | <title>Functional Programming</title> | ||
8 | <!-- metadata --> | ||
9 | <meta name="generator" content="S5" /> | ||
10 | <meta name="version" content="S5 1.1" /> | ||
11 | <meta name="presdate" content="20050728" /> | ||
12 | <meta name="author" content="Eric A. Meyer" /> | ||
13 | <meta name="company" content="Complex Spiral Consulting" /> | ||
14 | <!-- configuration parameters --> | ||
15 | <meta name="defaultView" content="slideshow" /> | ||
16 | <meta name="controlVis" content="hidden" /> | ||
17 | <!-- style sheet links --> | ||
18 | <link rel="stylesheet" href="ui/default/slides.css" type="text/css" media="projection" id="slideProj" /> | ||
19 | <link rel="stylesheet" href="ui/default/outline.css" type="text/css" media="screen" id="outlineStyle" /> | ||
20 | <link rel="stylesheet" href="ui/default/print.css" type="text/css" media="print" id="slidePrint" /> | ||
21 | <link rel="stylesheet" href="ui/default/opera.css" type="text/css" media="projection" id="operaFix" /> | ||
22 | <!-- S5 JS --> | ||
23 | <script src="ui/default/slides.js" type="text/javascript"></script> | ||
24 | </head> | ||
25 | <body> | ||
26 | |||
27 | <div class="layout"> | ||
28 | <div id="controls"><!-- DO NOT EDIT --></div> | ||
29 | <div id="currentSlide"><!-- DO NOT EDIT --></div> | ||
30 | <div id="header"></div> | ||
31 | <div id="footer"> | ||
32 | <h1>Functional Programming</h1> | ||
33 | </div> | ||
34 | |||
35 | </div> | ||
36 | |||
37 | |||
38 | <div class="presentation"> | ||
39 | |||
40 | <div class="slide"> | ||
41 | <h1>Functional Programming</h1> | ||
42 | <h2>in JavaScript</h2> | ||
43 | <h3>Alan Pearce</h3> | ||
44 | </div> | ||
45 | |||
46 | |||
47 | </div> | ||
48 | |||
49 | </body> | ||
50 | </html> | ||
diff --git a/static/talks/fp-js/ui/default/blank.gif b/static/talks/fp-js/ui/default/blank.gif deleted file mode 100644 index 75b945d..0000000 --- a/static/talks/fp-js/ui/default/blank.gif +++ /dev/null | |||
Binary files differ | |||
diff --git a/static/talks/fp-js/ui/default/bodybg.gif b/static/talks/fp-js/ui/default/bodybg.gif deleted file mode 100755 index 5f448a1..0000000 --- a/static/talks/fp-js/ui/default/bodybg.gif +++ /dev/null | |||
Binary files differ | |||
diff --git a/static/talks/fp-js/ui/default/framing.css b/static/talks/fp-js/ui/default/framing.css deleted file mode 100644 index 14d8509..0000000 --- a/static/talks/fp-js/ui/default/framing.css +++ /dev/null | |||
@@ -1,23 +0,0 @@ | |||
1 | /* The following styles size, place, and layer the slide components. | ||
2 | Edit these if you want to change the overall slide layout. | ||
3 | The commented lines can be uncommented (and modified, if necessary) | ||
4 | to help you with the rearrangement process. */ | ||
5 | |||
6 | /* target = 1024x768 */ | ||
7 | |||
8 | div#header, div#footer, .slide {width: 100%; top: 0; left: 0;} | ||
9 | div#header {top: 0; height: 3em; z-index: 1;} | ||
10 | div#footer {top: auto; bottom: 0; height: 2.5em; z-index: 5;} | ||
11 | .slide {top: 0; width: 92%; padding: 3.5em 4% 4%; z-index: 2; list-style: none;} | ||
12 | div#controls {left: 50%; bottom: 0; width: 50%; z-index: 100;} | ||
13 | div#controls form {position: absolute; bottom: 0; right: 0; width: 100%; | ||
14 | margin: 0;} | ||
15 | #currentSlide {position: absolute; width: 10%; left: 45%; bottom: 1em; z-index: 10;} | ||
16 | html>body #currentSlide {position: fixed;} | ||
17 | |||
18 | /* | ||
19 | div#header {background: #FCC;} | ||
20 | div#footer {background: #CCF;} | ||
21 | div#controls {background: #BBD;} | ||
22 | div#currentSlide {background: #FFC;} | ||
23 | */ | ||
diff --git a/static/talks/fp-js/ui/default/iepngfix.htc b/static/talks/fp-js/ui/default/iepngfix.htc deleted file mode 100644 index bba2db7..0000000 --- a/static/talks/fp-js/ui/default/iepngfix.htc +++ /dev/null | |||
@@ -1,42 +0,0 @@ | |||
1 | <public:component> | ||
2 | <public:attach event="onpropertychange" onevent="doFix()" /> | ||
3 | |||
4 | <script> | ||
5 | |||
6 | // IE5.5+ PNG Alpha Fix v1.0 by Angus Turnbull http://www.twinhelix.com | ||
7 | // Free usage permitted as long as this notice remains intact. | ||
8 | |||
9 | // This must be a path to a blank image. That's all the configuration you need here. | ||
10 | var blankImg = 'ui/default/blank.gif'; | ||
11 | |||
12 | var f = 'DXImageTransform.Microsoft.AlphaImageLoader'; | ||
13 | |||
14 | function filt(s, m) { | ||
15 | if (filters[f]) { | ||
16 | filters[f].enabled = s ? true : false; | ||
17 | if (s) with (filters[f]) { src = s; sizingMethod = m } | ||
18 | } else if (s) style.filter = 'progid:'+f+'(src="'+s+'",sizingMethod="'+m+'")'; | ||
19 | } | ||
20 | |||
21 | function doFix() { | ||
22 | if ((parseFloat(navigator.userAgent.match(/MSIE (\S+)/)[1]) < 5.5) || | ||
23 | (event && !/(background|src)/.test(event.propertyName))) return; | ||
24 | |||
25 | if (tagName == 'IMG') { | ||
26 | if ((/\.png$/i).test(src)) { | ||
27 | filt(src, 'image'); // was 'scale' | ||
28 | src = blankImg; | ||
29 | } else if (src.indexOf(blankImg) < 0) filt(); | ||
30 | } else if (style.backgroundImage) { | ||
31 | if (style.backgroundImage.match(/^url[("']+(.*\.png)[)"']+$/i)) { | ||
32 | var s = RegExp.$1; | ||
33 | style.backgroundImage = ''; | ||
34 | filt(s, 'crop'); | ||
35 | } else filt(); | ||
36 | } | ||
37 | } | ||
38 | |||
39 | doFix(); | ||
40 | |||
41 | </script> | ||
42 | </public:component> \ No newline at end of file | ||
diff --git a/static/talks/fp-js/ui/default/opera.css b/static/talks/fp-js/ui/default/opera.css deleted file mode 100644 index 9e9d2a3..0000000 --- a/static/talks/fp-js/ui/default/opera.css +++ /dev/null | |||
@@ -1,7 +0,0 @@ | |||
1 | /* DO NOT CHANGE THESE unless you really want to break Opera Show */ | ||
2 | .slide { | ||
3 | visibility: visible !important; | ||
4 | position: static !important; | ||
5 | page-break-before: always; | ||
6 | } | ||
7 | #slide0 {page-break-before: avoid;} | ||
diff --git a/static/talks/fp-js/ui/default/outline.css b/static/talks/fp-js/ui/default/outline.css deleted file mode 100644 index 62db519..0000000 --- a/static/talks/fp-js/ui/default/outline.css +++ /dev/null | |||
@@ -1,15 +0,0 @@ | |||
1 | /* don't change this unless you want the layout stuff to show up in the outline view! */ | ||
2 | |||
3 | .layout div, #footer *, #controlForm * {display: none;} | ||
4 | #footer, #controls, #controlForm, #navLinks, #toggle { | ||
5 | display: block; visibility: visible; margin: 0; padding: 0;} | ||
6 | #toggle {float: right; padding: 0.5em;} | ||
7 | html>body #toggle {position: fixed; top: 0; right: 0;} | ||
8 | |||
9 | /* making the outline look pretty-ish */ | ||
10 | |||
11 | #slide0 h1, #slide0 h2, #slide0 h3, #slide0 h4 {border: none; margin: 0;} | ||
12 | #slide0 h1 {padding-top: 1.5em;} | ||
13 | .slide h1 {margin: 1.5em 0 0; padding-top: 0.25em; | ||
14 | border-top: 1px solid #888; border-bottom: 1px solid #AAA;} | ||
15 | #toggle {border: 1px solid; border-width: 0 0 1px 1px; background: #FFF;} | ||
diff --git a/static/talks/fp-js/ui/default/pretty.css b/static/talks/fp-js/ui/default/pretty.css deleted file mode 100644 index 3d3acef..0000000 --- a/static/talks/fp-js/ui/default/pretty.css +++ /dev/null | |||
@@ -1,86 +0,0 @@ | |||
1 | /* Following are the presentation styles -- edit away! */ | ||
2 | |||
3 | body {background: #FFF url(bodybg.gif) -16px 0 no-repeat; color: #000; font-size: 2em;} | ||
4 | :link, :visited {text-decoration: none; color: #00C;} | ||
5 | #controls :active {color: #88A !important;} | ||
6 | #controls :focus {outline: 1px dotted #227;} | ||
7 | h1, h2, h3, h4 {font-size: 100%; margin: 0; padding: 0; font-weight: inherit;} | ||
8 | ul, pre {margin: 0; line-height: 1em;} | ||
9 | html, body {margin: 0; padding: 0;} | ||
10 | |||
11 | blockquote, q {font-style: italic;} | ||
12 | blockquote {padding: 0 2em 0.5em; margin: 0 1.5em 0.5em; text-align: center; font-size: 1em;} | ||
13 | blockquote p {margin: 0;} | ||
14 | blockquote i {font-style: normal;} | ||
15 | blockquote b {display: block; margin-top: 0.5em; font-weight: normal; font-size: smaller; font-style: normal;} | ||
16 | blockquote b i {font-style: italic;} | ||
17 | |||
18 | kbd {font-weight: bold; font-size: 1em;} | ||
19 | sup {font-size: smaller; line-height: 1px;} | ||
20 | |||
21 | .slide code {padding: 2px 0.25em; font-weight: bold; color: #533;} | ||
22 | .slide code.bad, code del {color: red;} | ||
23 | .slide code.old {color: silver;} | ||
24 | .slide pre {padding: 0; margin: 0.25em 0 0.5em 0.5em; color: #533; font-size: 90%;} | ||
25 | .slide pre code {display: block;} | ||
26 | .slide ul {margin-left: 5%; margin-right: 7%; list-style: disc;} | ||
27 | .slide li {margin-top: 0.75em; margin-right: 0;} | ||
28 | .slide ul ul {line-height: 1;} | ||
29 | .slide ul ul li {margin: .2em; font-size: 85%; list-style: square;} | ||
30 | .slide img.leader {display: block; margin: 0 auto;} | ||
31 | |||
32 | div#header, div#footer {background: #005; color: #AAB; | ||
33 | font-family: Verdana, Helvetica, sans-serif;} | ||
34 | div#header {background: #005 url(bodybg.gif) -16px 0 no-repeat; | ||
35 | line-height: 1px;} | ||
36 | div#footer {font-size: 0.5em; font-weight: bold; padding: 1em 0;} | ||
37 | #footer h1, #footer h2 {display: block; padding: 0 1em;} | ||
38 | #footer h2 {font-style: italic;} | ||
39 | |||
40 | div.long {font-size: 0.75em;} | ||
41 | .slide h1 {position: absolute; top: 0.7em; left: 87px; z-index: 1; | ||
42 | margin: 0; padding: 0.3em 0 0 50px; white-space: nowrap; | ||
43 | font: bold 150%/1em Helvetica, sans-serif; text-transform: capitalize; | ||
44 | color: #DDE; background: #005;} | ||
45 | .slide h3 {font-size: 130%;} | ||
46 | h1 abbr {font-variant: small-caps;} | ||
47 | |||
48 | div#controls {position: absolute; left: 50%; bottom: 0; | ||
49 | width: 50%; | ||
50 | text-align: right; font: bold 0.9em Verdana, Helvetica, sans-serif;} | ||
51 | html>body div#controls {position: fixed; padding: 0 0 1em 0; | ||
52 | top: auto;} | ||
53 | div#controls form {position: absolute; bottom: 0; right: 0; width: 100%; | ||
54 | margin: 0; padding: 0;} | ||
55 | #controls #navLinks a {padding: 0; margin: 0 0.5em; | ||
56 | background: #005; border: none; color: #779; | ||
57 | cursor: pointer;} | ||
58 | #controls #navList {height: 1em;} | ||
59 | #controls #navList #jumplist {position: absolute; bottom: 0; right: 0; background: #DDD; color: #227;} | ||
60 | |||
61 | #currentSlide {text-align: center; font-size: 0.5em; color: #449;} | ||
62 | |||
63 | #slide0 {padding-top: 3.5em; font-size: 90%;} | ||
64 | #slide0 h1 {position: static; margin: 1em 0 0; padding: 0; | ||
65 | font: bold 2em Helvetica, sans-serif; white-space: normal; | ||
66 | color: #000; background: transparent;} | ||
67 | #slide0 h2 {font: bold italic 1em Helvetica, sans-serif; margin: 0.25em;} | ||
68 | #slide0 h3 {margin-top: 1.5em; font-size: 1.5em;} | ||
69 | #slide0 h4 {margin-top: 0; font-size: 1em;} | ||
70 | |||
71 | ul.urls {list-style: none; display: inline; margin: 0;} | ||
72 | .urls li {display: inline; margin: 0;} | ||
73 | .note {display: none;} | ||
74 | .external {border-bottom: 1px dotted gray;} | ||
75 | html>body .external {border-bottom: none;} | ||
76 | .external:after {content: " \274F"; font-size: smaller; color: #77B;} | ||
77 | |||
78 | .incremental, .incremental *, .incremental *:after {color: #DDE; visibility: visible;} | ||
79 | img.incremental {visibility: hidden;} | ||
80 | .slide .current {color: #B02;} | ||
81 | |||
82 | |||
83 | /* diagnostics | ||
84 | |||
85 | li:after {content: " [" attr(class) "]"; color: #F88;} | ||
86 | */ \ No newline at end of file | ||
diff --git a/static/talks/fp-js/ui/default/print.css b/static/talks/fp-js/ui/default/print.css deleted file mode 100644 index e7a71d1..0000000 --- a/static/talks/fp-js/ui/default/print.css +++ /dev/null | |||
@@ -1 +0,0 @@ | |||
1 | /* The following rule is necessary to have all slides appear in print! DO NOT REMOVE IT! */ .slide, ul {page-break-inside: avoid; visibility: visible !important;} h1 {page-break-after: avoid;} body {font-size: 12pt; background: white;} * {color: black;} #slide0 h1 {font-size: 200%; border: none; margin: 0.5em 0 0.25em;} #slide0 h3 {margin: 0; padding: 0;} #slide0 h4 {margin: 0 0 0.5em; padding: 0;} #slide0 {margin-bottom: 3em;} h1 {border-top: 2pt solid gray; border-bottom: 1px dotted silver;} .extra {background: transparent !important;} div.extra, pre.extra, .example {font-size: 10pt; color: #333;} ul.extra a {font-weight: bold;} p.example {display: none;} #header {display: none;} #footer h1 {margin: 0; border-bottom: 1px solid; color: gray; font-style: italic;} #footer h2, #controls {display: none;} /* The following rule keeps the layout stuff out of print. Remove at your own risk! */ .layout, .layout * {display: none !important;} \ No newline at end of file | ||
diff --git a/static/talks/fp-js/ui/default/s5-core.css b/static/talks/fp-js/ui/default/s5-core.css deleted file mode 100644 index 86444e0..0000000 --- a/static/talks/fp-js/ui/default/s5-core.css +++ /dev/null | |||
@@ -1,9 +0,0 @@ | |||
1 | /* Do not edit or override these styles! The system will likely break if you do. */ | ||
2 | |||
3 | div#header, div#footer, div#controls, .slide {position: absolute;} | ||
4 | html>body div#header, html>body div#footer, | ||
5 | html>body div#controls, html>body .slide {position: fixed;} | ||
6 | .handout {display: none;} | ||
7 | .layout {display: block;} | ||
8 | .slide, .hideme, .incremental {visibility: hidden;} | ||
9 | #slide0 {visibility: visible;} | ||
diff --git a/static/talks/fp-js/ui/default/slides.css b/static/talks/fp-js/ui/default/slides.css deleted file mode 100644 index 0786d7d..0000000 --- a/static/talks/fp-js/ui/default/slides.css +++ /dev/null | |||
@@ -1,3 +0,0 @@ | |||
1 | @import url(s5-core.css); /* required to make the slide show run at all */ | ||
2 | @import url(framing.css); /* sets basic placement and size of slide components */ | ||
3 | @import url(pretty.css); /* stuff that makes the slides look better than blah */ \ No newline at end of file | ||
diff --git a/static/talks/fp-js/ui/default/slides.js b/static/talks/fp-js/ui/default/slides.js deleted file mode 100644 index 38fe853..0000000 --- a/static/talks/fp-js/ui/default/slides.js +++ /dev/null | |||
@@ -1,553 +0,0 @@ | |||
1 | // S5 v1.1 slides.js -- released into the Public Domain | ||
2 | // | ||
3 | // Please see http://www.meyerweb.com/eric/tools/s5/credits.html for information | ||
4 | // about all the wonderful and talented contributors to this code! | ||
5 | |||
6 | var undef; | ||
7 | var slideCSS = ''; | ||
8 | var snum = 0; | ||
9 | var smax = 1; | ||
10 | var incpos = 0; | ||
11 | var number = undef; | ||
12 | var s5mode = true; | ||
13 | var defaultView = 'slideshow'; | ||
14 | var controlVis = 'visible'; | ||
15 | |||
16 | var isIE = navigator.appName == 'Microsoft Internet Explorer' && navigator.userAgent.indexOf('Opera') < 1 ? 1 : 0; | ||
17 | var isOp = navigator.userAgent.indexOf('Opera') > -1 ? 1 : 0; | ||
18 | var isGe = navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('Safari') < 1 ? 1 : 0; | ||
19 | |||
20 | function hasClass(object, className) { | ||
21 | if (!object.className) return false; | ||
22 | return (object.className.search('(^|\\s)' + className + '(\\s|$)') != -1); | ||
23 | } | ||
24 | |||
25 | function hasValue(object, value) { | ||
26 | if (!object) return false; | ||
27 | return (object.search('(^|\\s)' + value + '(\\s|$)') != -1); | ||
28 | } | ||
29 | |||
30 | function removeClass(object,className) { | ||
31 | if (!object) return; | ||
32 | object.className = object.className.replace(new RegExp('(^|\\s)'+className+'(\\s|$)'), RegExp.$1+RegExp.$2); | ||
33 | } | ||
34 | |||
35 | function addClass(object,className) { | ||
36 | if (!object || hasClass(object, className)) return; | ||
37 | if (object.className) { | ||
38 | object.className += ' '+className; | ||
39 | } else { | ||
40 | object.className = className; | ||
41 | } | ||
42 | } | ||
43 | |||
44 | function GetElementsWithClassName(elementName,className) { | ||
45 | var allElements = document.getElementsByTagName(elementName); | ||
46 | var elemColl = new Array(); | ||
47 | for (var i = 0; i< allElements.length; i++) { | ||
48 | if (hasClass(allElements[i], className)) { | ||
49 | elemColl[elemColl.length] = allElements[i]; | ||
50 | } | ||
51 | } | ||
52 | return elemColl; | ||
53 | } | ||
54 | |||
55 | function isParentOrSelf(element, id) { | ||
56 | if (element == null || element.nodeName=='BODY') return false; | ||
57 | else if (element.id == id) return true; | ||
58 | else return isParentOrSelf(element.parentNode, id); | ||
59 | } | ||
60 | |||
61 | function nodeValue(node) { | ||
62 | var result = ""; | ||
63 | if (node.nodeType == 1) { | ||
64 | var children = node.childNodes; | ||
65 | for (var i = 0; i < children.length; ++i) { | ||
66 | result += nodeValue(children[i]); | ||
67 | } | ||
68 | } | ||
69 | else if (node.nodeType == 3) { | ||
70 | result = node.nodeValue; | ||
71 | } | ||
72 | return(result); | ||
73 | } | ||
74 | |||
75 | function slideLabel() { | ||
76 | var slideColl = GetElementsWithClassName('*','slide'); | ||
77 | var list = document.getElementById('jumplist'); | ||
78 | smax = slideColl.length; | ||
79 | for (var n = 0; n < smax; n++) { | ||
80 | var obj = slideColl[n]; | ||
81 | |||
82 | var did = 'slide' + n.toString(); | ||
83 | obj.setAttribute('id',did); | ||
84 | if (isOp) continue; | ||
85 | |||
86 | var otext = ''; | ||
87 | var menu = obj.firstChild; | ||
88 | if (!menu) continue; // to cope with empty slides | ||
89 | while (menu && menu.nodeType == 3) { | ||
90 | menu = menu.nextSibling; | ||
91 | } | ||
92 | if (!menu) continue; // to cope with slides with only text nodes | ||
93 | |||
94 | var menunodes = menu.childNodes; | ||
95 | for (var o = 0; o < menunodes.length; o++) { | ||
96 | otext += nodeValue(menunodes[o]); | ||
97 | } | ||
98 | list.options[list.length] = new Option(n + ' : ' + otext, n); | ||
99 | } | ||
100 | } | ||
101 | |||
102 | function currentSlide() { | ||
103 | var cs; | ||
104 | if (document.getElementById) { | ||
105 | cs = document.getElementById('currentSlide'); | ||
106 | } else { | ||
107 | cs = document.currentSlide; | ||
108 | } | ||
109 | cs.innerHTML = '<span id="csHere">' + snum + '<\/span> ' + | ||
110 | '<span id="csSep">\/<\/span> ' + | ||
111 | '<span id="csTotal">' + (smax-1) + '<\/span>'; | ||
112 | if (snum == 0) { | ||
113 | cs.style.visibility = 'hidden'; | ||
114 | } else { | ||
115 | cs.style.visibility = 'visible'; | ||
116 | } | ||
117 | } | ||
118 | |||
119 | function go(step) { | ||
120 | if (document.getElementById('slideProj').disabled || step == 0) return; | ||
121 | var jl = document.getElementById('jumplist'); | ||
122 | var cid = 'slide' + snum; | ||
123 | var ce = document.getElementById(cid); | ||
124 | if (incrementals[snum].length > 0) { | ||
125 | for (var i = 0; i < incrementals[snum].length; i++) { | ||
126 | removeClass(incrementals[snum][i], 'current'); | ||
127 | removeClass(incrementals[snum][i], 'incremental'); | ||
128 | } | ||
129 | } | ||
130 | if (step != 'j') { | ||
131 | snum += step; | ||
132 | lmax = smax - 1; | ||
133 | if (snum > lmax) snum = lmax; | ||
134 | if (snum < 0) snum = 0; | ||
135 | } else | ||
136 | snum = parseInt(jl.value); | ||
137 | var nid = 'slide' + snum; | ||
138 | var ne = document.getElementById(nid); | ||
139 | if (!ne) { | ||
140 | ne = document.getElementById('slide0'); | ||
141 | snum = 0; | ||
142 | } | ||
143 | if (step < 0) {incpos = incrementals[snum].length} else {incpos = 0;} | ||
144 | if (incrementals[snum].length > 0 && incpos == 0) { | ||
145 | for (var i = 0; i < incrementals[snum].length; i++) { | ||
146 | if (hasClass(incrementals[snum][i], 'current')) | ||
147 | incpos = i + 1; | ||
148 | else | ||
149 | addClass(incrementals[snum][i], 'incremental'); | ||
150 | } | ||
151 | } | ||
152 | if (incrementals[snum].length > 0 && incpos > 0) | ||
153 | addClass(incrementals[snum][incpos - 1], 'current'); | ||
154 | ce.style.visibility = 'hidden'; | ||
155 | ne.style.visibility = 'visible'; | ||
156 | jl.selectedIndex = snum; | ||
157 | currentSlide(); | ||
158 | number = 0; | ||
159 | } | ||
160 | |||
161 | function goTo(target) { | ||
162 | if (target >= smax || target == snum) return; | ||
163 | go(target - snum); | ||
164 | } | ||
165 | |||
166 | function subgo(step) { | ||
167 | if (step > 0) { | ||
168 | removeClass(incrementals[snum][incpos - 1],'current'); | ||
169 | removeClass(incrementals[snum][incpos], 'incremental'); | ||
170 | addClass(incrementals[snum][incpos],'current'); | ||
171 | incpos++; | ||
172 | } else { | ||
173 | incpos--; | ||
174 | removeClass(incrementals[snum][incpos],'current'); | ||
175 | addClass(incrementals[snum][incpos], 'incremental'); | ||
176 | addClass(incrementals[snum][incpos - 1],'current'); | ||
177 | } | ||
178 | } | ||
179 | |||
180 | function toggle() { | ||
181 | var slideColl = GetElementsWithClassName('*','slide'); | ||
182 | var slides = document.getElementById('slideProj'); | ||
183 | var outline = document.getElementById('outlineStyle'); | ||
184 | if (!slides.disabled) { | ||
185 | slides.disabled = true; | ||
186 | outline.disabled = false; | ||
187 | s5mode = false; | ||
188 | fontSize('1em'); | ||
189 | for (var n = 0; n < smax; n++) { | ||
190 | var slide = slideColl[n]; | ||
191 | slide.style.visibility = 'visible'; | ||
192 | } | ||
193 | } else { | ||
194 | slides.disabled = false; | ||
195 | outline.disabled = true; | ||
196 | s5mode = true; | ||
197 | fontScale(); | ||
198 | for (var n = 0; n < smax; n++) { | ||
199 | var slide = slideColl[n]; | ||
200 | slide.style.visibility = 'hidden'; | ||
201 | } | ||
202 | slideColl[snum].style.visibility = 'visible'; | ||
203 | } | ||
204 | } | ||
205 | |||
206 | function showHide(action) { | ||
207 | var obj = GetElementsWithClassName('*','hideme')[0]; | ||
208 | switch (action) { | ||
209 | case 's': obj.style.visibility = 'visible'; break; | ||
210 | case 'h': obj.style.visibility = 'hidden'; break; | ||
211 | case 'k': | ||
212 | if (obj.style.visibility != 'visible') { | ||
213 | obj.style.visibility = 'visible'; | ||
214 | } else { | ||
215 | obj.style.visibility = 'hidden'; | ||
216 | } | ||
217 | break; | ||
218 | } | ||
219 | } | ||
220 | |||
221 | // 'keys' code adapted from MozPoint (http://mozpoint.mozdev.org/) | ||
222 | function keys(key) { | ||
223 | if (!key) { | ||
224 | key = event; | ||
225 | key.which = key.keyCode; | ||
226 | } | ||
227 | if (key.which == 84) { | ||
228 | toggle(); | ||
229 | return; | ||
230 | } | ||
231 | if (s5mode) { | ||
232 | switch (key.which) { | ||
233 | case 10: // return | ||
234 | case 13: // enter | ||
235 | if (window.event && isParentOrSelf(window.event.srcElement, 'controls')) return; | ||
236 | if (key.target && isParentOrSelf(key.target, 'controls')) return; | ||
237 | if(number != undef) { | ||
238 | goTo(number); | ||
239 | break; | ||
240 | } | ||
241 | case 32: // spacebar | ||
242 | case 34: // page down | ||
243 | case 39: // rightkey | ||
244 | case 40: // downkey | ||
245 | if(number != undef) { | ||
246 | go(number); | ||
247 | } else if (!incrementals[snum] || incpos >= incrementals[snum].length) { | ||
248 | go(1); | ||
249 | } else { | ||
250 | subgo(1); | ||
251 | } | ||
252 | break; | ||
253 | case 33: // page up | ||
254 | case 37: // leftkey | ||
255 | case 38: // upkey | ||
256 | if(number != undef) { | ||
257 | go(-1 * number); | ||
258 | } else if (!incrementals[snum] || incpos <= 0) { | ||
259 | go(-1); | ||
260 | } else { | ||
261 | subgo(-1); | ||
262 | } | ||
263 | break; | ||
264 | case 36: // home | ||
265 | goTo(0); | ||
266 | break; | ||
267 | case 35: // end | ||
268 | goTo(smax-1); | ||
269 | break; | ||
270 | case 67: // c | ||
271 | showHide('k'); | ||
272 | break; | ||
273 | } | ||
274 | if (key.which < 48 || key.which > 57) { | ||
275 | number = undef; | ||
276 | } else { | ||
277 | if (window.event && isParentOrSelf(window.event.srcElement, 'controls')) return; | ||
278 | if (key.target && isParentOrSelf(key.target, 'controls')) return; | ||
279 | number = (((number != undef) ? number : 0) * 10) + (key.which - 48); | ||
280 | } | ||
281 | } | ||
282 | return false; | ||
283 | } | ||
284 | |||
285 | function clicker(e) { | ||
286 | number = undef; | ||
287 | var target; | ||
288 | if (window.event) { | ||
289 | target = window.event.srcElement; | ||
290 | e = window.event; | ||
291 | } else target = e.target; | ||
292 | if (target.getAttribute('href') != null || hasValue(target.rel, 'external') || isParentOrSelf(target, 'controls') || isParentOrSelf(target,'embed') || isParentOrSelf(target,'object')) return true; | ||
293 | if (!e.which || e.which == 1) { | ||
294 | if (!incrementals[snum] || incpos >= incrementals[snum].length) { | ||
295 | go(1); | ||
296 | } else { | ||
297 | subgo(1); | ||
298 | } | ||
299 | } | ||
300 | } | ||
301 | |||
302 | function findSlide(hash) { | ||
303 | var target = null; | ||
304 | var slides = GetElementsWithClassName('*','slide'); | ||
305 | for (var i = 0; i < slides.length; i++) { | ||
306 | var targetSlide = slides[i]; | ||
307 | if ( (targetSlide.name && targetSlide.name == hash) | ||
308 | || (targetSlide.id && targetSlide.id == hash) ) { | ||
309 | target = targetSlide; | ||
310 | break; | ||
311 | } | ||
312 | } | ||
313 | while(target != null && target.nodeName != 'BODY') { | ||
314 | if (hasClass(target, 'slide')) { | ||
315 | return parseInt(target.id.slice(5)); | ||
316 | } | ||
317 | target = target.parentNode; | ||
318 | } | ||
319 | return null; | ||
320 | } | ||
321 | |||
322 | function slideJump() { | ||
323 | if (window.location.hash == null) return; | ||
324 | var sregex = /^#slide(\d+)$/; | ||
325 | var matches = sregex.exec(window.location.hash); | ||
326 | var dest = null; | ||
327 | if (matches != null) { | ||
328 | dest = parseInt(matches[1]); | ||
329 | } else { | ||
330 | dest = findSlide(window.location.hash.slice(1)); | ||
331 | } | ||
332 | if (dest != null) | ||
333 | go(dest - snum); | ||
334 | } | ||
335 | |||
336 | function fixLinks() { | ||
337 | var thisUri = window.location.href; | ||
338 | thisUri = thisUri.slice(0, thisUri.length - window.location.hash.length); | ||
339 | var aelements = document.getElementsByTagName('A'); | ||
340 | for (var i = 0; i < aelements.length; i++) { | ||
341 | var a = aelements[i].href; | ||
342 | var slideID = a.match('\#slide[0-9]{1,2}'); | ||
343 | if ((slideID) && (slideID[0].slice(0,1) == '#')) { | ||
344 | var dest = findSlide(slideID[0].slice(1)); | ||
345 | if (dest != null) { | ||
346 | if (aelements[i].addEventListener) { | ||
347 | aelements[i].addEventListener("click", new Function("e", | ||
348 | "if (document.getElementById('slideProj').disabled) return;" + | ||
349 | "go("+dest+" - snum); " + | ||
350 | "if (e.preventDefault) e.preventDefault();"), true); | ||
351 | } else if (aelements[i].attachEvent) { | ||
352 | aelements[i].attachEvent("onclick", new Function("", | ||
353 | "if (document.getElementById('slideProj').disabled) return;" + | ||
354 | "go("+dest+" - snum); " + | ||
355 | "event.returnValue = false;")); | ||
356 | } | ||
357 | } | ||
358 | } | ||
359 | } | ||
360 | } | ||
361 | |||
362 | function externalLinks() { | ||
363 | if (!document.getElementsByTagName) return; | ||
364 | var anchors = document.getElementsByTagName('a'); | ||
365 | for (var i=0; i<anchors.length; i++) { | ||
366 | var anchor = anchors[i]; | ||
367 | if (anchor.getAttribute('href') && hasValue(anchor.rel, 'external')) { | ||
368 | anchor.target = '_blank'; | ||
369 | addClass(anchor,'external'); | ||
370 | } | ||
371 | } | ||
372 | } | ||
373 | |||
374 | function createControls() { | ||
375 | var controlsDiv = document.getElementById("controls"); | ||
376 | if (!controlsDiv) return; | ||
377 | var hider = ' onmouseover="showHide(\'s\');" onmouseout="showHide(\'h\');"'; | ||
378 | var hideDiv, hideList = ''; | ||
379 | if (controlVis == 'hidden') { | ||
380 | hideDiv = hider; | ||
381 | } else { | ||
382 | hideList = hider; | ||
383 | } | ||
384 | controlsDiv.innerHTML = '<form action="#" id="controlForm"' + hideDiv + '>' + | ||
385 | '<div id="navLinks">' + | ||
386 | '<a accesskey="t" id="toggle" href="javascript:toggle();">Ø<\/a>' + | ||
387 | '<a accesskey="z" id="prev" href="javascript:go(-1);">«<\/a>' + | ||
388 | '<a accesskey="x" id="next" href="javascript:go(1);">»<\/a>' + | ||
389 | '<div id="navList"' + hideList + '><select id="jumplist" onchange="go(\'j\');"><\/select><\/div>' + | ||
390 | '<\/div><\/form>'; | ||
391 | if (controlVis == 'hidden') { | ||
392 | var hidden = document.getElementById('navLinks'); | ||
393 | } else { | ||
394 | var hidden = document.getElementById('jumplist'); | ||
395 | } | ||
396 | addClass(hidden,'hideme'); | ||
397 | } | ||
398 | |||
399 | function fontScale() { // causes layout problems in FireFox that get fixed if browser's Reload is used; same may be true of other Gecko-based browsers | ||
400 | if (!s5mode) return false; | ||
401 | var vScale = 22; // both yield 32 (after rounding) at 1024x768 | ||
402 | var hScale = 32; // perhaps should auto-calculate based on theme's declared value? | ||
403 | if (window.innerHeight) { | ||
404 | var vSize = window.innerHeight; | ||
405 | var hSize = window.innerWidth; | ||
406 | } else if (document.documentElement.clientHeight) { | ||
407 | var vSize = document.documentElement.clientHeight; | ||
408 | var hSize = document.documentElement.clientWidth; | ||
409 | } else if (document.body.clientHeight) { | ||
410 | var vSize = document.body.clientHeight; | ||
411 | var hSize = document.body.clientWidth; | ||
412 | } else { | ||
413 | var vSize = 700; // assuming 1024x768, minus chrome and such | ||
414 | var hSize = 1024; // these do not account for kiosk mode or Opera Show | ||
415 | } | ||
416 | var newSize = Math.min(Math.round(vSize/vScale),Math.round(hSize/hScale)); | ||
417 | fontSize(newSize + 'px'); | ||
418 | if (isGe) { // hack to counter incremental reflow bugs | ||
419 | var obj = document.getElementsByTagName('body')[0]; | ||
420 | obj.style.display = 'none'; | ||
421 | obj.style.display = 'block'; | ||
422 | } | ||
423 | } | ||
424 | |||
425 | function fontSize(value) { | ||
426 | if (!(s5ss = document.getElementById('s5ss'))) { | ||
427 | if (!isIE) { | ||
428 | document.getElementsByTagName('head')[0].appendChild(s5ss = document.createElement('style')); | ||
429 | s5ss.setAttribute('media','screen, projection'); | ||
430 | s5ss.setAttribute('id','s5ss'); | ||
431 | } else { | ||
432 | document.createStyleSheet(); | ||
433 | document.s5ss = document.styleSheets[document.styleSheets.length - 1]; | ||
434 | } | ||
435 | } | ||
436 | if (!isIE) { | ||
437 | while (s5ss.lastChild) s5ss.removeChild(s5ss.lastChild); | ||
438 | s5ss.appendChild(document.createTextNode('body {font-size: ' + value + ' !important;}')); | ||
439 | } else { | ||
440 | document.s5ss.addRule('body','font-size: ' + value + ' !important;'); | ||
441 | } | ||
442 | } | ||
443 | |||
444 | function notOperaFix() { | ||
445 | slideCSS = document.getElementById('slideProj').href; | ||
446 | var slides = document.getElementById('slideProj'); | ||
447 | var outline = document.getElementById('outlineStyle'); | ||
448 | slides.setAttribute('media','screen'); | ||
449 | outline.disabled = true; | ||
450 | if (isGe) { | ||
451 | slides.setAttribute('href','null'); // Gecko fix | ||
452 | slides.setAttribute('href',slideCSS); // Gecko fix | ||
453 | } | ||
454 | if (isIE && document.styleSheets && document.styleSheets[0]) { | ||
455 | document.styleSheets[0].addRule('img', 'behavior: url(ui/default/iepngfix.htc)'); | ||
456 | document.styleSheets[0].addRule('div', 'behavior: url(ui/default/iepngfix.htc)'); | ||
457 | document.styleSheets[0].addRule('.slide', 'behavior: url(ui/default/iepngfix.htc)'); | ||
458 | } | ||
459 | } | ||
460 | |||
461 | function getIncrementals(obj) { | ||
462 | var incrementals = new Array(); | ||
463 | if (!obj) | ||
464 | return incrementals; | ||
465 | var children = obj.childNodes; | ||
466 | for (var i = 0; i < children.length; i++) { | ||
467 | var child = children[i]; | ||
468 | if (hasClass(child, 'incremental')) { | ||
469 | if (child.nodeName == 'OL' || child.nodeName == 'UL') { | ||
470 | removeClass(child, 'incremental'); | ||
471 | for (var j = 0; j < child.childNodes.length; j++) { | ||
472 | if (child.childNodes[j].nodeType == 1) { | ||
473 | addClass(child.childNodes[j], 'incremental'); | ||
474 | } | ||
475 | } | ||
476 | } else { | ||
477 | incrementals[incrementals.length] = child; | ||
478 | removeClass(child,'incremental'); | ||
479 | } | ||
480 | } | ||
481 | if (hasClass(child, 'show-first')) { | ||
482 | if (child.nodeName == 'OL' || child.nodeName == 'UL') { | ||
483 | removeClass(child, 'show-first'); | ||
484 | if (child.childNodes[isGe].nodeType == 1) { | ||
485 | removeClass(child.childNodes[isGe], 'incremental'); | ||
486 | } | ||
487 | } else { | ||
488 | incrementals[incrementals.length] = child; | ||
489 | } | ||
490 | } | ||
491 | incrementals = incrementals.concat(getIncrementals(child)); | ||
492 | } | ||
493 | return incrementals; | ||
494 | } | ||
495 | |||
496 | function createIncrementals() { | ||
497 | var incrementals = new Array(); | ||
498 | for (var i = 0; i < smax; i++) { | ||
499 | incrementals[i] = getIncrementals(document.getElementById('slide'+i)); | ||
500 | } | ||
501 | return incrementals; | ||
502 | } | ||
503 | |||
504 | function defaultCheck() { | ||
505 | var allMetas = document.getElementsByTagName('meta'); | ||
506 | for (var i = 0; i< allMetas.length; i++) { | ||
507 | if (allMetas[i].name == 'defaultView') { | ||
508 | defaultView = allMetas[i].content; | ||
509 | } | ||
510 | if (allMetas[i].name == 'controlVis') { | ||
511 | controlVis = allMetas[i].content; | ||
512 | } | ||
513 | } | ||
514 | } | ||
515 | |||
516 | // Key trap fix, new function body for trap() | ||
517 | function trap(e) { | ||
518 | if (!e) { | ||
519 | e = event; | ||
520 | e.which = e.keyCode; | ||
521 | } | ||
522 | try { | ||
523 | modifierKey = e.ctrlKey || e.altKey || e.metaKey; | ||
524 | } | ||
525 | catch(e) { | ||
526 | modifierKey = false; | ||
527 | } | ||
528 | return modifierKey || e.which == 0; | ||
529 | } | ||
530 | |||
531 | function startup() { | ||
532 | defaultCheck(); | ||
533 | if (!isOp) | ||
534 | createControls(); | ||
535 | slideLabel(); | ||
536 | fixLinks(); | ||
537 | externalLinks(); | ||
538 | fontScale(); | ||
539 | if (!isOp) { | ||
540 | notOperaFix(); | ||
541 | incrementals = createIncrementals(); | ||
542 | slideJump(); | ||
543 | if (defaultView == 'outline') { | ||
544 | toggle(); | ||
545 | } | ||
546 | document.onkeyup = keys; | ||
547 | document.onkeypress = trap; | ||
548 | document.onclick = clicker; | ||
549 | } | ||
550 | } | ||
551 | |||
552 | window.onload = startup; | ||
553 | window.onresize = function(){setTimeout('fontScale()', 50);} \ No newline at end of file | ||