Commits

Rich Manalang [Atlassian] committed 7637f2e

added brace preso

  • Participants
  • Parent commits 8c6762f

Comments (0)

Files changed (23)

File brace/README.md

+impress.js
+============
+
+It's a presentation framework based on the power of CSS3 transforms and 
+transitions in modern browsers and inspired by the idea behind prezi.com.
+
+**WARNING**
+
+impress.js may not help you if you have nothing interesting to say ;)
+
+
+ABOUT THE NAME
+----------------
+
+impress.js name in [courtesy of @skuzniak](http://twitter.com/skuzniak/status/143627215165333504).
+
+It's an (un)fortunate coincidence that a Open/LibreOffice presentation tool is called Impress ;)
+
+
+VERSION HISTORY
+-----------------
+
+
+### 0.5.1 ([browse](http://github.com/bartaz/impress.js/tree/0.5.1), [zip](http://github.com/bartaz/impress.js/zipball/0.5.1), [tar](http://github.com/bartaz/impress.js/tarball/0.5.1))
+
+#### BUGFIX RELEASE
+
+Changes in version 0.5 introduced a bug (#126) that was preventing clicks on links (or any clickable elements) on
+currently active step. This release fixes this issue.
+
+
+
+### 0.5 ([browse](http://github.com/bartaz/impress.js/tree/0.5), [zip](http://github.com/bartaz/impress.js/zipball/0.5), [tar](http://github.com/bartaz/impress.js/tarball/0.5))
+
+#### CHANGELOG
+
+* API changed, so that `impress()` function no longer automatically initialize presentation; new method called `init`
+  was added to API and it should be used to start the presentation
+* event `impress:init` is triggered on root presentation element (`#impress` by default) when presentation is initialized
+* new CSS classes were added: `impress-disabled` is added to body element by the impress.js script and it's changed to 
+  `impress-enabled` when `init()` function is called
+* events added when step is entered and left - custom `impress:stepenter` and `impress:stepleave` events are triggered
+  on step elements and can be handled like any other DOM events (with `addEventListener`)
+* additional `past`, `present` and `future` classes are added to step elements
+    - `future` class appears on steps that were not yet visited
+    - `present` class appears on currently visible step - it's different from `active` class as `present` class
+       is added when transition finishes (step is entered)
+    - `past` class is added to already visited steps (when the step is left)
+* and good news, `goto()` API method is back! it seems that `goto` **was** a future reserved word but isn't anymore,
+  so we can use this short and pretty name instead of camelCassy `stepTo` - and yes, that means API changed again...
+* additionally `goto()` method now supports new types of parameters:
+    - you can give it a number of step you want to go to: `impress().goto(7)`
+    - or its id: `impress().goto("the-best-slide-ever")`
+    - of course DOM element is still acceptable: `impress().goto( document.getElementById("overview") )`
+* and if it's not enough, `goto()` also accepts second parameter to define the transition duration in ms, for example
+  `impress().goto("make-it-quick", 300)` or `impress().goto("now", 0)`
+
+#### UPGRADING FROM PREVIOUS VERSIONS
+
+In current version calling `impress()` doesn't automatically initialize the presentation. You need to call `init()`
+function from the API. So in a place were you called `impress()` to initialize impress.js simply change this call
+to `impress().init()`.
+
+Version 0.4 changed `goto` API method into `stepTo`. It turned out that `goto` is not a reserved word anymore, so it
+can be used in JavaScript. That's why version 0.5 brings it back and removes `stepTo`.
+
+So if you have been using version 0.4 and have any reference to `stepTo` API method make sure to change it to `goto`.
+
+
+
+### 0.4.1 ([browse](http://github.com/bartaz/impress.js/tree/0.4.1), [zip](http://github.com/bartaz/impress.js/zipball/0.4.1), [tar](http://github.com/bartaz/impress.js/tarball/0.4.1))
+
+#### BUGFIX RELEASE
+
+Changes is version 0.4 introduced a bug causing JavaScript errors being thrown all over the place in fallback mode.
+This release fixes this issue.
+
+It also adds a flag `impress.supported` that can be used in JavaScript to check if impress.js is supported in the browser.
+
+
+
+### 0.4 ([browse](http://github.com/bartaz/impress.js/tree/0.4), [zip](http://github.com/bartaz/impress.js/zipball/0.4), [tar](http://github.com/bartaz/impress.js/tarball/0.4))
+
+#### CHANGELOG
+
+* configuration options on `#impress` element: `data-perspective` (in px, defaults so 1000),
+  `data-transition-duration` (in ms, defaults to 1000)
+* automatic scaling to fit window size, with configuration options:  `data-width` (in px, defaults to 1024),
+  `data-height` (in px, defaults to 768), `max-scale` (defaults to 1), `min-scale` (defaults to 0)
+* `goto` API function was renamed to `stepTo` because `goto` is a future reserved work in JavaScript,
+  so **please make sure to update your code**
+* fallback `impress-not-supported` class is now set on `body` element instead of `#impress` element and it's
+  replaced with `impress-supported` when browser supports all required features
+* classes `step-ID` used to indicate progress of the presentation are now renamed to `impress-on-ID` and are
+  set on `body` element, so **please make sure to update your code**
+* basic validation of configuration options
+* couple of typos and bugs fixed
+* favicon added ;)
+
+
+#### UPGRADING FROM PREVIOUS VERSIONS
+
+If in your custom JavaScript code you were using `goto()` function from impress.js API make sure to change it
+to `stepTo()`.
+
+If in your CSS you were using classes based on currently active step with `step-` prefix, such as `step-bored`
+(where `bored` is the id of the step element) make sure to change it to `impress-on-` prefix
+(for example `impress-on-bored`). Also in previous versions these classes were assigned to `#impress` element
+and now they are added to `body` element, so if your CSS code depends on this, it also should be updated.
+
+Same happened to `impress-not-supported` class name - it was moved from `#impress` element to `body`, so update
+your CSS if it's needed.
+
+#### NOTE ON BLACKBERRY PLAYBOOK
+
+Changes and fixes added in this version have broken the experience on Blackberry Playbook with OS in version 1.0.
+It happened due to a bug in the Playbook browser in this version. Fortunately in version 2.0 of Playbook OS this
+bug was fixed and impress.js works fine.
+
+So currently impress.js work only on Blackberry Playbook with latest OS. Fortunately, [it seems that most of the
+users](http://twitter.com/n_adam_stanley/status/178188611827679233) [are quite quick with updating their devices]
+(http://twitter.com/brcewane/status/178230406196379648)
+
+
+
+### 0.3 ([browse](http://github.com/bartaz/impress.js/tree/0.3), [zip](http://github.com/bartaz/impress.js/zipball/0.3), [tar](http://github.com/bartaz/impress.js/tarball/0.3))
+
+#### CHANGELOG
+
+* minor CSS 3D fixes
+* basic API to control the presentation flow from JavaScript
+* touch event support
+* basic support for iPad (iOS 5 and iOS 4 with polyfills) and Blackberry Playbook
+
+#### UPGRADING FROM PREVIOUS VERSIONS
+
+Because API was introduced the way impress.js script is initialized was changed a bit. You not only has to include
+`impress.js` script file, but also call `impress()` function.
+
+See the source of `index.html` for example and more details.
+
+
+### 0.2 ([browse](http://github.com/bartaz/impress.js/tree/0.2), [zip](http://github.com/bartaz/impress.js/zipball/0.2), [tar](http://github.com/bartaz/impress.js/tarball/0.2))
+
+* tutorial/documentation added to `index.html` source file
+* being even more strict with strict mode
+* code clean-up
+* couple of small bug-fixes
+
+
+### 0.1 ([browse](http://github.com/bartaz/impress.js/tree/0.1), [zip](http://github.com/bartaz/impress.js/zipball/0.1), [tar](http://github.com/bartaz/impress.js/tarball/0.1))
+
+First release.
+
+Contains basic functionality for step placement and transitions between them
+with simple fallback for non-supporting browsers.
+
+
+
+HOW TO USE IT
+---------------
+
+[Use the source](http://github.com/bartaz/impress.js/blob/master/index.html), Luke ;)
+
+If you have no idea what I mean by that, or you just clicked that link above and got 
+very confused by all these strange characters that got displayed on your screen,
+it's a sign, that impress.js is not for you.
+
+Sorry.
+
+Fortunately there are some guys on GitHub that got quite excited with the idea of building
+editing tool for impress.js. Let's hope they will manage to do it.
+
+
+EXAMPLES AND DEMOS
+--------------------
+
+### Official demo
+
+[impress.js demo](http://bartaz.github.com/impress.js) by [@bartaz](http://twitter.com/bartaz)
+
+### Presentations
+
+[CSS 3D transforms](http://bartaz.github.com/meetjs/css3d-summit) from [meet.js summit](http://summit.meetjs.pl) by [@bartaz](http://twitter.com/bartaz)
+
+[What the Heck is Responsive Web Design](http://johnpolacek.github.com/WhatTheHeckIsResponsiveWebDesign-impressjs/) by John Polacek [@johnpolacek](http://twitter.com/johnpolacek)
+
+[12412.org presentation to Digibury](http://extra.12412.org/digibury/) by Stephen Fulljames [@fulljames](http://twitter.com/fulljames)
+
+[Data center virtualization with Wakame-VDC](http://wakame.jp/wiki/materials/20120114_TLUG/) by Andreas Kieckens [@Metallion98](https://twitter.com/#!/Metallion98)
+
+[Asynchronous JavaScript](http://www.medikoo.com/asynchronous-javascript/3d/) by Mariusz Nowak [@medikoo](http://twitter.com/medikoo)
+
+[Introduction to Responsive Design](http://www.alecrust.com/factory/rd-presentation/) by Alec Rust [@alecrust] (http://twitter.com/alecrust)
+
+[Bonne année 2012](http://duael.fr/voeux/2012/) by Edouard Cunibil [@DuaelFr](http://twitter.com/DuaelFr)
+
+[Careers in Free and Open Source Software](http://exequiel09.github.com/symposium-presentation/) by Exequiel Ceasar Navarrete [@ichigo1411](http://twitter.com/ichigo1411)
+
+[HTML5 Future : to infinity and beyond!](http://sylvainw.github.com/HTML5-Future/index_en.html) by Sylvain Weber [@sylvainw](http://twitter.com/sylvainw)
+
+### Websites and portfolios
+
+[lioshi.com](http://lioshi.com) by @lioshi
+
+[alingham.com](http://www.alingham.com) by Al Ingham [@alingham](http://twitter.com/alingham)
+
+[nice-shots.de](http://nice-shots.de) by [@NiceShots](http://twitter.com/NiceShots)
+
+[museum140](http://www.youtube.com/watch?v=ObLiikJEt94) Shorty Award promo video [entirely made with ImpressJS](http://thingsinjars.com/post/446/museum140-shorty/) by [@thingsinjars](http://twitter.com/thingsinjars)
+
+[electricanimal.co.uk](http://www.electricanimal.co.uk) by [@elecmal](http://twitter.com/elecmal)
+
+[t3kila.com](http://www.t3kila.com) by Romain Wurtz
+
+If you have used impress.js in your presentation (or website) and would like to have it listed here,
+please contact me via GitHub or send me a pull request to updated `README.md` file.
+
+
+
+WANT TO CONTRIBUTE?
+---------------------
+
+If you've found a bug or have a great idea for new feature let me know by [adding your suggestion]
+(http://github.com/bartaz/impress.js/issues/new) to [issues list](https://github.com/bartaz/impress.js/issues).
+
+If you have fixed a bug or implemented a feature that you'd like to share, send your pull request against [dev branch]
+(http://github.com/bartaz/impress.js/tree/dev). But remember that I only accept code that fits my vision of impress.js
+and my coding standards - so make sure you are open for discussion :)
+
+
+
+BROWSER SUPPORT
+-----------------
+
+### TL;DR;
+
+Currently impress.js works fine in latest Chrome/Chromium browser, Safari 5.1 and Firefox 10.
+With addition of some HTML5 polyfills (see below for details) it should work in Internet Explorer 10
+(currently available as Developers Preview).
+It doesn't work in Opera, as it doesn't support CSS 3D transforms.
+
+As a presentation tool it was not developed with mobile browsers in mind, but some tablets are good
+enough to run it, so it should work quite well on iPad (iOS 5, or iOS 4 with HTML5 polyfills) and 
+Blackberry Playbook.
+
+### Still interested? Read more...
+
+Additionally for the animations to run smoothly it's required to have hardware
+acceleration support in your browser. This depends on the browser, your operating
+system and even kind of graphic hardware you have in your machine.
+
+For browsers not supporting CSS3 3D transforms impress.js adds `impress-not-supported`
+class on `#impress` element, so fallback styles can be applied to make all the content accessible.
+
+
+### Even more explanation and technical stuff
+
+Let's put this straight -- wide browser support was (and is) not on top of my priority list for
+impress.js. It's built on top of fresh technologies that just start to appear in the browsers
+and I'd like to rather look forward and develop for the future than being slowed down by the past.
+
+But it's not "hard-coded" for any particular browser or engine. If any browser in future will
+support features required to run impress.js, it will just begin to work there without changes in
+the code.
+
+From technical point of view all the positioning of presentation elements in 3D requires CSS 3D
+transforms support. Transitions between presentation steps are based on CSS transitions.
+So these two features are required by impress.js to display presentation correctly.
+
+Unfortunately the support for CSS 3D transforms and transitions is not enough for animations to
+run smoothly. If the browser doesn't support hardware acceleration or the graphic card is not 
+good enough the transitions will be laggy.
+
+Additionally the code of impress.js relies on APIs proposed in HTML5 specification, including
+`classList` and `dataset` APIs. If they are not available in the browser, impress.js will not work.
+
+Fortunately, as these are JavaScript APIs there are polyfill libraries that patch older browsers
+with these APIs.
+
+For example IE10 is said to support CSS 3D transforms and transitions, but it doesn't have `classList`
+not `dataset` APIs implemented at the moment. So including polyfill libraries *should* help IE10
+with running impress.js.
+
+
+### And few more details about mobile support
+
+Mobile browsers are currently not supported. Even Android browsers that support CSS 3D transforms are
+forced into fallback view at this point.
+
+Fortunately some tablets seem to have good enough hardware support and browsers to handle it.
+Currently impress.js presentations should work on iPad and Blackberry Playbook.
+
+In theory iPhone should also be able to run it (as it runs the same software as iPad), but I haven't
+found a good way to handle it's small screen.
+
+Also note that iOS supports `classList` and `dataset` APIs starting with version 5, so iOS 4.X and older
+requires polyfills to work.
+
+
+LICENSE
+---------
+
+Copyright 2011-2012 Bartek Szopka
+
+Released under the MIT and GPL Licenses.
+
+

File brace/apple-touch-icon.png

Added
New image

File brace/css/impress-demo.css

+/**
+ * This is a stylesheet for a demo presentation for impress.js
+ * 
+ * It is not meant to be a part of impress.js and is not required by impress.js.
+ * I expect that anyone creating a presentation for impress.js would create their own
+ * set of styles.
+ */
+
+
+/* http://meyerweb.com/eric/tools/css/reset/ 
+   v2.0 | 20110126
+   License: none (public domain)
+*/
+
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, embed, 
+figure, figcaption, footer, header, hgroup, 
+menu, nav, output, ruby, section, summary,
+time, mark, audio, video {
+    margin: 0;
+    padding: 0;
+    border: 0;
+    font-size: 100%;
+    font: inherit;
+    vertical-align: baseline;
+}
+
+/* HTML5 display-role reset for older browsers */
+article, aside, details, figcaption, figure, 
+footer, header, hgroup, menu, nav, section {
+    display: block;
+}
+body {
+    line-height: 1;
+}
+ol, ul {
+    list-style: none;
+}
+blockquote, q {
+    quotes: none;
+}
+blockquote:before, blockquote:after,
+q:before, q:after {
+    content: '';
+    content: none;
+}
+
+table {
+    border-collapse: collapse;
+    border-spacing: 0;
+}
+
+
+body {
+    font-family: 'Open Sans', 'PT Sans', sans-serif;
+    min-height: 740px;
+
+    /*
+    background: rgb(215, 215, 215);
+    background: -webkit-gradient(radial, 50% 50%, 0, 50% 50%, 500, from(rgb(240, 240, 240)), to(rgb(190, 190, 190)));
+    background: -webkit-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
+    background:    -moz-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
+    background:      -o-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
+    background:         radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
+    */
+    background: #eee;
+
+    -webkit-font-smoothing: antialiased;
+    color: #333;
+    text-shadow:2px 2px 0 #fff !important;
+}
+
+b, strong { font-weight: bold }
+i, em { font-style: italic}
+
+a {
+    color: inherit;
+    text-decoration: none;
+    padding: 0 0.1em;
+    background: rgba(255,255,255,0.5);
+    text-shadow: -1px -1px 2px rgba(100,100,100,0.9);
+    border-radius: 0.2em;
+    
+    -webkit-transition: 0.5s;
+    -moz-transition:    0.5s;
+    -ms-transition:     0.5s;
+    -o-transition:      0.5s;
+    transition:         0.5s;
+}
+
+a:hover {
+    background: rgba(255,255,255,1);
+    text-shadow: -1px -1px 2px rgba(100,100,100,0.5);
+}
+
+/* enable clicking on elements 'hiding' behind body in 3D */
+body     { pointer-events: none; }
+#impress { pointer-events: auto; }
+
+/* COMMON STEP STYLES */
+
+.step {
+    width: 1000px;
+    padding: 40px;
+
+    -webkit-box-sizing: border-box;
+    -moz-box-sizing:    border-box;
+    -ms-box-sizing:     border-box;
+    -o-box-sizing:      border-box;
+    box-sizing:         border-box;
+
+
+    font-size: 48px;
+    line-height: 1.5;
+}
+
+.step {
+    -webkit-transition: opacity 1s;
+    -moz-transition:    opacity 1s;
+    -ms-transition:     opacity 1s;
+    -o-transition:      opacity 1s;
+    transition:         opacity 1s;
+}
+
+/* fade out inactive slides */
+.step:not(.active) {
+    opacity: 0.1;
+}
+
+/* STEP SPECIFIC STYLES */
+
+/* hint on the first slide */
+
+.hint {
+    position: fixed;
+    left: 0;
+    right: 0;
+    bottom: 200px;
+    
+    background: rgba(0,0,0,0.5);
+    color: #EEE;
+    text-align: center;
+    
+    font-size: 50px;
+    padding: 20px;
+    
+    z-index: 100;
+    
+    opacity: 0;
+    
+    -webkit-transform: translateY(400px);
+    -moz-transform:    translateY(400px);
+    -ms-transform:     translateY(400px);
+    -o-transform:      translateY(400px);
+    transform:         translateY(400px);
+
+    -webkit-transition: opacity 1s, -webkit-transform 0.5s 1s;
+    -moz-transition:    opacity 1s,    -moz-transform 0.5s 1s;
+    -ms-transition:     opacity 1s,     -ms-transform 0.5s 1s;
+    -o-transition:      opacity 1s,      -o-transform 0.5s 1s;
+    transition:         opacity 1s,         transform 0.5s 1s;
+}
+
+.impress-on-bored .hint {
+    opacity: 1;
+    
+    -webkit-transition: opacity 1s 5s, -webkit-transform 0.5s;
+    -moz-transition:    opacity 1s 5s,    -moz-transform 0.5s;
+    -ms-transition:     opacity 1s 5s,     -ms-transform 0.5s;
+    -o-transition:      opacity 1s 5s,      -o-transform 0.5s;
+    transition:         opacity 1s 5s,         transform 0.5s;
+    
+    -webkit-transform: translateY(0px);
+    -moz-transform:    translateY(0px);
+    -ms-transform:     translateY(0px);
+    -o-transform:      translateY(0px);
+    transform:         translateY(0px);
+}
+
+/* impress.js title */
+
+#title {
+    padding: 0;
+}
+
+#title .try {
+    font-size: 64px;
+    position: absolute;
+    top: -0.5em;
+    left: 1.5em;
+    
+    -webkit-transform: translateZ(20px);
+    -moz-transform:    translateZ(20px);
+    -ms-transform:     translateZ(20px);
+    -o-transform:      translateZ(20px);
+    transform:         translateZ(20px);
+}
+
+#title h1 {
+    font-size: 190px;
+    
+    -webkit-transform: translateZ(50px);
+    -moz-transform:    translateZ(50px);
+    -ms-transform:     translateZ(50px);
+    -o-transform:      translateZ(50px);
+    transform:         translateZ(50px);
+}
+
+#title .footnote {
+    font-size: 32px;
+}
+
+/* big thoughts */
+
+#big {
+    width: 600px;
+    text-align: center;
+    font-size: 60px;
+    line-height: 1;
+}
+
+#big b {
+    display: block;
+    font-size: 250px;
+    line-height: 250px;
+}
+
+#big .thoughts {
+    font-size: 90px;
+    line-height: 150px;
+}
+
+/* tiny ideas */
+
+#tiny {
+    width: 500px;
+    text-align: center;
+}
+
+#ing {
+    width: 500px;
+}
+
+#ing b {
+    display: inline-block;
+    -webkit-transition: 0.5s;
+    -moz-transition:    0.5s;
+    -ms-transition:     0.5s;
+    -o-transition:      0.5s;
+    transition:         0.5s;
+}
+
+#ing.present .positioning {
+    -webkit-transform: translateY(-10px);
+    -moz-transform:    translateY(-10px);
+    -ms-transform:     translateY(-10px);
+    -o-transform:      translateY(-10px);
+    transform:         translateY(-10px);
+}
+
+#ing.present .rotating {
+    -webkit-transform: rotate(-10deg);
+    -moz-transform:    rotate(-10deg);
+    -ms-transform:     rotate(-10deg);
+    -o-transform:      rotate(-10deg);
+    transform:         rotate(-10deg);
+
+    -webkit-transition-delay: 0.25s;
+    -moz-transition-delay:    0.25s;
+    -ms-transition-delay:     0.25s;
+    -o-transition-delay:      0.25s;
+    transition-delay:         0.25s;
+}
+
+#ing.present .scaling {
+    -webkit-transform: scale(0.7);
+    -moz-transform:    scale(0.7);
+    -ms-transform:     scale(0.7);
+    -o-transform:      scale(0.7);
+    transform:         scale(0.7);
+
+    -webkit-transition-delay: 0.5s;
+    -moz-transition-delay:    0.5s;
+    -ms-transition-delay:     0.5s;
+    -o-transition-delay:      0.5s;
+    transition-delay:         0.5s;
+}
+
+/* imagination */
+
+#imagination {
+    width: 600px;
+}
+
+#imagination .imagination {
+    font-size: 78px;
+}
+
+/* use the source, Luke */
+
+#source {
+    width: 700px;
+    padding-bottom: 300px;
+    
+    /* Yoda Icon :: Pixel Art from Star Wars http://www.pixeljoint.com/pixelart/1423.htm */
+    background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARgAAAEYCAMAAACwUBm+AAAAAXNSR0IArs4c6QAAAKtQTFRFsAAAvbWSLUUrLEQqY1s8UYJMqJ1vNTEgOiIdIzYhjIFVLhsXZ6lgSEIsP2U8JhcCVzMsSXZEgXdOO145XJdWOl03LzAYMk4vSXNExr+hwcuxRTs1Qmk+RW9Am49eFRANQz4pUoNMQWc+OSMDTz0wLBsCNVMxa2NBOyUDUoNNSnlEWo9VRGxAVzYFl6tXCggHbLNmMUIcHhwTXkk5f3VNRT8wUT8xAAAACQocRBWFFwAAAAF0Uk5TAEDm2GYAAAPCSURBVHja7d3JctNAFIZRMwRCCGEmzPM8z/D+T8bu/ptbXXJFdij5fMt2Wuo+2UgqxVmtttq5WVotLzBgwIABAwYMGDCn0qVqbo69psPqVpWx+1XG5iaavF8wYMCAAQMGDBgwi4DJ6Y6qkxB1HNlcN3a92gbR5P2CAQMGDBgwYMCAWSxMlrU+UY5yu2l9okfV4bAxUVbf7TJnAwMGDBgwYMCAAbMLMHeqbGR82Zy+VR1Ht81nVca6R+UdTLaU24Ruzd3qM/e4yjnAgAEDBgwYMGDA7AJMd1l/3NRdVGcj3eX/2WEhCmDGxnM7yqygu8XIPjJj8iN/MGDAgAEDBgwYMAuDGb8q0RGlLCHLv1t9qDKWn3vdNHVuEI6HPaxO9Jo3GDBgwIABAwYMmIXBdC9ShGgMk+XnkXUeuGcsP/e1+lhNnZsL/G5Vs3OAAQMGDBgwYMCAWSxMR3SzOmraG5atdy9wZKzb+vg16qyqe2FltbnAgAEDBgwYMGDALAxmTJSuN3WA76rnVca6GTnemGN1WoEBAwYMGDBgwIBZGMxUomy4+xO899V4LAg5Xnc2MGDAgAEDBgwYMGA218Wq+2K1LDqvY9xZu8zN8fICdM6btYABAwYMGDBgwIABMzfH0+pGU5afze2tXebmeAfVz+p8BQYMGDBgwIABAwbMPBzZ+oWmfJrln1273FhkbHzee9WWbw7AgAEDBgwYMGDALAKm43hcdctKgblcPamOhuXnXlY5Xs6bsW4FGyQCAwYMGDBgwIABswiYMceZKgvMo+h8mrHLTdn676rj+FEFoTtHd8MwOxEYMGDAgAEDBgyYRcBM5UhXqiymW3R3c9ARhWO/OmjqfjVZy+xEYMCAAQMGDBgwYBYG073OnCV0RFNhMhaOa9WfKmOB6XjHMN1tQmaAAQMGDBgwYMCA2VWY7vXjz1U4croAzgPztwIDBgwYMGDAgAEDZhswh035NBw59Dww3RgYMGDAgAEDBgwYMJuD6f4tXT7NUqfCdBvZLkxXdgQGDBgwYMCAAQNmt2DGj8WzwAfV/w7T/aq7mxwwYMCAAQMGDBgwuwqTOo7uTwTngflSzQ3TdaJvAwEDBgwYMGDAgAED5gSvgbyo5oHZ4Pc+gwEDBgwYMGDAgAEzhOm+5G0qTGaAAQMGDBgwYMCAAXNaMOcnls3tNwWm+zRzp54NDBgwYMCAAQMGDJh5YNL36k1TLuGvVq+qnKMbS5n7tulT9asCAwYMGDBgwIABA2ZumKuztLnjgQEDBgwYMGDAgNl5mH/4/ltKA6vBNAAAAABJRU5ErkJggg==);
+    background-position: bottom right;
+    background-repeat: no-repeat;
+}
+
+#source q {
+    font-size: 60px;
+}
+
+/* it's in 3D */
+
+#its-in-3d p {
+    -webkit-transform-style: preserve-3d;
+    -moz-transform-style:    preserve-3d; /* Y U need this Firefox?! */
+    -ms-transform-style:     preserve-3d;
+    -o-transform-style:      preserve-3d;
+    transform-style:         preserve-3d;
+}
+
+#its-in-3d span,
+#its-in-3d b {
+    display: inline-block;
+    -webkit-transform: translateZ(40px);
+    -moz-transform:    translateZ(40px);
+    -ms-transform:     translateZ(40px);
+    -o-transform:      translateZ(40px);
+     transform:        translateZ(40px);
+            
+    -webkit-transition: 0.5s;
+    -moz-transition:    0.5s;
+    -ms-transition:     0.5s;
+    -o-transition:      0.5s;
+    transition:         0.5s;
+}
+
+#its-in-3d .have {
+    -webkit-transform: translateZ(-40px);
+    -moz-transform:    translateZ(-40px);
+    -ms-transform:     translateZ(-40px);
+    -o-transform:      translateZ(-40px);
+    transform:         translateZ(-40px);
+}
+
+#its-in-3d .you {
+    -webkit-transform: translateZ(20px);
+    -moz-transform:    translateZ(20px);
+    -ms-transform:     translateZ(20px);
+    -o-transform:      translateZ(20px);
+    transform:         translateZ(20px);
+}
+
+#its-in-3d .noticed {
+    -webkit-transform: translateZ(-40px);
+    -moz-transform:    translateZ(-40px);
+    -ms-transform:     translateZ(-40px);
+    -o-transform:      translateZ(-40px);
+    transform:         translateZ(-40px);
+}
+
+#its-in-3d .its {
+    -webkit-transform: translateZ(60px);
+    -moz-transform:    translateZ(60px);
+    -ms-transform:     translateZ(60px);
+    -o-transform:      translateZ(60px);
+    transform:         translateZ(60px);
+}
+
+#its-in-3d .in {
+    -webkit-transform: translateZ(-10px);
+    -moz-transform:    translateZ(-10px);
+    -ms-transform:     translateZ(-10px);
+    -o-transform:      translateZ(-10px);
+    transform:         translateZ(-10px);
+}
+
+#its-in-3d .footnote {
+    font-size: 32px;
+
+    -webkit-transform: translateZ(-10px);
+    -moz-transform:    translateZ(-10px);
+    -ms-transform:     translateZ(-10px);
+    -o-transform:      translateZ(-10px);
+    transform:         translateZ(-10px);
+}
+
+#its-in-3d.present span,
+#its-in-3d.present b {
+    -webkit-transform: translateZ(0px);
+    -moz-transform:    translateZ(0px);
+    -ms-transform:     translateZ(0px);
+    -o-transform:      translateZ(0px);
+    transform:         translateZ(0px);
+}
+
+/* overview step */
+
+#overview {
+    z-index: -1;
+    padding: 0;
+}
+
+/* on overview step everything is visible */
+
+.impress-on-overview .step {
+    opacity: 1;
+    cursor: pointer;
+}
+
+/*
+ * SLIDE STEP STYLES
+ *
+ * inspired by: http://html5slides.googlecode.com/svn/trunk/styles.css
+ *
+ * ;)
+ */
+
+.slide {
+    display: block;
+
+    width: 1000px;
+    height: 700px;
+
+    padding: 40px 60px;
+
+    border-radius: 10px;
+
+    background-color: white;
+
+    box-shadow: 0 2px 6px rgba(0, 0, 0, .1);
+    border: 1px solid rgba(0, 0, 0, .3);
+
+
+    color: rgb(102, 102, 102);
+
+    font-size: 30px;
+    line-height: 36px;
+
+    letter-spacing: -1px;
+}
+
+.slide q {
+    display: block;
+    font-size: 50px;
+    line-height: 72px;
+
+    margin-top: 100px;
+}
+
+.slide q strong {
+    white-space: nowrap;
+}
+
+
+/* IMPRESS NOT SUPPORTED STYLES */
+
+.fallback-message {
+    line-height: 1.3;
+    
+    display: none;
+    width: 780px;
+    padding: 10px 10px 0;
+    margin: 20px auto;
+
+    border-radius: 10px;
+    border: 1px solid #E4C652;
+    background: #EEDC94;
+}
+
+.fallback-message p {
+    margin-bottom: 10px;
+}
+
+.impress-disabled .step,
+.impress-not-supported .step {
+    position: relative;
+    opacity: 1;
+    margin: 20px auto;
+}
+
+.impress-not-supported .fallback-message {
+    display: block;
+}
+
+.profile {
+    width:800px;
+    font-size:2.1em;
+}
+.profile img {
+    float:left;
+    width:90px;
+    border-radius:100px;
+    margin-right:15px;
+    box-shadow:rgba(0,0,0,.7) 0 3px 10px;
+}
+.centered {
+    text-align:center;
+}
+h1 { font-size: 2em; font-weight:700; }
+h3 { font-size: .75em; font-weight:normal }
+h4 { font-size: 1em; font-weight:700 }
+h5 { font-size: .8em; font-weight:600 }
+pre {
+  border-radius:20px;
+  text-shadow:none;
+  font-family:"Menlo","American Typewriter";
+  font-size:24px;
+  padding:20px;
+  line-height:0;
+}
+pre code {
+  padding-bottom:0;
+  margin-bottom:0;
+  line-height:120%;
+}

