diff options
Diffstat (limited to 'static')
23 files changed, 1661 insertions, 58 deletions
diff --git a/static/.well-known/keybase.txt b/static/.well-known/keybase.txt new file mode 100644 index 0000000..f027aa9 --- /dev/null +++ b/static/.well-known/keybase.txt @@ -0,0 +1,56 @@ +================================================================== +https://keybase.io/alanpearce +-------------------------------------------------------------------- + +I hereby claim: + + * I am an admin of https://www.alanpearce.eu + * I am alanpearce (https://keybase.io/alanpearce) on keybase. + * I have a public key ASAktAZWh67GLebI8PNw4QNlJ4zEiIogKiQQ8WsqVsQa8Qo + +To do so, I am signing this object: + +{ + "body": { + "key": { + "eldest_kid": "01200d892fbb34517abd5120fa546cb65dad1172cd85405cfae4936dcdb8bf5ac1850a", + "host": "keybase.io", + "kid": "012024b4065687aec62de6c8f0f370e10365278cc4888a202a2410f16b2a56c41af10a", + "uid": "91ae6da6b67277c6eded2451d6925919", + "username": "alanpearce" + }, + "merkle_root": { + "ctime": 1529691082, + "hash": "6588b60bdcbf5836c74db6647f69ed9f88e8d45b237f896e75d790534fcb3058a3c2e3e9b7f026469b0ca30fe58f20e47fbe074306e02eba912348f19ab1abd2", + "hash_meta": "68aec5954816532401f402af55121c0c7496f3aac93475db68ea50e38e7e45b4", + "seqno": 3127786 + }, + "service": { + "entropy": "pkn3peHHXkyLn2KYC2q0CKkC", + "hostname": "www.alanpearce.eu", + "protocol": "https:" + }, + "type": "web_service_binding", + "version": 2 + }, + "client": { + "name": "keybase.io go client", + "version": "2.1.1" + }, + "ctime": 1529691093, + "expire_in": 504576000, + "prev": "9d580c5bd9f3b4a01356f507d808de55add562ddf3fded05a7d74299c5766503", + "seqno": 25, + "tag": "signature" +} + +which yields the signature: + +hKRib2R5hqhkZXRhY2hlZMOpaGFzaF90eXBlCqNrZXnEIwEgJLQGVoeuxi3myPDzcOEDZSeMxIiKICokEPFrKlbEGvEKp3BheWxvYWTESpcCGcQgnVgMW9nztKATVvUH2AjeVa3VYt3z/e0Fp9dCmcV2ZQPEIIRjxssrSyS8RF3Xr7Br780Q1Y0vy58txz8S6XBBaYpCAgHCo3NpZ8RAeAwN++lz+C+csgxZXXLSv76w2WcYaH41EcagALVLrULinV0j+Ea1TOUhmBfI9KNKFkOSiuEm+kOVktdf4BrWA6hzaWdfdHlwZSCkaGFzaIKkdHlwZQildmFsdWXEIFnViSN5iA8WlkQfuHAD3PEQ0gkZyjv59iuNx7EoBhrRo3RhZ80CAqd2ZXJzaW9uAQ== + +And finally, I am proving ownership of this host by posting or +appending to this document. + +View my publicly-auditable identity here: https://keybase.io/alanpearce + +================================================================== \ No newline at end of file diff --git a/static/css/fonts.css b/static/css/fonts.css deleted file mode 100644 index 8ffcecd..0000000 --- a/static/css/fonts.css +++ /dev/null @@ -1,7 +0,0 @@ -body { - font-family: Optima, Candara, Calibri, Arial, sans-serif; -} -code { - font-family: "Lucida Console", Monaco, monospace; - font-size: 85%; -} diff --git a/static/css/style.css b/static/css/style.css deleted file mode 100644 index 4dc3ae4..0000000 --- a/static/css/style.css +++ /dev/null @@ -1,51 +0,0 @@ -body { - max-width: 800px; - margin: auto; - padding: 1em; - line-height: 1.5em; -} - -/* header and footer areas */ -.menu { padding: 0; } -.menu li { display: inline-block; } -.article-meta, .menu a { - text-decoration: none; - background: #eee; - padding: 5px; - border-radius: 5px; -} -.menu, .article-meta, footer { text-align: center; } -.title { font-size: 1.1em; } -footer a { text-decoration: none; } -hr { - border-style: dashed; - color: #ddd; -} - -/* code */ -pre { - border: 1px solid #ddd; - box-shadow: 5px 5px 5px #eee; - padding: 1em; - overflow-x: auto; -} -code { background: #f9f9f9; } -pre code { background: none; } - -/* misc elements */ -img, iframe, video { max-width: 100%; } -main { hyphens: auto; } -blockquote { - background: #f9f9f9; - border-left: 5px solid #ccc; - padding: 3px 1em 3px; -} - -table { - margin: auto; - border-top: 1px solid #666; - border-bottom: 1px solid #666; -} -table thead th { border-bottom: 1px solid #ddd; } -th, td { padding: 5px; } -thead, tfoot, tr:nth-child(even) { background: #eee; } diff --git a/static/css/syntax.css b/static/css/syntax.css new file mode 100644 index 0000000..23a21be --- /dev/null +++ b/static/css/syntax.css @@ -0,0 +1,81 @@ +/* Background */ .chroma { background-color: #f8f8f8 } +/* Other */ .chroma .x { color: #000000 } +/* Error */ .chroma .err { color: #a40000 } +/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; } +/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; width: auto; overflow: auto; display: block; } +/* LineHighlight */ .chroma .hl { display: block; width: 100%;background-color: #ffffcc } +/* LineNumbersTable */ .chroma .lnt { margin-right: 0.4em; padding: 0 0.4em 0 0.4em; display: block; } +/* LineNumbers */ .chroma .ln { margin-right: 0.4em; padding: 0 0.4em 0 0.4em; } +/* Keyword */ .chroma .k { color: #204a87; font-weight: bold } +/* KeywordConstant */ .chroma .kc { color: #204a87; font-weight: bold } +/* KeywordDeclaration */ .chroma .kd { color: #204a87; font-weight: bold } +/* KeywordNamespace */ .chroma .kn { color: #204a87; font-weight: bold } +/* KeywordPseudo */ .chroma .kp { color: #204a87; font-weight: bold } +/* KeywordReserved */ .chroma .kr { color: #204a87; font-weight: bold } +/* KeywordType */ .chroma .kt { color: #204a87; font-weight: bold } +/* Name */ .chroma .n { color: #000000 } +/* NameAttribute */ .chroma .na { color: #c4a000 } +/* NameBuiltin */ .chroma .nb { color: #204a87 } +/* NameBuiltinPseudo */ .chroma .bp { color: #3465a4 } +/* NameClass */ .chroma .nc { color: #000000 } +/* NameConstant */ .chroma .no { color: #000000 } +/* NameDecorator */ .chroma .nd { color: #5c35cc; font-weight: bold } +/* NameEntity */ .chroma .ni { color: #ce5c00 } +/* NameException */ .chroma .ne { color: #cc0000; font-weight: bold } +/* NameFunction */ .chroma .nf { color: #000000 } +/* NameFunctionMagic */ .chroma .fm { color: #000000 } +/* NameLabel */ .chroma .nl { color: #f57900 } +/* NameNamespace */ .chroma .nn { color: #000000 } +/* NameOther */ .chroma .nx { color: #000000 } +/* NameProperty */ .chroma .py { color: #000000 } +/* NameTag */ .chroma .nt { color: #204a87; font-weight: bold } +/* NameVariable */ .chroma .nv { color: #000000 } +/* NameVariableClass */ .chroma .vc { color: #000000 } +/* NameVariableGlobal */ .chroma .vg { color: #000000 } +/* NameVariableInstance */ .chroma .vi { color: #000000 } +/* NameVariableMagic */ .chroma .vm { color: #000000 } +/* Literal */ .chroma .l { color: #000000 } +/* LiteralDate */ .chroma .ld { color: #000000 } +/* LiteralString */ .chroma .s { color: #4e9a06 } +/* LiteralStringAffix */ .chroma .sa { color: #4e9a06 } +/* LiteralStringBacktick */ .chroma .sb { color: #4e9a06 } +/* LiteralStringChar */ .chroma .sc { color: #4e9a06 } +/* LiteralStringDelimiter */ .chroma .dl { color: #4e9a06 } +/* LiteralStringDoc */ .chroma .sd { color: #8f5902; font-style: italic } +/* LiteralStringDouble */ .chroma .s2 { color: #4e9a06 } +/* LiteralStringEscape */ .chroma .se { color: #4e9a06 } +/* LiteralStringHeredoc */ .chroma .sh { color: #4e9a06 } +/* LiteralStringInterpol */ .chroma .si { color: #4e9a06 } +/* LiteralStringOther */ .chroma .sx { color: #4e9a06 } +/* LiteralStringRegex */ .chroma .sr { color: #4e9a06 } +/* LiteralStringSingle */ .chroma .s1 { color: #4e9a06 } +/* LiteralStringSymbol */ .chroma .ss { color: #4e9a06 } +/* LiteralNumber */ .chroma .m { color: #0000cf; font-weight: bold } +/* LiteralNumberBin */ .chroma .mb { color: #0000cf; font-weight: bold } +/* LiteralNumberFloat */ .chroma .mf { color: #0000cf; font-weight: bold } +/* LiteralNumberHex */ .chroma .mh { color: #0000cf; font-weight: bold } +/* LiteralNumberInteger */ .chroma .mi { color: #0000cf; font-weight: bold } +/* LiteralNumberIntegerLong */ .chroma .il { color: #0000cf; font-weight: bold } +/* LiteralNumberOct */ .chroma .mo { color: #0000cf; font-weight: bold } +/* Operator */ .chroma .o { color: #ce5c00; font-weight: bold } +/* OperatorWord */ .chroma .ow { color: #204a87; font-weight: bold } +/* Punctuation */ .chroma .p { color: #000000; font-weight: bold } +/* Comment */ .chroma .c { color: #8f5902; font-style: italic } +/* CommentHashbang */ .chroma .ch { color: #8f5902; font-style: italic } +/* CommentMultiline */ .chroma .cm { color: #8f5902; font-style: italic } +/* CommentSingle */ .chroma .c1 { color: #8f5902; font-style: italic } +/* CommentSpecial */ .chroma .cs { color: #8f5902; font-style: italic } +/* CommentPreproc */ .chroma .cp { color: #8f5902; font-style: italic } +/* CommentPreprocFile */ .chroma .cpf { color: #8f5902; font-style: italic } +/* Generic */ .chroma .g { color: #000000 } +/* GenericDeleted */ .chroma .gd { color: #a40000 } +/* GenericEmph */ .chroma .ge { color: #000000; font-style: italic } +/* GenericError */ .chroma .gr { color: #ef2929 } +/* GenericHeading */ .chroma .gh { color: #000080; font-weight: bold } +/* GenericInserted */ .chroma .gi { color: #00a000 } +/* GenericOutput */ .chroma .go { color: #000000; font-style: italic } +/* GenericPrompt */ .chroma .gp { color: #8f5902 } +/* GenericStrong */ .chroma .gs { color: #000000; font-weight: bold } +/* GenericSubheading */ .chroma .gu { color: #800080; font-weight: bold } +/* GenericTraceback */ .chroma .gt { color: #a40000; font-weight: bold } +/* TextWhitespace */ .chroma .w { color: #f8f8f8 } diff --git a/static/img/me-large.jpg b/static/img/me-large.jpg new file mode 100644 index 0000000..1a1b369 --- /dev/null +++ b/static/img/me-large.jpg Binary files differdiff --git a/static/img/me-thumb.jpg b/static/img/me-thumb.jpg new file mode 100644 index 0000000..d3c82f0 --- /dev/null +++ b/static/img/me-thumb.jpg Binary files differdiff --git a/static/keybase.txt b/static/keybase.txt new file mode 100644 index 0000000..71b773e --- /dev/null +++ b/static/keybase.txt @@ -0,0 +1,55 @@ +================================================================== +https://keybase.io/alanpearce +-------------------------------------------------------------------- + +I hereby claim: + + * I am an admin of https://alanpearce.uk + * I am alanpearce (https://keybase.io/alanpearce) on keybase. + * I have a public key ASANiS-7NFF6vVEg-lRstl2tEXLNhUBc-uSTbc24v1rBhQo + +To do so, I am signing this object: + +{ + "body": { + "key": { + "eldest_kid": "01200d892fbb34517abd5120fa546cb65dad1172cd85405cfae4936dcdb8bf5ac1850a", + "host": "keybase.io", + "kid": "01200d892fbb34517abd5120fa546cb65dad1172cd85405cfae4936dcdb8bf5ac1850a", + "uid": "91ae6da6b67277c6eded2451d6925919", + "username": "alanpearce" + }, + "merkle_root": { + "ctime": 1528123072, + "hash": "e0815741a6d5837c8c4a3af6900f5921484ebc1a0c8931284247134587f5cb2a330840662e835ad3d21397c5617cfa3c6152777b5b643821f5e80cd00d107af3", + "hash_meta": "c92918ad06b58621eea19be246eff63e3a86d00c619c5457ae41cc090bbcfd70", + "seqno": 2990872 + }, + "service": { + "hostname": "alanpearce.uk", + "protocol": "https:" + }, + "type": "web_service_binding", + "version": 1 + }, + "client": { + "name": "keybase.io go client", + "version": "1.0.44" + }, + "ctime": 1528123107, + "expire_in": 504576000, + "prev": "7c22548954f852d481dec3c2f6ccefb4be4322e2cd51016e2beb4be359b65763", + "seqno": 6, + "tag": "signature" +} + +which yields the signature: + +hKRib2R5hqhkZXRhY2hlZMOpaGFzaF90eXBlCqNrZXnEIwEgDYkvuzRRer1RIPpUbLZdrRFyzYVAXPrkk23NuL9awYUKp3BheWxvYWTFA0d7ImJvZHkiOnsia2V5Ijp7ImVsZGVzdF9raWQiOiIwMTIwMGQ4OTJmYmIzNDUxN2FiZDUxMjBmYTU0NmNiNjVkYWQxMTcyY2Q4NTQwNWNmYWU0OTM2ZGNkYjhiZjVhYzE4NTBhIiwiaG9zdCI6ImtleWJhc2UuaW8iLCJraWQiOiIwMTIwMGQ4OTJmYmIzNDUxN2FiZDUxMjBmYTU0NmNiNjVkYWQxMTcyY2Q4NTQwNWNmYWU0OTM2ZGNkYjhiZjVhYzE4NTBhIiwidWlkIjoiOTFhZTZkYTZiNjcyNzdjNmVkZWQyNDUxZDY5MjU5MTkiLCJ1c2VybmFtZSI6ImFsYW5wZWFyY2UifSwibWVya2xlX3Jvb3QiOnsiY3RpbWUiOjE1MjgxMjMwNzIsImhhc2giOiJlMDgxNTc0MWE2ZDU4MzdjOGM0YTNhZjY5MDBmNTkyMTQ4NGViYzFhMGM4OTMxMjg0MjQ3MTM0NTg3ZjVjYjJhMzMwODQwNjYyZTgzNWFkM2QyMTM5N2M1NjE3Y2ZhM2M2MTUyNzc3YjViNjQzODIxZjVlODBjZDAwZDEwN2FmMyIsImhhc2hfbWV0YSI6ImM5MjkxOGFkMDZiNTg2MjFlZWExOWJlMjQ2ZWZmNjNlM2E4NmQwMGM2MTljNTQ1N2FlNDFjYzA5MGJiY2ZkNzAiLCJzZXFubyI6Mjk5MDg3Mn0sInNlcnZpY2UiOnsiaG9zdG5hbWUiOiJhbGFucGVhcmNlLnVrIiwicHJvdG9jb2wiOiJodHRwczoifSwidHlwZSI6IndlYl9zZXJ2aWNlX2JpbmRpbmciLCJ2ZXJzaW9uIjoxfSwiY2xpZW50Ijp7Im5hbWUiOiJrZXliYXNlLmlvIGdvIGNsaWVudCIsInZlcnNpb24iOiIxLjAuNDQifSwiY3RpbWUiOjE1MjgxMjMxMDcsImV4cGlyZV9pbiI6NTA0NTc2MDAwLCJwcmV2IjoiN2MyMjU0ODk1NGY4NTJkNDgxZGVjM2MyZjZjY2VmYjRiZTQzMjJlMmNkNTEwMTZlMmJlYjRiZTM1OWI2NTc2MyIsInNlcW5vIjo2LCJ0YWciOiJzaWduYXR1cmUifaNzaWfEQLd80AcgXet5yGW0bL5y5IAf/rQ2R15NNRCk0T6qz/kQlf30JN810HcLoGrX3RcalgHbb8QbcgCWyd0kiep4CgWoc2lnX3R5cGUgpGhhc2iCpHR5cGUIpXZhbHVlxCB8OF/NBSFEq73lc0tGCM3gCNgr29QmOgZd9jvX68GNaKN0YWfNAgKndmVyc2lvbgE= + +And finally, I am proving ownership of this host by posting or +appending to this document. + +View my publicly-auditable identity here: https://keybase.io/alanpearce + +================================================================== diff --git a/static/public_key.asc b/static/public_key.asc new file mode 100644 index 0000000..f081429 --- /dev/null +++ b/static/public_key.asc @@ -0,0 +1,16 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEXOZxBhYJKwYBBAHaRw8BAQdApEDmvmbv0fkrkND5LsR32g9QX8KtXAybgcCv +euU6N9O0IEFsYW4gUGVhcmNlIDxhbGFuQGFsYW5wZWFyY2UuZXU+iIAEExYIABwF +AlzmcQYCCwkCGwMEFQgJCgQWAgMBAheAAh4BABYJEM1L65Ko1GWDCxpUUkVaT1It +R1BHgDABAICw5varaVWeuVlzJ0/XpLDFSHfY1CvQbMHe1LJ/iwGJAP9m3XC0yTyX +uEG7w3R32Md5urcGH3fTIKK0ea6M+QVtArQgQWxhbiBQZWFyY2UgPGFsYW5Ac2F0 +b3NoaXBheS5pbz6IkAQTFggAOBYhBEjmV2wHBziMuL79DM1L65Ko1GWDBQJc5p5N +AhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEM1L65Ko1GWD2Z4A/jggQexr +za4DXNK2jolKjIBL9S7pOGbXxldHo69HC+dLAP4lJlaExUbompFaXzV/FETH2pdQ +azi51lmD8wN5YX4AA7g4BFzmcQYSCisGAQQBl1UBBQEBB0C5WCVOLRJpEHSWMVFH +0xtWavMXh3QUaoNIrX0jcEtpIAMBCAeIbQQYFggACQUCXOZxBgIbDAAWCRDNS+uS +qNRlgwsaVFJFWk9SLUdQR5PKAP93z83yYaOZMQKZYAD3h2LHdlKD2wl2LaLiFOll +4N4ghgEA5iTNV6d5PHo8NV73T4xm97qY94LpF1cDWwBDYhb0ywI= +=VSmX +-----END PGP PUBLIC KEY BLOCK----- diff --git a/static/robots.txt b/static/robots.txt new file mode 100644 index 0000000..ef30e6f --- /dev/null +++ b/static/robots.txt @@ -0,0 +1,7 @@ +User-agent: * +Disallow: +Host: alanpearce.eu +Sitemap: https://alanpearce.eu/sitemap.xml + +User-agent: googlebot +Disallow: / diff --git a/static/talks/fp-js/index.html b/static/talks/fp-js/index.html new file mode 100644 index 0000000..e90650c --- /dev/null +++ b/static/talks/fp-js/index.html @@ -0,0 +1,431 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" +"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> +<head> +<!-- 2017-10-16 Mon 10:10 --> +<meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> +<meta name="viewport" content="width=device-width, initial-scale=1" /> +<title>Functional Programming in JavaScript</title> +<meta name="generator" content="Org mode" /> +<meta name="author" content="Alan Pearce" /> +<meta name="version" content="S5 1.2a2" /> +<meta name='defaultView' content='slideshow' /> +<meta name='controlVis' content='hidden' /> +<!-- style sheet links --> +<link rel='stylesheet' href='ui/default/outline.css' type='text/css' media='screen' id='outlineStyle' /> +<link rel='stylesheet' href='ui/default/print.css' type='text/css' media='print' id='slidePrint' /> +<link rel='stylesheet' href='ui/default/opera.css' type='text/css' media='projection' id='operaFix' /> +<link rel='stylesheet' href='ui/default/slides.css' type='text/css' media='screen' id='slideProj' /> +<!-- S5 JS --> +<script src='ui/default/slides.js' type='text/javascript'></script> + + +</head> +<body> +<div class="layout"> +<div id="controls"><!-- no edit --></div> +<div id="currentSlide"><!-- no edit --></div> +<div id="header" class="status"> +  +</div> + +<div id="footer" class="status"> +<h1>Alan Pearce - Functional Programming in JavaScript</h1> +</div> + +</div> +<div id="content" class="presentation"> +<div id='title-slide' class='slide'> +<h1>Functional Programming in JavaScript</h1> +<h2></h2> +<h2>Alan Pearce</h2> +<h3><a href="mailto:alan@alanpearce.eu">alan@alanpearce.eu</a></h3> +<h4></h4> +</div> +<div id='table-of-contents' class='slide'> +<h1>Table of Contents</h1> +<div id="text-table-of-contents"> +<ul> +<li>1. Why?</li> +<li>2. Concepts</li> +<li>3. Further concepts</li> +<li>4. First-class functions</li> +<li>5. Higher-order functions</li> +<li>6. Higher-order functions (cont.)</li> +<li>7. Pure functions</li> +<li>8. Recursion</li> +<li>9. Partial application</li> +<li>10. Partial application (cont.)</li> +<li>11. Currying</li> +<li>12. Easy Currying</li> +<li>13. Practical Currying</li> +<li>14. Functional composition</li> +<li>15. Functional composition (cont.)</li> +<li>16. pipe</li> +<li>17. Point-free programming</li> +<li>18. Further Resources</li> +</ul> +</div> +</div> + +<div id="outline-container-org05e10f9" class="outline-1 slide"> +<h1 id="org05e10f9"><span class="section-number-1">1</span> Why?</h1> +<div class="outline-text-1" id="text-1"> +<p> +Imperative programming is concerned with <b>how</b> +</p> + +<p> +Functional programming is concerned with <b>what</b> +</p> +</div> +</div> + +<div id="outline-container-org14fa5d9" class="outline-1 slide"> +<h1 id="org14fa5d9"><span class="section-number-1">2</span> Concepts</h1> +<div class="outline-text-1" id="text-2"> +<ul class="org-ul"> +<li>First-class Functions</li> +<li>Higher-order Functions</li> +<li>Recursion</li> +<li>Pure functions</li> +<li>Currying & Partial Application</li> +</ul> +</div> +</div> + +<div id="outline-container-orgd930436" class="outline-1 slide"> +<h1 id="orgd930436"><span class="section-number-1">3</span> Further concepts</h1> +<div class="outline-text-1" id="text-3"> +<ul class="org-ul"> +<li>Lazy Evaluation</li> +<li>Types & Data Structures</li> +<li>Category Theory</li> +</ul> +</div> +</div> + +<div id="outline-container-orgbd39891" class="outline-1 slide"> +<h1 id="orgbd39891"><span class="section-number-1">4</span> First-class functions</h1> +<div class="outline-text-1" id="text-4"> +<ul class="org-ul"> +<li>Are values</li> +<li>Have no restriction on their use</li> +<li>Enable the use of callback functions in JavaScript</li> +</ul> + +<p> + +</p> + +<div class="org-src-container"> +<pre><code class="src src-js">var fn = function () { + return 2 +} +</code></pre> +</div> +</div> +</div> + + +<div id="outline-container-org8d1ee8a" class="outline-1 slide"> +<h1 id="org8d1ee8a"><span class="section-number-1">5</span> Higher-order functions</h1> +<div class="outline-text-1" id="text-5"> +<p> +Functions that operate on other functions are higher-order functions +</p> + +<p> + +</p> + +<div class="org-src-container"> +<pre><code class="src src-js">var succ = function (x) { + return x + 1 +} + +var arr = [1, 2, 3, 4] + +arr.map(succ) +</code></pre> +</div> + +<p> +Here, <code>Array.prototype.map</code> is the higher-order function +</p> +</div> +</div> + +<div id="outline-container-org3bbf4ac" class="outline-1 slide"> +<h1 id="org3bbf4ac"><span class="section-number-1">6</span> Higher-order functions (cont.)</h1> +<div class="outline-text-1" id="text-6"> +<p> +Functions that return functions are also higher-order functions +</p> + +<p> + +</p> + +<div class="org-src-container"> +<pre><code class="src src-js">function <span style="font-weight: bold;">adder</span> (n) { + return function (x) { + return n + x + } +} + +var add1 = adder(1) +</code></pre> +</div> + +<p> +<code>adder</code> is a higher-order function +</p> +</div> +</div> + +<div id="outline-container-orgfb6eee2" class="outline-1 slide"> +<h1 id="orgfb6eee2"><span class="section-number-1">7</span> Pure functions</h1> +<div class="outline-text-1" id="text-7"> +<p> +Functions without side-effects +</p> + +<div class="org-src-container"> +<pre><code class="src src-js">var succ = (x) => x + 1 + +console.log(succ(succ(1))) + +<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> + +console.log(3) +</code></pre> +</div> +</div> +</div> + +<div id="outline-container-org16e9966" class="outline-1 slide"> +<h1 id="org16e9966"><span class="section-number-1">8</span> Recursion</h1> +<div class="outline-text-1" id="text-8"> +<p> +Functions that call themselves +</p> + +<div class="org-src-container"> +<pre><code class="src src-js">function <span style="font-weight: bold;">fibonacci</span> (n) { + switch (n) { + case 0: + case 1: + return 1 + default: + return fibonacci(n - 1) + fibonacci(n - 2) + } +} +</code></pre> +</div> +</div> +</div> + +<div id="outline-container-org1b7b0af" class="outline-1 slide"> +<h1 id="org1b7b0af"><span class="section-number-1">9</span> Partial application</h1> +<div class="outline-text-1" id="text-9"> +<p> +The infamous <code>Function.prototype.bind</code> in JavaScript +</p> + +<div class="org-src-container"> +<pre><code class="src src-js">function <span style="font-weight: bold;">add</span> (x, y) { + return x + y +} + +var add1 = add.bind(add, 1) + +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> +</code></pre> +</div> +</div> +</div> + +<div id="outline-container-org796e6d3" class="outline-1 slide"> +<h1 id="org796e6d3"><span class="section-number-1">10</span> Partial application (cont.)</h1> +<div class="outline-text-1" id="text-10"> +<p> +After ES6 introduced arrow functions, partial application has become +more popular +</p> + +<div class="org-src-container"> +<pre><code class="src src-js">var add = x => y => x + y +</code></pre> +</div> +</div> +</div> + +<div id="outline-container-orgeb4d67b" class="outline-1 slide"> +<h1 id="orgeb4d67b"><span class="section-number-1">11</span> Currying</h1> +<div class="outline-text-1" id="text-11"> +<p> +Related to partial application, but more implicit and general +</p> + +<p> +Translates <b><i>1</i> function of arity <i>n</i></b> to <b><i>n</i> functions of arity <i>1</i></b> +</p> + +<div class="org-src-container"> +<pre><code class="src src-js">function <span style="font-weight: bold;">volume</span> (w, d, h) { + return w * d * h +} + +var vol = curry(volume) +vol(10)(20)(30) +<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> +volume(10, 20, 30) +</code></pre> +</div> +</div> +</div> + +<div id="outline-container-orgdfd8353" class="outline-1 slide"> +<h1 id="orgdfd8353"><span class="section-number-1">12</span> Easy Currying</h1> +<div class="outline-text-1" id="text-12"> +<p> +In order to make currying (and partial application) easier to use, +move the <b>most important</b> argument to a function to the end: +</p> + +<div class="org-src-container"> +<pre><code class="src src-js">var badMap = (arr, fn) => arr.map(fn) +var goodMap = (fn, arr) => arr.map(fn) +var curriedBadMap = curry(badmap) +var curriedGoodMap = curry(goodMap) + +var goodDoubleArray = goodMap(x => x * 2) +var badDoubleArray = badMap(_, x => x * 2) +</code></pre> +</div> + +<p> +The bad version requires the curry function to support a magic +placeholder argument and doesn't look as clean. +</p> +</div> +</div> + +<div id="outline-container-org30accd5" class="outline-1 slide"> +<h1 id="org30accd5"><span class="section-number-1">13</span> Practical Currying</h1> +<div class="outline-text-1" id="text-13"> +<p> +Currying is not automatic in JavaScript, as in other languages +</p> + +<p> +External tools aren't (so far) able to statically analyse curried +functions +</p> + +<p> +Solution: Don't expose curried functions +Instead, write functions as if currying were automatic +</p> + +<p> +If consumers want to curry, they can. If they don't, their editor or +language server will show them the arguments +</p> +</div> +</div> + +<div id="outline-container-org5f352dc" class="outline-1 slide"> +<h1 id="org5f352dc"><span class="section-number-1">14</span> Functional composition</h1> +<div class="outline-text-1" id="text-14"> +<p> +Creating functions from other functions +</p> + +<p> +Usually provided by <code>compose</code> (right-to-left) and <code>pipe</code> (left-to-right) +</p> + +<p> +A very simple definition of <code>compose</code> for only two functions would look like this +</p> + +<div class="org-src-container"> +<pre><code class="src src-js">function <span style="font-weight: bold;">compose</span> (f, g) { + return function (...args) { + return f(g(...args)) + } +} +</code></pre> +</div> +</div> +</div> + +<div id="outline-container-org0c2c4f9" class="outline-1 slide"> +<h1 id="org0c2c4f9"><span class="section-number-1">15</span> Functional composition (cont.)</h1> +<div class="outline-text-1" id="text-15"> +<div class="org-src-container"> +<pre><code class="src src-js">var plusOne = x => x + 1 +var timesTwo = x => x * 2 + +var plusOneTimesTwo = compose(timesTwo, plusOne) +var timesTwoPlusOne = compose(plusOne, timesTwo) + +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> +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> +</code></pre> +</div> +</div> +</div> + +<div id="outline-container-orgddc61f6" class="outline-1 slide"> +<h1 id="orgddc61f6"><span class="section-number-1">16</span> pipe</h1> +<div class="outline-text-1" id="text-16"> +<p> +What about <code>pipe</code>? +</p> + +<p> +<code>pipe</code> does the same thing, but runs the functions the other way around +</p> + +<p> +<code>pipe(f, g)</code> is the same as <code>compose(g, f)</code> +</p> +</div> +</div> + +<div id="outline-container-org7426c58" class="outline-1 slide"> +<h1 id="org7426c58"><span class="section-number-1">17</span> Point-free programming</h1> +<div class="outline-text-1" id="text-17"> +<p> +With currying and higher-order functions, we (often) don't need to declare function arguments +</p> + +<div class="org-src-container"> +<pre><code class="src src-js">var modulo = a => b => b % a +var eq = a => b => a === b + +var isEven = x => eq(0)(modulo(2)(x)) +var isEvenPointFree = compose(eq(0), modulo(2)) +</code></pre> +</div> +</div> +</div> + +<div id="outline-container-org8868cfb" class="outline-1 slide"> +<h1 id="org8868cfb"><span class="section-number-1">18</span> Further Resources</h1> +<div class="outline-text-1" id="text-18"> +<ul class="org-ul"> +<li><a href="https://drboolean.gitbooks.io/mostly-adequate-guide/content/">Mostly adequate guide to FP (in javascript)</a></li> +<li><a href="http://ramdajs.com/">Ramda</a>, a general-purpose FP library</li> +<li><a href="https://sanctuary.js.org/">Sanctuary</a>, a JavaScript library for Haskellers</li> +</ul> +</div> +</div> + + +</div> +</body> +</html> diff --git a/static/talks/fp-js/index.org b/static/talks/fp-js/index.org new file mode 100644 index 0000000..8a4bf6c --- /dev/null +++ b/static/talks/fp-js/index.org @@ -0,0 +1,226 @@ +#+TITLE: Functional Programming in JavaScript +#+PROPERTY: :html-toplevel-hlevel 1 +#+PROPERTY: :with-toc 0 +* Why? + +Imperative programming is concerned with *how* + +Functional programming is concerned with *what* + +* Concepts + + - First-class Functions + - Higher-order Functions + - Recursion + - Pure functions + - Currying & Partial Application + +* Further concepts + + - Lazy Evaluation + - Types & Data Structures + - Category Theory + +* First-class functions + + - Have no restriction on their use + - Are values + - Enable the use of callback functions in JavaScript + + + +#+BEGIN_SRC js +const fn = function () { + return 2 +} +#+END_SRC + + +* Higher-order functions + +Functions that operate on other functions are higher-order functions + + + +#+BEGIN_SRC js +const succ = function (x) { + return x + 1 +} + +const arr = [1, 2, 3, 4] + +arr.map(succ) +#+END_SRC + +Here, =Array.prototype.map= is the higher-order function + +* Higher-order functions (cont.) + +Functions that return functions are also higher-order functions + + + +#+BEGIN_SRC js +function adder (n) { + return function (x) { + return n + x + } +} + +const add1 = adder(1) +#+END_SRC + +=adder= is a higher-order function + +* Pure functions + +Functions without side-effects + +#+BEGIN_SRC js +const succ = (x) => x + 1 + +console.log(succ(succ(1))) + +// could be optimised away by a compiler, e.g.: + +console.log(3) +#+END_SRC + +* Recursion + +Functions that call themselves + +#+BEGIN_SRC js +function fibonacci (n) { + switch (n) { + case 0: + case 1: + return 1 + default: + return fibonacci(n - 1) + fibonacci(n - 2) + } +} +#+END_SRC + +* Partial application + +The infamous =Function.prototype.bind= in JavaScript + +#+BEGIN_SRC js +function add (x, y) { + return x + y +} + +const add1 = add.bind(add, 1) + +add1(3) // = 4 +#+END_SRC + +* Partial application (cont.) + +After ES6 introduced arrow functions, partial application has become +more popular + +#+BEGIN_SRC js +const add = x => y => x + y +#+END_SRC + +* Currying + +Related to partial application, but more implicit and general + +Translates */1/ function of arity /n/* to */n/ functions of arity /1/* + +#+BEGIN_SRC js +function volume (w, d, h) { + return w * d * h +} + +const vol = curry(volume) +vol(10)(20)(30) +// is strictly equivalent to +volume(10, 20, 30) +#+END_SRC + +* Easy Currying + +In order to make currying (and partial application) easier to use, +move the *most important* argument to a function to the end: + +#+BEGIN_SRC js +const badMap = (arr, fn) => arr.map(fn) +const goodMap = (fn, arr) => arr.map(fn) +const curriedBadMap = curry(badmap) +const curriedGoodMap = curry(goodMap) + +const goodDoubleArray = goodMap(x => x * 2) +const badDoubleArray = badMap(_, x => x * 2) +#+END_SRC + +The bad version requires the curry function to support a magic +placeholder argument and doesn't look as clean. + +* Practical Currying + +Currying is not automatic in JavaScript, as in other languages + +External tools don't (currently) to statically analyse curried +functions + +Solution: Don't expose curried functions +Instead, write functions as if currying were automatic + +* Functional composition + +Creating functions from other functions + +Usually provided by =compose= (right-to-left) and =pipe= (left-to-right) + +A very simple definition of =compose= for only two functions would look like this + +#+BEGIN_SRC js +function compose (f, g) { + return function (...args) { + return f(g(...args)) + } +} +#+END_SRC + +* Functional composition (cont.) + +#+BEGIN_SRC js +const plusOne = x => x + 1 +const timesTwo = x => x * 2 + +const plusOneTimesTwo = compose(timesTwo, plusOne) +const timesTwoPlusOne = compose(plusOne, timesTwo) + +nextDoubled(3) // = (3 + 1) * 2 = 8 +timesTwoPlusOne(3) // = (3 * 2) + 1 = 7 +#+END_SRC + +* pipe + +What about =pipe=? + +=pipe= does the same thing, but runs the functions the other way around + +=pipe(f, g)= is the same as =compose(g, f)= + +* Point-free programming + +With currying and higher-order functions, we (often) don't need to declare function arguments + +#+BEGIN_SRC js +const modulo = a => b => b % a +const eq = a => b => a === b + +const isEven = x => eq(0)(modulo(2)(x)) +const isEvenPointFree = compose(eq(0), modulo(2)) +#+END_SRC + +* Further Resources + +- [[https://drboolean.gitbooks.io/mostly-adequate-guide/content/][Mostly adequate guide to FP (in javascript)]] +- [[http://ramdajs.com/][Ramda]], a general-purpose FP library +- [[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 new file mode 100644 index 0000000..0d126c7 --- /dev/null +++ b/static/talks/fp-js/s5-blank.html @@ -0,0 +1,50 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> + +<html xmlns="http://www.w3.org/1999/xhtml"> + +<head> +<title>Functional Programming</title> +<!-- metadata --> +<meta name="generator" content="S5" /> +<meta name="version" content="S5 1.1" /> +<meta name="presdate" content="20050728" /> +<meta name="author" content="Eric A. Meyer" /> +<meta name="company" content="Complex Spiral Consulting" /> +<!-- configuration parameters --> +<meta name="defaultView" content="slideshow" /> +<meta name="controlVis" content="hidden" /> +<!-- style sheet links --> +<link rel="stylesheet" href="ui/default/slides.css" type="text/css" media="projection" id="slideProj" /> +<link rel="stylesheet" href="ui/default/outline.css" type="text/css" media="screen" id="outlineStyle" /> +<link rel="stylesheet" href="ui/default/print.css" type="text/css" media="print" id="slidePrint" /> +<link rel="stylesheet" href="ui/default/opera.css" type="text/css" media="projection" id="operaFix" /> +<!-- S5 JS --> +<script src="ui/default/slides.js" type="text/javascript"></script> +</head> +<body> + +<div class="layout"> +<div id="controls"><!-- DO NOT EDIT --></div> +<div id="currentSlide"><!-- DO NOT EDIT --></div> +<div id="header"></div> +<div id="footer"> +<h1>Functional Programming</h1> +</div> + +</div> + + +<div class="presentation"> + +<div class="slide"> +<h1>Functional Programming</h1> +<h2>in JavaScript</h2> +<h3>Alan Pearce</h3> +</div> + + +</div> + +</body> +</html> diff --git a/static/talks/fp-js/ui/default/blank.gif b/static/talks/fp-js/ui/default/blank.gif new file mode 100644 index 0000000..75b945d --- /dev/null +++ b/static/talks/fp-js/ui/default/blank.gif Binary files differdiff --git a/static/talks/fp-js/ui/default/bodybg.gif b/static/talks/fp-js/ui/default/bodybg.gif new file mode 100755 index 0000000..5f448a1 --- /dev/null +++ b/static/talks/fp-js/ui/default/bodybg.gif Binary files differdiff --git a/static/talks/fp-js/ui/default/framing.css b/static/talks/fp-js/ui/default/framing.css new file mode 100644 index 0000000..14d8509 --- /dev/null +++ b/static/talks/fp-js/ui/default/framing.css @@ -0,0 +1,23 @@ +/* The following styles size, place, and layer the slide components. + Edit these if you want to change the overall slide layout. + The commented lines can be uncommented (and modified, if necessary) + to help you with the rearrangement process. */ + +/* target = 1024x768 */ + +div#header, div#footer, .slide {width: 100%; top: 0; left: 0;} +div#header {top: 0; height: 3em; z-index: 1;} +div#footer {top: auto; bottom: 0; height: 2.5em; z-index: 5;} +.slide {top: 0; width: 92%; padding: 3.5em 4% 4%; z-index: 2; list-style: none;} +div#controls {left: 50%; bottom: 0; width: 50%; z-index: 100;} +div#controls form {position: absolute; bottom: 0; right: 0; width: 100%; + margin: 0;} +#currentSlide {position: absolute; width: 10%; left: 45%; bottom: 1em; z-index: 10;} +html>body #currentSlide {position: fixed;} + +/* +div#header {background: #FCC;} +div#footer {background: #CCF;} +div#controls {background: #BBD;} +div#currentSlide {background: #FFC;} +*/ diff --git a/static/talks/fp-js/ui/default/iepngfix.htc b/static/talks/fp-js/ui/default/iepngfix.htc new file mode 100644 index 0000000..bba2db7 --- /dev/null +++ b/static/talks/fp-js/ui/default/iepngfix.htc @@ -0,0 +1,42 @@ +<public:component> +<public:attach event="onpropertychange" onevent="doFix()" /> + +<script> + +// IE5.5+ PNG Alpha Fix v1.0 by Angus Turnbull http://www.twinhelix.com +// Free usage permitted as long as this notice remains intact. + +// This must be a path to a blank image. That's all the configuration you need here. +var blankImg = 'ui/default/blank.gif'; + +var f = 'DXImageTransform.Microsoft.AlphaImageLoader'; + +function filt(s, m) { + if (filters[f]) { + filters[f].enabled = s ? true : false; + if (s) with (filters[f]) { src = s; sizingMethod = m } + } else if (s) style.filter = 'progid:'+f+'(src="'+s+'",sizingMethod="'+m+'")'; +} + +function doFix() { + if ((parseFloat(navigator.userAgent.match(/MSIE (\S+)/)[1]) < 5.5) || + (event && !/(background|src)/.test(event.propertyName))) return; + + if (tagName == 'IMG') { + if ((/\.png$/i).test(src)) { + filt(src, 'image'); // was 'scale' + src = blankImg; + } else if (src.indexOf(blankImg) < 0) filt(); + } else if (style.backgroundImage) { + if (style.backgroundImage.match(/^url[("']+(.*\.png)[)"']+$/i)) { + var s = RegExp.$1; + style.backgroundImage = ''; + filt(s, 'crop'); + } else filt(); + } +} + +doFix(); + +</script> +</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 new file mode 100644 index 0000000..9e9d2a3 --- /dev/null +++ b/static/talks/fp-js/ui/default/opera.css @@ -0,0 +1,7 @@ +/* DO NOT CHANGE THESE unless you really want to break Opera Show */ +.slide { + visibility: visible !important; + position: static !important; + page-break-before: always; +} +#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 new file mode 100644 index 0000000..62db519 --- /dev/null +++ b/static/talks/fp-js/ui/default/outline.css @@ -0,0 +1,15 @@ +/* don't change this unless you want the layout stuff to show up in the outline view! */ + +.layout div, #footer *, #controlForm * {display: none;} +#footer, #controls, #controlForm, #navLinks, #toggle { + display: block; visibility: visible; margin: 0; padding: 0;} +#toggle {float: right; padding: 0.5em;} +html>body #toggle {position: fixed; top: 0; right: 0;} + +/* making the outline look pretty-ish */ + +#slide0 h1, #slide0 h2, #slide0 h3, #slide0 h4 {border: none; margin: 0;} +#slide0 h1 {padding-top: 1.5em;} +.slide h1 {margin: 1.5em 0 0; padding-top: 0.25em; + border-top: 1px solid #888; border-bottom: 1px solid #AAA;} +#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 new file mode 100644 index 0000000..3d3acef --- /dev/null +++ b/static/talks/fp-js/ui/default/pretty.css @@ -0,0 +1,86 @@ +/* Following are the presentation styles -- edit away! */ + +body {background: #FFF url(bodybg.gif) -16px 0 no-repeat; color: #000; font-size: 2em;} +:link, :visited {text-decoration: none; color: #00C;} +#controls :active {color: #88A !important;} +#controls :focus {outline: 1px dotted #227;} +h1, h2, h3, h4 {font-size: 100%; margin: 0; padding: 0; font-weight: inherit;} +ul, pre {margin: 0; line-height: 1em;} +html, body {margin: 0; padding: 0;} + +blockquote, q {font-style: italic;} +blockquote {padding: 0 2em 0.5em; margin: 0 1.5em 0.5em; text-align: center; font-size: 1em;} +blockquote p {margin: 0;} +blockquote i {font-style: normal;} +blockquote b {display: block; margin-top: 0.5em; font-weight: normal; font-size: smaller; font-style: normal;} +blockquote b i {font-style: italic;} + +kbd {font-weight: bold; font-size: 1em;} +sup {font-size: smaller; line-height: 1px;} + +.slide code {padding: 2px 0.25em; font-weight: bold; color: #533;} +.slide code.bad, code del {color: red;} +.slide code.old {color: silver;} +.slide pre {padding: 0; margin: 0.25em 0 0.5em 0.5em; color: #533; font-size: 90%;} +.slide pre code {display: block;} +.slide ul {margin-left: 5%; margin-right: 7%; list-style: disc;} +.slide li {margin-top: 0.75em; margin-right: 0;} +.slide ul ul {line-height: 1;} +.slide ul ul li {margin: .2em; font-size: 85%; list-style: square;} +.slide img.leader {display: block; margin: 0 auto;} + +div#header, div#footer {background: #005; color: #AAB; + font-family: Verdana, Helvetica, sans-serif;} +div#header {background: #005 url(bodybg.gif) -16px 0 no-repeat; + line-height: 1px;} +div#footer {font-size: 0.5em; font-weight: bold; padding: 1em 0;} +#footer h1, #footer h2 {display: block; padding: 0 1em;} +#footer h2 {font-style: italic;} + +div.long {font-size: 0.75em;} +.slide h1 {position: absolute; top: 0.7em; left: 87px; z-index: 1; + margin: 0; padding: 0.3em 0 0 50px; white-space: nowrap; + font: bold 150%/1em Helvetica, sans-serif; text-transform: capitalize; + color: #DDE; background: #005;} +.slide h3 {font-size: 130%;} +h1 abbr {font-variant: small-caps;} + +div#controls {position: absolute; left: 50%; bottom: 0; + width: 50%; + text-align: right; font: bold 0.9em Verdana, Helvetica, sans-serif;} +html>body div#controls {position: fixed; padding: 0 0 1em 0; + top: auto;} +div#controls form {position: absolute; bottom: 0; right: 0; width: 100%; + margin: 0; padding: 0;} +#controls #navLinks a {padding: 0; margin: 0 0.5em; + background: #005; border: none; color: #779; + cursor: pointer;} +#controls #navList {height: 1em;} +#controls #navList #jumplist {position: absolute; bottom: 0; right: 0; background: #DDD; color: #227;} + +#currentSlide {text-align: center; font-size: 0.5em; color: #449;} + +#slide0 {padding-top: 3.5em; font-size: 90%;} +#slide0 h1 {position: static; margin: 1em 0 0; padding: 0; + font: bold 2em Helvetica, sans-serif; white-space: normal; + color: #000; background: transparent;} +#slide0 h2 {font: bold italic 1em Helvetica, sans-serif; margin: 0.25em;} +#slide0 h3 {margin-top: 1.5em; font-size: 1.5em;} +#slide0 h4 {margin-top: 0; font-size: 1em;} + +ul.urls {list-style: none; display: inline; margin: 0;} +.urls li {display: inline; margin: 0;} +.note {display: none;} +.external {border-bottom: 1px dotted gray;} +html>body .external {border-bottom: none;} +.external:after {content: " \274F"; font-size: smaller; color: #77B;} + +.incremental, .incremental *, .incremental *:after {color: #DDE; visibility: visible;} +img.incremental {visibility: hidden;} +.slide .current {color: #B02;} + + +/* diagnostics + +li:after {content: " [" attr(class) "]"; color: #F88;} + */ \ 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 new file mode 100644 index 0000000..e7a71d1 --- /dev/null +++ b/static/talks/fp-js/ui/default/print.css @@ -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 new file mode 100644 index 0000000..86444e0 --- /dev/null +++ b/static/talks/fp-js/ui/default/s5-core.css @@ -0,0 +1,9 @@ +/* Do not edit or override these styles! The system will likely break if you do. */ + +div#header, div#footer, div#controls, .slide {position: absolute;} +html>body div#header, html>body div#footer, + html>body div#controls, html>body .slide {position: fixed;} +.handout {display: none;} +.layout {display: block;} +.slide, .hideme, .incremental {visibility: hidden;} +#slide0 {visibility: visible;} diff --git a/static/talks/fp-js/ui/default/slides.css b/static/talks/fp-js/ui/default/slides.css new file mode 100644 index 0000000..0786d7d --- /dev/null +++ b/static/talks/fp-js/ui/default/slides.css @@ -0,0 +1,3 @@ +@import url(s5-core.css); /* required to make the slide show run at all */ +@import url(framing.css); /* sets basic placement and size of slide components */ +@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 new file mode 100644 index 0000000..38fe853 --- /dev/null +++ b/static/talks/fp-js/ui/default/slides.js @@ -0,0 +1,553 @@ +// S5 v1.1 slides.js -- released into the Public Domain +// +// Please see http://www.meyerweb.com/eric/tools/s5/credits.html for information +// about all the wonderful and talented contributors to this code! + +var undef; +var slideCSS = ''; +var snum = 0; +var smax = 1; +var incpos = 0; +var number = undef; +var s5mode = true; +var defaultView = 'slideshow'; +var controlVis = 'visible'; + +var isIE = navigator.appName == 'Microsoft Internet Explorer' && navigator.userAgent.indexOf('Opera') < 1 ? 1 : 0; +var isOp = navigator.userAgent.indexOf('Opera') > -1 ? 1 : 0; +var isGe = navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('Safari') < 1 ? 1 : 0; + +function hasClass(object, className) { + if (!object.className) return false; + return (object.className.search('(^|\\s)' + className + '(\\s|$)') != -1); +} + +function hasValue(object, value) { + if (!object) return false; + return (object.search('(^|\\s)' + value + '(\\s|$)') != -1); +} + +function removeClass(object,className) { + if (!object) return; + object.className = object.className.replace(new RegExp('(^|\\s)'+className+'(\\s|$)'), RegExp.$1+RegExp.$2); +} + +function addClass(object,className) { + if (!object || hasClass(object, className)) return; + if (object.className) { + object.className += ' '+className; + } else { + object.className = className; + } +} + +function GetElementsWithClassName(elementName,className) { + var allElements = document.getElementsByTagName(elementName); + var elemColl = new Array(); + for (var i = 0; i< allElements.length; i++) { + if (hasClass(allElements[i], className)) { + elemColl[elemColl.length] = allElements[i]; + } + } + return elemColl; +} + +function isParentOrSelf(element, id) { + if (element == null || element.nodeName=='BODY') return false; + else if (element.id == id) return true; + else return isParentOrSelf(element.parentNode, id); +} + +function nodeValue(node) { + var result = ""; + if (node.nodeType == 1) { + var children = node.childNodes; + for (var i = 0; i < children.length; ++i) { + result += nodeValue(children[i]); + } + } + else if (node.nodeType == 3) { + result = node.nodeValue; + } + return(result); +} + +function slideLabel() { + var slideColl = GetElementsWithClassName('*','slide'); + var list = document.getElementById('jumplist'); + smax = slideColl.length; + for (var n = 0; n < smax; n++) { + var obj = slideColl[n]; + + var did = 'slide' + n.toString(); + obj.setAttribute('id',did); + if (isOp) continue; + + var otext = ''; + var menu = obj.firstChild; + if (!menu) continue; // to cope with empty slides + while (menu && menu.nodeType == 3) { + menu = menu.nextSibling; + } + if (!menu) continue; // to cope with slides with only text nodes + + var menunodes = menu.childNodes; + for (var o = 0; o < menunodes.length; o++) { + otext += nodeValue(menunodes[o]); + } + list.options[list.length] = new Option(n + ' : ' + otext, n); + } +} + +function currentSlide() { + var cs; + if (document.getElementById) { + cs = document.getElementById('currentSlide'); + } else { + cs = document.currentSlide; + } + cs.innerHTML = '<span id="csHere">' + snum + '<\/span> ' + + '<span id="csSep">\/<\/span> ' + + '<span id="csTotal">' + (smax-1) + '<\/span>'; + if (snum == 0) { + cs.style.visibility = 'hidden'; + } else { + cs.style.visibility = 'visible'; + } +} + +function go(step) { + if (document.getElementById('slideProj').disabled || step == 0) return; + var jl = document.getElementById('jumplist'); + var cid = 'slide' + snum; + var ce = document.getElementById(cid); + if (incrementals[snum].length > 0) { + for (var i = 0; i < incrementals[snum].length; i++) { + removeClass(incrementals[snum][i], 'current'); + removeClass(incrementals[snum][i], 'incremental'); + } + } + if (step != 'j') { + snum += step; + lmax = smax - 1; + if (snum > lmax) snum = lmax; + if (snum < 0) snum = 0; + } else + snum = parseInt(jl.value); + var nid = 'slide' + snum; + var ne = document.getElementById(nid); + if (!ne) { + ne = document.getElementById('slide0'); + snum = 0; + } + if (step < 0) {incpos = incrementals[snum].length} else {incpos = 0;} + if (incrementals[snum].length > 0 && incpos == 0) { + for (var i = 0; i < incrementals[snum].length; i++) { + if (hasClass(incrementals[snum][i], 'current')) + incpos = i + 1; + else + addClass(incrementals[snum][i], 'incremental'); + } + } + if (incrementals[snum].length > 0 && incpos > 0) + addClass(incrementals[snum][incpos - 1], 'current'); + ce.style.visibility = 'hidden'; + ne.style.visibility = 'visible'; + jl.selectedIndex = snum; + currentSlide(); + number = 0; +} + +function goTo(target) { + if (target >= smax || target == snum) return; + go(target - snum); +} + +function subgo(step) { + if (step > 0) { + removeClass(incrementals[snum][incpos - 1],'current'); + removeClass(incrementals[snum][incpos], 'incremental'); + addClass(incrementals[snum][incpos],'current'); + incpos++; + } else { + incpos--; + removeClass(incrementals[snum][incpos],'current'); + addClass(incrementals[snum][incpos], 'incremental'); + addClass(incrementals[snum][incpos - 1],'current'); + } +} + +function toggle() { + var slideColl = GetElementsWithClassName('*','slide'); + var slides = document.getElementById('slideProj'); + var outline = document.getElementById('outlineStyle'); + if (!slides.disabled) { + slides.disabled = true; + outline.disabled = false; + s5mode = false; + fontSize('1em'); + for (var n = 0; n < smax; n++) { + var slide = slideColl[n]; + slide.style.visibility = 'visible'; + } + } else { + slides.disabled = false; + outline.disabled = true; + s5mode = true; + fontScale(); + for (var n = 0; n < smax; n++) { + var slide = slideColl[n]; + slide.style.visibility = 'hidden'; + } + slideColl[snum].style.visibility = 'visible'; + } +} + +function showHide(action) { + var obj = GetElementsWithClassName('*','hideme')[0]; + switch (action) { + case 's': obj.style.visibility = 'visible'; break; + case 'h': obj.style.visibility = 'hidden'; break; + case 'k': + if (obj.style.visibility != 'visible') { + obj.style.visibility = 'visible'; + } else { + obj.style.visibility = 'hidden'; + } + break; + } +} + +// 'keys' code adapted from MozPoint (http://mozpoint.mozdev.org/) +function keys(key) { + if (!key) { + key = event; + key.which = key.keyCode; + } + if (key.which == 84) { + toggle(); + return; + } + if (s5mode) { + switch (key.which) { + case 10: // return + case 13: // enter + if (window.event && isParentOrSelf(window.event.srcElement, 'controls')) return; + if (key.target && isParentOrSelf(key.target, 'controls')) return; + if(number != undef) { + goTo(number); + break; + } + case 32: // spacebar + case 34: // page down + case 39: // rightkey + case 40: // downkey + if(number != undef) { + go(number); + } else if (!incrementals[snum] || incpos >= incrementals[snum].length) { + go(1); + } else { + subgo(1); + } + break; + case 33: // page up + case 37: // leftkey + case 38: // upkey + if(number != undef) { + go(-1 * number); + } else if (!incrementals[snum] || incpos <= 0) { + go(-1); + } else { + subgo(-1); + } + break; + case 36: // home + goTo(0); + break; + case 35: // end + goTo(smax-1); + break; + case 67: // c + showHide('k'); + break; + } + if (key.which < 48 || key.which > 57) { + number = undef; + } else { + if (window.event && isParentOrSelf(window.event.srcElement, 'controls')) return; + if (key.target && isParentOrSelf(key.target, 'controls')) return; + number = (((number != undef) ? number : 0) * 10) + (key.which - 48); + } + } + return false; +} + +function clicker(e) { + number = undef; + var target; + if (window.event) { + target = window.event.srcElement; + e = window.event; + } else target = e.target; + if (target.getAttribute('href') != null || hasValue(target.rel, 'external') || isParentOrSelf(target, 'controls') || isParentOrSelf(target,'embed') || isParentOrSelf(target,'object')) return true; + if (!e.which || e.which == 1) { + if (!incrementals[snum] || incpos >= incrementals[snum].length) { + go(1); + } else { + subgo(1); + } + } +} + +function findSlide(hash) { + var target = null; + var slides = GetElementsWithClassName('*','slide'); + for (var i = 0; i < slides.length; i++) { + var targetSlide = slides[i]; + if ( (targetSlide.name && targetSlide.name == hash) + || (targetSlide.id && targetSlide.id == hash) ) { + target = targetSlide; + break; + } + } + while(target != null && target.nodeName != 'BODY') { + if (hasClass(target, 'slide')) { + return parseInt(target.id.slice(5)); + } + target = target.parentNode; + } + return null; +} + +function slideJump() { + if (window.location.hash == null) return; + var sregex = /^#slide(\d+)$/; + var matches = sregex.exec(window.location.hash); + var dest = null; + if (matches != null) { + dest = parseInt(matches[1]); + } else { + dest = findSlide(window.location.hash.slice(1)); + } + if (dest != null) + go(dest - snum); +} + +function fixLinks() { + var thisUri = window.location.href; + thisUri = thisUri.slice(0, thisUri.length - window.location.hash.length); + var aelements = document.getElementsByTagName('A'); + for (var i = 0; i < aelements.length; i++) { + var a = aelements[i].href; + var slideID = a.match('\#slide[0-9]{1,2}'); + if ((slideID) && (slideID[0].slice(0,1) == '#')) { + var dest = findSlide(slideID[0].slice(1)); + if (dest != null) { + if (aelements[i].addEventListener) { + aelements[i].addEventListener("click", new Function("e", + "if (document.getElementById('slideProj').disabled) return;" + + "go("+dest+" - snum); " + + "if (e.preventDefault) e.preventDefault();"), true); + } else if (aelements[i].attachEvent) { + aelements[i].attachEvent("onclick", new Function("", + "if (document.getElementById('slideProj').disabled) return;" + + "go("+dest+" - snum); " + + "event.returnValue = false;")); + } + } + } + } +} + +function externalLinks() { + if (!document.getElementsByTagName) return; + var anchors = document.getElementsByTagName('a'); + for (var i=0; i<anchors.length; i++) { + var anchor = anchors[i]; + if (anchor.getAttribute('href') && hasValue(anchor.rel, 'external')) { + anchor.target = '_blank'; + addClass(anchor,'external'); + } + } +} + +function createControls() { + var controlsDiv = document.getElementById("controls"); + if (!controlsDiv) return; + var hider = ' onmouseover="showHide(\'s\');" onmouseout="showHide(\'h\');"'; + var hideDiv, hideList = ''; + if (controlVis == 'hidden') { + hideDiv = hider; + } else { + hideList = hider; + } + controlsDiv.innerHTML = '<form action="#" id="controlForm"' + hideDiv + '>' + + '<div id="navLinks">' + + '<a accesskey="t" id="toggle" href="javascript:toggle();">Ø<\/a>' + + '<a accesskey="z" id="prev" href="javascript:go(-1);">«<\/a>' + + '<a accesskey="x" id="next" href="javascript:go(1);">»<\/a>' + + '<div id="navList"' + hideList + '><select id="jumplist" onchange="go(\'j\');"><\/select><\/div>' + + '<\/div><\/form>'; + if (controlVis == 'hidden') { + var hidden = document.getElementById('navLinks'); + } else { + var hidden = document.getElementById('jumplist'); + } + addClass(hidden,'hideme'); +} + +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 + if (!s5mode) return false; + var vScale = 22; // both yield 32 (after rounding) at 1024x768 + var hScale = 32; // perhaps should auto-calculate based on theme's declared value? + if (window.innerHeight) { + var vSize = window.innerHeight; + var hSize = window.innerWidth; + } else if (document.documentElement.clientHeight) { + var vSize = document.documentElement.clientHeight; + var hSize = document.documentElement.clientWidth; + } else if (document.body.clientHeight) { + var vSize = document.body.clientHeight; + var hSize = document.body.clientWidth; + } else { + var vSize = 700; // assuming 1024x768, minus chrome and such + var hSize = 1024; // these do not account for kiosk mode or Opera Show + } + var newSize = Math.min(Math.round(vSize/vScale),Math.round(hSize/hScale)); + fontSize(newSize + 'px'); + if (isGe) { // hack to counter incremental reflow bugs + var obj = document.getElementsByTagName('body')[0]; + obj.style.display = 'none'; + obj.style.display = 'block'; + } +} + +function fontSize(value) { + if (!(s5ss = document.getElementById('s5ss'))) { + if (!isIE) { + document.getElementsByTagName('head')[0].appendChild(s5ss = document.createElement('style')); + s5ss.setAttribute('media','screen, projection'); + s5ss.setAttribute('id','s5ss'); + } else { + document.createStyleSheet(); + document.s5ss = document.styleSheets[document.styleSheets.length - 1]; + } + } + if (!isIE) { + while (s5ss.lastChild) s5ss.removeChild(s5ss.lastChild); + s5ss.appendChild(document.createTextNode('body {font-size: ' + value + ' !important;}')); + } else { + document.s5ss.addRule('body','font-size: ' + value + ' !important;'); + } +} + +function notOperaFix() { + slideCSS = document.getElementById('slideProj').href; + var slides = document.getElementById('slideProj'); + var outline = document.getElementById('outlineStyle'); + slides.setAttribute('media','screen'); + outline.disabled = true; + if (isGe) { + slides.setAttribute('href','null'); // Gecko fix + slides.setAttribute('href',slideCSS); // Gecko fix + } + if (isIE && document.styleSheets && document.styleSheets[0]) { + document.styleSheets[0].addRule('img', 'behavior: url(ui/default/iepngfix.htc)'); + document.styleSheets[0].addRule('div', 'behavior: url(ui/default/iepngfix.htc)'); + document.styleSheets[0].addRule('.slide', 'behavior: url(ui/default/iepngfix.htc)'); + } +} + +function getIncrementals(obj) { + var incrementals = new Array(); + if (!obj) + return incrementals; + var children = obj.childNodes; + for (var i = 0; i < children.length; i++) { + var child = children[i]; + if (hasClass(child, 'incremental')) { + if (child.nodeName == 'OL' || child.nodeName == 'UL') { + removeClass(child, 'incremental'); + for (var j = 0; j < child.childNodes.length; j++) { + if (child.childNodes[j].nodeType == 1) { + addClass(child.childNodes[j], 'incremental'); + } + } + } else { + incrementals[incrementals.length] = child; + removeClass(child,'incremental'); + } + } + if (hasClass(child, 'show-first')) { + if (child.nodeName == 'OL' || child.nodeName == 'UL') { + removeClass(child, 'show-first'); + if (child.childNodes[isGe].nodeType == 1) { + removeClass(child.childNodes[isGe], 'incremental'); + } + } else { + incrementals[incrementals.length] = child; + } + } + incrementals = incrementals.concat(getIncrementals(child)); + } + return incrementals; +} + +function createIncrementals() { + var incrementals = new Array(); + for (var i = 0; i < smax; i++) { + incrementals[i] = getIncrementals(document.getElementById('slide'+i)); + } + return incrementals; +} + +function defaultCheck() { + var allMetas = document.getElementsByTagName('meta'); + for (var i = 0; i< allMetas.length; i++) { + if (allMetas[i].name == 'defaultView') { + defaultView = allMetas[i].content; + } + if (allMetas[i].name == 'controlVis') { + controlVis = allMetas[i].content; + } + } +} + +// Key trap fix, new function body for trap() +function trap(e) { + if (!e) { + e = event; + e.which = e.keyCode; + } + try { + modifierKey = e.ctrlKey || e.altKey || e.metaKey; + } + catch(e) { + modifierKey = false; + } + return modifierKey || e.which == 0; +} + +function startup() { + defaultCheck(); + if (!isOp) + createControls(); + slideLabel(); + fixLinks(); + externalLinks(); + fontScale(); + if (!isOp) { + notOperaFix(); + incrementals = createIncrementals(); + slideJump(); + if (defaultView == 'outline') { + toggle(); + } + document.onkeyup = keys; + document.onkeypress = trap; + document.onclick = clicker; + } +} + +window.onload = startup; +window.onresize = function(){setTimeout('fontScale()', 50);} \ No newline at end of file |