File brace/css/themes/all-hallows-eve.css

+/**
+ * All Hallows Eve theme
+ *
+ * @author Flinn Mueller
+ * @version 1.0
+ * @attribution Adapted from Ultraviolet RubyGem
+ */
+pre {
+    background: #000;
+    white-space: pre-wrap;
+    white-space: -moz-pre-wrap;
+    white-space: -pre-wrap;
+    white-space: -o-pre-wrap;
+    word-wrap: break-word;
+    margin: 0px;
+    padding: 0px;
+    padding: 10px;
+    color: #fff;
+    font-size: 14px;
+    margin-bottom: 20px;
+}
+
+pre, code {
+    font-family: 'Monaco', courier, monospace;
+}
+
+pre .comment {
+    color: #9933CC;
+}
+
+pre .constant, pre .integer {
+    color: #3387CC;
+}
+
+pre .storage {
+    color: #CC7833;
+}
+
+pre .string {
+    color: #66CC33;
+}
+
+pre .keyword, pre .selector {
+    color: #CC7833;
+}
+
+pre .parent {
+    font-style: italic;
+}
+
+pre .entity, pre .meta {
+}
+
+pre .support {
+    color: #C83730;
+}

File brace/css/themes/blackboard.css

+/**
+ * Blackboard theme
+ *
+ * @author Domenico Carbotta
+ * @version 1.0
+ */
+pre {
+    background: #0B1022;
+    white-space: pre-wrap;
+    white-space: -moz-pre-wrap;
+    white-space: -pre-wrap;
+    white-space: -o-pre-wrap;
+    word-wrap: break-word;
+    margin: 0px;
+    padding: 0px;
+    padding: 10px;
+    color: #fff;
+    font-size: 14px;
+    margin-bottom: 20px;
+}
+
+pre, code {
+    font-family: 'Monaco', courier, monospace;
+}
+
+pre .comment {
+    color: #727272;
+}
+
+pre .constant, pre .integer {
+    color: #D8FA3C;
+}
+
+pre .storage {
+    color: #FBDE2D;
+}
+
+pre .string {
+    color: #61CE3C;
+}
+
+pre .keyword, pre .selector {
+    color: #FBDE2D;
+}
+
+pre .parent {
+    font-style: italic;
+}
+
+pre .entity, pre .meta {
+    color: #FF6400;
+}
+
+pre .support {
+    color: #8DA6CE;
+}
+
+pre .variable.global, pre .variable.class, pre .variable.instance {
+    color: #FF6400;
+}

File brace/css/themes/tricolore.css

+/**
+ * Tricolore theme
+ *
+ * @author Jean Nicolas
+ * @version 1.0
+ */
+pre {
+    background: #FFF;
+    white-space: pre-wrap;
+    white-space: -moz-pre-wrap;
+    white-space: -pre-wrap;
+    white-space: -o-pre-wrap;
+    word-wrap: break-word;
+    margin: 0px;
+    padding: 0px;
+    padding: 10px;
+    color: #000;
+    font-size: 12px;
+    margin-bottom: 20px;
+	line-height: 16px;
+}
+
+pre, code {
+    font-family: 'Monaco', 'Consolas', monospace;
+}
+
+pre .comment {
+    color: #7E7E7E;
+	font-style: italic;
+}
+
+pre .constant, pre .integer {
+    color: #18838A;
+	font-weight: bold;
+}
+
+pre .storage {
+    color: #0000A1;
+}
+
+pre .string {
+    color: #8E0022;
+}
+
+pre .keyword, pre .selector {
+    color: #0000A1;
+	font-weight: bold;
+}
+
+pre .parent {
+    font-style: italic;
+}
+
+pre .entity, pre .meta {
+    color: #3E853F;
+}
+
+pre .support {
+    color: #192140;
+}
+
+pre .variable.global, pre .variable.class, pre .variable.instance {
+    color: #3E853F;
+}

File brace/favicon.png

Added
New image

File brace/img/brace.png

Added
New image

File brace/img/donttreadonme.jpg

Added
New image

File brace/img/manalang.jpg

Added
New image

File brace/index.html

+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <title>Brace yourself.
+
+    </title>
+    <meta name="description" content="A 15 minute overview of Backbone Brace --  a simple extension to support your Backbone projects">
+    <meta name="author" content="Rich Manalang">
+    <link href="http://fonts.googleapis.com/css?family=Open+Sans:400,600,700,800" rel="stylesheet" type="text/css">
+    <link href="./css/themes/blackboard.css" rel="stylesheet">
+    <link href="./css/impress-demo.css" rel="stylesheet">
+  </head>
+  <body>
+    <div id="impress">
+      <div id="title" data-x="1000" data-y="0" class="step"><img src="./img/brace.png">
+      </div>
+      <div data-x="2000" data-y="0" class="step centered">
+        <h1>Backbone Brace
+        </h1>
+        <h3>pragmatic support for your Backbone projects
+        </h3>
+      </div>
+      <div data-x="3000" data-y="0" class="step profile"><img src="./img/manalang.jpg">
+        <div class="col span120">
+          <h4>Rich Manalang &rarr; @rmanalan
+          </h4>
+          <h5 class="black normal">Atlassian
+
+          </h5>
+        </div>
+      </div>
+      <div data-x="4000" data-y="0" class="step"><img src="./img/donttreadonme.jpg" width="920">
+      </div>
+      <div data-x="5000" data-y="0" class="step">
+        <h1>Fact:
+        </h1>
+        <p style="font-size:1.3em;">Large front-end dev teams tread on each other
+
+        </p>
+      </div>
+      <div data-x="6000" data-y="0" class="step">
+        <h1>Backbone is awesome, but...
+        </h1>
+      </div>
+      <div data-x="7000" data-y="0" class="step">
+        <pre><code data-language="javascript">var PersonView = Backbone.View.extend({
+  initialize: function() {
+    this.log("Initialized");
+  },
+  log: function(msg) {
+    console.log(msg);
+  }
+});
+</code>
+        </pre>
+      </div>
+      <div data-x="8000" data-y="0" class="step">
+        <h1>Smelly
+        </h1>
+        <pre><code data-language="javascript">function log(msg) {
+  console.log(msg)
+    }
+
+
+var PersonView = Backbone.View.extend({
+  initialize: function() {
+    this.log("Initialized");
+  },
+  log:log //<- WAT?!
+});
+
+var EventView = Backbone.View.extend({
+  initialize: function() {
+    this.log("Initialized");
+  },
+  log:log //<- This stinks!
+});
+</code>
+        </pre>
+      </div>
+      <div data-x="9000" data-y="0" class="step">
+        <h1>Mixins!
+        </h1>
+        <pre><code data-language="javascript">var Loggable = {
+  log: function(msg) {
+    console.log(msg);
+  }
+};
+
+var PersonView = Brace.View.extend({
+  mixins: [Loggable], // <- Much nicer!
+  initialize: function() {
+    this.log("Initialized");
+  }
+});
+</code>
+        </pre>
+        <p style="font-size:.6em">You can do this with models, collections, views, and routers
+        </p>
+      </div>
+      <div data-x="10000" data-y="0" class="step centered">
+        <h1>Named Attributes
+        </h1>
+      </div>
+      <div data-x="11000" data-y="0" class="step">
+        <h1>Silent fail
+        </h1>
+        <pre><code data-language="javascript">// Traditional Backbone model
+var Person = Backbone.Model.extend({});
+var person = new Person({name: "Rich"});
+
+person.get("name"); // Ok
+
+person.get("firstName"); // Undefined. Silent fail!
+</code>
+        </pre>
+      </div>
+      <div data-x="12000" data-y="0" class="step">
+        <h1>Fail loudly&trade;
+        </h1>
+        <pre><code data-language="javascript">// Brace extended Backbone model
+var Person = Brace.Model.extend({
+  namedAttributes: [ "name" ]
+});
+var person = new Person();
+person.setName("Rich");
+person.getName(); // Rich
+person.getFirstName() // throws exception!
+
+person.set("name", "Rich"); // Ok
+person.get("name"); // Ok
+
+person.get("lost"); // throws exception!
+person.set("lost", "in space"); // throws exception!
+person.set({
+  lost: "in space"
+}); // throws exception!
+</code>
+        </pre>
+      </div>
+      <div data-x="13000" data-y="0" class="step">
+        <h1>Named events
+        </h1>
+        <pre><code data-language="javascript">var Person = Brace.Model.extend({
+  namedEvents [ "sleep" ]
+});
+
+// on[Event] and trigger[Event] methods are magically created
+person.onSleep(function(dream) { 
+  console.log("Dreaming about " + dream); 
+});
+person.triggerSleep("Unicorns");
+</code>
+        </pre>
+      </div>
+      <div data-x="14000" data-y="0" class="step">
+        <h1>Mix and match
+        </h1>
+        <pre><code data-language="javascript" style="font-size:20px">// Mixin
+var Selectable = {
+  namedAttributes: ["selected"],
+  namedEvents: ["select"],
+
+  initialize: function() {
+    this.on("change:selected", _.bind(function(model, selected) {
+      if (selected) {
+        this.triggerSelect();
+      }
+    }, this));
+  }
+};
+
+var Person = Brace.Model.extend({
+  mixins: [Selectable]
+});
+
+var person = new Person();
+person.onSelect(function() { console.log("Person was selected"); });
+person.setSelected(true);
+person.getSelected();
+person.triggerSelect();
+</code>
+        </pre>
+      </div>
+      <div data-x="15000" data-y="0" class="step">
+        <h1>Fail loudly&trade;
+        </h1>
+        <pre><code data-language="javascript">var Runnable = {
+  run: function() {}
+};
+
+var RunModel = Brace.Model.extend({
+    mixins: [Runnable],
+    run: function() {}
+}); // throws "Mixin error: class already has property 'run' defined"
+
+</code>
+        </pre>
+      </div>
+      <div data-x="16000" data-y="0" class="step centered">
+        <h1>3k
+        </h1><a href="https://bitbucket.org/atlassian/backbone-brace" style="font-size:1em">bitbucket.org/atlassian/backbone-brace</a>
+      </div>
+      <div data-x="17000" data-y="0" class="step centered">
+        <h1 style="font-size:1.3em">Atlassian are hiring JS devs!
+        </h1>
+        <p><a href="http://bit.ly/js-hire" style="font-size:3.2em">bit.ly/js-hire</a>
+        </p>
+      </div>
+    </div>
+    <div class="hint">
+      <p>Use a spacebar or arrow keys to navigate
+      </p>
+    </div>
+    <script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.3.1/underscore-min.js"></script>
+    <script src="http://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.2/backbone-min.js"></script>
+    <script src="./js/backbone.brace.js"></script>
+    <script src="./js/impress.js"></script>
+    <script src="./js/rainbow.min.js"></script>
+    <script src="./js/language/generic.js"></script>
+    <script src="./js/language/javascript.js"></script>
+    <script>impress().init();
+document.write('<script src="http://' + (location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1"></' + 'script>')
+</script>
+  </body>
+</html>

File brace/index.jade

+!!! 5
+html(lang='en')
+  head
+    meta(charset='utf-8')
+    title
+      | Brace yourself.
+    meta(name='description', content="A 15 minute overview of Backbone Brace --  a simple extension to support your Backbone projects")
+    meta(name='author', content='Rich Manalang')
+    link(href='http://fonts.googleapis.com/css?family=Open+Sans:400,600,700,800', rel='stylesheet', type='text/css')
+    link(href='./css/themes/blackboard.css', rel='stylesheet')
+    link(href='./css/impress-demo.css', rel='stylesheet')
+
+    - var row1 = {x:0,y:0};
+    - function row1X(i){ return (i===0 && row1.x===0) ? 0 : ((typeof i === "undefined") ? row1.x += 1000 : row1.x += i);}
+
+  body
+    #impress
+
+      #title.step(data-x="#{row1X()}", data-y='0')
+        img(src="./img/brace.png")
+
+      .step.centered(data-x="#{row1X()}", data-y='0')
+        h1 Backbone Brace
+        h3 pragmatic support for your Backbone projects
+
+      .step.profile(data-x="#{row1X()}", data-y="#{row1.y}")
+        img(src="./img/manalang.jpg")
+        .col.span120
+          h4 Rich Manalang &rarr; @rmanalan
+          h5.black.normal
+            | Atlassian
+
+      .step(data-x="#{row1X()}", data-y="#{row1.y}")
+        img(src="./img/donttreadonme.jpg", width="920")
+
+      .step(data-x="#{row1X()}", data-y="#{row1.y}")
+        h1 Fact:
+        p(style="font-size:1.3em;")
+          | Large front-end dev teams tread on each other
+
+      .step(data-x="#{row1X()}", data-y="#{row1.y}")
+        h1 Backbone is awesome, but...
+
+      .step(data-x="#{row1X()}", data-y="#{row1.y}")
+        pre
+          code(data-language="javascript").
+            var PersonView = Backbone.View.extend({
+              initialize: function() {
+                this.log("Initialized");
+              },
+              log: function(msg) {
+                console.log(msg);
+              }
+            });
+      .step(data-x="#{row1X()}", data-y="#{row1.y}")
+        h1 Smelly
+        pre
+          code(data-language="javascript").
+            function log(msg) {
+              console.log(msg)
+                }
+
+            var PersonView = Backbone.View.extend({
+              initialize: function() {
+                this.log("Initialized");
+              },
+              log:log //<- WAT?!
+            });
+
+            var EventView = Backbone.View.extend({
+              initialize: function() {
+                this.log("Initialized");
+              },
+              log:log //<- This stinks!
+            });
+      .step(data-x="#{row1X()}", data-y="#{row1.y}")
+        h1 Mixins!
+        pre
+          code(data-language="javascript").
+            var Loggable = {
+              log: function(msg) {
+                console.log(msg);
+              }
+            };
+
+            var PersonView = Brace.View.extend({
+              mixins: [Loggable], // <- Much nicer!
+              initialize: function() {
+                this.log("Initialized");
+              }
+            });
+        p(style="font-size:.6em") You can do this with models, collections, views, and routers
+
+      .step.centered(data-x="#{row1X()}", data-y="#{row1.y}")
+        h1 Named Attributes
+
+      .step(data-x="#{row1X()}", data-y="#{row1.y}")
+        h1 Silent fail
+        pre
+          code(data-language="javascript").
+            // Traditional Backbone model
+            var Person = Backbone.Model.extend({});
+            var person = new Person({name: "Rich"});
+
+            person.get("name"); // Ok
+
+            person.get("firstName"); // Undefined. Silent fail!
+      .step(data-x="#{row1X()}", data-y="#{row1.y}")
+        h1 Fail loudly&trade;
+        pre
+          code(data-language="javascript").
+            // Brace extended Backbone model
+            var Person = Brace.Model.extend({
+              namedAttributes: [ "name" ]
+            });
+            var person = new Person();
+            person.setName("Rich");
+            person.getName(); // Rich
+            person.getFirstName() // throws exception!
+
+            person.set("name", "Rich"); // Ok
+            person.get("name"); // Ok
+
+            person.get("lost"); // throws exception!
+            person.set("lost", "in space"); // throws exception!
+            person.set({
+              lost: "in space"
+            }); // throws exception!
+      .step(data-x="#{row1X()}", data-y="#{row1.y}")
+        h1 Named events
+        pre
+          code(data-language="javascript").
+            var Person = Brace.Model.extend({
+              namedEvents [ "sleep" ]
+            });
+
+            // on[Event] and trigger[Event] methods are magically created
+            person.onSleep(function(dream) { 
+              console.log("Dreaming about " + dream); 
+            });
+            person.triggerSleep("Unicorns");
+      .step(data-x="#{row1X()}", data-y="#{row1.y}")
+        h1 Mix and match
+        pre
+          code(data-language="javascript", style="font-size:20px").
+            // Mixin
+            var Selectable = {
+              namedAttributes: ["selected"],
+              namedEvents: ["select"],
+
+              initialize: function() {
+                this.on("change:selected", _.bind(function(model, selected) {
+                  if (selected) {
+                    this.triggerSelect();
+                  }
+                }, this));
+              }
+            };
+
+            var Person = Brace.Model.extend({
+              mixins: [Selectable]
+            });
+
+            var person = new Person();
+            person.onSelect(function() { console.log("Person was selected"); });
+            person.setSelected(true);
+            person.getSelected();
+            person.triggerSelect();
+      .step(data-x="#{row1X()}", data-y="#{row1.y}")
+        h1 Fail loudly&trade;
+        pre
+          code(data-language="javascript").
+            var Runnable = {
+              run: function() {}
+            };
+
+            var RunModel = Brace.Model.extend({
+                mixins: [Runnable],
+                run: function() {}
+            }); // throws "Mixin error: class already has property 'run' defined"
+
+      .step.centered(data-x="#{row1X()}", data-y="#{row1.y}")
+        h1 3k
+        a(href="https://bitbucket.org/atlassian/backbone-brace", style="font-size:1em") bitbucket.org/atlassian/backbone-brace
+      .step.centered(data-x="#{row1X()}", data-y="#{row1.y}")
+        h1(style="font-size:1.3em") Atlassian are hiring JS devs!
+        p
+          a(href="http://bit.ly/js-hire", style="font-size:3.2em") bit.ly/js-hire
+     .hint
+      p Use a spacebar or arrow keys to navigate
+
+    script(src='http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.3.1/underscore-min.js')
+    script(src='http://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.2/backbone-min.js')
+    script(src='./js/backbone.brace.js')
+    script(src='./js/impress.js')
+    script(src='./js/rainbow.min.js')
+    script(src='./js/language/generic.js')
+    script(src='./js/language/javascript.js')
+    script
+      impress().init();
+      document.write('<script src="http://' + (location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1"></' + 'script>')

File brace/js/backbone.brace.js

+
+(function () {
+
+    // node / browser imports, copied from here
+    var root = this;
+    var Brace;
+    if (typeof exports !== 'undefined') {
+        Brace = exports;
+    } else {
+        Brace = root.Brace = {};
+    }
+
+    var _ = root._;
+    if (!_ && (typeof require !== 'undefined')) _ = require('underscore');
+
+    var Backbone = root.Backbone;
+    if (!Backbone && (typeof require !== 'undefined')) Backbone = require('backbone');
+
+    // ### Brace.Mixins ###
+    // Mixin utilities
+    Brace.Mixins = {
+        // Creates a camelCased method name
+        createMethodName: function(prefix, suffix) {
+            return prefix + suffix.charAt(0).toUpperCase() + suffix.substr(1);
+        },
+
+        // Applies a mixin to the given constructor's prototype.
+        applyMixin: function(ctor, mixin) {
+            _.forEach(_.keys(mixin), function(key) {
+                var proto = ctor.prototype;
+
+                // `initialize` is not mixed in - we compose the mixin's initialize with the existing initialize method (if it exists).
+                if ("initialize" === key) {
+                    var oldInitialize = proto.initialize;
+                    proto.initialize = function() {
+                        mixin.initialize.apply(this, arguments);
+                        if (oldInitialize) {
+                            oldInitialize.apply(this, arguments);
+                        }
+                    };
+                    return;
+                }
+                // `validate` is not mixed in - we compose the mixin's validate with the existing validate method (if it exists).
+                if ("validate" === key) {
+                    var oldValidate = proto.validate;
+                    proto.validate = function() {
+                        var errors = mixin.validate.apply(this, arguments);
+                        if (errors) {
+                            return errors;
+                        }
+                        if (oldValidate) {
+                            return oldValidate.apply(this, arguments);
+                        }
+                    };
+                    return;
+                }
+                // `defaults` are not mixed in - we compose the mixin's defaults with existing defaults if they exist
+                if ("defaults" === key) {
+                    var defaults = proto.defaults || (proto.defaults = {});
+                    var mixinDefaults = mixin[key];
+                    for (var id in mixinDefaults) {
+                        if (defaults.hasOwnProperty(id)) {
+                            throw "Mixin error: class already has default '" + id + "' defined";
+                        }
+                        defaults[id] = mixinDefaults[id];
+                    }
+                    return;
+                }
+                // `namedAttributes` are added to the mixin, and we mixin in getters and setters for each attribute.
+                if ("namedAttributes" === key) {
+                    // `namedAttributes` must be an array
+                    if (!_.isArray(mixin[key])) {
+                        throw "Expects namedAttributes member on mixin to be an array";
+                    }
+                    if (!proto.namedAttributes) {
+                        proto.namedAttributes = [];
+                    }
+                    proto.namedAttributes = proto.namedAttributes.concat(mixin[key]);
+                    return;
+                }
+
+                // `namedEvents` are added to the mixin, and we mix in on and trigger methods for each event.
+                if ("namedEvents" === key) {
+                    // `events` must be an array
+                    if (!_.isArray(mixin[key])) {
+                        throw "Expects events member on mixin to be an array";
+                    }
+                    if (!proto.namedEvents) {
+                        proto.namedEvents = [];
+                    }
+                    proto.namedEvents = proto.namedEvents.concat(mixin[key]);
+                    return;
+                }
+                // Name collisions with other mixins or or the object we're mixing into result in violent and forceful disapproval.
+                if (proto.hasOwnProperty(key)) {
+                    throw "Mixin error: class already has property '" + key + "' defined";
+                }
+                proto[key] = mixin[key];
+            }, this);
+        }
+    };
+
+    // ### Brace.AttributesMixinCreator ###
+    Brace.AttributesMixinCreator = {
+
+        // Creates a mixin of getter and setter methods for each item in the given attribute list.
+        // A getter and setter for `id` is always generated.
+        create: function(attributes) {
+            var methods = {};
+
+            if (_.indexOf(attributes, "id") === -1) {
+                attributes.unshift("id");
+            }
+
+            _.each(attributes, function (attribute) {
+                // TODO: has, escape, unset
+                var setter = Brace.Mixins.createMethodName("set", attribute);
+                methods[setter] = function (val,options) {
+                    var obj = {};
+                    obj[attribute] = val;
+                    this.set(obj,options);
+                    return this;
+                };
+                var getter = Brace.Mixins.createMethodName("get", attribute);
+                methods[getter] = function () {
+                    return this.get(attribute);
+                };
+            });
+            return methods;
+        }
+    };
+
+    // ### Brace.EventsMixinCreator ###
+    Brace.EventsMixinCreator = {
+
+        // Creates a mixin of on and trigger methods for each item in the given list of events.
+        create: function(events) {
+            var eventMethods = {};
+            var createEvent = function(eventName) {
+                // TODO: off
+                var binder = Brace.Mixins.createMethodName("on", eventName);
+                eventMethods[binder] = function() {
+                    return this.on.apply(this, [eventName].concat(_.toArray(arguments)));
+                };
+                var trigger = Brace.Mixins.createMethodName("trigger", eventName);
+                eventMethods[trigger] = function() {
+                    return this.trigger.apply(this, [eventName].concat(_.toArray(arguments)));
+                };
+            };
+            _.each(events, _.bind(createEvent,this));
+
+            return eventMethods;
+        }
+    };
+
+    /*
+     * Generates an `extend` method that overrides Backbone's default `extend`. The new extend calls Backbone's `extend`, then:
+     * <ul>
+     *     <li>Adds all mixins specified in the `mixins` array.</li>
+     *     <li>Adds a `Brace.EventsMixinCreator` to mix in on and trigger methods for events specified in the `namedEvents` array,</li>
+     *     <li>Adds a `Brace.AttributesMixinCreator` to mix in get and set methods for attributes specified in the `attributes` array,</li>
+     * </ul>
+     */
+    function generateMixinExtend(oldExtend) {
+        return function(protoProps, classProps) {
+            var child;
+            var cleanProtoProps = _.extend({}, protoProps);
+            // Remove `mixins` - we don't want to see them on the created prototype. Note that we do want to see `namedAttributes` and `namedEvents` for debugging
+            var mixins;
+            if (protoProps && protoProps.mixins) {
+                mixins = protoProps.mixins;
+                delete cleanProtoProps.mixins;
+            }
+            child = oldExtend.call(this, cleanProtoProps, classProps);
+            if (mixins) {
+                _.each(protoProps.mixins, function(mixin) {
+                    Brace.Mixins.applyMixin(child, mixin);
+                });
+            }
+            if (child.prototype.namedEvents) {
+                Brace.Mixins.applyMixin(child, Brace.EventsMixinCreator.create(child.prototype.namedEvents));
+            }
+            if (child.prototype.namedAttributes) {
+                Brace.Mixins.applyMixin(child, Brace.AttributesMixinCreator.create(child.prototype.namedAttributes));
+            }
+            child.extend = arguments.callee;
+            return child;
+        };
+    }
+
+    // Overrides Backbone's `get` and `set` methods to validate that the attribute being get / set is a namedAttribute.
+    function overrideSetGet(ctor, childCtor) {
+        var proto = ctor.prototype;
+        var childProto = childCtor.prototype;
+
+        var oldSet = proto.set;
+        childProto.set = function(key, value, options) {
+            // TODO: has, escape, unset
+            var attrs,
+                attributes = this.namedAttributes;
+            if (attributes) {
+                if (_.isObject(key) || key == null) {
+                    attrs = key;
+                } else {
+                    attrs = {};
+                    attrs[key] = value;
+                }
+                for (var attr in attrs) {
+                    if (_.indexOf(attributes, attr) < 0) {
+                        throw "Attribute '" + attr + "' does not exist";
+                    }
+                }
+            }
+            return oldSet.apply(this, arguments);
+        };
+
+        var oldGet = proto.get;
+        childProto.get = function(attr) {
+            if (this.namedAttributes && _.indexOf(this.namedAttributes, attr) < 0) {
+                throw "Attribute '" + attr + "' does not exist";
+            }
+            return oldGet.apply(this, arguments);
+        };
+    }
+
+    // Applies extensions to the given constructor function:
+    // <ul>
+    //   <li>Sets `extend` to a method generated by `generateMixinExtend`</li>
+    // </ul>
+    function applyExtensions(ctor) {
+        var child = ctor.extend();
+        var oldExtend = ctor.extend;
+        child.extend = generateMixinExtend(oldExtend);
+        return child;
+    }
+
+
+    // Applies extensions to the given constructor function:
+    // <ul>
+    //   <li>Sets `extend` to a method generated by `generateMixinExtend`</li>
+    // </ul>
+    function applyModelExtensions(ctor) {
+        var child = applyExtensions(ctor);
+        overrideSetGet(ctor, child);
+        return child;
+    }
+
+    // Extend base Backbone classes
+    Brace.Model = applyModelExtensions(Backbone.Model);
+    Brace.Collection = applyExtensions(Backbone.Collection);
+    Brace.View = applyExtensions(Backbone.View);
+    Brace.Router = applyExtensions(Backbone.Router);
+
+})();

File brace/js/impress.js

+/**
+ * impress.js
+ *
+ * impress.js is a presentation tool based on the power of CSS3 transforms and transitions
+ * in modern browsers and inspired by the idea behind prezi.com.
+ *
+ *
+ * Copyright 2011-2012 Bartek Szopka (@bartaz)
+ *
+ * Released under the MIT and GPL Licenses.
+ *
+ * ------------------------------------------------
+ *  author:  Bartek Szopka
+ *  version: 0.5.1
+ *  url:     http://bartaz.github.com/impress.js/
+ *  source:  http://github.com/bartaz/impress.js/
+ */
+
+/*jshint bitwise:true, curly:true, eqeqeq:true, forin:true, latedef:true, newcap:true,
+         noarg:true, noempty:true, undef:true, strict:true, browser:true */
+
+(function ( document, window ) {
+    'use strict';
+
+    // HELPER FUNCTIONS
+    
+    var pfx = (function () {
+
+        var style = document.createElement('dummy').style,
+            prefixes = 'Webkit Moz O ms Khtml'.split(' '),
+            memory = {};
+            
+        return function ( prop ) {
+            if ( typeof memory[ prop ] === "undefined" ) {
+
+                var ucProp  = prop.charAt(0).toUpperCase() + prop.substr(1),
+                    props   = (prop + ' ' + prefixes.join(ucProp + ' ') + ucProp).split(' ');
+
+                memory[ prop ] = null;
+                for ( var i in props ) {
+                    if ( style[ props[i] ] !== undefined ) {
+                        memory[ prop ] = props[i];
+                        break;
+                    }
+                }
+
+            }
+
+            return memory[ prop ];
+        };
+
+    })();
+
+    var arrayify = function ( a ) {
+        return [].slice.call( a );
+    };
+    
+    var css = function ( el, props ) {
+        var key, pkey;
+        for ( key in props ) {
+            if ( props.hasOwnProperty(key) ) {
+                pkey = pfx(key);
+                if ( pkey !== null ) {
+                    el.style[pkey] = props[key];
+                }
+            }
+        }
+        return el;
+    };
+    
+    var toNumber = function (numeric, fallback) {
+        return isNaN(numeric) ? (fallback || 0) : Number(numeric);
+    };
+    
+    var byId = function ( id ) {
+        return document.getElementById(id);
+    };
+    
+    var $ = function ( selector, context ) {
+        context = context || document;
+        return context.querySelector(selector);
+    };
+    
+    var $$ = function ( selector, context ) {
+        context = context || document;
+        return arrayify( context.querySelectorAll(selector) );
+    };
+    
+    var triggerEvent = function (el, eventName, detail) {
+        var event = document.createEvent("CustomEvent");
+        event.initCustomEvent(eventName, true, true, detail);
+        el.dispatchEvent(event);
+    };
+    
+    var translate = function ( t ) {
+        return " translate3d(" + t.x + "px," + t.y + "px," + t.z + "px) ";
+    };
+    
+    var rotate = function ( r, revert ) {
+        var rX = " rotateX(" + r.x + "deg) ",
+            rY = " rotateY(" + r.y + "deg) ",
+            rZ = " rotateZ(" + r.z + "deg) ";
+        
+        return revert ? rZ+rY+rX : rX+rY+rZ;
+    };
+    
+    var scale = function ( s ) {
+        return " scale(" + s + ") ";
+    };
+    
+    var perspective = function ( p ) {
+        return " perspective(" + p + "px) ";
+    };
+    
+    var getElementFromUrl = function () {
+        // get id from url # by removing `#` or `#/` from the beginning,
+        // so both "fallback" `#slide-id` and "enhanced" `#/slide-id` will work
+        return byId( window.location.hash.replace(/^#\/?/,"") );
+    };
+    
+    var computeWindowScale = function ( config ) {
+        var hScale = window.innerHeight / config.height,
+            wScale = window.innerWidth / config.width,
+            scale = hScale > wScale ? wScale : hScale;
+        
+        if (config.maxScale && scale > config.maxScale) {
+            scale = config.maxScale;
+        }
+        
+        if (config.minScale && scale < config.minScale) {
+            scale = config.minScale;
+        }
+        
+        return scale;
+    };
+    
+    // CHECK SUPPORT
+    var body = document.body;
+    
+    var ua = navigator.userAgent.toLowerCase();
+    var impressSupported = ( pfx("perspective") !== null ) &&
+                           ( body.classList ) &&
+                           ( body.dataset ) &&
+                           ( ua.search(/(iphone)|(ipod)|(android)/) === -1 );
+    
+    if (!impressSupported) {
+        // we can't be sure that `classList` is supported
+        body.className += " impress-not-supported ";
+    } else {
+        body.classList.remove("impress-not-supported");
+        body.classList.add("impress-supported");
+    }
+    
+    // cross-browser transitionEnd event name
+    // based on https://developer.mozilla.org/en/CSS/CSS_transitions
+    var transitionEnd = ({
+            'transition':'transitionEnd',
+            'OTransition':'oTransitionEnd',
+            'msTransition':'MSTransitionEnd', // who knows how it will end up?
+            'MozTransition':'transitionend',
+            'WebkitTransition':'webkitTransitionEnd'
+        })[pfx("transition")];
+    
+    var roots = {};
+    
+    var defaults = {
+        width: 1024,
+        height: 768,
+        maxScale: 1,
+        minScale: 0,
+        
+        perspective: 1000,
+        
+        transitionDuration: 500
+    };
+    
+    var empty = function () { return false; };
+    
+    var impress = window.impress = function ( rootId ) {
+        
+        // if impress.js is not supported by the browser return a dummy API
+        // it may not be a perfect solution but we return early and avoid
+        // running code that may use features not implemented in the browser
+        if (!impressSupported) {
+            return {
+                init: empty,
+                goto: empty,
+                prev: empty,
+                next: empty
+            };
+        }
+        
+        rootId = rootId || "impress";
+        
+        // if already initialized just return the API
+        if (roots["impress-root-" + rootId]) {
+            return roots["impress-root-" + rootId];
+        }
+        
+        // data of all presentation steps
+        var stepsData = {};
+        
+        // element of currently active step
+        var activeStep = null;
+        
+        // current state (position, rotation and scale) of the presentation
+        var currentState = null;
+        
+        // array of step elements
+        var steps = null;
+        
+        // configuration options
+        var config = null;
+        
+        // scale factor of the browser window
+        var windowScale = null;        
+        
+        // root presentation elements
+        var root = byId( rootId );
+        var canvas = document.createElement("div");
+        
+        var initialized = false;
+        
+        // step events
+        
+        var lastEntered = null;
+        var onStepEnter = function (step) {
+            if (lastEntered !== step) {
+                triggerEvent(step, "impress:stepenter");
+                lastEntered = step;
+            }
+        };
+        
+        var onStepLeave = function (step) {
+            if (lastEntered === step) {
+                triggerEvent(step, "impress:stepleave");
+                lastEntered = null;
+            }
+        };
+        
+        // transitionEnd event handler
+        
+        var expectedTransitionTarget = null;
+        
+        var onTransitionEnd = function (event) {
+            // we only care about transitions on `root` and `canvas` elements
+            if (event.target === expectedTransitionTarget) {
+                onStepEnter(activeStep);
+                event.stopPropagation(); // prevent propagation from `canvas` to `root`
+            }
+        };
+        
+        var initStep = function ( el, idx ) {
+            var data = el.dataset,
+                step = {
+                    translate: {
+                        x: toNumber(data.x),
+                        y: toNumber(data.y),
+                        z: toNumber(data.z)
+                    },
+                    rotate: {
+                        x: toNumber(data.rotateX),
+                        y: toNumber(data.rotateY),
+                        z: toNumber(data.rotateZ || data.rotate)
+                    },
+                    scale: toNumber(data.scale, 1),
+                    el: el
+                };
+            
+            if ( !el.id ) {
+                el.id = "step-" + (idx + 1);
+            }
+            
+            stepsData["impress-" + el.id] = step;
+            
+            css(el, {
+                position: "absolute",
+                transform: "translate(-50%,-50%)" +
+                           translate(step.translate) +
+                           rotate(step.rotate) +
+                           scale(step.scale),
+                transformStyle: "preserve-3d"
+            });
+        };
+        
+        var init = function () {
+            if (initialized) { return; }
+            
+            // setup viewport for mobile devices
+            var meta = $("meta[name='viewport']") || document.createElement("meta");
+            meta.content = "width=device-width, minimum-scale=1, maximum-scale=1, user-scalable=no";
+            if (meta.parentNode !== document.head) {
+                meta.name = 'viewport';
+                document.head.appendChild(meta);
+            }
+            
+            // initialize configuration object
+            var rootData = root.dataset;
+            config = {
+                width: toNumber( rootData.width, defaults.width ),
+                height: toNumber( rootData.height, defaults.height ),
+                maxScale: toNumber( rootData.maxScale, defaults.maxScale ),
+                minScale: toNumber( rootData.minScale, defaults.minScale ),                
+                perspective: toNumber( rootData.perspective, defaults.perspective ),
+                transitionDuration: toNumber( rootData.transitionDuration, defaults.transitionDuration )
+            };
+            
+            windowScale = computeWindowScale( config );
+            
+            // wrap steps with "canvas" element
+            arrayify( root.childNodes ).forEach(function ( el ) {
+                canvas.appendChild( el );
+            });
+            root.appendChild(canvas);
+            
+            // set initial styles
+            document.documentElement.style.height = "100%";
+            
+            css(body, {
+                height: "100%",
+                overflow: "hidden"
+            });
+            
+            var rootStyles = {
+                position: "absolute",
+                transformOrigin: "top left",
+                transition: "all 0s ease-in-out",
+                transformStyle: "preserve-3d"
+            };
+            
+            css(root, rootStyles);
+            css(root, {
+                top: "50%",
+                left: "50%",
+                transform: perspective( config.perspective/windowScale ) + scale( windowScale )
+            });
+            css(canvas, rootStyles);
+            
+            root.addEventListener(transitionEnd, onTransitionEnd, false);
+            canvas.addEventListener(transitionEnd, onTransitionEnd, false);
+            
+            body.classList.remove("impress-disabled");
+            body.classList.add("impress-enabled");
+            
+            // get and init steps
+            steps = $$(".step", root);            
+            steps.forEach( initStep );
+            
+            currentState = {
+                translate: { x: 0, y: 0, z: 0 },
+                rotate:    { x: 0, y: 0, z: 0 },
+                scale:     1
+            };
+            
+            initialized = true;
+            
+            triggerEvent(root, "impress:init", { api: roots[ "impress-root-" + rootId ] });
+        };
+        
+        var getStep = function ( step ) {
+            if (typeof step === "number") {
+                step = step < 0 ? steps[ steps.length + step] : steps[ step ];
+            } else if (typeof step === "string") {
+                step = byId(step);
+            }
+            return (step && step.id && stepsData["impress-" + step.id]) ? step : null;
+        };
+        
+        var goto = function ( el, duration ) {
+            
+            if ( !initialized || !(el = getStep(el)) ) {
+                // presentation not initialized or given element is not a step
+                return false;
+            }
+            
+            // Sometimes it's possible to trigger focus on first link with some keyboard action.
+            // Browser in such a case tries to scroll the page to make this element visible
+            // (even that body overflow is set to hidden) and it breaks our careful positioning.
+            //
+            // So, as a lousy (and lazy) workaround we will make the page scroll back to the top
+            // whenever slide is selected
+            //