├── .gitignore ├── _deps.styl ├── gulpfile.js ├── images ├── 1.jpg ├── 10.jpg ├── 2.jpg ├── 3.jpg ├── 4.jpg ├── 5.jpg ├── 6.jpg ├── 7.jpg ├── 8.jpg ├── 9.jpg ├── BrowserSync.mp4 ├── Twitter_logo_white.png ├── arrows.png ├── babel.svg ├── babeled.png ├── brain.svg ├── canada-flag.png ├── canadian-stylesheets.png ├── commandline.png ├── cover.png ├── critical.png ├── css-next-logo.svg ├── css-sourcemaps.png ├── cssnext-gulp.png ├── es6.png ├── fetch.png ├── flexbox.png ├── german-stylesheets.png ├── grunt-logo.png ├── gulp-logo.png ├── hackerYou-logomark.png ├── image-compress.png ├── images-gulp.png ├── jslogo.jpg ├── lodash.png ├── modules.png ├── npm-mess.png ├── postcss-logo.png ├── purify.png ├── scoped-variables.png ├── sharkbite-depth.jpg ├── soldering.jpg ├── sourcemaps-in-devtools.png ├── sourcemaps.png ├── ugggggggly.png ├── uglify.png ├── webpack.png └── wow-scripts.png ├── index.html ├── index.jade ├── package.json ├── prettify.js ├── readme.md ├── slides.js ├── styles.css └── styles.styl /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .DS_Store 3 | updates.md 4 | -------------------------------------------------------------------------------- /_deps.styl: -------------------------------------------------------------------------------- 1 | /* 2 | Google HTML5 slides template 3 | 4 | Authors: Luke Mahé (code) 5 | Marcin Wichary (code and design) 6 | 7 | Dominic Mazzoni (browser compatibility) 8 | Charles Chen (ChromeVox support) 9 | 10 | URL: http://code.google.com/p/html5slides/ 11 | */ 12 | 13 | /* Framework */ 14 | 15 | 16 | .wrapper { 17 | background: #BADA55; 18 | color:#D12028; 19 | border:1px solid #fff; 20 | } 21 | 22 | html { 23 | height: 100%; 24 | } 25 | 26 | body { 27 | margin: 0; 28 | padding: 0; 29 | display: block !important; 30 | height: 100%; 31 | min-height: 740px; 32 | overflow-x: hidden; 33 | overflow-y: auto; 34 | } 35 | 36 | .slides { 37 | width: 100%; 38 | height: 100%; 39 | left: 0; 40 | top: 0; 41 | color:#f21818; 42 | background:#193549; 43 | position: absolute; 44 | -webkit-transform: translate3d(0, 0, 0); 45 | } 46 | 47 | 48 | /* 49 | Slide Widths 50 | */ 51 | 52 | w = 1920px 53 | h = 1080px 54 | 55 | .slides > article { 56 | display: block; 57 | position: absolute; 58 | overflow: hidden; 59 | width w 60 | height h 61 | margin-left: -(w / 2); 62 | margin-top: -(h / 2); 63 | 64 | left: 50%; 65 | top: 50%; 66 | border-radius 5px; 67 | padding: 0; 68 | background-color: none; 69 | transition: all 0.3s ease-out; 70 | justify-content center 71 | flex-direction column 72 | & > * { 73 | display block 74 | clear both 75 | min-width 100% 76 | flex-wrap wrap 77 | } 78 | & > img { 79 | min-width: 0; 80 | display: block; 81 | margin: 0 auto 82 | } 83 | 84 | } 85 | 86 | .slide-area { 87 | z-index: 1000; 88 | 89 | position: absolute; 90 | left: 0; 91 | top: 0; 92 | width: 150px; 93 | height: 100%; 94 | left: 0; 95 | top: 0; 96 | cursor: pointer; 97 | tap-highlight-color: transparent; 98 | 99 | } 100 | #prev-slide-area { 101 | 102 | } 103 | #next-slide-area { 104 | right 0 105 | left auto 106 | } 107 | .slides.layout-widescreen #prev-slide-area, 108 | .slides.layout-faux-widescreen #prev-slide-area { 109 | margin-left: -650px; 110 | } 111 | .slides.layout-widescreen #next-slide-area, 112 | .slides.layout-faux-widescreen #next-slide-area { 113 | margin-left: 500px; 114 | } 115 | 116 | /* Slides */ 117 | 118 | .slides > article { 119 | display: none; 120 | text-align: center; 121 | } 122 | .slides > article.far-past { 123 | display: block; 124 | transform: translate(-4000px); 125 | display none; 126 | } 127 | .slides > article.past { 128 | display: flex; 129 | transform: scale(0.5) translate(-(w + (0))px); 130 | opacity: 0.3; 131 | 132 | } 133 | .slides > article.current { 134 | display: flex; 135 | transform: translate(0); 136 | z-index 10 137 | } 138 | .slides > article.next { 139 | display: flex; 140 | transform: scale(0.5) translate((w + (0))px); 141 | opacity: 0.3; 142 | } 143 | .slides > article.far-next { 144 | display: flex; 145 | transform: translate(4000px); 146 | display none; 147 | } 148 | 149 | 150 | /* Styles for slides */ 151 | 152 | .slides > article { 153 | color: #6C818F; 154 | font-size: 30px; 155 | letter-spacing: -1px; 156 | } 157 | 158 | b { 159 | font-weight: 600; 160 | } 161 | 162 | .blue { 163 | color: rgb(0, 102, 204); 164 | } 165 | .yellow { 166 | color: rgb(255, 211, 25); 167 | } 168 | .green { 169 | color: #29E254; 170 | } 171 | .red { 172 | color: rgb(255, 0, 0); 173 | } 174 | .black { 175 | color: black; 176 | } 177 | .white { 178 | color: white; 179 | } 180 | 181 | a { 182 | color: #FFDD00; 183 | } 184 | 185 | ::-moz-selection { background: #FFDD00; } 186 | ::selection { background: #FFDD00; } 187 | 188 | 189 | p { 190 | margin: 0; 191 | padding: 0; 192 | } 193 | p:first-child { 194 | margin-top: 0; 195 | } 196 | 197 | body { 198 | // font-family: inconsolata, courier; 199 | font-family: 'Open Sans', sans-serif; 200 | } 201 | h1 { 202 | font-size: 60px; 203 | line-height: 60px; 204 | padding: 0; 205 | margin: 0; 206 | color: white; 207 | } 208 | 209 | h2 { 210 | font-size: 45px; 211 | line-height: 45px; 212 | 213 | bottom: 150px; 214 | 215 | padding: 0; 216 | margin: 0; 217 | font-weight: 600; 218 | color:white; 219 | letter-spacing: -2px; 220 | } 221 | 222 | h2 a { 223 | text-decoration: none; 224 | } 225 | 226 | h3 { 227 | font-size: 20px; 228 | line-height: 36px; 229 | padding: 0 0 10px 0; 230 | margin: 0; 231 | padding-right: 40px; 232 | font-weight: 600; 233 | letter-spacing: -1px; 234 | color:#EAEAEA; 235 | } 236 | 237 | .half { 238 | width:350px; 239 | float:left; 240 | } 241 | 242 | .button:hover { 243 | color:#fff; 244 | background:#392C44; 245 | } 246 | 247 | pre.half { 248 | width: 400px; 249 | font-size:17px; 250 | } 251 | 252 | p.small { 253 | color:#000; 254 | font-size:18px; 255 | } 256 | article.fill h3 { 257 | background: rgba(255, 255, 255, .75); 258 | padding-top: .2em; 259 | padding-bottom: .3em; 260 | margin-top: -.2em; 261 | margin-left: -60px; 262 | padding-left: 60px; 263 | margin-right: -60px; 264 | padding-right: 60px; 265 | } 266 | 267 | h4 { 268 | margin:0; 269 | } 270 | 271 | 272 | .center { 273 | text-align:center; 274 | } 275 | 276 | .center h3 { 277 | font-size:100px; 278 | margin-top:220px; 279 | } 280 | ul { 281 | list-style: none; 282 | margin: 0; 283 | padding: 0; 284 | 285 | margin-top: 40px; 286 | 287 | margin-left: .75em; 288 | } 289 | ul:first-child { 290 | margin-top: 0; 291 | } 292 | ul ul { 293 | margin-top: .5em; 294 | } 295 | li { 296 | padding: 0; 297 | margin: 0; 298 | 299 | margin-bottom: .5em; 300 | } 301 | li::before { 302 | content: '·'; 303 | 304 | width: .75em; 305 | margin-left: -.75em; 306 | 307 | position: absolute; 308 | } 309 | 310 | pre { 311 | 312 | font-size: 20px; 313 | line-height: 28px; 314 | padding: 5px 10px; 315 | 316 | letter-spacing: -1px; 317 | 318 | margin-top: 20px; 319 | margin-bottom: 20px; 320 | 321 | color: black; 322 | background: rgb(240, 240, 240); 323 | border: 1px solid rgb(224, 224, 224); 324 | box-shadow: inset 0 2px 6px rgba(0, 0, 0, .1); 325 | 326 | overflow: hidden; 327 | } 328 | 329 | code { 330 | font-size: 95%; 331 | font-family: 'Droid Sans Mono', 'Courier New', monospace; 332 | display: inline-block; 333 | background rgba(255,255,255,0.2); 334 | padding 10px 335 | border-radius 4px; 336 | font-weight 600 337 | } 338 | 339 | iframe { 340 | width: 100%; 341 | height: 620px; 342 | background: white; 343 | border: 1px solid rgb(192, 192, 192); 344 | margin: -1px; 345 | /*box-shadow: inset 0 2px 6px rgba(0, 0, 0, .1);*/ 346 | } 347 | 348 | img { 349 | max-width: 100%; 350 | max-height:570px; 351 | } 352 | 353 | h3 + iframe { 354 | margin-top: 40px; 355 | height: 540px; 356 | } 357 | 358 | article.fill iframe { 359 | position: absolute; 360 | left: 0; 361 | top: 0; 362 | width: 100%; 363 | height: 100%; 364 | 365 | border: 0; 366 | margin: 0; 367 | 368 | border-radius: 10px; 369 | -o-border-radius: 10px; 370 | -moz-border-radius: 10px; 371 | -webkit-border-radius: 10px; 372 | 373 | z-index: -1; 374 | } 375 | 376 | article.fill img { 377 | position: absolute; 378 | left: 0; 379 | top: 0; 380 | min-width: 100%; 381 | min-height: 100%; 382 | 383 | border-radius: 10px; 384 | -o-border-radius: 10px; 385 | -moz-border-radius: 10px; 386 | -webkit-border-radius: 10px; 387 | 388 | z-index: -1; 389 | } 390 | img.centered { 391 | margin: 0 auto; 392 | display: block; 393 | } 394 | 395 | table { 396 | width: 100%; 397 | border-collapse: collapse; 398 | margin-top: 40px; 399 | } 400 | th { 401 | font-weight: 600; 402 | text-align: left; 403 | } 404 | td, 405 | th { 406 | border: 1px solid rgb(224, 224, 224); 407 | padding: 5px 10px; 408 | vertical-align: top; 409 | } 410 | 411 | .source { 412 | position: absolute; 413 | left: 60px; 414 | top: 644px; 415 | padding-right: 175px; 416 | 417 | font-size: 15px; 418 | letter-spacing: 0; 419 | line-height: 18px; 420 | } 421 | 422 | q { 423 | display: block; 424 | font-size: 60px; 425 | line-height: 72px; 426 | 427 | margin-left: 20px; 428 | 429 | margin-top: 100px; 430 | margin-right: 150px; 431 | } 432 | q::before { 433 | content: '“'; 434 | 435 | position: absolute; 436 | display: inline-block; 437 | margin-left: -2.1em; 438 | width: 2em; 439 | text-align: right; 440 | 441 | font-size: 90px; 442 | color: rgb(192, 192, 192); 443 | } 444 | q::after { 445 | content: '”'; 446 | 447 | position: absolute; 448 | margin-left: .1em; 449 | 450 | font-size: 90px; 451 | color: rgb(192, 192, 192); 452 | } 453 | div.author { 454 | text-align: right; 455 | font-size: 40px; 456 | 457 | margin-top: 20px; 458 | margin-right: 150px; 459 | } 460 | div.author::before { 461 | content: '—'; 462 | } 463 | 464 | /* Size variants */ 465 | 466 | article.smaller p, 467 | article.smaller ul { 468 | font-size: 20px; 469 | line-height: 24px; 470 | letter-spacing: 0; 471 | } 472 | article.smaller table { 473 | font-size: 20px; 474 | line-height: 24px; 475 | letter-spacing: 0; 476 | } 477 | article.smaller pre { 478 | font-size: 15px; 479 | line-height: 20px; 480 | letter-spacing: 0; 481 | } 482 | article.smaller q { 483 | font-size: 40px; 484 | line-height: 48px; 485 | } 486 | article.smaller q::before, 487 | article.smaller q::after { 488 | font-size: 60px; 489 | } 490 | 491 | /* Builds */ 492 | 493 | .build > * { 494 | transition: opacity 0.2s ease-in-out 0.2s; 495 | } 496 | 497 | .to-build { 498 | opacity: 0; 499 | } 500 | 501 | /* Pretty print */ 502 | 503 | .prettyprint .str, /* string content */ 504 | .prettyprint .atv { /* a markup attribute value */ 505 | color: rgb(0, 138, 53); 506 | } 507 | .prettyprint .kwd, /* a keyword */ 508 | .prettyprint .tag { /* a markup tag name */ 509 | color: rgb(0, 102, 204); 510 | } 511 | .prettyprint .com { /* a comment */ 512 | color: rgb(127, 127, 127); 513 | font-style: italic; 514 | } 515 | .prettyprint .lit { /* a literal value */ 516 | color: rgb(127, 0, 0); 517 | } 518 | .prettyprint .pun, /* punctuation, lisp open bracket, lisp close bracket */ 519 | .prettyprint .opn, 520 | .prettyprint .clo { 521 | color: rgb(127, 127, 127); 522 | } 523 | .prettyprint .typ, /* a type name */ 524 | .prettyprint .atn, /* a markup attribute name */ 525 | .prettyprint .dec, 526 | .prettyprint .var { /* a declaration; a variable name */ 527 | color: rgb(127, 0, 127); 528 | } 529 | 530 | 531 | 532 | 533 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var p = require('gulp-load-plugins')(); 3 | 4 | var browserSync = require('browser-sync'); 5 | var reload = browserSync.reload; 6 | 7 | // Start the server 8 | gulp.task('browser-sync', function() { 9 | browserSync({ 10 | open : true, 11 | server: { 12 | baseDir: "./" 13 | } 14 | }); 15 | }); 16 | 17 | gulp.task('slides',function () { 18 | gulp.src('index.jade') 19 | .pipe(p.jade()) 20 | .pipe(gulp.dest('./')) 21 | .pipe(reload({stream:true})) 22 | }); 23 | 24 | 25 | gulp.task('styles',function() { 26 | gulp.src('./styles.styl') 27 | .pipe(p.stylus()) 28 | .pipe(p.autoprefixer()) 29 | .pipe(gulp.dest('./')) 30 | .pipe(reload({stream:true})) 31 | }); 32 | 33 | 34 | gulp.task('default', ['slides','styles','browser-sync'] ,function() { 35 | gulp.watch('./**/*.jade',['slides']); 36 | gulp.watch('./**/*.styl',['styles']); 37 | }); 38 | -------------------------------------------------------------------------------- /images/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/1.jpg -------------------------------------------------------------------------------- /images/10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/10.jpg -------------------------------------------------------------------------------- /images/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/2.jpg -------------------------------------------------------------------------------- /images/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/3.jpg -------------------------------------------------------------------------------- /images/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/4.jpg -------------------------------------------------------------------------------- /images/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/5.jpg -------------------------------------------------------------------------------- /images/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/6.jpg -------------------------------------------------------------------------------- /images/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/7.jpg -------------------------------------------------------------------------------- /images/8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/8.jpg -------------------------------------------------------------------------------- /images/9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/9.jpg -------------------------------------------------------------------------------- /images/BrowserSync.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/BrowserSync.mp4 -------------------------------------------------------------------------------- /images/Twitter_logo_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/Twitter_logo_white.png -------------------------------------------------------------------------------- /images/arrows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/arrows.png -------------------------------------------------------------------------------- /images/babel.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 47 | 73 | 105 | 137 | 170 | 171 | -------------------------------------------------------------------------------- /images/babeled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/babeled.png -------------------------------------------------------------------------------- /images/brain.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /images/canada-flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/canada-flag.png -------------------------------------------------------------------------------- /images/canadian-stylesheets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/canadian-stylesheets.png -------------------------------------------------------------------------------- /images/commandline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/commandline.png -------------------------------------------------------------------------------- /images/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/cover.png -------------------------------------------------------------------------------- /images/critical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/critical.png -------------------------------------------------------------------------------- /images/css-next-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | -------------------------------------------------------------------------------- /images/css-sourcemaps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/css-sourcemaps.png -------------------------------------------------------------------------------- /images/cssnext-gulp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/cssnext-gulp.png -------------------------------------------------------------------------------- /images/es6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/es6.png -------------------------------------------------------------------------------- /images/fetch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/fetch.png -------------------------------------------------------------------------------- /images/flexbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/flexbox.png -------------------------------------------------------------------------------- /images/german-stylesheets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/german-stylesheets.png -------------------------------------------------------------------------------- /images/grunt-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/grunt-logo.png -------------------------------------------------------------------------------- /images/gulp-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/gulp-logo.png -------------------------------------------------------------------------------- /images/hackerYou-logomark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/hackerYou-logomark.png -------------------------------------------------------------------------------- /images/image-compress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/image-compress.png -------------------------------------------------------------------------------- /images/images-gulp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/images-gulp.png -------------------------------------------------------------------------------- /images/jslogo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/jslogo.jpg -------------------------------------------------------------------------------- /images/lodash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/lodash.png -------------------------------------------------------------------------------- /images/modules.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/modules.png -------------------------------------------------------------------------------- /images/npm-mess.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/npm-mess.png -------------------------------------------------------------------------------- /images/postcss-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/postcss-logo.png -------------------------------------------------------------------------------- /images/purify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/purify.png -------------------------------------------------------------------------------- /images/scoped-variables.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/scoped-variables.png -------------------------------------------------------------------------------- /images/sharkbite-depth.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/sharkbite-depth.jpg -------------------------------------------------------------------------------- /images/soldering.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/soldering.jpg -------------------------------------------------------------------------------- /images/sourcemaps-in-devtools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/sourcemaps-in-devtools.png -------------------------------------------------------------------------------- /images/sourcemaps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/sourcemaps.png -------------------------------------------------------------------------------- /images/ugggggggly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/ugggggggly.png -------------------------------------------------------------------------------- /images/uglify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/uglify.png -------------------------------------------------------------------------------- /images/webpack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/webpack.png -------------------------------------------------------------------------------- /images/wow-scripts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/Modern-Workflow-and-Tooling-Talk/96316bdfb6f7ae6b10438685db5edda6b690c489/images/wow-scripts.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | Modern Workflow & Tooling

Modern
Workflow
& Tooling

🔥 Tasty Tooling Tips


wesbos.com@wesbos

I'm
Wes Bos


These slides will be available shortly after this talk
I'll tweet the link out.

@wesbos

ReactForBeginners.com
SublimeTextBook.com
CommandLinePowerUser.com
flexbox.io

Let's talk about tooling.

First, a story

It's about
the plumbing industry

Traditional
Tools

Enter Shark Bite

SharkBite is a new type of fitting that plumbers can use to attach pipes together

It's Lego for plumbing!

Shark Bite is more expensive,
- but -
it allows for faster work
and is much less error prone.

Many Plumbers have written it off.
It's too expensive.
It's too fancy.

It's changing the industry

many are blind to the benefits.

Web tooling is evolving in exactly the same way.

Some developers write tooling off as over-engineering, while others have realized the benefits.

There is a fine line between tooling for tooling's sake and getting real work done.

Let's be smart about the tools we use.

Any time invested in tooling should be paid back by greater efficiency and better, more resilient code.

"There are too many tools!"

"What Should I use?"

"It changes too fast!"

😭

I'm overwhelmed.

[loudly crying]

😭 → 😢 → 😑 → 😮 → 😁 → 😄

This talk is going to review popular workflow components.
I hope you can take away 3 or 4 things
to improve your own workflow.

Build Tools

The Biggest Barrier to Entry

Grunt, Gulp, Broccoli, NPM, WebPack

Not all tools are Equal

What are they for?

Grunt

One task at a time

Gruntfile is Configured

Write to file system

Gulp

Sequential Tasks

Gulpfile is Coded

pipes contents through streams

NPM as a build system

Uses NPM Scripts in your package.json to run a string of bash tasks

Installed node_modules expose commands to package.json like `babel`, `browser-sync` or `uglify`

Instead of using Grunt Uglify or Gulp Autoprefixer, it uses the exposed CLI commands directly.

No special gulp/grunt adapter libs needed - just use the package!

NPM Scripts

"So Simple + Easy"😳

Taken from http://blog.keithcirkel.co.uk/how-to-use-npm-as-a-build-tool/

Flow control can get tough!

WebPack

What Should I use?

Whatever Works Best!

For Websites

Gulp is the good fit because: 2 |
Build Speed 3 |
Package Availability 4 |
Ease of Authoring and Understanding 5 |
Overall Industry Acceptance
47% use Gulp while near 20% don't use one at all

For Large JS Apps

Webpack is in very active development

Huge barrier to entry, but huge possible gains!

Having a build tool is most important.

Once you have something in place, we have thousands of packages available to us — let's look at some!

Performance!

Critical

First Paint is extremely important

Extracts & inlines critical-path (above-the-fold) CSS from HTML

Purify

Removes Unused CSS.

Works with JavaScript Generated classes & Selectors

ImageMin

Image Compression is Hard.


Most devs don't care enough to do it.

ImageMin provides a common, easy to use interface for 17 different image compression libraries.

Do you have
7.75 seconds?

60.5%!

UglifyJS

Minifies, Compresses and Mangles Code

2.3m → 405k with just the defaults

Dependency + Module Management

We're seeing a huge shift
in how we manage
front end dependencies

The Old Way:

One day you wake up and...

Or you have 5 versions of jQuery😂

Or 3 of your plugins are all loading Lodash

The Future is
Modules

We have have modules in Node.js since the beginning, but have seen limited uptake in the browser.

With the rise in popularity of build tools, now is the time to start!

How?

1. Install your libraries:

$ npm install jquery

— or —

$ bower install jquery

— or —

$ jspm install jquery

2. Write your code

Common JS → Node Style

var $ = require('jquery');

ES6 Modules

import $ from 'jquery'

3. Compile into a single or multiple bundles

Browserify → Simpliest API

WebPack → Handles CSS, batteries included, Native ES6 Modules

JSPM → Client Side. Native ES6 Modules

STOP!
What about Gulp? Grunt?
Where do they fit in?

Gulp and Grunt are task runners, one of your tasks will be to bundle your JavaScript.

Gulp and Grunt can call run Browserify, WebPack and JSPM for you, or you can call them directly via NPM Scripts.

Back to Modules
Let's look at some code

Small Modules that
do one thing
and
one thing well.

Only need Ajax?

$npm install fetch

— Then —

Pick + Choose from Lodash

NPM,
Bower,
or JSPM?

Registries

When it comes to installing dependencies, there are two registries: NPM and Bower. JSPM uses NPM's registry.

Use NPM for everything!


Even though NPM was initially for Node, it's been expanded to be the Package Manager for everything JavaScript + Front End.

The Future

JavaScript + CSS are evolving a rapid pace. Let's start writing future code today.

Future JavaScript

ES6
ES7
ES-Next

We have all kinds of useful new features

Arrow Functions

let and const variable declarations

Template Strings

+ Many More

These are things we want to use today - and we can!

Gulp → gulp-babel

Grunt → grunt-babel

Browserify → babelify transformer

WebPack → babel-loader

CLI → babel script.js --out-file script-compiled.js

CSS4

Everyone is
😍😍😍
over Sass
Less & Stylus

But CSS is changing
We're getting the things we like in regular CSS:

Variables, Nesting, Scoping...

Write in CSS4,
compile for current browser support

Start using the new features today

It's Written in JavaScript and compile times are lightning fast

It's part of PostCSS ecosystem

Write your own CSS transformers or grab one of the dozens from NPM (like Autoprefixer!)

Workflow Treats

Small things you should add to your workflow!

BrowserSync

Sourcemaps

It's not uncommon for our code to go though several transforms before it hits the browser as regular CSS

StylusAutoPrefixerCSS

ES6BabelBrowserifyUglifyJSJavaScript

What happens when there is an error?

How do we trace it back to the original file?

CSS bug in _typography.scss:10 but browser shows error in compiled and minified app.css

JavaScript error in your React JSX component Store.js:25, but is untraceable after running through Babel and Browserify!

Sourcemaps are
Treasuremaps
for bugs

😮😮😮
Whew.
That was a lot.

Tooling is important

Put a
build process
in place

Sky is the limit!

Thanks!

@wesbosWesBos.com
-------------------------------------------------------------------------------- /index.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | title Modern Workflow & Tooling 5 | meta(charset='utf-8') 6 | script(src='slides.js') 7 | link(href='https://fonts.googleapis.com/css?family=Bangers|Open+Sans:400,700' rel='stylesheet' type='text/css') 8 | link(href='styles.css' rel='stylesheet' type='text/css') 9 | body 10 | section.slides.layout-regular.template-default 11 | article.first 12 | h1(style="font-size:150px;") Modern
Workflow
& Tooling 13 | p 🔥 Tasty Tooling Tips 14 | br 15 | a(href='http://wesbos.com') wesbos.com 16 | a(href='http://twitter.com/wesbos') @wesbos 17 | 18 | article 19 | h1(style="font-size:170px;") I'm
Wes Bos 20 | br 21 | img(src="images/canada-flag.png") 22 | 23 | article 24 | h2(style="font-size:60px;") These slides will be available shortly after this talk
I'll tweet the link out. 25 | img(src='images/Twitter_logo_white.png', width='300' style="margin:100px auto") 26 | 27 | h2 28 | a(href='http://twitter.com/wesbos', target='_blank') @wesbos 29 | 30 | article(style='background:white;') 31 | a(href='https://ReactForBeginners.com/?utm_source=toolingtalk&utm_medium=link&utm_campaign=talkpromo') 32 | img(src='https://reactforbeginners.com/images/facebook-share.png') 33 | a(href='https://ReactForBeginners.com/?utm_source=toolingtalk&utm_medium=link&utm_campaign=talkpromo') ReactForBeginners.com 34 | 35 | article(style='background:white;') 36 | a(href='https://SublimeTextBook.com/?utm_source=toolingtalk&utm_medium=link&utm_campaign=talkpromo') 37 | img(src='images/cover.png') 38 | a(href='https://SublimeTextBook.com/?utm_source=toolingtalk&utm_medium=link&utm_campaign=talkpromo') SublimeTextBook.com 39 | 40 | article.plain 41 | img(src="images/commandline.png") 42 | a(href="http://commandlinepoweruser.com") CommandLinePowerUser.com 43 | 44 | article.plain 45 | img.high(src="images/flexbox.png") 46 | a(href="http://flexbox.io") flexbox.io 47 | 48 | article 49 | h1 Let's talk about tooling. 50 | p First, a story 51 | 52 | article 53 | h1 It's about
the plumbing industry 54 | 55 | article.old-tools 56 | h1.b Traditional
Tools 57 | ul.build.flex 58 | li 59 | span Copper Soldering 60 | br 61 | small Uses Blow Torch / Starts Fires 62 | br 63 | small Prone to crack at joints 64 | br 65 | small High level of experience needed 66 | li 67 | span Messy Glues 68 | br 69 | small Time Senstive 70 | br 71 | small Undoable 72 | li 73 | | Compression Fittings 74 | br 75 | small Prone to Leaking 76 | br 77 | small Difficult to Install 78 | 79 | article 80 | h1 Enter Shark Bite 81 | img(src="images/sharkbite-depth.jpg") 82 | 83 | article 84 | h2(style="font-size:90px; margin-top:50px;") SharkBite is a new type of fitting that plumbers can use to attach pipes together 85 | p It's Lego for plumbing! 86 | ul.build 87 | li Snaps together instantly 88 | li Can connect any material to copper 89 | li Can be redone any time 90 | li Much less prone to leaks 91 | 92 | article 93 | h2(style="font-size:100px;") Shark Bite is more expensive,
- but -
it allows for faster work
and is much less error prone. 94 | 95 | article 96 | h2 Many Plumbers have written it off.
It's too expensive.
It's too fancy.
97 | 98 | article 99 | h1 It's changing the industry

many are blind to the benefits. 100 | 101 | article 102 | h2 Web tooling is evolving in exactly the same way. 103 | p Some developers write tooling off as over-engineering, while others have realized the benefits. 104 | 105 | article 106 | h1(style="font-size:130px;") There is a fine line between tooling for tooling's sake and getting real work done. 107 | 108 | article 109 | img(src="images/brain.svg") 110 | h2 Let's be smart about the tools we use. 111 | p Any time invested in tooling should be paid back by greater efficiency and better, more resilient code. 112 | 113 | article 114 | 115 | p "There are too many tools!" 116 | p "What Should I use?" 117 | p "It changes too fast!" 118 | h2(style="font-size:116px;") 😭 119 | h2 I'm overwhelmed. 120 | em(style="color:white;") [loudly crying] 121 | 122 | article 123 | h2(style="font-size:60px;") 😭 → 😢 → 😑 → 😮 → 😁 → 😄 124 | h3 This talk is going to review popular workflow components.
I hope you can take away 3 or 4 things
to improve your own workflow. 125 | 126 | article 127 | h2 Build Tools 128 | p The Biggest Barrier to Entry 129 | //- TODO: Logos for build systems here 130 | p Grunt, Gulp, Broccoli, NPM, WebPack 131 | p Not all tools are Equal 132 | 133 | article 134 | h2 What are they for? 135 | ul.build 136 | li Compiling + Converting Styles 137 | li Concatination 138 | li Test Running 139 | li Deployment 140 | li + any task you want automate 141 | 142 | article 143 | .halves 144 | .half 145 | img(src="images/grunt-logo.png" height=200, style="background:white; padding:20px; border-radius:50%;") 146 | h2 Grunt 147 | p One task at a time 148 | p Gruntfile is Configured 149 | p Write to file system 150 | .half 151 | img(src="images/gulp-logo.png" height=200, style="background:white; padding:20px 70px; border-radius:50%;") 152 | h2 Gulp 153 | p Sequential Tasks 154 | p Gulpfile is Coded 155 | p pipes contents through streams 156 | 157 | article 158 | h2 NPM as a build system 159 | p Uses NPM Scripts in your package.json to run a string of bash tasks 160 | p Installed node_modules expose commands to package.json like `babel`, `browser-sync` or `uglify` 161 | p Instead of using Grunt Uglify or Gulp Autoprefixer, it uses the exposed CLI commands directly. 162 | p No special gulp/grunt adapter libs needed - just use the package! 163 | 164 | article 165 | h2 NPM Scripts 166 | ul.build 167 | li No globally installed modules or clashing version from other projects 168 | li No complex build files 169 | li Useful for small build tasks 170 | li Kicking off servers 171 | li Cleaning Directories 172 | li Starting a webpack build 173 | 174 | article 175 | h2 176 | | "So Simple + Easy" 177 | span.emoji 😳 178 | img(src="images/npm-mess.png", alt="Taken from http://blog.keithcirkel.co.uk/how-to-use-npm-as-a-build-tool/") 179 | p Flow control can get tough! 180 | 181 | article 182 | img(src="images/webpack.png", width="200") 183 | h2 WebPack 184 | ul.build 185 | li Focused on JavaScript Modules - Gulp meets Browserify 186 | li Comes Batteries Included for many Common Tasks — concatination, module bundling, uglify, sourcemaps... 187 | li Can handle loading CSS on a per-module basis 188 | li Powerful, but hard to learn API 189 | li Popular in JavaScript community - Hot Loading, Dead Code elimination 190 | 191 | article 192 | h1.big What Should I use? 193 | 194 | article 195 | h2 Whatever Works Best! 196 | 197 | article 198 | h2 For Websites 199 | p Gulp is the good fit because: 200 |
Build Speed 201 |
Package Availability 202 |
Ease of Authoring and Understanding 203 |
Overall Industry Acceptance
47% use Gulp while near 20% don't use one at all 204 | 205 | article 206 | h2 For Large JS Apps 207 | p Webpack is in very active development 208 | p Huge barrier to entry, but huge possible gains! 209 | 210 | article 211 | h2 Having a build tool is most important. 212 | p Once you have something in place, we have thousands of packages available to us — let's look at some! 213 | 214 | article.performance 215 | h1(style="font-size:170px;") Performance! 216 | 217 | article.critical 218 | h1.big 219 | | Critical 220 | a(href="https://github.com/addyosmani/critical" target=_blank) ● 221 | p First Paint is extremely important 222 | p Extracts & inlines critical-path (above-the-fold) CSS from HTML 223 | article 224 | img.high(src="images/critical.png") 225 | article.purify 226 | h1.big 227 | | Purify 228 | a(href="https://github.com/purifycss/purifycss", target=_blank) ● 229 | p Removes Unused CSS. 230 | p Works with JavaScript Generated classes & Selectors 231 | 232 | article 233 | img.high(src="images/purify.png") 234 | 235 | article.imagemin 236 | h1.big 237 | | ImageMin 238 | a(href="https://github.com/imagemin/imagemin", target=_blank) ● 239 | 240 | article 241 | h2(style="font-size:150px;") Image Compression is Hard. 242 | br 243 | p Most devs don't care enough to do it. 244 | p ImageMin provides a common, easy to use interface for 17 different image compression libraries. 245 | 246 | article 247 | img.high(src="images/image-compress.png") 248 | 249 | article 250 | h1.big Do you have
7.75 seconds? 251 | 252 | article 253 | h2 60.5%! 254 | img(src="images/images-gulp.png") 255 | 256 | article.uglifyjs 257 | h1.big 258 | | UglifyJS 259 | a(href="https://github.com/mishoo/UglifyJS2", target=_blank) ● 260 | p Minifies, Compresses and Mangles Code 261 | 262 | article 263 | h2 2.3m → 405k with just the defaults 264 | img(src="images/uglify.png") 265 | 266 | article 267 | img.wide(src="images/ugggggggly.png") 268 | 269 | //- MODULES 270 | 271 | article 272 | h1(style="font-size:200px;") Dependency + Module Management 273 | 274 | article 275 | h1(style="font-size:130px;") We're seeing a huge shift
in how we manage
front end dependencies 276 | 277 | article 278 | h2 The Old Way: 279 | ul 280 | li Find monolith library we want 281 | li Download from Github 282 | li Unzip 283 | li Move over files to project 284 | li pop in another <script> tag 285 | li Rinse + Repeat 286 | 287 | article 288 | h2 One day you wake up and... 289 | img.wide(src="images/wow-scripts.png") 290 | 291 | article 292 | h2 293 | | Or you have 5 versions of jQuery 294 | span.emoji 😂 295 | 296 | article 297 | h2 Or 3 of your plugins are all loading Lodash 298 | 299 | article 300 | h1.big The Future is
Modules 301 | p We have have modules in Node.js since the beginning, but have seen limited uptake in the browser. 302 | p With the rise in popularity of build tools, now is the time to start! 303 | 304 | article 305 | h1.huge How? 306 | 307 | article 308 | h2 1. Install your libraries: 309 | p 310 | span.hl $ 311 | | npm install jquery 312 | p — or — 313 | p 314 | span.hl $ 315 | | bower install jquery 316 | p — or — 317 | p 318 | span.hl $ 319 | | jspm install jquery 320 | 321 | article 322 | h2 2. Write your code 323 | h3 Common JS → Node Style 324 | p var $ = require('jquery'); 325 | h3 ES6 Modules 326 | p import $ from 'jquery' 327 | 328 | article 329 | h2 3. Compile into a single or multiple bundles 330 | p Browserify → Simpliest API 331 | p WebPack → Handles CSS, batteries included, Native ES6 Modules 332 | p JSPM → Client Side. Native ES6 Modules 333 | 334 | article 335 | h1.large STOP!
What about Gulp? Grunt?
Where do they fit in? 336 | 337 | article 338 | h2 Gulp and Grunt are task runners, one of your tasks will be to bundle your JavaScript. 339 | p Gulp and Grunt can call run Browserify, WebPack and JSPM for you, or you can call them directly via NPM Scripts. 340 | 341 | article 342 | h1.large Back to Modules
Let's look at some code 343 | 344 | article 345 | img.high(src="images/modules.png") 346 | 347 | article 348 | h1 Small Modules that
do one thing
and
one thing well. 349 | 350 | article 351 | h2 Only need Ajax? 352 | p 353 | span.hl $ 354 | | npm install fetch 355 | p — Then — 356 | img(src="images/fetch.png") 357 | 358 | article 359 | h2 Pick + Choose from Lodash 360 | 361 | 362 | 363 | article 364 | h1(style="font-size:200px;") NPM,
Bower,
or JSPM? 365 | 366 | article 367 | h2 Registries 368 | p When it comes to installing dependencies, there are two registries: NPM and Bower. JSPM uses NPM's registry. 369 | 370 | article 371 | h1(style="font-size:170px;") Use NPM for everything! 372 | br 373 | p Even though NPM was initially for Node, it's been expanded to be the Package Manager for everything JavaScript + Front End. 374 | 375 | //- article 376 | //- h1 377 | //- span.green Ecosystems:
378 | //- span Coming soon to an
npm install near you 379 | //- br 380 | //- p Will categorize into ‘Browser’, ‘Server’, ‘Gulp’, ‘PhoneGap’ + more 381 | 382 | article 383 | h1.huge The Future 384 | p JavaScript + CSS are evolving a rapid pace. Let's start writing future code today. 385 | 386 | article 387 | h1.big Future JavaScript 388 | 389 | article 390 | h1.big ES6
ES7
ES-Next 391 | p We have all kinds of useful new features 392 | 393 | article 394 | h2 Arrow Functions 395 | img(src="images/arrows.png") 396 | 397 | article 398 | h2 let and const variable declarations 399 | img(src="images/scoped-variables.png") 400 | 401 | article 402 | h2 Template Strings 403 | img.wide(src="images/es6.png") 404 | 405 | article 406 | h2 + Many More 407 | p These are things we want to use today - and we can! 408 | 409 | article(style="background:#f5da55;").plain 410 | img(src="images/babel.svg") 411 | 412 | article 413 | img.high(src="images/babeled.png") 414 | 415 | article 416 | h4 Gulp → gulp-babel 417 | h4 Grunt → grunt-babel 418 | h4 Browserify → babelify transformer 419 | h4 WebPack → babel-loader 420 | h4 CLI → babel script.js --out-file script-compiled.js 421 | 422 | //- article 423 | //- h2 What about new Language Features? 424 | //- p Not everything new is just Syntax
We have new methods, loops + more features. 425 | //- p For that we use a polyfill - require('babel/polyfill'); 426 | 427 | //- Future CSS 428 | article 429 | h1(style="font-size:500px") CSS4 430 | 431 | article 432 | h1 Everyone is
😍😍😍
over Sass
Less & Stylus 433 | 434 | article 435 | h1 But CSS is changing
We're getting the things we like in regular CSS: 436 | p Variables, Nesting, Scoping... 437 | 438 | article.plain(style="background:white;") 439 | img(src="images/css-next-logo.svg") 440 | 441 | article 442 | h2 Write in CSS4,
compile for current browser support 443 | p Start using the new features today 444 | 445 | article 446 | img(src="images/jslogo.jpg" width="200") 447 | h2(style="font-size:120px;") It's Written in JavaScript and compile times are lightning fast 448 | 449 | article 450 | img(src="images/postcss-logo.png" style="border-radius:50%; border:10px solid white;") 451 | h2 It's part of PostCSS ecosystem 452 | p Write your own CSS transformers or grab one of the dozens from NPM (like Autoprefixer!) 453 | 454 | article 455 | img.high(src="images/german-stylesheets.png") 456 | 457 | article 458 | img.high(src="images/canadian-stylesheets.png") 459 | 460 | article 461 | img(src="images/cssnext-gulp.png") 462 | 463 | //- Othe Treats 464 | article.treats 465 | h1 Workflow Treats 466 | p Small things you should add to your workflow! 467 | article 468 | video(src="images/BrowserSync.mp4" loop autoplay) 469 | 470 | article 471 | h2 BrowserSync 472 | ul.build 473 | li Instant Reloading after changes are made 474 | li Includes Server with cert for easy local HTTPS 475 | li Proxies existing applications / servers - WordPress, Ruby, Python... 476 | li Exposes server via local IP so you can test on multiple devices 477 | li Syncs clicks, submits and scrolls 478 | li Absolute MAGIC! 479 | 480 | article 481 | h2 Sourcemaps 482 | p It's not uncommon for our code to go though several transforms before it hits the browser as regular CSS 483 | p StylusAutoPrefixerCSS 484 | p ES6BabelBrowserifyUglifyJSJavaScript 485 | 486 | article 487 | h2 What happens when there is an error? 488 | p How do we trace it back to the original file? 489 | 490 | article 491 | p CSS bug in _typography.scss:10 but browser shows error in compiled and minified app.css 492 | p JavaScript error in your React JSX component Store.js:25, but is untraceable after running through Babel and Browserify! 493 | 494 | article 495 | h1(style="font-size:160px;") Sourcemaps are
Treasuremaps
for bugs 496 | 497 | article 498 | img.high(src="images/sourcemaps.png") 499 | 500 | article 501 | img.high(src="images/css-sourcemaps.png") 502 | 503 | article 504 | img.wide(src="images/sourcemaps-in-devtools.png") 505 | 506 | //- Sum it all up! 507 | article 508 | h1 😮😮😮
Whew.
That was a lot. 509 | 510 | article 511 | h1.big Tooling is important 512 | 513 | article 514 | h1.big Put a
build process
in place 515 | 516 | article 517 | h1.big Sky is the limit! 518 | 519 | article 520 | h1.big Thanks! 521 | a(href="http://twitter.com/wesbos") @wesbos 522 | a(href="http://wesbos.com") WesBos.com 523 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "modern-workflow-and-tooling", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "slides.js", 6 | "directories": { 7 | "example": "examples" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "author": "", 13 | "license": "ISC", 14 | "devDependencies": { 15 | "browser-sync": "^2.9.6", 16 | "gulp": "^3.9.0", 17 | "gulp-autoprefixer": "^3.0.2", 18 | "gulp-jade": "^1.1.0", 19 | "gulp-load-plugins": "^1.0.0-rc.1", 20 | "gulp-stylus": "^2.0.7" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /prettify.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2006 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | 16 | /** 17 | * @fileoverview 18 | * some functions for browser-side pretty printing of code contained in html. 19 | * 20 | *

21 | * For a fairly comprehensive set of languages see the 22 | * README 23 | * file that came with this source. At a minimum, the lexer should work on a 24 | * number of languages including C and friends, Java, Python, Bash, SQL, HTML, 25 | * XML, CSS, Javascript, and Makefiles. It works passably on Ruby, PHP and Awk 26 | * and a subset of Perl, but, because of commenting conventions, doesn't work on 27 | * Smalltalk, Lisp-like, or CAML-like languages without an explicit lang class. 28 | *

29 | * Usage:

    30 | *
  1. include this source file in an html page via 31 | * {@code } 32 | *
  2. define style rules. See the example page for examples. 33 | *
  3. mark the {@code
    } and {@code } tags in your source with
      34 |  *    {@code class=prettyprint.}
      35 |  *    You can also use the (html deprecated) {@code } tag, but the pretty
      36 |  *    printer needs to do more substantial DOM manipulations to support that, so
      37 |  *    some css styles may not be preserved.
      38 |  * </ol>
      39 |  * That's it.  I wanted to keep the API as simple as possible, so there's no
      40 |  * need to specify which language the code is in, but if you wish, you can add
      41 |  * another class to the {@code <pre>} or {@code <code>} element to specify the
      42 |  * language, as in {@code <pre class="prettyprint lang-java">}.  Any class that
      43 |  * starts with "lang-" followed by a file extension, specifies the file type.
      44 |  * See the "lang-*.js" files in this directory for code that implements
      45 |  * per-language file handlers.
      46 |  * <p>
      47 |  * Change log:<br>
      48 |  * cbeust, 2006/08/22
      49 |  * <blockquote>
      50 |  *   Java annotations (start with "@") are now captured as literals ("lit")
      51 |  * </blockquote>
      52 |  * @requires console
      53 |  */
      54 | 
      55 | // JSLint declarations
      56 | /*global console, document, navigator, setTimeout, window */
      57 | 
      58 | /**
      59 |  * Split {@code prettyPrint} into multiple timeouts so as not to interfere with
      60 |  * UI events.
      61 |  * If set to {@code false}, {@code prettyPrint()} is synchronous.
      62 |  */
      63 | window['PR_SHOULD_USE_CONTINUATION'] = true;
      64 | 
      65 | /** the number of characters between tab columns */
      66 | window['PR_TAB_WIDTH'] = 8;
      67 | 
      68 | /** Contains functions for creating and registering new language handlers.
      69 |   * @type {Object}
      70 |   */
      71 | window['PR']
      72 | 
      73 | /** Pretty print a chunk of code.
      74 |   *
      75 |   * @param {string} sourceCodeHtml code as html
      76 |   * @return {string} code as html, but prettier
      77 |   */
      78 |   = window['prettyPrintOne']
      79 | /** Find all the {@code <pre>} and {@code <code>} tags in the DOM with
      80 |   * {@code class=prettyprint} and prettify them.
      81 |   * @param {Function?} opt_whenDone if specified, called when the last entry
      82 |   *     has been finished.
      83 |   */
      84 |   = window['prettyPrint'] = void 0;
      85 | 
      86 | 
      87 | (function () {
      88 |   // Keyword lists for various languages.
      89 |   var FLOW_CONTROL_KEYWORDS =
      90 |       "break continue do else for if return while ";
      91 |   var C_KEYWORDS = FLOW_CONTROL_KEYWORDS + "auto case char const default " +
      92 |       "double enum extern float goto int long register short signed sizeof " +
      93 |       "static struct switch typedef union unsigned void volatile ";
      94 |   var COMMON_KEYWORDS = C_KEYWORDS + "catch class delete false import " +
      95 |       "new operator private protected public this throw true try typeof ";
      96 |   var CPP_KEYWORDS = COMMON_KEYWORDS + "alignof align_union asm axiom bool " +
      97 |       "concept concept_map const_cast constexpr decltype " +
      98 |       "dynamic_cast explicit export friend inline late_check " +
      99 |       "mutable namespace nullptr reinterpret_cast static_assert static_cast " +
     100 |       "template typeid typename using virtual wchar_t where ";
     101 |   var JAVA_KEYWORDS = COMMON_KEYWORDS +
     102 |       "abstract boolean byte extends final finally implements import " +
     103 |       "instanceof null native package strictfp super synchronized throws " +
     104 |       "transient ";
     105 |   var CSHARP_KEYWORDS = JAVA_KEYWORDS +
     106 |       "as base by checked decimal delegate descending dynamic event " +
     107 |       "fixed foreach from group implicit in interface internal into is lock " +
     108 |       "object out override orderby params partial readonly ref sbyte sealed " +
     109 |       "stackalloc string select uint ulong unchecked unsafe ushort var ";
     110 |   var COFFEE_KEYWORDS = "all and by catch class else extends false finally " +
     111 |       "for if in is isnt loop new no not null of off on or return super then " +
     112 |       "true try unless until when while yes ";
     113 |   var JSCRIPT_KEYWORDS = COMMON_KEYWORDS +
     114 |       "debugger eval export function get null set undefined var with " +
     115 |       "Infinity NaN ";
     116 |   var PERL_KEYWORDS = "caller delete die do dump elsif eval exit foreach for " +
     117 |       "goto if import last local my next no our print package redo require " +
     118 |       "sub undef unless until use wantarray while BEGIN END ";
     119 |   var PYTHON_KEYWORDS = FLOW_CONTROL_KEYWORDS + "and as assert class def del " +
     120 |       "elif except exec finally from global import in is lambda " +
     121 |       "nonlocal not or pass print raise try with yield " +
     122 |       "False True None ";
     123 |   var RUBY_KEYWORDS = FLOW_CONTROL_KEYWORDS + "alias and begin case class def" +
     124 |       " defined elsif end ensure false in module next nil not or redo rescue " +
     125 |       "retry self super then true undef unless until when yield BEGIN END ";
     126 |   var SH_KEYWORDS = FLOW_CONTROL_KEYWORDS + "case done elif esac eval fi " +
     127 |       "function in local set then until ";
     128 |   var ALL_KEYWORDS = (
     129 |       CPP_KEYWORDS + CSHARP_KEYWORDS + JSCRIPT_KEYWORDS + PERL_KEYWORDS +
     130 |       PYTHON_KEYWORDS + RUBY_KEYWORDS + SH_KEYWORDS);
     131 | 
     132 |   // token style names.  correspond to css classes
     133 |   /** token style for a string literal */
     134 |   var PR_STRING = 'str';
     135 |   /** token style for a keyword */
     136 |   var PR_KEYWORD = 'kwd';
     137 |   /** token style for a comment */
     138 |   var PR_COMMENT = 'com';
     139 |   /** token style for a type */
     140 |   var PR_TYPE = 'typ';
     141 |   /** token style for a literal value.  e.g. 1, null, true. */
     142 |   var PR_LITERAL = 'lit';
     143 |   /** token style for a punctuation string. */
     144 |   var PR_PUNCTUATION = 'pun';
     145 |   /** token style for a punctuation string. */
     146 |   var PR_PLAIN = 'pln';
     147 | 
     148 |   /** token style for an sgml tag. */
     149 |   var PR_TAG = 'tag';
     150 |   /** token style for a markup declaration such as a DOCTYPE. */
     151 |   var PR_DECLARATION = 'dec';
     152 |   /** token style for embedded source. */
     153 |   var PR_SOURCE = 'src';
     154 |   /** token style for an sgml attribute name. */
     155 |   var PR_ATTRIB_NAME = 'atn';
     156 |   /** token style for an sgml attribute value. */
     157 |   var PR_ATTRIB_VALUE = 'atv';
     158 | 
     159 |   /**
     160 |    * A class that indicates a section of markup that is not code, e.g. to allow
     161 |    * embedding of line numbers within code listings.
     162 |    */
     163 |   var PR_NOCODE = 'nocode';
     164 | 
     165 |   /** A set of tokens that can precede a regular expression literal in
     166 |     * javascript.
     167 |     * http://www.mozilla.org/js/language/js20/rationale/syntax.html has the full
     168 |     * list, but I've removed ones that might be problematic when seen in
     169 |     * languages that don't support regular expression literals.
     170 |     *
     171 |     * <p>Specifically, I've removed any keywords that can't precede a regexp
     172 |     * literal in a syntactically legal javascript program, and I've removed the
     173 |     * "in" keyword since it's not a keyword in many languages, and might be used
     174 |     * as a count of inches.
     175 |     *
     176 |     * <p>The link a above does not accurately describe EcmaScript rules since
     177 |     * it fails to distinguish between (a=++/b/i) and (a++/b/i) but it works
     178 |     * very well in practice.
     179 |     *
     180 |     * @private
     181 |     */
     182 |   var REGEXP_PRECEDER_PATTERN = function () {
     183 |       var preceders = [
     184 |           "!", "!=", "!==", "#", "%", "%=", "&", "&&", "&&=",
     185 |           "&=", "(", "*", "*=", /* "+", */ "+=", ",", /* "-", */ "-=",
     186 |           "->", /*".", "..", "...", handled below */ "/", "/=", ":", "::", ";",
     187 |           "<", "<<", "<<=", "<=", "=", "==", "===", ">",
     188 |           ">=", ">>", ">>=", ">>>", ">>>=", "?", "@", "[",
     189 |           "^", "^=", "^^", "^^=", "{", "|", "|=", "||",
     190 |           "||=", "~" /* handles =~ and !~ */,
     191 |           "break", "case", "continue", "delete",
     192 |           "do", "else", "finally", "instanceof",
     193 |           "return", "throw", "try", "typeof"
     194 |           ];
     195 |       var pattern = '(?:^^|[+-]';
     196 |       for (var i = 0; i < preceders.length; ++i) {
     197 |         pattern += '|' + preceders[i].replace(/([^=<>:&a-z])/g, '\\$1');
     198 |       }
     199 |       pattern += ')\\s*';  // matches at end, and matches empty string
     200 |       return pattern;
     201 |       // CAVEAT: this does not properly handle the case where a regular
     202 |       // expression immediately follows another since a regular expression may
     203 |       // have flags for case-sensitivity and the like.  Having regexp tokens
     204 |       // adjacent is not valid in any language I'm aware of, so I'm punting.
     205 |       // TODO: maybe style special characters inside a regexp as punctuation.
     206 |     }();
     207 | 
     208 |   
     209 |   /**
     210 |    * Given a group of {@link RegExp}s, returns a {@code RegExp} that globally
     211 |    * matches the union of the sets of strings matched by the input RegExp.
     212 |    * Since it matches globally, if the input strings have a start-of-input
     213 |    * anchor (/^.../), it is ignored for the purposes of unioning.
     214 |    * @param {Array.<RegExp>} regexs non multiline, non-global regexs.
     215 |    * @return {RegExp} a global regex.
     216 |    */
     217 |   function combinePrefixPatterns(regexs) {
     218 |     var capturedGroupIndex = 0;
     219 |   
     220 |     var needToFoldCase = false;
     221 |     var ignoreCase = false;
     222 |     for (var i = 0, n = regexs.length; i < n; ++i) {
     223 |       var regex = regexs[i];
     224 |       if (regex.ignoreCase) {
     225 |         ignoreCase = true;
     226 |       } else if (/[a-z]/i.test(regex.source.replace(
     227 |                      /\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi, ''))) {
     228 |         needToFoldCase = true;
     229 |         ignoreCase = false;
     230 |         break;
     231 |       }
     232 |     }
     233 |   
     234 |     function decodeEscape(charsetPart) {
     235 |       if (charsetPart.charAt(0) !== '\\') { return charsetPart.charCodeAt(0); }
     236 |       switch (charsetPart.charAt(1)) {
     237 |         case 'b': return 8;
     238 |         case 't': return 9;
     239 |         case 'n': return 0xa;
     240 |         case 'v': return 0xb;
     241 |         case 'f': return 0xc;
     242 |         case 'r': return 0xd;
     243 |         case 'u': case 'x':
     244 |           return parseInt(charsetPart.substring(2), 16)
     245 |               || charsetPart.charCodeAt(1);
     246 |         case '0': case '1': case '2': case '3': case '4':
     247 |         case '5': case '6': case '7':
     248 |           return parseInt(charsetPart.substring(1), 8);
     249 |         default: return charsetPart.charCodeAt(1);
     250 |       }
     251 |     }
     252 |   
     253 |     function encodeEscape(charCode) {
     254 |       if (charCode < 0x20) {
     255 |         return (charCode < 0x10 ? '\\x0' : '\\x') + charCode.toString(16);
     256 |       }
     257 |       var ch = String.fromCharCode(charCode);
     258 |       if (ch === '\\' || ch === '-' || ch === '[' || ch === ']') {
     259 |         ch = '\\' + ch;
     260 |       }
     261 |       return ch;
     262 |     }
     263 |   
     264 |     function caseFoldCharset(charSet) {
     265 |       var charsetParts = charSet.substring(1, charSet.length - 1).match(
     266 |           new RegExp(
     267 |               '\\\\u[0-9A-Fa-f]{4}'
     268 |               + '|\\\\x[0-9A-Fa-f]{2}'
     269 |               + '|\\\\[0-3][0-7]{0,2}'
     270 |               + '|\\\\[0-7]{1,2}'
     271 |               + '|\\\\[\\s\\S]'
     272 |               + '|-'
     273 |               + '|[^-\\\\]',
     274 |               'g'));
     275 |       var groups = [];
     276 |       var ranges = [];
     277 |       var inverse = charsetParts[0] === '^';
     278 |       for (var i = inverse ? 1 : 0, n = charsetParts.length; i < n; ++i) {
     279 |         var p = charsetParts[i];
     280 |         switch (p) {
     281 |           case '\\B': case '\\b':
     282 |           case '\\D': case '\\d':
     283 |           case '\\S': case '\\s':
     284 |           case '\\W': case '\\w':
     285 |             groups.push(p);
     286 |             continue;
     287 |         }
     288 |         var start = decodeEscape(p);
     289 |         var end;
     290 |         if (i + 2 < n && '-' === charsetParts[i + 1]) {
     291 |           end = decodeEscape(charsetParts[i + 2]);
     292 |           i += 2;
     293 |         } else {
     294 |           end = start;
     295 |         }
     296 |         ranges.push([start, end]);
     297 |         // If the range might intersect letters, then expand it.
     298 |         if (!(end < 65 || start > 122)) {
     299 |           if (!(end < 65 || start > 90)) {
     300 |             ranges.push([Math.max(65, start) | 32, Math.min(end, 90) | 32]);
     301 |           }
     302 |           if (!(end < 97 || start > 122)) {
     303 |             ranges.push([Math.max(97, start) & ~32, Math.min(end, 122) & ~32]);
     304 |           }
     305 |         }
     306 |       }
     307 |   
     308 |       // [[1, 10], [3, 4], [8, 12], [14, 14], [16, 16], [17, 17]]
     309 |       // -> [[1, 12], [14, 14], [16, 17]]
     310 |       ranges.sort(function (a, b) { return (a[0] - b[0]) || (b[1]  - a[1]); });
     311 |       var consolidatedRanges = [];
     312 |       var lastRange = [NaN, NaN];
     313 |       for (var i = 0; i < ranges.length; ++i) {
     314 |         var range = ranges[i];
     315 |         if (range[0] <= lastRange[1] + 1) {
     316 |           lastRange[1] = Math.max(lastRange[1], range[1]);
     317 |         } else {
     318 |           consolidatedRanges.push(lastRange = range);
     319 |         }
     320 |       }
     321 |   
     322 |       var out = ['['];
     323 |       if (inverse) { out.push('^'); }
     324 |       out.push.apply(out, groups);
     325 |       for (var i = 0; i < consolidatedRanges.length; ++i) {
     326 |         var range = consolidatedRanges[i];
     327 |         out.push(encodeEscape(range[0]));
     328 |         if (range[1] > range[0]) {
     329 |           if (range[1] + 1 > range[0]) { out.push('-'); }
     330 |           out.push(encodeEscape(range[1]));
     331 |         }
     332 |       }
     333 |       out.push(']');
     334 |       return out.join('');
     335 |     }
     336 |   
     337 |     function allowAnywhereFoldCaseAndRenumberGroups(regex) {
     338 |       // Split into character sets, escape sequences, punctuation strings
     339 |       // like ('(', '(?:', ')', '^'), and runs of characters that do not
     340 |       // include any of the above.
     341 |       var parts = regex.source.match(
     342 |           new RegExp(
     343 |               '(?:'
     344 |               + '\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]'  // a character set
     345 |               + '|\\\\u[A-Fa-f0-9]{4}'  // a unicode escape
     346 |               + '|\\\\x[A-Fa-f0-9]{2}'  // a hex escape
     347 |               + '|\\\\[0-9]+'  // a back-reference or octal escape
     348 |               + '|\\\\[^ux0-9]'  // other escape sequence
     349 |               + '|\\(\\?[:!=]'  // start of a non-capturing group
     350 |               + '|[\\(\\)\\^]'  // start/emd of a group, or line start
     351 |               + '|[^\\x5B\\x5C\\(\\)\\^]+'  // run of other characters
     352 |               + ')',
     353 |               'g'));
     354 |       var n = parts.length;
     355 |   
     356 |       // Maps captured group numbers to the number they will occupy in
     357 |       // the output or to -1 if that has not been determined, or to
     358 |       // undefined if they need not be capturing in the output.
     359 |       var capturedGroups = [];
     360 |   
     361 |       // Walk over and identify back references to build the capturedGroups
     362 |       // mapping.
     363 |       for (var i = 0, groupIndex = 0; i < n; ++i) {
     364 |         var p = parts[i];
     365 |         if (p === '(') {
     366 |           // groups are 1-indexed, so max group index is count of '('
     367 |           ++groupIndex;
     368 |         } else if ('\\' === p.charAt(0)) {
     369 |           var decimalValue = +p.substring(1);
     370 |           if (decimalValue && decimalValue <= groupIndex) {
     371 |             capturedGroups[decimalValue] = -1;
     372 |           }
     373 |         }
     374 |       }
     375 |   
     376 |       // Renumber groups and reduce capturing groups to non-capturing groups
     377 |       // where possible.
     378 |       for (var i = 1; i < capturedGroups.length; ++i) {
     379 |         if (-1 === capturedGroups[i]) {
     380 |           capturedGroups[i] = ++capturedGroupIndex;
     381 |         }
     382 |       }
     383 |       for (var i = 0, groupIndex = 0; i < n; ++i) {
     384 |         var p = parts[i];
     385 |         if (p === '(') {
     386 |           ++groupIndex;
     387 |           if (capturedGroups[groupIndex] === undefined) {
     388 |             parts[i] = '(?:';
     389 |           }
     390 |         } else if ('\\' === p.charAt(0)) {
     391 |           var decimalValue = +p.substring(1);
     392 |           if (decimalValue && decimalValue <= groupIndex) {
     393 |             parts[i] = '\\' + capturedGroups[groupIndex];
     394 |           }
     395 |         }
     396 |       }
     397 |   
     398 |       // Remove any prefix anchors so that the output will match anywhere.
     399 |       // ^^ really does mean an anchored match though.
     400 |       for (var i = 0, groupIndex = 0; i < n; ++i) {
     401 |         if ('^' === parts[i] && '^' !== parts[i + 1]) { parts[i] = ''; }
     402 |       }
     403 |   
     404 |       // Expand letters to groups to handle mixing of case-sensitive and
     405 |       // case-insensitive patterns if necessary.
     406 |       if (regex.ignoreCase && needToFoldCase) {
     407 |         for (var i = 0; i < n; ++i) {
     408 |           var p = parts[i];
     409 |           var ch0 = p.charAt(0);
     410 |           if (p.length >= 2 && ch0 === '[') {
     411 |             parts[i] = caseFoldCharset(p);
     412 |           } else if (ch0 !== '\\') {
     413 |             // TODO: handle letters in numeric escapes.
     414 |             parts[i] = p.replace(
     415 |                 /[a-zA-Z]/g,
     416 |                 function (ch) {
     417 |                   var cc = ch.charCodeAt(0);
     418 |                   return '[' + String.fromCharCode(cc & ~32, cc | 32) + ']';
     419 |                 });
     420 |           }
     421 |         }
     422 |       }
     423 |   
     424 |       return parts.join('');
     425 |     }
     426 |   
     427 |     var rewritten = [];
     428 |     for (var i = 0, n = regexs.length; i < n; ++i) {
     429 |       var regex = regexs[i];
     430 |       if (regex.global || regex.multiline) { throw new Error('' + regex); }
     431 |       rewritten.push(
     432 |           '(?:' + allowAnywhereFoldCaseAndRenumberGroups(regex) + ')');
     433 |     }
     434 |   
     435 |     return new RegExp(rewritten.join('|'), ignoreCase ? 'gi' : 'g');
     436 |   }
     437 | 
     438 | 
     439 |   /**
     440 |    * Split markup into a string of source code and an array mapping ranges in
     441 |    * that string to the text nodes in which they appear.
     442 |    *
     443 |    * <p>
     444 |    * The HTML DOM structure:</p>
     445 |    * <pre>
     446 |    * (Element   "p"
     447 |    *   (Element "b"
     448 |    *     (Text  "print "))       ; #1
     449 |    *   (Text    "'Hello '")      ; #2
     450 |    *   (Element "br")            ; #3
     451 |    *   (Text    "  + 'World';")) ; #4
     452 |    * </pre>
     453 |    * <p>
     454 |    * corresponds to the HTML
     455 |    * {@code <p><b>print </b>'Hello '<br>  + 'World';</p>}.</p>
     456 |    *
     457 |    * <p>
     458 |    * It will produce the output:</p>
     459 |    * <pre>
     460 |    * {
     461 |    *   source: "print 'Hello '\n  + 'World';",
     462 |    *   //                 1         2
     463 |    *   //       012345678901234 5678901234567
     464 |    *   spans: [0, #1, 6, #2, 14, #3, 15, #4]
     465 |    * }
     466 |    * </pre>
     467 |    * <p>
     468 |    * where #1 is a reference to the {@code "print "} text node above, and so
     469 |    * on for the other text nodes.
     470 |    * </p>
     471 |    *
     472 |    * <p>
     473 |    * The {@code} spans array is an array of pairs.  Even elements are the start
     474 |    * indices of substrings, and odd elements are the text nodes (or BR elements)
     475 |    * that contain the text for those substrings.
     476 |    * Substrings continue until the next index or the end of the source.
     477 |    * </p>
     478 |    *
     479 |    * @param {Node} node an HTML DOM subtree containing source-code.
     480 |    * @return {Object} source code and the text nodes in which they occur.
     481 |    */
     482 |   function extractSourceSpans(node) {
     483 |     var nocode = /(?:^|\s)nocode(?:\s|$)/;
     484 |   
     485 |     var chunks = [];
     486 |     var length = 0;
     487 |     var spans = [];
     488 |     var k = 0;
     489 |   
     490 |     var whitespace;
     491 |     if (node.currentStyle) {
     492 |       whitespace = node.currentStyle.whiteSpace;
     493 |     } else if (window.getComputedStyle) {
     494 |       whitespace = document.defaultView.getComputedStyle(node, null)
     495 |           .getPropertyValue('white-space');
     496 |     }
     497 |     var isPreformatted = whitespace && 'pre' === whitespace.substring(0, 3);
     498 |   
     499 |     function walk(node) {
     500 |       switch (node.nodeType) {
     501 |         case 1:  // Element
     502 |           if (nocode.test(node.className)) { return; }
     503 |           for (var child = node.firstChild; child; child = child.nextSibling) {
     504 |             walk(child);
     505 |           }
     506 |           var nodeName = node.nodeName;
     507 |           if ('BR' === nodeName || 'LI' === nodeName) {
     508 |             chunks[k] = '\n';
     509 |             spans[k << 1] = length++;
     510 |             spans[(k++ << 1) | 1] = node;
     511 |           }
     512 |           break;
     513 |         case 3: case 4:  // Text
     514 |           var text = node.nodeValue;
     515 |           if (text.length) {
     516 |             if (!isPreformatted) {
     517 |               text = text.replace(/[ \t\r\n]+/g, ' ');
     518 |             } else {
     519 |               text = text.replace(/\r\n?/g, '\n');  // Normalize newlines.
     520 |             }
     521 |             // TODO: handle tabs here?
     522 |             chunks[k] = text;
     523 |             spans[k << 1] = length;
     524 |             length += text.length;
     525 |             spans[(k++ << 1) | 1] = node;
     526 |           }
     527 |           break;
     528 |       }
     529 |     }
     530 |   
     531 |     walk(node);
     532 |   
     533 |     return {
     534 |       source: chunks.join('').replace(/\n$/, ''),
     535 |       spans: spans
     536 |     };
     537 |   }
     538 | 
     539 | 
     540 |   /**
     541 |    * Apply the given language handler to sourceCode and add the resulting
     542 |    * decorations to out.
     543 |    * @param {number} basePos the index of sourceCode within the chunk of source
     544 |    *    whose decorations are already present on out.
     545 |    */
     546 |   function appendDecorations(basePos, sourceCode, langHandler, out) {
     547 |     if (!sourceCode) { return; }
     548 |     var job = {
     549 |       source: sourceCode,
     550 |       basePos: basePos
     551 |     };
     552 |     langHandler(job);
     553 |     out.push.apply(out, job.decorations);
     554 |   }
     555 | 
     556 |   /** Given triples of [style, pattern, context] returns a lexing function,
     557 |     * The lexing function interprets the patterns to find token boundaries and
     558 |     * returns a decoration list of the form
     559 |     * [index_0, style_0, index_1, style_1, ..., index_n, style_n]
     560 |     * where index_n is an index into the sourceCode, and style_n is a style
     561 |     * constant like PR_PLAIN.  index_n-1 <= index_n, and style_n-1 applies to
     562 |     * all characters in sourceCode[index_n-1:index_n].
     563 |     *
     564 |     * The stylePatterns is a list whose elements have the form
     565 |     * [style : string, pattern : RegExp, DEPRECATED, shortcut : string].
     566 |     *
     567 |     * Style is a style constant like PR_PLAIN, or can be a string of the
     568 |     * form 'lang-FOO', where FOO is a language extension describing the
     569 |     * language of the portion of the token in $1 after pattern executes.
     570 |     * E.g., if style is 'lang-lisp', and group 1 contains the text
     571 |     * '(hello (world))', then that portion of the token will be passed to the
     572 |     * registered lisp handler for formatting.
     573 |     * The text before and after group 1 will be restyled using this decorator
     574 |     * so decorators should take care that this doesn't result in infinite
     575 |     * recursion.  For example, the HTML lexer rule for SCRIPT elements looks
     576 |     * something like ['lang-js', /<[s]cript>(.+?)<\/script>/].  This may match
     577 |     * '<script>foo()<\/script>', which would cause the current decorator to
     578 |     * be called with '<script>' which would not match the same rule since
     579 |     * group 1 must not be empty, so it would be instead styled as PR_TAG by
     580 |     * the generic tag rule.  The handler registered for the 'js' extension would
     581 |     * then be called with 'foo()', and finally, the current decorator would
     582 |     * be called with '<\/script>' which would not match the original rule and
     583 |     * so the generic tag rule would identify it as a tag.
     584 |     *
     585 |     * Pattern must only match prefixes, and if it matches a prefix, then that
     586 |     * match is considered a token with the same style.
     587 |     *
     588 |     * Context is applied to the last non-whitespace, non-comment token
     589 |     * recognized.
     590 |     *
     591 |     * Shortcut is an optional string of characters, any of which, if the first
     592 |     * character, gurantee that this pattern and only this pattern matches.
     593 |     *
     594 |     * @param {Array} shortcutStylePatterns patterns that always start with
     595 |     *   a known character.  Must have a shortcut string.
     596 |     * @param {Array} fallthroughStylePatterns patterns that will be tried in
     597 |     *   order if the shortcut ones fail.  May have shortcuts.
     598 |     *
     599 |     * @return {function (Object)} a
     600 |     *   function that takes source code and returns a list of decorations.
     601 |     */
     602 |   function createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns) {
     603 |     var shortcuts = {};
     604 |     var tokenizer;
     605 |     (function () {
     606 |       var allPatterns = shortcutStylePatterns.concat(fallthroughStylePatterns);
     607 |       var allRegexs = [];
     608 |       var regexKeys = {};
     609 |       for (var i = 0, n = allPatterns.length; i < n; ++i) {
     610 |         var patternParts = allPatterns[i];
     611 |         var shortcutChars = patternParts[3];
     612 |         if (shortcutChars) {
     613 |           for (var c = shortcutChars.length; --c >= 0;) {
     614 |             shortcuts[shortcutChars.charAt(c)] = patternParts;
     615 |           }
     616 |         }
     617 |         var regex = patternParts[1];
     618 |         var k = '' + regex;
     619 |         if (!regexKeys.hasOwnProperty(k)) {
     620 |           allRegexs.push(regex);
     621 |           regexKeys[k] = null;
     622 |         }
     623 |       }
     624 |       allRegexs.push(/[\0-\uffff]/);
     625 |       tokenizer = combinePrefixPatterns(allRegexs);
     626 |     })();
     627 | 
     628 |     var nPatterns = fallthroughStylePatterns.length;
     629 |     var notWs = /\S/;
     630 | 
     631 |     /**
     632 |      * Lexes job.source and produces an output array job.decorations of style
     633 |      * classes preceded by the position at which they start in job.source in
     634 |      * order.
     635 |      *
     636 |      * @param {Object} job an object like {@code
     637 |      *    source: {string} sourceText plain text,
     638 |      *    basePos: {int} position of job.source in the larger chunk of
     639 |      *        sourceCode.
     640 |      * }
     641 |      */
     642 |     var decorate = function (job) {
     643 |       var sourceCode = job.source, basePos = job.basePos;
     644 |       /** Even entries are positions in source in ascending order.  Odd enties
     645 |         * are style markers (e.g., PR_COMMENT) that run from that position until
     646 |         * the end.
     647 |         * @type {Array.<number|string>}
     648 |         */
     649 |       var decorations = [basePos, PR_PLAIN];
     650 |       var pos = 0;  // index into sourceCode
     651 |       var tokens = sourceCode.match(tokenizer) || [];
     652 |       var styleCache = {};
     653 | 
     654 |       for (var ti = 0, nTokens = tokens.length; ti < nTokens; ++ti) {
     655 |         var token = tokens[ti];
     656 |         var style = styleCache[token];
     657 |         var match = void 0;
     658 | 
     659 |         var isEmbedded;
     660 |         if (typeof style === 'string') {
     661 |           isEmbedded = false;
     662 |         } else {
     663 |           var patternParts = shortcuts[token.charAt(0)];
     664 |           if (patternParts) {
     665 |             match = token.match(patternParts[1]);
     666 |             style = patternParts[0];
     667 |           } else {
     668 |             for (var i = 0; i < nPatterns; ++i) {
     669 |               patternParts = fallthroughStylePatterns[i];
     670 |               match = token.match(patternParts[1]);
     671 |               if (match) {
     672 |                 style = patternParts[0];
     673 |                 break;
     674 |               }
     675 |             }
     676 | 
     677 |             if (!match) {  // make sure that we make progress
     678 |               style = PR_PLAIN;
     679 |             }
     680 |           }
     681 | 
     682 |           isEmbedded = style.length >= 5 && 'lang-' === style.substring(0, 5);
     683 |           if (isEmbedded && !(match && typeof match[1] === 'string')) {
     684 |             isEmbedded = false;
     685 |             style = PR_SOURCE;
     686 |           }
     687 | 
     688 |           if (!isEmbedded) { styleCache[token] = style; }
     689 |         }
     690 | 
     691 |         var tokenStart = pos;
     692 |         pos += token.length;
     693 | 
     694 |         if (!isEmbedded) {
     695 |           decorations.push(basePos + tokenStart, style);
     696 |         } else {  // Treat group 1 as an embedded block of source code.
     697 |           var embeddedSource = match[1];
     698 |           var embeddedSourceStart = token.indexOf(embeddedSource);
     699 |           var embeddedSourceEnd = embeddedSourceStart + embeddedSource.length;
     700 |           if (match[2]) {
     701 |             // If embeddedSource can be blank, then it would match at the
     702 |             // beginning which would cause us to infinitely recurse on the
     703 |             // entire token, so we catch the right context in match[2].
     704 |             embeddedSourceEnd = token.length - match[2].length;
     705 |             embeddedSourceStart = embeddedSourceEnd - embeddedSource.length;
     706 |           }
     707 |           var lang = style.substring(5);
     708 |           // Decorate the left of the embedded source
     709 |           appendDecorations(
     710 |               basePos + tokenStart,
     711 |               token.substring(0, embeddedSourceStart),
     712 |               decorate, decorations);
     713 |           // Decorate the embedded source
     714 |           appendDecorations(
     715 |               basePos + tokenStart + embeddedSourceStart,
     716 |               embeddedSource,
     717 |               langHandlerForExtension(lang, embeddedSource),
     718 |               decorations);
     719 |           // Decorate the right of the embedded section
     720 |           appendDecorations(
     721 |               basePos + tokenStart + embeddedSourceEnd,
     722 |               token.substring(embeddedSourceEnd),
     723 |               decorate, decorations);
     724 |         }
     725 |       }
     726 |       job.decorations = decorations;
     727 |     };
     728 |     return decorate;
     729 |   }
     730 | 
     731 |   /** returns a function that produces a list of decorations from source text.
     732 |     *
     733 |     * This code treats ", ', and ` as string delimiters, and \ as a string
     734 |     * escape.  It does not recognize perl's qq() style strings.
     735 |     * It has no special handling for double delimiter escapes as in basic, or
     736 |     * the tripled delimiters used in python, but should work on those regardless
     737 |     * although in those cases a single string literal may be broken up into
     738 |     * multiple adjacent string literals.
     739 |     *
     740 |     * It recognizes C, C++, and shell style comments.
     741 |     *
     742 |     * @param {Object} options a set of optional parameters.
     743 |     * @return {function (Object)} a function that examines the source code
     744 |     *     in the input job and builds the decoration list.
     745 |     */
     746 |   function sourceDecorator(options) {
     747 |     var shortcutStylePatterns = [], fallthroughStylePatterns = [];
     748 |     if (options['tripleQuotedStrings']) {
     749 |       // '''multi-line-string''', 'single-line-string', and double-quoted
     750 |       shortcutStylePatterns.push(
     751 |           [PR_STRING,  /^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,
     752 |            null, '\'"']);
     753 |     } else if (options['multiLineStrings']) {
     754 |       // 'multi-line-string', "multi-line-string"
     755 |       shortcutStylePatterns.push(
     756 |           [PR_STRING,  /^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,
     757 |            null, '\'"`']);
     758 |     } else {
     759 |       // 'single-line-string', "single-line-string"
     760 |       shortcutStylePatterns.push(
     761 |           [PR_STRING,
     762 |            /^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,
     763 |            null, '"\'']);
     764 |     }
     765 |     if (options['verbatimStrings']) {
     766 |       // verbatim-string-literal production from the C# grammar.  See issue 93.
     767 |       fallthroughStylePatterns.push(
     768 |           [PR_STRING, /^@\"(?:[^\"]|\"\")*(?:\"|$)/, null]);
     769 |     }
     770 |     var hc = options['hashComments'];
     771 |     if (hc) {
     772 |       if (options['cStyleComments']) {
     773 |         if (hc > 1) {  // multiline hash comments
     774 |           shortcutStylePatterns.push(
     775 |               [PR_COMMENT, /^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/, null, '#']);
     776 |         } else {
     777 |           // Stop C preprocessor declarations at an unclosed open comment
     778 |           shortcutStylePatterns.push(
     779 |               [PR_COMMENT, /^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,
     780 |                null, '#']);
     781 |         }
     782 |         fallthroughStylePatterns.push(
     783 |             [PR_STRING,
     784 |              /^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,
     785 |              null]);
     786 |       } else {
     787 |         shortcutStylePatterns.push([PR_COMMENT, /^#[^\r\n]*/, null, '#']);
     788 |       }
     789 |     }
     790 |     if (options['cStyleComments']) {
     791 |       fallthroughStylePatterns.push([PR_COMMENT, /^\/\/[^\r\n]*/, null]);
     792 |       fallthroughStylePatterns.push(
     793 |           [PR_COMMENT, /^\/\*[\s\S]*?(?:\*\/|$)/, null]);
     794 |     }
     795 |     if (options['regexLiterals']) {
     796 |       var REGEX_LITERAL = (
     797 |           // A regular expression literal starts with a slash that is
     798 |           // not followed by * or / so that it is not confused with
     799 |           // comments.
     800 |           '/(?=[^/*])'
     801 |           // and then contains any number of raw characters,
     802 |           + '(?:[^/\\x5B\\x5C]'
     803 |           // escape sequences (\x5C),
     804 |           +    '|\\x5C[\\s\\S]'
     805 |           // or non-nesting character sets (\x5B\x5D);
     806 |           +    '|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+'
     807 |           // finally closed by a /.
     808 |           + '/');
     809 |       fallthroughStylePatterns.push(
     810 |           ['lang-regex',
     811 |            new RegExp('^' + REGEXP_PRECEDER_PATTERN + '(' + REGEX_LITERAL + ')')
     812 |            ]);
     813 |     }
     814 | 
     815 |     var keywords = options['keywords'].replace(/^\s+|\s+$/g, '');
     816 |     if (keywords.length) {
     817 |       fallthroughStylePatterns.push(
     818 |           [PR_KEYWORD,
     819 |            new RegExp('^(?:' + keywords.replace(/\s+/g, '|') + ')\\b'), null]);
     820 |     }
     821 | 
     822 |     shortcutStylePatterns.push([PR_PLAIN,       /^\s+/, null, ' \r\n\t\xA0']);
     823 |     fallthroughStylePatterns.push(
     824 |         // TODO(mikesamuel): recognize non-latin letters and numerals in idents
     825 |         [PR_LITERAL,     /^@[a-z_$][a-z_$@0-9]*/i, null],
     826 |         [PR_TYPE,        /^@?[A-Z]+[a-z][A-Za-z_$@0-9]*/, null],
     827 |         [PR_PLAIN,       /^[a-z_$][a-z_$@0-9]*/i, null],
     828 |         [PR_LITERAL,
     829 |          new RegExp(
     830 |              '^(?:'
     831 |              // A hex number
     832 |              + '0x[a-f0-9]+'
     833 |              // or an octal or decimal number,
     834 |              + '|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)'
     835 |              // possibly in scientific notation
     836 |              + '(?:e[+\\-]?\\d+)?'
     837 |              + ')'
     838 |              // with an optional modifier like UL for unsigned long
     839 |              + '[a-z]*', 'i'),
     840 |          null, '0123456789'],
     841 |         // Don't treat escaped quotes in bash as starting strings.  See issue 144.
     842 |         [PR_PLAIN,       /^\\[\s\S]?/, null],
     843 |         [PR_PUNCTUATION, /^.[^\s\w\.$@\'\"\`\/\#\\]*/, null]);
     844 | 
     845 |     return createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns);
     846 |   }
     847 | 
     848 |   var decorateSource = sourceDecorator({
     849 |         'keywords': ALL_KEYWORDS,
     850 |         'hashComments': true,
     851 |         'cStyleComments': true,
     852 |         'multiLineStrings': true,
     853 |         'regexLiterals': true
     854 |       });
     855 | 
     856 |   /**
     857 |    * Given a DOM subtree, wraps it in a list, and puts each line into its own
     858 |    * list item.
     859 |    *
     860 |    * @param {Node} node modified in place.  Its content is pulled into an
     861 |    *     HTMLOListElement, and each line is moved into a separate list item.
     862 |    *     This requires cloning elements, so the input might not have unique
     863 |    *     IDs after numbering.
     864 |    */
     865 |   function numberLines(node, opt_startLineNum) {
     866 |     var nocode = /(?:^|\s)nocode(?:\s|$)/;
     867 |     var lineBreak = /\r\n?|\n/;
     868 |   
     869 |     var document = node.ownerDocument;
     870 |   
     871 |     var whitespace;
     872 |     if (node.currentStyle) {
     873 |       whitespace = node.currentStyle.whiteSpace;
     874 |     } else if (window.getComputedStyle) {
     875 |       whitespace = document.defaultView.getComputedStyle(node, null)
     876 |           .getPropertyValue('white-space');
     877 |     }
     878 |     // If it's preformatted, then we need to split lines on line breaks
     879 |     // in addition to <BR>s.
     880 |     var isPreformatted = whitespace && 'pre' === whitespace.substring(0, 3);
     881 |   
     882 |     var li = document.createElement('LI');
     883 |     while (node.firstChild) {
     884 |       li.appendChild(node.firstChild);
     885 |     }
     886 |     // An array of lines.  We split below, so this is initialized to one
     887 |     // un-split line.
     888 |     var listItems = [li];
     889 |   
     890 |     function walk(node) {
     891 |       switch (node.nodeType) {
     892 |         case 1:  // Element
     893 |           if (nocode.test(node.className)) { break; }
     894 |           if ('BR' === node.nodeName) {
     895 |             breakAfter(node);
     896 |             // Discard the <BR> since it is now flush against a </LI>.
     897 |             if (node.parentNode) {
     898 |               node.parentNode.removeChild(node);
     899 |             }
     900 |           } else {
     901 |             for (var child = node.firstChild; child; child = child.nextSibling) {
     902 |               walk(child);
     903 |             }
     904 |           }
     905 |           break;
     906 |         case 3: case 4:  // Text
     907 |           if (isPreformatted) {
     908 |             var text = node.nodeValue;
     909 |             var match = text.match(lineBreak);
     910 |             if (match) {
     911 |               var firstLine = text.substring(0, match.index);
     912 |               node.nodeValue = firstLine;
     913 |               var tail = text.substring(match.index + match[0].length);
     914 |               if (tail) {
     915 |                 var parent = node.parentNode;
     916 |                 parent.insertBefore(
     917 |                     document.createTextNode(tail), node.nextSibling);
     918 |               }
     919 |               breakAfter(node);
     920 |               if (!firstLine) {
     921 |                 // Don't leave blank text nodes in the DOM.
     922 |                 node.parentNode.removeChild(node);
     923 |               }
     924 |             }
     925 |           }
     926 |           break;
     927 |       }
     928 |     }
     929 |   
     930 |     // Split a line after the given node.
     931 |     function breakAfter(lineEndNode) {
     932 |       // If there's nothing to the right, then we can skip ending the line
     933 |       // here, and move root-wards since splitting just before an end-tag
     934 |       // would require us to create a bunch of empty copies.
     935 |       while (!lineEndNode.nextSibling) {
     936 |         lineEndNode = lineEndNode.parentNode;
     937 |         if (!lineEndNode) { return; }
     938 |       }
     939 |   
     940 |       function breakLeftOf(limit, copy) {
     941 |         // Clone shallowly if this node needs to be on both sides of the break.
     942 |         var rightSide = copy ? limit.cloneNode(false) : limit;
     943 |         var parent = limit.parentNode;
     944 |         if (parent) {
     945 |           // We clone the parent chain.
     946 |           // This helps us resurrect important styling elements that cross lines.
     947 |           // E.g. in <i>Foo<br>Bar</i>
     948 |           // should be rewritten to <li><i>Foo</i></li><li><i>Bar</i></li>.
     949 |           var parentClone = breakLeftOf(parent, 1);
     950 |           // Move the clone and everything to the right of the original
     951 |           // onto the cloned parent.
     952 |           var next = limit.nextSibling;
     953 |           parentClone.appendChild(rightSide);
     954 |           for (var sibling = next; sibling; sibling = next) {
     955 |             next = sibling.nextSibling;
     956 |             parentClone.appendChild(sibling);
     957 |           }
     958 |         }
     959 |         return rightSide;
     960 |       }
     961 |   
     962 |       var copiedListItem = breakLeftOf(lineEndNode.nextSibling, 0);
     963 |   
     964 |       // Walk the parent chain until we reach an unattached LI.
     965 |       for (var parent;
     966 |            // Check nodeType since IE invents document fragments.
     967 |            (parent = copiedListItem.parentNode) && parent.nodeType === 1;) {
     968 |         copiedListItem = parent;
     969 |       }
     970 |       // Put it on the list of lines for later processing.
     971 |       listItems.push(copiedListItem);
     972 |     }
     973 |   
     974 |     // Split lines while there are lines left to split.
     975 |     for (var i = 0;  // Number of lines that have been split so far.
     976 |          i < listItems.length;  // length updated by breakAfter calls.
     977 |          ++i) {
     978 |       walk(listItems[i]);
     979 |     }
     980 |   
     981 |     // Make sure numeric indices show correctly.
     982 |     if (opt_startLineNum === (opt_startLineNum|0)) {
     983 |       listItems[0].setAttribute('value', opt_startLineNum);
     984 |     }
     985 |   
     986 |     var ol = document.createElement('OL');
     987 |     ol.className = 'linenums';
     988 |     var offset = Math.max(0, ((opt_startLineNum - 1 /* zero index */)) | 0) || 0;
     989 |     for (var i = 0, n = listItems.length; i < n; ++i) {
     990 |       li = listItems[i];
     991 |       // Stick a class on the LIs so that stylesheets can
     992 |       // color odd/even rows, or any other row pattern that
     993 |       // is co-prime with 10.
     994 |       li.className = 'L' + ((i + offset) % 10);
     995 |       if (!li.firstChild) {
     996 |         li.appendChild(document.createTextNode('\xA0'));
     997 |       }
     998 |       ol.appendChild(li);
     999 |     }
    1000 |   
    1001 |     node.appendChild(ol);
    1002 |   }
    1003 | 
    1004 |   /**
    1005 |    * Breaks {@code job.source} around style boundaries in {@code job.decorations}
    1006 |    * and modifies {@code job.sourceNode} in place.
    1007 |    * @param {Object} job like <pre>{
    1008 |    *    source: {string} source as plain text,
    1009 |    *    spans: {Array.<number|Node>} alternating span start indices into source
    1010 |    *       and the text node or element (e.g. {@code <BR>}) corresponding to that
    1011 |    *       span.
    1012 |    *    decorations: {Array.<number|string} an array of style classes preceded
    1013 |    *       by the position at which they start in job.source in order
    1014 |    * }</pre>
    1015 |    * @private
    1016 |    */
    1017 |   function recombineTagsAndDecorations(job) {
    1018 |     var isIE = /\bMSIE\b/.test(navigator.userAgent);
    1019 |     var newlineRe = /\n/g;
    1020 |   
    1021 |     var source = job.source;
    1022 |     var sourceLength = source.length;
    1023 |     // Index into source after the last code-unit recombined.
    1024 |     var sourceIndex = 0;
    1025 |   
    1026 |     var spans = job.spans;
    1027 |     var nSpans = spans.length;
    1028 |     // Index into spans after the last span which ends at or before sourceIndex.
    1029 |     var spanIndex = 0;
    1030 |   
    1031 |     var decorations = job.decorations;
    1032 |     var nDecorations = decorations.length;
    1033 |     // Index into decorations after the last decoration which ends at or before sourceIndex.
    1034 |     var decorationIndex = 0;
    1035 |   
    1036 |     // Simplify decorations.
    1037 |     var decPos = 0;
    1038 |     for (var i = 0; i < nDecorations;) {
    1039 |       // Skip over any zero-length decorations.
    1040 |       var startPos = decorations[i];
    1041 |       var start = i;
    1042 |       while (start + 2 < nDecorations && decorations[start + 2] === startPos) {
    1043 |         start += 2;
    1044 |       }
    1045 |       // Conflate all adjacent decorations that use the same style.
    1046 |       var startDec = decorations[start + 1];
    1047 |       var end = start + 2;
    1048 |       while (end + 2 <= nDecorations
    1049 |              && (decorations[end + 1] === startDec
    1050 |                  || decorations[end] === decorations[end + 2])) {
    1051 |         end += 2;
    1052 |       }
    1053 |       decorations[decPos++] = startPos;
    1054 |       decorations[decPos++] = startDec;
    1055 |       i = end;
    1056 |     }
    1057 |   
    1058 |     // Strip any zero-length decoration at the end.
    1059 |     if (decPos && decorations[decPos - 2] === sourceLength) { decPos -= 2; }
    1060 |     nDecorations = decorations.length = decPos;
    1061 |   
    1062 |     var decoration = null;
    1063 |     while (spanIndex < nSpans) {
    1064 |       var spanStart = spans[spanIndex];
    1065 |       var spanEnd = spans[spanIndex + 2] || sourceLength;
    1066 |   
    1067 |       var decStart = decorations[decorationIndex];
    1068 |       var decEnd = decorations[decorationIndex + 2] || sourceLength;
    1069 |   
    1070 |       var end = Math.min(spanEnd, decEnd);
    1071 |   
    1072 |       var textNode = spans[spanIndex + 1];
    1073 |       if (textNode.nodeType !== 1) {  // Don't muck with <BR>s or <LI>s
    1074 |         var styledText = source.substring(sourceIndex, end);
    1075 |         // This may seem bizarre, and it is.  Emitting LF on IE causes the
    1076 |         // code to display with spaces instead of line breaks.
    1077 |         // Emitting Windows standard issue linebreaks (CRLF) causes a blank
    1078 |         // space to appear at the beginning of every line but the first.
    1079 |         // Emitting an old Mac OS 9 line separator makes everything spiffy.
    1080 |         if (isIE) { styledText = styledText.replace(newlineRe, '\r'); }
    1081 |         textNode.nodeValue = styledText;
    1082 |         var document = textNode.ownerDocument;
    1083 |         var span = document.createElement('SPAN');
    1084 |         span.className = decorations[decorationIndex + 1];
    1085 |         var parentNode = textNode.parentNode;
    1086 |         parentNode.replaceChild(span, textNode);
    1087 |         span.appendChild(textNode);
    1088 |         if (sourceIndex < spanEnd) {  // Split off a text node.
    1089 |           spans[spanIndex + 1] = textNode
    1090 |               // TODO: Possibly optimize by using '' if there's no flicker.
    1091 |               = document.createTextNode(source.substring(end, spanEnd));
    1092 |           parentNode.insertBefore(textNode, span.nextSibling);
    1093 |         }
    1094 |       }
    1095 |   
    1096 |       sourceIndex = end;
    1097 |   
    1098 |       if (sourceIndex >= spanEnd) {
    1099 |         spanIndex += 2;
    1100 |       }
    1101 |       if (sourceIndex >= decEnd) {
    1102 |         decorationIndex += 2;
    1103 |       }
    1104 |     }
    1105 |   }
    1106 | 
    1107 | 
    1108 |   /** Maps language-specific file extensions to handlers. */
    1109 |   var langHandlerRegistry = {};
    1110 |   /** Register a language handler for the given file extensions.
    1111 |     * @param {function (Object)} handler a function from source code to a list
    1112 |     *      of decorations.  Takes a single argument job which describes the
    1113 |     *      state of the computation.   The single parameter has the form
    1114 |     *      {@code {
    1115 |     *        source: {string} as plain text.
    1116 |     *        decorations: {Array.<number|string>} an array of style classes
    1117 |     *                     preceded by the position at which they start in
    1118 |     *                     job.source in order.
    1119 |     *                     The language handler should assigned this field.
    1120 |     *        basePos: {int} the position of source in the larger source chunk.
    1121 |     *                 All positions in the output decorations array are relative
    1122 |     *                 to the larger source chunk.
    1123 |     *      } }
    1124 |     * @param {Array.<string>} fileExtensions
    1125 |     */
    1126 |   function registerLangHandler(handler, fileExtensions) {
    1127 |     for (var i = fileExtensions.length; --i >= 0;) {
    1128 |       var ext = fileExtensions[i];
    1129 |       if (!langHandlerRegistry.hasOwnProperty(ext)) {
    1130 |         langHandlerRegistry[ext] = handler;
    1131 |       } else if ('console' in window) {
    1132 |         console['warn']('cannot override language handler %s', ext);
    1133 |       }
    1134 |     }
    1135 |   }
    1136 |   function langHandlerForExtension(extension, source) {
    1137 |     if (!(extension && langHandlerRegistry.hasOwnProperty(extension))) {
    1138 |       // Treat it as markup if the first non whitespace character is a < and
    1139 |       // the last non-whitespace character is a >.
    1140 |       extension = /^\s*</.test(source)
    1141 |           ? 'default-markup'
    1142 |           : 'default-code';
    1143 |     }
    1144 |     return langHandlerRegistry[extension];
    1145 |   }
    1146 |   registerLangHandler(decorateSource, ['default-code']);
    1147 |   registerLangHandler(
    1148 |       createSimpleLexer(
    1149 |           [],
    1150 |           [
    1151 |            [PR_PLAIN,       /^[^<?]+/],
    1152 |            [PR_DECLARATION, /^<!\w[^>]*(?:>|$)/],
    1153 |            [PR_COMMENT,     /^<\!--[\s\S]*?(?:-\->|$)/],
    1154 |            // Unescaped content in an unknown language
    1155 |            ['lang-',        /^<\?([\s\S]+?)(?:\?>|$)/],
    1156 |            ['lang-',        /^<%([\s\S]+?)(?:%>|$)/],
    1157 |            [PR_PUNCTUATION, /^(?:<[%?]|[%?]>)/],
    1158 |            ['lang-',        /^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],
    1159 |            // Unescaped content in javascript.  (Or possibly vbscript).
    1160 |            ['lang-js',      /^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],
    1161 |            // Contains unescaped stylesheet content
    1162 |            ['lang-css',     /^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i],
    1163 |            ['lang-in.tag',  /^(<\/?[a-z][^<>]*>)/i]
    1164 |           ]),
    1165 |       ['default-markup', 'htm', 'html', 'mxml', 'xhtml', 'xml', 'xsl']);
    1166 |   registerLangHandler(
    1167 |       createSimpleLexer(
    1168 |           [
    1169 |            [PR_PLAIN,        /^[\s]+/, null, ' \t\r\n'],
    1170 |            [PR_ATTRIB_VALUE, /^(?:\"[^\"]*\"?|\'[^\']*\'?)/, null, '\"\'']
    1171 |            ],
    1172 |           [
    1173 |            [PR_TAG,          /^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],
    1174 |            [PR_ATTRIB_NAME,  /^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],
    1175 |            ['lang-uq.val',   /^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],
    1176 |            [PR_PUNCTUATION,  /^[=<>\/]+/],
    1177 |            ['lang-js',       /^on\w+\s*=\s*\"([^\"]+)\"/i],
    1178 |            ['lang-js',       /^on\w+\s*=\s*\'([^\']+)\'/i],
    1179 |            ['lang-js',       /^on\w+\s*=\s*([^\"\'>\s]+)/i],
    1180 |            ['lang-css',      /^style\s*=\s*\"([^\"]+)\"/i],
    1181 |            ['lang-css',      /^style\s*=\s*\'([^\']+)\'/i],
    1182 |            ['lang-css',      /^style\s*=\s*([^\"\'>\s]+)/i]
    1183 |            ]),
    1184 |       ['in.tag']);
    1185 |   registerLangHandler(
    1186 |       createSimpleLexer([], [[PR_ATTRIB_VALUE, /^[\s\S]+/]]), ['uq.val']);
    1187 |   registerLangHandler(sourceDecorator({
    1188 |           'keywords': CPP_KEYWORDS,
    1189 |           'hashComments': true,
    1190 |           'cStyleComments': true
    1191 |         }), ['c', 'cc', 'cpp', 'cxx', 'cyc', 'm']);
    1192 |   registerLangHandler(sourceDecorator({
    1193 |           'keywords': 'null true false'
    1194 |         }), ['json']);
    1195 |   registerLangHandler(sourceDecorator({
    1196 |           'keywords': CSHARP_KEYWORDS,
    1197 |           'hashComments': true,
    1198 |           'cStyleComments': true,
    1199 |           'verbatimStrings': true
    1200 |         }), ['cs']);
    1201 |   registerLangHandler(sourceDecorator({
    1202 |           'keywords': JAVA_KEYWORDS,
    1203 |           'cStyleComments': true
    1204 |         }), ['java']);
    1205 |   registerLangHandler(sourceDecorator({
    1206 |           'keywords': SH_KEYWORDS,
    1207 |           'hashComments': true,
    1208 |           'multiLineStrings': true
    1209 |         }), ['bsh', 'csh', 'sh']);
    1210 |   registerLangHandler(sourceDecorator({
    1211 |           'keywords': PYTHON_KEYWORDS,
    1212 |           'hashComments': true,
    1213 |           'multiLineStrings': true,
    1214 |           'tripleQuotedStrings': true
    1215 |         }), ['cv', 'py']);
    1216 |   registerLangHandler(sourceDecorator({
    1217 |           'keywords': PERL_KEYWORDS,
    1218 |           'hashComments': true,
    1219 |           'multiLineStrings': true,
    1220 |           'regexLiterals': true
    1221 |         }), ['perl', 'pl', 'pm']);
    1222 |   registerLangHandler(sourceDecorator({
    1223 |           'keywords': RUBY_KEYWORDS,
    1224 |           'hashComments': true,
    1225 |           'multiLineStrings': true,
    1226 |           'regexLiterals': true
    1227 |         }), ['rb']);
    1228 |   registerLangHandler(sourceDecorator({
    1229 |           'keywords': JSCRIPT_KEYWORDS,
    1230 |           'cStyleComments': true,
    1231 |           'regexLiterals': true
    1232 |         }), ['js']);
    1233 |   registerLangHandler(sourceDecorator({
    1234 |           'keywords': COFFEE_KEYWORDS,
    1235 |           'hashComments': 3,  // ### style block comments
    1236 |           'cStyleComments': true,
    1237 |           'multilineStrings': true,
    1238 |           'tripleQuotedStrings': true,
    1239 |           'regexLiterals': true
    1240 |         }), ['coffee']);
    1241 |   registerLangHandler(createSimpleLexer([], [[PR_STRING, /^[\s\S]+/]]), ['regex']);
    1242 | 
    1243 |   function applyDecorator(job) {
    1244 |     var opt_langExtension = job.langExtension;
    1245 | 
    1246 |     try {
    1247 |       // Extract tags, and convert the source code to plain text.
    1248 |       var sourceAndSpans = extractSourceSpans(job.sourceNode);
    1249 |       /** Plain text. @type {string} */
    1250 |       var source = sourceAndSpans.source;
    1251 |       job.source = source;
    1252 |       job.spans = sourceAndSpans.spans;
    1253 |       job.basePos = 0;
    1254 | 
    1255 |       // Apply the appropriate language handler
    1256 |       langHandlerForExtension(opt_langExtension, source)(job);
    1257 | 
    1258 |       // Integrate the decorations and tags back into the source code,
    1259 |       // modifying the sourceNode in place.
    1260 |       recombineTagsAndDecorations(job);
    1261 |     } catch (e) {
    1262 |       if ('console' in window) {
    1263 |         console['log'](e && e['stack'] ? e['stack'] : e);
    1264 |       }
    1265 |     }
    1266 |   }
    1267 | 
    1268 |   /**
    1269 |    * @param sourceCodeHtml {string} The HTML to pretty print.
    1270 |    * @param opt_langExtension {string} The language name to use.
    1271 |    *     Typically, a filename extension like 'cpp' or 'java'.
    1272 |    * @param opt_numberLines {number|boolean} True to number lines,
    1273 |    *     or the 1-indexed number of the first line in sourceCodeHtml.
    1274 |    */
    1275 |   function prettyPrintOne(sourceCodeHtml, opt_langExtension, opt_numberLines) {
    1276 |     var container = document.createElement('PRE');
    1277 |     // This could cause images to load and onload listeners to fire.
    1278 |     // E.g. <img onerror="alert(1337)" src="nosuchimage.png">.
    1279 |     // We assume that the inner HTML is from a trusted source.
    1280 |     container.innerHTML = sourceCodeHtml;
    1281 |     if (opt_numberLines) {
    1282 |       numberLines(container, opt_numberLines);
    1283 |     }
    1284 | 
    1285 |     var job = {
    1286 |       langExtension: opt_langExtension,
    1287 |       numberLines: opt_numberLines,
    1288 |       sourceNode: container
    1289 |     };
    1290 |     applyDecorator(job);
    1291 |     return container.innerHTML;
    1292 |   }
    1293 | 
    1294 |   function prettyPrint(opt_whenDone) {
    1295 |     function byTagName(tn) { return document.getElementsByTagName(tn); }
    1296 |     // fetch a list of nodes to rewrite
    1297 |     var codeSegments = [byTagName('pre'), byTagName('code'), byTagName('xmp')];
    1298 |     var elements = [];
    1299 |     for (var i = 0; i < codeSegments.length; ++i) {
    1300 |       for (var j = 0, n = codeSegments[i].length; j < n; ++j) {
    1301 |         elements.push(codeSegments[i][j]);
    1302 |       }
    1303 |     }
    1304 |     codeSegments = null;
    1305 | 
    1306 |     var clock = Date;
    1307 |     if (!clock['now']) {
    1308 |       clock = { 'now': function () { return (new Date).getTime(); } };
    1309 |     }
    1310 | 
    1311 |     // The loop is broken into a series of continuations to make sure that we
    1312 |     // don't make the browser unresponsive when rewriting a large page.
    1313 |     var k = 0;
    1314 |     var prettyPrintingJob;
    1315 | 
    1316 |     function doWork() {
    1317 |       var endTime = (window['PR_SHOULD_USE_CONTINUATION'] ?
    1318 |                      clock.now() + 250 /* ms */ :
    1319 |                      Infinity);
    1320 |       for (; k < elements.length && clock.now() < endTime; k++) {
    1321 |         var cs = elements[k];
    1322 |         if (cs.className && cs.className.indexOf('prettyprint') >= 0) {
    1323 |           // If the classes includes a language extensions, use it.
    1324 |           // Language extensions can be specified like
    1325 |           //     <pre class="prettyprint lang-cpp">
    1326 |           // the language extension "cpp" is used to find a language handler as
    1327 |           // passed to PR.registerLangHandler.
    1328 |           var langExtension = cs.className.match(/\blang-(\w+)\b/);
    1329 |           if (langExtension) { langExtension = langExtension[1]; }
    1330 | 
    1331 |           // make sure this is not nested in an already prettified element
    1332 |           var nested = false;
    1333 |           for (var p = cs.parentNode; p; p = p.parentNode) {
    1334 |             if ((p.tagName === 'pre' || p.tagName === 'code' ||
    1335 |                  p.tagName === 'xmp') &&
    1336 |                 p.className && p.className.indexOf('prettyprint') >= 0) {
    1337 |               nested = true;
    1338 |               break;
    1339 |             }
    1340 |           }
    1341 |           if (!nested) {
    1342 |             // Look for a class like linenums or linenums:<n> where <n> is the
    1343 |             // 1-indexed number of the first line.
    1344 |             var lineNums = cs.className.match(/\blinenums\b(?::(\d+))?/);
    1345 |             lineNums = lineNums
    1346 |                   ? lineNums[1] && lineNums[1].length ? +lineNums[1] : true
    1347 |                   : false;
    1348 |             if (lineNums) { numberLines(cs, lineNums); }
    1349 | 
    1350 |             // do the pretty printing
    1351 |             prettyPrintingJob = {
    1352 |               langExtension: langExtension,
    1353 |               sourceNode: cs,
    1354 |               numberLines: lineNums
    1355 |             };
    1356 |             applyDecorator(prettyPrintingJob);
    1357 |           }
    1358 |         }
    1359 |       }
    1360 |       if (k < elements.length) {
    1361 |         // finish up in a continuation
    1362 |         setTimeout(doWork, 250);
    1363 |       } else if (opt_whenDone) {
    1364 |         opt_whenDone();
    1365 |       }
    1366 |     }
    1367 | 
    1368 |     doWork();
    1369 |   }
    1370 | 
    1371 |   window['prettyPrintOne'] = prettyPrintOne;
    1372 |   window['prettyPrint'] = prettyPrint;
    1373 |   window['PR'] = {
    1374 |         'createSimpleLexer': createSimpleLexer,
    1375 |         'registerLangHandler': registerLangHandler,
    1376 |         'sourceDecorator': sourceDecorator,
    1377 |         'PR_ATTRIB_NAME': PR_ATTRIB_NAME,
    1378 |         'PR_ATTRIB_VALUE': PR_ATTRIB_VALUE,
    1379 |         'PR_COMMENT': PR_COMMENT,
    1380 |         'PR_DECLARATION': PR_DECLARATION,
    1381 |         'PR_KEYWORD': PR_KEYWORD,
    1382 |         'PR_LITERAL': PR_LITERAL,
    1383 |         'PR_NOCODE': PR_NOCODE,
    1384 |         'PR_PLAIN': PR_PLAIN,
    1385 |         'PR_PUNCTUATION': PR_PUNCTUATION,
    1386 |         'PR_SOURCE': PR_SOURCE,
    1387 |         'PR_STRING': PR_STRING,
    1388 |         'PR_TAG': PR_TAG,
    1389 |         'PR_TYPE': PR_TYPE
    1390 |       };
    1391 | })();
    1392 | 
    
    
    --------------------------------------------------------------------------------
    /readme.md:
    --------------------------------------------------------------------------------
     1 | # Modern Workflow + Tooling
     2 | 
     3 | These are the slides to my talk - they might not make a ton of sense without the talk, but you are welcome to look at them. 
     4 | 
     5 | Watch it here → <http://wesbos.com/modern-javascript-workflow-tooling/>
     6 | 
     7 | [![](http://wesbos.com/wp-content/uploads/2015/11/ss-2015-11-17-at-10.18.05-AM.png)](http://wesbos.com/modern-javascript-workflow-tooling/)
     8 | 
     9 | View at [wesbos.github.io/Modern-Workflow-and-Tooling-Talk](http://wesbos.github.io/Modern-Workflow-and-Tooling-Talk). Use your `←` and `→` keys to move through the slides. 
    10 | 
    11 | Small screen users should zoom out - these slides are 1080p.
    12 | 
    13 | ## The Code + Content
    14 | 
    15 | These slides are built with Flexbox, Gulp, Jade, Stylus and Browsersync all on top of the Google slide deck JS.
    16 | 
    17 | The code Licensed MIT - you can use this to make your own slide deck.
    18 | 
    19 | If you'd like to use the content for training or doing your own talk, please get in touch with me first. 
    20 | 
    21 | 
    22 | 
    23 | 
    
    
    --------------------------------------------------------------------------------
    /slides.js:
    --------------------------------------------------------------------------------
      1 | /*
      2 |   Google HTML5 slides template
      3 | 
      4 |   Authors: Luke Mahé (code)
      5 |            Marcin Wichary (code and design)
      6 | 
      7 |            Dominic Mazzoni (browser compatibility)
      8 |            Charles Chen (ChromeVox support)
      9 | 
     10 |   URL: http://code.google.com/p/html5slides/
     11 | */
     12 | 
     13 | // var PERMANENT_URL_PREFIX = 'http://localhost/demos/talks/snow/';
     14 | 
     15 | 
     16 | var PERMANENT_URL_PREFIX = window.location.origin + window.location.pathname;
     17 | 
     18 | // var PERMANENT_URL_PREFIX = window.location.href.split('/index.html')[0] + '/'
     19 | 
     20 | var SLIDE_CLASSES = ['far-past', 'past', 'current', 'next', 'far-next'];
     21 | 
     22 | var PM_TOUCH_SENSITIVITY = 15;
     23 | 
     24 | var curSlide;
     25 | 
     26 | /* ---------------------------------------------------------------------- */
     27 | /* classList polyfill by Eli Grey 
     28 |  * (http://purl.eligrey.com/github/classList.js/blob/master/classList.js) */
     29 | 
     30 | if (typeof document !== "undefined" && !("classList" in document.createElement("a"))) {
     31 | 
     32 | (function (view) {
     33 | 
     34 | var
     35 |     classListProp = "classList"
     36 |   , protoProp = "prototype"
     37 |   , elemCtrProto = (view.HTMLElement || view.Element)[protoProp]
     38 |   , objCtr = Object
     39 |     strTrim = String[protoProp].trim || function () {
     40 |     return this.replace(/^\s+|\s+$/g, "");
     41 |   }
     42 |   , arrIndexOf = Array[protoProp].indexOf || function (item) {
     43 |     for (var i = 0, len = this.length; i < len; i++) {
     44 |       if (i in this && this[i] === item) {
     45 |         return i;
     46 |       }
     47 |     }
     48 |     return -1;
     49 |   }
     50 |   // Vendors: please allow content code to instantiate DOMExceptions
     51 |   , DOMEx = function (type, message) {
     52 |     this.name = type;
     53 |     this.code = DOMException[type];
     54 |     this.message = message;
     55 |   }
     56 |   , checkTokenAndGetIndex = function (classList, token) {
     57 |     if (token === "") {
     58 |       throw new DOMEx(
     59 |           "SYNTAX_ERR"
     60 |         , "An invalid or illegal string was specified"
     61 |       );
     62 |     }
     63 |     if (/\s/.test(token)) {
     64 |       throw new DOMEx(
     65 |           "INVALID_CHARACTER_ERR"
     66 |         , "String contains an invalid character"
     67 |       );
     68 |     }
     69 |     return arrIndexOf.call(classList, token);
     70 |   }
     71 |   , ClassList = function (elem) {
     72 |     var
     73 |         trimmedClasses = strTrim.call(elem.className)
     74 |       , classes = trimmedClasses ? trimmedClasses.split(/\s+/) : []
     75 |     ;
     76 |     for (var i = 0, len = classes.length; i < len; i++) {
     77 |       this.push(classes[i]);
     78 |     }
     79 |     this._updateClassName = function () {
     80 |       elem.className = this.toString();
     81 |     };
     82 |   }
     83 |   , classListProto = ClassList[protoProp] = []
     84 |   , classListGetter = function () {
     85 |     return new ClassList(this);
     86 |   }
     87 | ;
     88 | // Most DOMException implementations don't allow calling DOMException's toString()
     89 | // on non-DOMExceptions. Error's toString() is sufficient here.
     90 | DOMEx[protoProp] = Error[protoProp];
     91 | classListProto.item = function (i) {
     92 |   return this[i] || null;
     93 | };
     94 | classListProto.contains = function (token) {
     95 |   token += "";
     96 |   return checkTokenAndGetIndex(this, token) !== -1;
     97 | };
     98 | classListProto.add = function (token) {
     99 |   token += "";
    100 |   if (checkTokenAndGetIndex(this, token) === -1) {
    101 |     this.push(token);
    102 |     this._updateClassName();
    103 |   }
    104 | };
    105 | classListProto.remove = function (token) {
    106 |   token += "";
    107 |   var index = checkTokenAndGetIndex(this, token);
    108 |   if (index !== -1) {
    109 |     this.splice(index, 1);
    110 |     this._updateClassName();
    111 |   }
    112 | };
    113 | classListProto.toggle = function (token) {
    114 |   token += "";
    115 |   if (checkTokenAndGetIndex(this, token) === -1) {
    116 |     this.add(token);
    117 |   } else {
    118 |     this.remove(token);
    119 |   }
    120 | };
    121 | classListProto.toString = function () {
    122 |   return this.join(" ");
    123 | };
    124 | 
    125 | if (objCtr.defineProperty) {
    126 |   var classListPropDesc = {
    127 |       get: classListGetter
    128 |     , enumerable: true
    129 |     , configurable: true
    130 |   };
    131 |   try {
    132 |     objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
    133 |   } catch (ex) { // IE 8 doesn't support enumerable:true
    134 |     if (ex.number === -0x7FF5EC54) {
    135 |       classListPropDesc.enumerable = false;
    136 |       objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
    137 |     }
    138 |   }
    139 | } else if (objCtr[protoProp].__defineGetter__) {
    140 |   elemCtrProto.__defineGetter__(classListProp, classListGetter);
    141 | }
    142 | 
    143 | }(self));
    144 | 
    145 | }
    146 | /* ---------------------------------------------------------------------- */
    147 | 
    148 | /* Slide movement */
    149 | 
    150 | function getSlideEl(no) {
    151 |   if ((no < 0) || (no >= slideEls.length)) { 
    152 |     return null;
    153 |   } else {
    154 |     return slideEls[no];
    155 |   }
    156 | };
    157 | 
    158 | function updateSlideClass(slideNo, className) {
    159 |   var el = getSlideEl(slideNo);
    160 |   
    161 |   if (!el) {
    162 |     return;
    163 |   }
    164 |   
    165 |   if (className) {
    166 |     el.classList.add(className);
    167 |   }
    168 |     
    169 |   for (var i in SLIDE_CLASSES) {
    170 |     if (className != SLIDE_CLASSES[i]) {
    171 |       el.classList.remove(SLIDE_CLASSES[i]);
    172 |     }
    173 |   }
    174 | };
    175 | 
    176 | function updateSlides() {
    177 |   for (var i = 0; i < slideEls.length; i++) {
    178 |     switch (i) {
    179 |       case curSlide - 2:
    180 |         updateSlideClass(i, 'far-past');
    181 |         break;
    182 |       case curSlide - 1:
    183 |         updateSlideClass(i, 'past');
    184 |         break;
    185 |       case curSlide: 
    186 |         updateSlideClass(i, 'current');
    187 |         break;
    188 |       case curSlide + 1:
    189 |         updateSlideClass(i, 'next');      
    190 |         break;
    191 |       case curSlide + 2:
    192 |         updateSlideClass(i, 'far-next');      
    193 |         break;
    194 |       default:
    195 |         updateSlideClass(i);
    196 |         break;
    197 |     }
    198 |   }
    199 | 
    200 |   triggerLeaveEvent(curSlide - 1);
    201 |   triggerEnterEvent(curSlide);
    202 | 
    203 |   window.setTimeout(function() {
    204 |     // Hide after the slide
    205 |     disableSlideFrames(curSlide - 2);
    206 |   }, 301);
    207 | 
    208 |   enableSlideFrames(curSlide - 1);
    209 |   enableSlideFrames(curSlide + 2);
    210 |   
    211 |   if (isChromeVoxActive()) {
    212 |     speakAndSyncToNode(slideEls[curSlide]);
    213 |   }  
    214 | 
    215 |   updateHash();
    216 | };
    217 | 
    218 | function buildNextItem() {
    219 |   var toBuild  = slideEls[curSlide].querySelectorAll('.to-build');
    220 | 
    221 |   if (!toBuild.length) {
    222 |     return false;
    223 |   }
    224 | 
    225 |   toBuild[0].classList.remove('to-build');
    226 | 
    227 |   if (isChromeVoxActive()) {
    228 |     speakAndSyncToNode(toBuild[0]);
    229 |   }
    230 | 
    231 |   return true;
    232 | };
    233 | 
    234 | function prevSlide() {
    235 |   if (curSlide > 0) {
    236 |     curSlide--;
    237 |     updateSlides();
    238 |   }
    239 | };
    240 | 
    241 | function nextSlide() {
    242 |   if (buildNextItem()) {
    243 |     return;
    244 |   }
    245 | 
    246 |   if (curSlide < slideEls.length - 1) {
    247 |     curSlide++;
    248 |     updateSlides();
    249 |   }
    250 | };
    251 | 
    252 | 
    253 | function triggerEnterEvent(no) {
    254 |   var el = getSlideEl(no);
    255 |   if (!el) {
    256 |     return;
    257 |   }
    258 | 
    259 |   var onEnter = el.getAttribute('onslideenter');
    260 |   if (onEnter) {
    261 |     new Function(onEnter).call(el);
    262 |   }
    263 | 
    264 |   var evt = document.createEvent('Event');
    265 |   evt.initEvent('slideenter', true, true);
    266 |   evt.slideNumber = no + 1; // Make it readable
    267 | 
    268 |   el.dispatchEvent(evt);
    269 | };
    270 | 
    271 | function triggerLeaveEvent(no) {
    272 |   var el = getSlideEl(no);
    273 |   if (!el) {
    274 |     return;
    275 |   }
    276 | 
    277 |   var onLeave = el.getAttribute('onslideleave');
    278 |   if (onLeave) {
    279 |     new Function(onLeave).call(el);
    280 |   }
    281 | 
    282 |   var evt = document.createEvent('Event');
    283 |   evt.initEvent('slideleave', true, true);
    284 |   evt.slideNumber = no + 1; // Make it readable
    285 |   
    286 |   el.dispatchEvent(evt);
    287 | };
    288 | 
    289 | /* Touch events */
    290 | 
    291 | function handleTouchStart(event) {
    292 |   if (event.touches.length == 1) {
    293 |     touchDX = 0;
    294 |     touchDY = 0;
    295 | 
    296 |     touchStartX = event.touches[0].pageX;
    297 |     touchStartY = event.touches[0].pageY;
    298 | 
    299 |     document.body.addEventListener('touchmove', handleTouchMove, true);
    300 |     document.body.addEventListener('touchend', handleTouchEnd, true);
    301 |   }
    302 | };
    303 | 
    304 | function handleTouchMove(event) {
    305 |   if (event.touches.length > 1) {
    306 |     cancelTouch();
    307 |   } else {
    308 |     touchDX = event.touches[0].pageX - touchStartX;
    309 |     touchDY = event.touches[0].pageY - touchStartY;
    310 |   }
    311 | };
    312 | 
    313 | function handleTouchEnd(event) {
    314 |   var dx = Math.abs(touchDX);
    315 |   var dy = Math.abs(touchDY);
    316 | 
    317 |   if ((dx > PM_TOUCH_SENSITIVITY) && (dy < (dx * 2 / 3))) {
    318 |     if (touchDX > 0) {
    319 |       prevSlide();
    320 |     } else {
    321 |       nextSlide();
    322 |     }
    323 |   }
    324 |   
    325 |   cancelTouch();
    326 | };
    327 | 
    328 | function cancelTouch() {
    329 |   document.body.removeEventListener('touchmove', handleTouchMove, true);
    330 |   document.body.removeEventListener('touchend', handleTouchEnd, true);  
    331 | };
    332 | 
    333 | /* Preloading frames */
    334 | 
    335 | function disableSlideFrames(no) {
    336 |   var el = getSlideEl(no);
    337 |   if (!el) {
    338 |     return;
    339 |   }
    340 | 
    341 |   var frames = el.getElementsByTagName('iframe');
    342 |   for (var i = 0, frame; frame = frames[i]; i++) {
    343 |     disableFrame(frame);
    344 |   }
    345 | };
    346 | 
    347 | function enableSlideFrames(no) {
    348 |   var el = getSlideEl(no);
    349 |   if (!el) {
    350 |     return;
    351 |   }
    352 | 
    353 |   var frames = el.getElementsByTagName('iframe');
    354 |   for (var i = 0, frame; frame = frames[i]; i++) {
    355 |     enableFrame(frame);
    356 |   }
    357 | };
    358 | 
    359 | function disableFrame(frame) {
    360 |   frame.src = 'about:blank';
    361 | };
    362 | 
    363 | function enableFrame(frame) {
    364 |   var src = frame._src;
    365 | 
    366 |   if (frame.src != src && src != 'about:blank') {
    367 |     frame.src = src;
    368 |   }
    369 | };
    370 | 
    371 | function setupFrames() {
    372 |   var frames = document.querySelectorAll('iframe');
    373 |   for (var i = 0, frame; frame = frames[i]; i++) {
    374 |     frame._src = frame.src;
    375 |     disableFrame(frame);
    376 |   }
    377 |   
    378 |   enableSlideFrames(curSlide);
    379 |   enableSlideFrames(curSlide + 1);
    380 |   enableSlideFrames(curSlide + 2);  
    381 | };
    382 | 
    383 | function setupInteraction() {
    384 |   /* Clicking and tapping */
    385 |   
    386 |   var el = document.createElement('div');
    387 |   el.className = 'slide-area';
    388 |   el.id = 'prev-slide-area';  
    389 |   el.addEventListener('click', prevSlide, false);
    390 |   document.querySelector('section.slides').appendChild(el);
    391 | 
    392 |   var el = document.createElement('div');
    393 |   el.className = 'slide-area';
    394 |   el.id = 'next-slide-area';  
    395 |   el.addEventListener('click', nextSlide, false);
    396 |   document.querySelector('section.slides').appendChild(el);  
    397 |   
    398 |   /* Swiping */
    399 |   
    400 |   document.body.addEventListener('touchstart', handleTouchStart, false);
    401 | }
    402 | 
    403 | /* ChromeVox support */
    404 | 
    405 | function isChromeVoxActive() {
    406 |   if (typeof(cvox) == 'undefined') {
    407 |     return false;
    408 |   } else {
    409 |     return true;
    410 |   }
    411 | };
    412 | 
    413 | function speakAndSyncToNode(node) {
    414 |   if (!isChromeVoxActive()) {
    415 |     return;
    416 |   }
    417 |   
    418 |   cvox.ChromeVox.navigationManager.switchToStrategy(
    419 |       cvox.ChromeVoxNavigationManager.STRATEGIES.LINEARDOM, 0, true);  
    420 |   cvox.ChromeVox.navigationManager.syncToNode(node);
    421 |   cvox.ChromeVoxUserCommands.finishNavCommand('');
    422 |   var target = node;
    423 |   while (target.firstChild) {
    424 |     target = target.firstChild;
    425 |   }
    426 |   cvox.ChromeVox.navigationManager.syncToNode(target);
    427 | };
    428 | 
    429 | function speakNextItem() {
    430 |   if (!isChromeVoxActive()) {
    431 |     return;
    432 |   }
    433 |   
    434 |   cvox.ChromeVox.navigationManager.switchToStrategy(
    435 |       cvox.ChromeVoxNavigationManager.STRATEGIES.LINEARDOM, 0, true);
    436 |   cvox.ChromeVox.navigationManager.next(true);
    437 |   if (!cvox.DomUtil.isDescendantOfNode(
    438 |       cvox.ChromeVox.navigationManager.getCurrentNode(), slideEls[curSlide])){
    439 |     var target = slideEls[curSlide];
    440 |     while (target.firstChild) {
    441 |       target = target.firstChild;
    442 |     }
    443 |     cvox.ChromeVox.navigationManager.syncToNode(target);
    444 |     cvox.ChromeVox.navigationManager.next(true);
    445 |   }
    446 |   cvox.ChromeVoxUserCommands.finishNavCommand('');
    447 | };
    448 | 
    449 | function speakPrevItem() {
    450 |   if (!isChromeVoxActive()) {
    451 |     return;
    452 |   }
    453 |   
    454 |   cvox.ChromeVox.navigationManager.switchToStrategy(
    455 |       cvox.ChromeVoxNavigationManager.STRATEGIES.LINEARDOM, 0, true);
    456 |   cvox.ChromeVox.navigationManager.previous(true);
    457 |   if (!cvox.DomUtil.isDescendantOfNode(
    458 |       cvox.ChromeVox.navigationManager.getCurrentNode(), slideEls[curSlide])){
    459 |     var target = slideEls[curSlide];
    460 |     while (target.lastChild){
    461 |       target = target.lastChild;
    462 |     }
    463 |     cvox.ChromeVox.navigationManager.syncToNode(target);
    464 |     cvox.ChromeVox.navigationManager.previous(true);
    465 |   }
    466 |   cvox.ChromeVoxUserCommands.finishNavCommand('');
    467 | };
    468 | 
    469 | /* Hash functions */
    470 | 
    471 | function getCurSlideFromHash() {
    472 |   var slideNo = parseInt(location.hash.substr(1));
    473 | 
    474 |   if (slideNo) {
    475 |     curSlide = slideNo - 1;
    476 |   } else {
    477 |     curSlide = 0;
    478 |   }
    479 | };
    480 | 
    481 | function updateHash() {
    482 |   location.replace('#' + (curSlide + 1));
    483 | };
    484 | 
    485 | /* Event listeners */
    486 | 
    487 | function handleBodyKeyDown(event) {
    488 |   switch (event.keyCode) {
    489 |     case 39: // right arrow
    490 |     case 13: // Enter
    491 |     case 32: // space
    492 |     case 34: // PgDn
    493 |       nextSlide();
    494 |       event.preventDefault();
    495 |       break;
    496 | 
    497 |     case 37: // left arrow
    498 |     case 8: // Backspace
    499 |     case 33: // PgUp
    500 |       prevSlide();
    501 |       event.preventDefault();
    502 |       break;
    503 | 
    504 |     case 40: // down arrow
    505 |       if (isChromeVoxActive()) {
    506 |         speakNextItem();
    507 |       } else {
    508 |         nextSlide();
    509 |       }
    510 |       event.preventDefault();
    511 |       break;
    512 | 
    513 |     case 38: // up arrow
    514 |       if (isChromeVoxActive()) {
    515 |         speakPrevItem();
    516 |       } else {
    517 |         prevSlide();
    518 |       }
    519 |       event.preventDefault();
    520 |       break;
    521 |   }
    522 | };
    523 | 
    524 | function addEventListeners() {
    525 |   document.addEventListener('keydown', handleBodyKeyDown, false);  
    526 | };
    527 | 
    528 | /* Initialization */
    529 | 
    530 | function addPrettify() {
    531 |   var els = document.querySelectorAll('pre');
    532 |   for (var i = 0, el; el = els[i]; i++) {
    533 |     if (!el.classList.contains('noprettyprint')) {
    534 |       el.classList.add('prettyprint');
    535 |     }
    536 |   }
    537 |   
    538 |   var el = document.createElement('script');
    539 |   el.type = 'text/javascript';
    540 |   el.src = PERMANENT_URL_PREFIX + 'prettify.js';
    541 |   el.onload = function() {
    542 |     prettyPrint();
    543 |   }
    544 |   document.body.appendChild(el);
    545 | };
    546 | 
    547 | function addFontStyle() {
    548 |   return; //NONONONON
    549 |   var el = document.createElement('link');
    550 |   el.rel = 'stylesheet';
    551 |   el.type = 'text/css';
    552 |   el.href = 'http://fonts.googleapis.com/css?family=' +
    553 |             'Open+Sans:regular,semibold,italic,italicsemibold|Droid+Sans+Mono';
    554 | 
    555 |   document.body.appendChild(el);
    556 | };
    557 | 
    558 | function addGeneralStyle() {
    559 |   var el = document.createElement('link');
    560 |   el.rel = 'stylesheet';
    561 |   el.type = 'text/css';
    562 |   el.href = PERMANENT_URL_PREFIX + 'styles.css';
    563 |   document.body.appendChild(el);
    564 |   
    565 |   var el = document.createElement('meta');
    566 |   el.name = 'viewport';
    567 |   el.content = 'width=1100,height=750';
    568 |   document.querySelector('head').appendChild(el);
    569 |   
    570 |   var el = document.createElement('meta');
    571 |   el.name = 'apple-mobile-web-app-capable';
    572 |   el.content = 'yes';
    573 |   document.querySelector('head').appendChild(el);
    574 | };
    575 | 
    576 | function makeBuildLists() {
    577 |   for (var i = curSlide, slide; slide = slideEls[i]; i++) {
    578 |     var items = slide.querySelectorAll('.build > *');
    579 |     for (var j = 0, item; item = items[j]; j++) {
    580 |       if (item.classList) {
    581 |         item.classList.add('to-build');
    582 |       }
    583 |     }
    584 |   }
    585 | };
    586 | 
    587 | function handleDomLoaded() {
    588 |   slideEls = document.querySelectorAll('section.slides > article');
    589 | 
    590 |   setupFrames();
    591 | 
    592 |   addFontStyle();
    593 |   // addGeneralStyle(); do it myself
    594 |   addPrettify();
    595 |   addEventListeners();
    596 | 
    597 |   updateSlides();
    598 | 
    599 |   setupInteraction();
    600 |   makeBuildLists();
    601 | 
    602 |   document.body.classList.add('loaded');
    603 | };
    604 | 
    605 | function initialize() {
    606 |   getCurSlideFromHash();
    607 | 
    608 |   if (window['_DEBUG']) {
    609 |     PERMANENT_URL_PREFIX = '../';
    610 |   }
    611 | 
    612 |   if (window['_DCL']) {
    613 |     handleDomLoaded();
    614 |   } else {
    615 |     document.addEventListener('DOMContentLoaded', handleDomLoaded, false);
    616 |   }
    617 | }
    618 | 
    619 | // If ?debug exists then load the script relative instead of absolute
    620 | if (!window['_DEBUG'] && document.location.href.indexOf('?debug') !== -1) {
    621 |   document.addEventListener('DOMContentLoaded', function() {
    622 |     // Avoid missing the DomContentLoaded event
    623 |     window['_DCL'] = true
    624 |   }, false);
    625 | 
    626 |   window['_DEBUG'] = true;
    627 |   var script = document.createElement('script');
    628 |   script.type = 'text/javascript';
    629 |   script.src = '../slides.js';
    630 |   var s = document.getElementsByTagName('script')[0];
    631 |   s.parentNode.insertBefore(script, s);
    632 | 
    633 |   // Remove this script
    634 |   s.parentNode.removeChild(s);
    635 | } else {
    636 |   initialize();
    637 | }
    638 | 
    
    
    --------------------------------------------------------------------------------
    /styles.css:
    --------------------------------------------------------------------------------
      1 | .wrapper {
      2 |   background: #bada55;
      3 |   color: #d12028;
      4 |   border: 1px solid #fff;
      5 | }
      6 | html {
      7 |   height: 100%;
      8 | }
      9 | body {
     10 |   margin: 0;
     11 |   padding: 0;
     12 |   display: block !important;
     13 |   height: 100%;
     14 |   min-height: 740px;
     15 |   overflow-x: hidden;
     16 |   overflow-y: auto;
     17 | }
     18 | .slides {
     19 |   width: 100%;
     20 |   height: 100%;
     21 |   left: 0;
     22 |   top: 0;
     23 |   color: #f21818;
     24 |   background: #193549;
     25 |   position: absolute;
     26 |   -webkit-transform: translate3d(0, 0, 0);
     27 | }
     28 | .slides > article {
     29 |   display: block;
     30 |   position: absolute;
     31 |   overflow: hidden;
     32 |   width: 1920px;
     33 |   height: 1080px;
     34 |   margin-left: -960px;
     35 |   margin-top: -540px;
     36 |   left: 50%;
     37 |   top: 50%;
     38 |   border-radius: 5px;
     39 |   padding: 0;
     40 |   background-color: none;
     41 |   -webkit-transition: all 0.3s ease-out;
     42 |           transition: all 0.3s ease-out;
     43 |   -webkit-box-pack: center;
     44 |   -webkit-justify-content: center;
     45 |       -ms-flex-pack: center;
     46 |           justify-content: center;
     47 |   -webkit-box-orient: vertical;
     48 |   -webkit-box-direction: normal;
     49 |   -webkit-flex-direction: column;
     50 |       -ms-flex-direction: column;
     51 |           flex-direction: column;
     52 | }
     53 | .slides > article > * {
     54 |   display: block;
     55 |   clear: both;
     56 |   min-width: 100%;
     57 |   -webkit-flex-wrap: wrap;
     58 |       -ms-flex-wrap: wrap;
     59 |           flex-wrap: wrap;
     60 | }
     61 | .slides > article > img {
     62 |   min-width: 0;
     63 |   display: block;
     64 |   margin: 0 auto;
     65 | }
     66 | .slide-area {
     67 |   z-index: 1000;
     68 |   position: absolute;
     69 |   left: 0;
     70 |   top: 0;
     71 |   width: 150px;
     72 |   height: 100%;
     73 |   left: 0;
     74 |   top: 0;
     75 |   cursor: pointer;
     76 |   tap-highlight-color: transparent;
     77 | }
     78 | #next-slide-area {
     79 |   right: 0;
     80 |   left: auto;
     81 | }
     82 | .slides.layout-widescreen #prev-slide-area,
     83 | .slides.layout-faux-widescreen #prev-slide-area {
     84 |   margin-left: -650px;
     85 | }
     86 | .slides.layout-widescreen #next-slide-area,
     87 | .slides.layout-faux-widescreen #next-slide-area {
     88 |   margin-left: 500px;
     89 | }
     90 | .slides > article {
     91 |   display: none;
     92 |   text-align: center;
     93 | }
     94 | .slides > article.far-past {
     95 |   display: block;
     96 |   -webkit-transform: translate(-4000px);
     97 |       -ms-transform: translate(-4000px);
     98 |           transform: translate(-4000px);
     99 |   display: none;
    100 | }
    101 | .slides > article.past {
    102 |   display: -webkit-box;
    103 |   display: -webkit-flex;
    104 |   display: -ms-flexbox;
    105 |   display: flex;
    106 |   -webkit-transform: scale(0.5) translate(-1920px);
    107 |       -ms-transform: scale(0.5) translate(-1920px);
    108 |           transform: scale(0.5) translate(-1920px);
    109 |   opacity: 0.3;
    110 | }
    111 | .slides > article.current {
    112 |   display: -webkit-box;
    113 |   display: -webkit-flex;
    114 |   display: -ms-flexbox;
    115 |   display: flex;
    116 |   -webkit-transform: translate(0);
    117 |       -ms-transform: translate(0);
    118 |           transform: translate(0);
    119 |   z-index: 10;
    120 | }
    121 | .slides > article.next {
    122 |   display: -webkit-box;
    123 |   display: -webkit-flex;
    124 |   display: -ms-flexbox;
    125 |   display: flex;
    126 |   -webkit-transform: scale(0.5) translate(1920px);
    127 |       -ms-transform: scale(0.5) translate(1920px);
    128 |           transform: scale(0.5) translate(1920px);
    129 |   opacity: 0.3;
    130 | }
    131 | .slides > article.far-next {
    132 |   display: -webkit-box;
    133 |   display: -webkit-flex;
    134 |   display: -ms-flexbox;
    135 |   display: flex;
    136 |   -webkit-transform: translate(4000px);
    137 |       -ms-transform: translate(4000px);
    138 |           transform: translate(4000px);
    139 |   display: none;
    140 | }
    141 | .slides > article {
    142 |   color: #6c818f;
    143 |   font-size: 30px;
    144 |   letter-spacing: -1px;
    145 | }
    146 | b {
    147 |   font-weight: 600;
    148 | }
    149 | .blue {
    150 |   color: #06c;
    151 | }
    152 | .yellow {
    153 |   color: #ffd319;
    154 | }
    155 | .green {
    156 |   color: #29e254;
    157 | }
    158 | .red {
    159 |   color: #f00;
    160 | }
    161 | .black {
    162 |   color: #000;
    163 | }
    164 | .white {
    165 |   color: #fff;
    166 | }
    167 | a {
    168 |   color: #fd0;
    169 | }
    170 | ::-moz-selection {
    171 |   background: #fd0;
    172 | }
    173 | ::selection {
    174 |   background: #fd0;
    175 | }
    176 | p {
    177 |   margin: 0;
    178 |   padding: 0;
    179 | }
    180 | p:first-child {
    181 |   margin-top: 0;
    182 | }
    183 | body {
    184 |   font-family: 'Open Sans', sans-serif;
    185 | }
    186 | h1 {
    187 |   font-size: 60px;
    188 |   line-height: 60px;
    189 |   padding: 0;
    190 |   margin: 0;
    191 |   color: #fff;
    192 | }
    193 | h2 {
    194 |   font-size: 45px;
    195 |   line-height: 45px;
    196 |   bottom: 150px;
    197 |   padding: 0;
    198 |   margin: 0;
    199 |   font-weight: 600;
    200 |   color: #fff;
    201 |   letter-spacing: -2px;
    202 | }
    203 | h2 a {
    204 |   text-decoration: none;
    205 | }
    206 | h3 {
    207 |   font-size: 20px;
    208 |   line-height: 36px;
    209 |   padding: 0 0 10px 0;
    210 |   margin: 0;
    211 |   padding-right: 40px;
    212 |   font-weight: 600;
    213 |   letter-spacing: -1px;
    214 |   color: #eaeaea;
    215 | }
    216 | .half {
    217 |   width: 350px;
    218 |   float: left;
    219 | }
    220 | .button:hover {
    221 |   color: #fff;
    222 |   background: #392c44;
    223 | }
    224 | pre.half {
    225 |   width: 400px;
    226 |   font-size: 17px;
    227 | }
    228 | p.small {
    229 |   color: #000;
    230 |   font-size: 18px;
    231 | }
    232 | article.fill h3 {
    233 |   background: rgba(255,255,255,0.75);
    234 |   padding-top: 0.2em;
    235 |   padding-bottom: 0.3em;
    236 |   margin-top: -0.2em;
    237 |   margin-left: -60px;
    238 |   padding-left: 60px;
    239 |   margin-right: -60px;
    240 |   padding-right: 60px;
    241 | }
    242 | h4 {
    243 |   margin: 0;
    244 | }
    245 | .center {
    246 |   text-align: center;
    247 | }
    248 | .center h3 {
    249 |   font-size: 100px;
    250 |   margin-top: 220px;
    251 | }
    252 | ul {
    253 |   list-style: none;
    254 |   margin: 0;
    255 |   padding: 0;
    256 |   margin-top: 40px;
    257 |   margin-left: 0.75em;
    258 | }
    259 | ul:first-child {
    260 |   margin-top: 0;
    261 | }
    262 | ul ul {
    263 |   margin-top: 0.5em;
    264 | }
    265 | li {
    266 |   padding: 0;
    267 |   margin: 0;
    268 |   margin-bottom: 0.5em;
    269 | }
    270 | li::before {
    271 |   content: '·';
    272 |   width: 0.75em;
    273 |   margin-left: -0.75em;
    274 |   position: absolute;
    275 | }
    276 | pre {
    277 |   font-size: 20px;
    278 |   line-height: 28px;
    279 |   padding: 5px 10px;
    280 |   letter-spacing: -1px;
    281 |   margin-top: 20px;
    282 |   margin-bottom: 20px;
    283 |   color: #000;
    284 |   background: #f0f0f0;
    285 |   border: 1px solid #e0e0e0;
    286 |   box-shadow: inset 0 2px 6px rgba(0,0,0,0.1);
    287 |   overflow: hidden;
    288 | }
    289 | code {
    290 |   font-size: 95%;
    291 |   font-family: 'Droid Sans Mono', 'Courier New', monospace;
    292 |   display: inline-block;
    293 |   background: rgba(255,255,255,0.2);
    294 |   padding: 10px;
    295 |   border-radius: 4px;
    296 |   font-weight: 600;
    297 | }
    298 | iframe {
    299 |   width: 100%;
    300 |   height: 620px;
    301 |   background: #fff;
    302 |   border: 1px solid #c0c0c0;
    303 |   margin: -1px;
    304 | /*box-shadow: inset 0 2px 6px rgba(0, 0, 0, .1);*/
    305 | }
    306 | img {
    307 |   max-width: 100%;
    308 |   max-height: 570px;
    309 | }
    310 | h3 + iframe {
    311 |   margin-top: 40px;
    312 |   height: 540px;
    313 | }
    314 | article.fill iframe {
    315 |   position: absolute;
    316 |   left: 0;
    317 |   top: 0;
    318 |   width: 100%;
    319 |   height: 100%;
    320 |   border: 0;
    321 |   margin: 0;
    322 |   border-radius: 10px;
    323 |   -o-border-radius: 10px;
    324 |   -moz-border-radius: 10px;
    325 |   -webkit-border-radius: 10px;
    326 |   z-index: -1;
    327 | }
    328 | article.fill img {
    329 |   position: absolute;
    330 |   left: 0;
    331 |   top: 0;
    332 |   min-width: 100%;
    333 |   min-height: 100%;
    334 |   border-radius: 10px;
    335 |   -o-border-radius: 10px;
    336 |   -moz-border-radius: 10px;
    337 |   -webkit-border-radius: 10px;
    338 |   z-index: -1;
    339 | }
    340 | img.centered {
    341 |   margin: 0 auto;
    342 |   display: block;
    343 | }
    344 | table {
    345 |   width: 100%;
    346 |   border-collapse: collapse;
    347 |   margin-top: 40px;
    348 | }
    349 | th {
    350 |   font-weight: 600;
    351 |   text-align: left;
    352 | }
    353 | td,
    354 | th {
    355 |   border: 1px solid #e0e0e0;
    356 |   padding: 5px 10px;
    357 |   vertical-align: top;
    358 | }
    359 | .source {
    360 |   position: absolute;
    361 |   left: 60px;
    362 |   top: 644px;
    363 |   padding-right: 175px;
    364 |   font-size: 15px;
    365 |   letter-spacing: 0;
    366 |   line-height: 18px;
    367 | }
    368 | q {
    369 |   display: block;
    370 |   font-size: 60px;
    371 |   line-height: 72px;
    372 |   margin-left: 20px;
    373 |   margin-top: 100px;
    374 |   margin-right: 150px;
    375 | }
    376 | q::before {
    377 |   content: '“';
    378 |   position: absolute;
    379 |   display: inline-block;
    380 |   margin-left: -2.1em;
    381 |   width: 2em;
    382 |   text-align: right;
    383 |   font-size: 90px;
    384 |   color: #c0c0c0;
    385 | }
    386 | q::after {
    387 |   content: '”';
    388 |   position: absolute;
    389 |   margin-left: 0.1em;
    390 |   font-size: 90px;
    391 |   color: #c0c0c0;
    392 | }
    393 | div.author {
    394 |   text-align: right;
    395 |   font-size: 40px;
    396 |   margin-top: 20px;
    397 |   margin-right: 150px;
    398 | }
    399 | div.author::before {
    400 |   content: '—';
    401 | }
    402 | article.smaller p,
    403 | article.smaller ul {
    404 |   font-size: 20px;
    405 |   line-height: 24px;
    406 |   letter-spacing: 0;
    407 | }
    408 | article.smaller table {
    409 |   font-size: 20px;
    410 |   line-height: 24px;
    411 |   letter-spacing: 0;
    412 | }
    413 | article.smaller pre {
    414 |   font-size: 15px;
    415 |   line-height: 20px;
    416 |   letter-spacing: 0;
    417 | }
    418 | article.smaller q {
    419 |   font-size: 40px;
    420 |   line-height: 48px;
    421 | }
    422 | article.smaller q::before,
    423 | article.smaller q::after {
    424 |   font-size: 60px;
    425 | }
    426 | .build > * {
    427 |   -webkit-transition: opacity 0.2s ease-in-out 0.2s;
    428 |           transition: opacity 0.2s ease-in-out 0.2s;
    429 | }
    430 | .to-build {
    431 |   opacity: 0;
    432 | }
    433 | .prettyprint .str,
    434 | .prettyprint .atv {
    435 | /* a markup attribute value */
    436 |   color: #008a35;
    437 | }
    438 | .prettyprint .kwd,
    439 | .prettyprint .tag {
    440 | /* a markup tag name */
    441 |   color: #06c;
    442 | }
    443 | .prettyprint .com {
    444 | /* a comment */
    445 |   color: #7f7f7f;
    446 |   font-style: italic;
    447 | }
    448 | .prettyprint .lit {
    449 | /* a literal value */
    450 |   color: #7f0000;
    451 | }
    452 | .prettyprint .pun,
    453 | .prettyprint .opn,
    454 | .prettyprint .clo {
    455 |   color: #7f7f7f;
    456 | }
    457 | .prettyprint .typ,
    458 | .prettyprint .atn,
    459 | .prettyprint .dec,
    460 | .prettyprint .var {
    461 | /* a declaration; a variable name */
    462 |   color: #7f007f;
    463 | }
    464 | html {
    465 |   box-sizing: border-box;
    466 | }
    467 | *,
    468 | *:before,
    469 | *:after {
    470 |   box-sizing: inherit;
    471 | }
    472 | .slides > article {
    473 |   padding: 50px;
    474 | }
    475 | .slides > article:before {
    476 |   content: '';
    477 |   display: block;
    478 |   width: 100%;
    479 |   height: 100%;
    480 |   position: absolute;
    481 |   -webkit-filter: grayscale(100%) contrast(160%);
    482 |           filter: grayscale(100%) contrast(160%);
    483 |   background: url("images/3.jpg");
    484 |   background-size: cover;
    485 |   top: 0;
    486 |   left: 0;
    487 |   z-index: -2;
    488 | }
    489 | .slides > article:after {
    490 |   z-index: -1;
    491 |   content: '';
    492 |   display: block;
    493 |   width: 100%;
    494 |   height: 100%;
    495 |   position: absolute;
    496 |   top: 0;
    497 |   left: 0;
    498 |   opacity: 0.8;
    499 | }
    500 | .slides > article.plain:before,
    501 | .slides > article.plain:after {
    502 |   display: none;
    503 | }
    504 | .slides > article:nth-child(5n+1):after {
    505 |   background-image: -webkit-linear-gradient(298deg, #81af9b 0%, #7aa8ac 27%, #6693da 100%);
    506 |   background-image: linear-gradient(-208deg, #81af9b 0%, #7aa8ac 27%, #6693da 100%);
    507 | }
    508 | .slides > article:nth-child(5n+2):after {
    509 |   background-image: -webkit-linear-gradient(222deg, #fd0 0%, #6dd7ba 100%);
    510 |   background-image: linear-gradient(-132deg, #fd0 0%, #6dd7ba 100%);
    511 | }
    512 | .slides > article.old-tools:before {
    513 |   background-image: url("images/soldering.jpg") !important;
    514 |   background-size: contain;
    515 | }
    516 | .slides > article.old-tools:after {
    517 |   opacity: 0.1;
    518 | }
    519 | .slides > article:nth-child(39n+1):before {
    520 |   background-image: url("images/5.jpg");
    521 |   content: #14e839;
    522 | }
    523 | .slides > article:nth-child(39n+1):after {
    524 |   background-image: -webkit-linear-gradient(90deg, #14e839 0%, #3456fd 100%);
    525 |   background-image: linear-gradient(360deg, #14e839 0%, #3456fd 100%);
    526 | }
    527 | .slides > article:nth-child(39n+2):before {
    528 |   background-image: url("images/9.jpg");
    529 |   content: #ae5ceb;
    530 | }
    531 | .slides > article:nth-child(39n+2):after {
    532 |   background-image: -webkit-linear-gradient(345deg, #ae5ceb 0%, #6aa4e1 100%);
    533 |   background-image: linear-gradient(105deg, #ae5ceb 0%, #6aa4e1 100%);
    534 | }
    535 | .slides > article:nth-child(39n+3):before {
    536 |   background-image: url("images/8.jpg");
    537 |   content: #3768f7;
    538 | }
    539 | .slides > article:nth-child(39n+3):after {
    540 |   background-image: -webkit-linear-gradient(349deg, #3768f7 0%, #6d6528 100%);
    541 |   background-image: linear-gradient(101deg, #3768f7 0%, #6d6528 100%);
    542 | }
    543 | .slides > article:nth-child(39n+4):before {
    544 |   background-image: url("images/6.jpg");
    545 |   content: #14e839;
    546 | }
    547 | .slides > article:nth-child(39n+4):after {
    548 |   background-image: -webkit-linear-gradient(266deg, #14e839 0%, #3456fd 100%);
    549 |   background-image: linear-gradient(184deg, #14e839 0%, #3456fd 100%);
    550 | }
    551 | .slides > article:nth-child(39n+5):before {
    552 |   background-image: url("images/7.jpg");
    553 |   content: #52aeea;
    554 | }
    555 | .slides > article:nth-child(39n+5):after {
    556 |   background-image: -webkit-linear-gradient(328deg, #52aeea 0%, #2decf5 100%);
    557 |   background-image: linear-gradient(122deg, #52aeea 0%, #2decf5 100%);
    558 | }
    559 | .slides > article:nth-child(39n+6):before {
    560 |   background-image: url("images/1.jpg");
    561 |   content: #006c8d;
    562 | }
    563 | .slides > article:nth-child(39n+6):after {
    564 |   background-image: -webkit-linear-gradient(171deg, #006c8d 0%, #006c8d 100%);
    565 |   background-image: linear-gradient(279deg, #006c8d 0%, #006c8d 100%);
    566 | }
    567 | .slides > article:nth-child(39n+7):before {
    568 |   background-image: url("images/2.jpg");
    569 |   content: #52aeea;
    570 | }
    571 | .slides > article:nth-child(39n+7):after {
    572 |   background-image: -webkit-linear-gradient(213deg, #52aeea 0%, #2decf5 100%);
    573 |   background-image: linear-gradient(237deg, #52aeea 0%, #2decf5 100%);
    574 | }
    575 | .slides > article:nth-child(39n+8):before {
    576 |   background-image: url("images/9.jpg");
    577 |   content: #52aeea;
    578 | }
    579 | .slides > article:nth-child(39n+8):after {
    580 |   background-image: -webkit-linear-gradient(331deg, #52aeea 0%, #2decf5 100%);
    581 |   background-image: linear-gradient(119deg, #52aeea 0%, #2decf5 100%);
    582 | }
    583 | .slides > article:nth-child(39n+9):before {
    584 |   background-image: url("images/1.jpg");
    585 |   content: #ff00e0;
    586 | }
    587 | .slides > article:nth-child(39n+9):after {
    588 |   background-image: -webkit-linear-gradient(330deg, #ff00e0 0%, #11feec 100%);
    589 |   background-image: linear-gradient(120deg, #ff00e0 0%, #11feec 100%);
    590 | }
    591 | .slides > article:nth-child(39n+10):before {
    592 |   background-image: url("images/9.jpg");
    593 |   content: #7d5794;
    594 | }
    595 | .slides > article:nth-child(39n+10):after {
    596 |   background-image: -webkit-linear-gradient(332deg, #7d5794 0%, #ca5723 100%);
    597 |   background-image: linear-gradient(118deg, #7d5794 0%, #ca5723 100%);
    598 | }
    599 | .slides > article:nth-child(39n+11):before {
    600 |   background-image: url("images/6.jpg");
    601 |   content: #eb821b;
    602 | }
    603 | .slides > article:nth-child(39n+11):after {
    604 |   background-image: -webkit-linear-gradient(155deg, #eb821b 0%, #3a5c8e 100%);
    605 |   background-image: linear-gradient(295deg, #eb821b 0%, #3a5c8e 100%);
    606 | }
    607 | .slides > article:nth-child(39n+12):before {
    608 |   background-image: url("images/4.jpg");
    609 |   content: #5e9ccd;
    610 | }
    611 | .slides > article:nth-child(39n+12):after {
    612 |   background-image: -webkit-linear-gradient(108deg, #5e9ccd 0%, #226443 100%);
    613 |   background-image: linear-gradient(342deg, #5e9ccd 0%, #226443 100%);
    614 | }
    615 | .slides > article:nth-child(39n+13):before {
    616 |   background-image: url("images/1.jpg");
    617 |   content: #3768f7;
    618 | }
    619 | .slides > article:nth-child(39n+13):after {
    620 |   background-image: -webkit-linear-gradient(251deg, #3768f7 0%, #6d6528 100%);
    621 |   background-image: linear-gradient(199deg, #3768f7 0%, #6d6528 100%);
    622 | }
    623 | .slides > article:nth-child(39n+14):before {
    624 |   background-image: url("images/7.jpg");
    625 |   content: #94eced;
    626 | }
    627 | .slides > article:nth-child(39n+14):after {
    628 |   background-image: -webkit-linear-gradient(315deg, #94eced 0%, #29519e 100%);
    629 |   background-image: linear-gradient(135deg, #94eced 0%, #29519e 100%);
    630 | }
    631 | .slides > article:nth-child(39n+15):before {
    632 |   background-image: url("images/8.jpg");
    633 |   content: #5e9ccd;
    634 | }
    635 | .slides > article:nth-child(39n+15):after {
    636 |   background-image: -webkit-linear-gradient(193deg, #5e9ccd 0%, #226443 100%);
    637 |   background-image: linear-gradient(257deg, #5e9ccd 0%, #226443 100%);
    638 | }
    639 | .slides > article:nth-child(39n+16):before {
    640 |   background-image: url("images/10.jpg");
    641 |   content: #3768f7;
    642 | }
    643 | .slides > article:nth-child(39n+16):after {
    644 |   background-image: -webkit-linear-gradient(268deg, #3768f7 0%, #6d6528 100%);
    645 |   background-image: linear-gradient(182deg, #3768f7 0%, #6d6528 100%);
    646 | }
    647 | .slides > article:nth-child(39n+17):before {
    648 |   background-image: url("images/7.jpg");
    649 |   content: #1e4823;
    650 | }
    651 | .slides > article:nth-child(39n+17):after {
    652 |   background-image: -webkit-linear-gradient(131deg, #1e4823 0%, #e26ab5 100%);
    653 |   background-image: linear-gradient(319deg, #1e4823 0%, #e26ab5 100%);
    654 | }
    655 | .slides > article:nth-child(39n+18):before {
    656 |   background-image: url("images/3.jpg");
    657 |   content: #006c8d;
    658 | }
    659 | .slides > article:nth-child(39n+18):after {
    660 |   background-image: -webkit-linear-gradient(158deg, #006c8d 0%, #006c8d 100%);
    661 |   background-image: linear-gradient(292deg, #006c8d 0%, #006c8d 100%);
    662 | }
    663 | .slides > article:nth-child(39n+19):before {
    664 |   background-image: url("images/8.jpg");
    665 |   content: #5e9ccd;
    666 | }
    667 | .slides > article:nth-child(39n+19):after {
    668 |   background-image: -webkit-linear-gradient(280deg, #5e9ccd 0%, #226443 100%);
    669 |   background-image: linear-gradient(170deg, #5e9ccd 0%, #226443 100%);
    670 | }
    671 | .slides > article:nth-child(39n+20):before {
    672 |   background-image: url("images/1.jpg");
    673 |   content: #7d5794;
    674 | }
    675 | .slides > article:nth-child(39n+20):after {
    676 |   background-image: -webkit-linear-gradient(311deg, #7d5794 0%, #ca5723 100%);
    677 |   background-image: linear-gradient(139deg, #7d5794 0%, #ca5723 100%);
    678 | }
    679 | .slides > article:nth-child(39n+21):before {
    680 |   background-image: url("images/5.jpg");
    681 |   content: #7d5794;
    682 | }
    683 | .slides > article:nth-child(39n+21):after {
    684 |   background-image: -webkit-linear-gradient(131deg, #7d5794 0%, #ca5723 100%);
    685 |   background-image: linear-gradient(319deg, #7d5794 0%, #ca5723 100%);
    686 | }
    687 | .slides > article:nth-child(39n+22):before {
    688 |   background-image: url("images/10.jpg");
    689 |   content: #e1e34a;
    690 | }
    691 | .slides > article:nth-child(39n+22):after {
    692 |   background-image: -webkit-linear-gradient(187deg, #e1e34a 0%, #d07dd7 100%);
    693 |   background-image: linear-gradient(263deg, #e1e34a 0%, #d07dd7 100%);
    694 | }
    695 | .slides > article:nth-child(39n+23):before {
    696 |   background-image: url("images/9.jpg");
    697 |   content: #ae5ceb;
    698 | }
    699 | .slides > article:nth-child(39n+23):after {
    700 |   background-image: -webkit-linear-gradient(243deg, #ae5ceb 0%, #6aa4e1 100%);
    701 |   background-image: linear-gradient(207deg, #ae5ceb 0%, #6aa4e1 100%);
    702 | }
    703 | .slides > article:nth-child(39n+24):before {
    704 |   background-image: url("images/7.jpg");
    705 |   content: #006c8d;
    706 | }
    707 | .slides > article:nth-child(39n+24):after {
    708 |   background-image: -webkit-linear-gradient(333deg, #006c8d 0%, #006c8d 100%);
    709 |   background-image: linear-gradient(117deg, #006c8d 0%, #006c8d 100%);
    710 | }
    711 | .slides > article:nth-child(39n+25):before {
    712 |   background-image: url("images/6.jpg");
    713 |   content: #7d5794;
    714 | }
    715 | .slides > article:nth-child(39n+25):after {
    716 |   background-image: -webkit-linear-gradient(69deg, #7d5794 0%, #ca5723 100%);
    717 |   background-image: linear-gradient(21deg, #7d5794 0%, #ca5723 100%);
    718 | }
    719 | .slides > article:nth-child(39n+26):before {
    720 |   background-image: url("images/10.jpg");
    721 |   content: #52aeea;
    722 | }
    723 | .slides > article:nth-child(39n+26):after {
    724 |   background-image: -webkit-linear-gradient(216deg, #52aeea 0%, #2decf5 100%);
    725 |   background-image: linear-gradient(234deg, #52aeea 0%, #2decf5 100%);
    726 | }
    727 | .slides > article:nth-child(39n+27):before {
    728 |   background-image: url("images/7.jpg");
    729 |   content: #94eced;
    730 | }
    731 | .slides > article:nth-child(39n+27):after {
    732 |   background-image: -webkit-linear-gradient(250deg, #94eced 0%, #29519e 100%);
    733 |   background-image: linear-gradient(200deg, #94eced 0%, #29519e 100%);
    734 | }
    735 | .slides > article:nth-child(39n+28):before {
    736 |   background-image: url("images/3.jpg");
    737 |   content: #e1e34a;
    738 | }
    739 | .slides > article:nth-child(39n+28):after {
    740 |   background-image: -webkit-linear-gradient(60deg, #e1e34a 0%, #d07dd7 100%);
    741 |   background-image: linear-gradient(30deg, #e1e34a 0%, #d07dd7 100%);
    742 | }
    743 | .slides > article:nth-child(39n+29):before {
    744 |   background-image: url("images/3.jpg");
    745 |   content: #14e839;
    746 | }
    747 | .slides > article:nth-child(39n+29):after {
    748 |   background-image: -webkit-linear-gradient(88deg, #14e839 0%, #3456fd 100%);
    749 |   background-image: linear-gradient(2deg, #14e839 0%, #3456fd 100%);
    750 | }
    751 | .slides > article:nth-child(39n+30):before {
    752 |   background-image: url("images/10.jpg");
    753 |   content: #ae5ceb;
    754 | }
    755 | .slides > article:nth-child(39n+30):after {
    756 |   background-image: -webkit-linear-gradient(236deg, #ae5ceb 0%, #6aa4e1 100%);
    757 |   background-image: linear-gradient(214deg, #ae5ceb 0%, #6aa4e1 100%);
    758 | }
    759 | .slides > article:nth-child(39n+31):before {
    760 |   background-image: url("images/10.jpg");
    761 |   content: #006c8d;
    762 | }
    763 | .slides > article:nth-child(39n+31):after {
    764 |   background-image: -webkit-linear-gradient(353deg, #006c8d 0%, #006c8d 100%);
    765 |   background-image: linear-gradient(97deg, #006c8d 0%, #006c8d 100%);
    766 | }
    767 | .slides > article:nth-child(39n+32):before {
    768 |   background-image: url("images/10.jpg");
    769 |   content: #14e839;
    770 | }
    771 | .slides > article:nth-child(39n+32):after {
    772 |   background-image: -webkit-linear-gradient(119deg, #14e839 0%, #3456fd 100%);
    773 |   background-image: linear-gradient(331deg, #14e839 0%, #3456fd 100%);
    774 | }
    775 | .slides > article:nth-child(39n+33):before {
    776 |   background-image: url("images/8.jpg");
    777 |   content: #e1e34a;
    778 | }
    779 | .slides > article:nth-child(39n+33):after {
    780 |   background-image: -webkit-linear-gradient(195deg, #e1e34a 0%, #d07dd7 100%);
    781 |   background-image: linear-gradient(255deg, #e1e34a 0%, #d07dd7 100%);
    782 | }
    783 | .slides > article:nth-child(39n+34):before {
    784 |   background-image: url("images/5.jpg");
    785 |   content: #e1e34a;
    786 | }
    787 | .slides > article:nth-child(39n+34):after {
    788 |   background-image: -webkit-linear-gradient(217deg, #e1e34a 0%, #d07dd7 100%);
    789 |   background-image: linear-gradient(233deg, #e1e34a 0%, #d07dd7 100%);
    790 | }
    791 | .slides > article:nth-child(39n+35):before {
    792 |   background-image: url("images/6.jpg");
    793 |   content: #94eced;
    794 | }
    795 | .slides > article:nth-child(39n+35):after {
    796 |   background-image: -webkit-linear-gradient(318deg, #94eced 0%, #29519e 100%);
    797 |   background-image: linear-gradient(132deg, #94eced 0%, #29519e 100%);
    798 | }
    799 | .slides > article:nth-child(39n+36):before {
    800 |   background-image: url("images/10.jpg");
    801 |   content: #e1e34a;
    802 | }
    803 | .slides > article:nth-child(39n+36):after {
    804 |   background-image: -webkit-linear-gradient(98deg, #e1e34a 0%, #d07dd7 100%);
    805 |   background-image: linear-gradient(352deg, #e1e34a 0%, #d07dd7 100%);
    806 | }
    807 | .slides > article:nth-child(39n+37):before {
    808 |   background-image: url("images/5.jpg");
    809 |   content: #1e4823;
    810 | }
    811 | .slides > article:nth-child(39n+37):after {
    812 |   background-image: -webkit-linear-gradient(257deg, #1e4823 0%, #e26ab5 100%);
    813 |   background-image: linear-gradient(193deg, #1e4823 0%, #e26ab5 100%);
    814 | }
    815 | .slides > article:nth-child(39n+38):before {
    816 |   background-image: url("images/1.jpg");
    817 |   content: #5e9ccd;
    818 | }
    819 | .slides > article:nth-child(39n+38):after {
    820 |   background-image: -webkit-linear-gradient(40deg, #5e9ccd 0%, #226443 100%);
    821 |   background-image: linear-gradient(50deg, #5e9ccd 0%, #226443 100%);
    822 | }
    823 | .slides > article:nth-child(39n+39):before {
    824 |   background-image: url("images/3.jpg");
    825 |   content: #1e4823;
    826 | }
    827 | .slides > article:nth-child(39n+39):after {
    828 |   background-image: -webkit-linear-gradient(323deg, #1e4823 0%, #e26ab5 100%);
    829 |   background-image: linear-gradient(127deg, #1e4823 0%, #e26ab5 100%);
    830 | }
    831 | h1,
    832 | h2,
    833 | h3,
    834 | h4,
    835 | h5,
    836 | h6 {
    837 |   font-family: bangers;
    838 |   font-weight: 100;
    839 |   letter-spacing: 1px;
    840 |   line-height: 1;
    841 |   text-shadow: 5px 5px 0 rgba(0,0,0,0.3);
    842 |   margin: 15px 0;
    843 | }
    844 | p,
    845 | a,
    846 | li {
    847 |   text-shadow: 4px 3px 0 rgba(0,0,0,0.3);
    848 |   margin: 15px 0;
    849 | }
    850 | p,
    851 | li,
    852 | small {
    853 |   color: #fff;
    854 |   font-size: 50px;
    855 |   font-weight: 700;
    856 | }
    857 | small {
    858 |   font-size: 25px;
    859 | }
    860 | h1 {
    861 |   -webkit-transform: skew(0, -8deg);
    862 |       -ms-transform: skew(0, -8deg);
    863 |           transform: skew(0, -8deg);
    864 |   font-size: 120px;
    865 |   color: #fd0;
    866 | }
    867 | h1 a {
    868 |   text-decoration: none;
    869 |   font-size: 20px;
    870 |   color: #000;
    871 |   padding-left: 10px;
    872 | }
    873 | h2 {
    874 |   font-size: 200px;
    875 |   color: #fd0;
    876 |   line-height: 1;
    877 |   margin: 0;
    878 | }
    879 | .large {
    880 |   font-size: 200px;
    881 | }
    882 | .big {
    883 |   font-size: 250px;
    884 | }
    885 | .huge {
    886 |   font-size: 450px;
    887 | }
    888 | h3 {
    889 |   font-size: 70px;
    890 |   color: #fd0;
    891 |   line-height: 1.5;
    892 | }
    893 | h4 {
    894 |   color: #fd0;
    895 |   font-size: 50px;
    896 | }
    897 | .halves {
    898 |   width: 100%;
    899 | }
    900 | .half {
    901 |   width: 50%;
    902 | }
    903 | img.wide {
    904 |   width: 100%;
    905 |   height: auto;
    906 |   max-height: none;
    907 | }
    908 | img.high {
    909 |   width: auto;
    910 |   height: 100%;
    911 |   max-height: 1200px;
    912 | }
    913 | .hl {
    914 |   color: #fd0;
    915 | }
    916 | article.treats h1 {
    917 |   margin-bottom: 90px;
    918 |   font-size: 250px;
    919 | }
    920 | .flex {
    921 |   display: -webkit-box !important;
    922 |   display: -webkit-flex !important;
    923 |   display: -ms-flexbox !important;
    924 |   display: flex !important;
    925 | }
    926 | .flex > * {
    927 |   -webkit-box-flex: 1;
    928 |   -webkit-flex: 1;
    929 |       -ms-flex: 1;
    930 |           flex: 1;
    931 | }
    932 | .flex > *:before {
    933 |   content: '';
    934 | }
    935 | .emoji {
    936 |   font-size: 116px;
    937 |   font-family: serif;
    938 | }
    939 | body.square .slides > article {
    940 |   width: 1024px;
    941 |   height: 768px;
    942 |   margin-left: -512px;
    943 |   margin-top: -384px;
    944 |   border-radius: 0;
    945 | }
    946 | body.square .slides > article h2 {
    947 |   font-size: 110px;
    948 | }
    949 | body.square .slides > article p,
    950 | body.square .slides > article li,
    951 | body.square .slides > article small {
    952 |   font-size: 30px;
    953 | }
    954 | body.square .slides > article .big {
    955 |   font-size: 180px;
    956 | }
    957 | body.square .slides > article .large {
    958 |   font-size: 140px;
    959 | }
    960 | body.square .slides > article .huge {
    961 |   font-size: 220px;
    962 | }
    963 | 
    
    
    --------------------------------------------------------------------------------
    /styles.styl:
    --------------------------------------------------------------------------------
      1 | @import "_deps"
      2 | 
      3 | yellow = #FFDD00
      4 | orange = #FF9D00
      5 | 
      6 | html {
      7 |   box-sizing: border-box;
      8 | }
      9 | *, *:before, *:after {
     10 |   box-sizing: inherit;
     11 | }
     12 | 
     13 | .slides > article
     14 |   padding 50px
     15 |   &:before
     16 |     content ''
     17 |     display block
     18 |     width 100%
     19 |     height 100%
     20 |     position absolute
     21 |     filter grayscale(100%) contrast(160%)
     22 |     background:url(images/3.jpg); // blacksmith
     23 |     background-size cover
     24 |     top 0
     25 |     left 0
     26 |     z-index -2
     27 |   &:after
     28 |     z-index -1
     29 |     content ''
     30 |     display block
     31 |     width 100%
     32 |     height 100%
     33 |     position absolute
     34 |     top 0
     35 |     left 0
     36 |     opacity 0.8
     37 |   &.plain
     38 |     &:before, &:after
     39 |       display none
     40 | 
     41 | .slides > article:nth-child(5n+1):after
     42 |     background-image linear-gradient(-208deg, #81af9b 0%, #7aa8ac 27%, #6693da 100%)
     43 | 
     44 | .slides > article:nth-child(5n+2):after
     45 |     background-image: linear-gradient(-132deg, #FFDD00 0%, #6DD7BA 100%);
     46 | 
     47 | 
     48 | .slides > article.old-tools
     49 |   &:before
     50 |     background-image url("images/soldering.jpg") !important
     51 |     background-size contain
     52 |   &:after
     53 |     opacity 0.1
     54 | 
     55 | // Mixin
     56 | random(min,max)
     57 |   return floor(math(0, 'random')*(max - min + 1) + min)
     58 | 
     59 | randomColor()
     60 |   return rgb(random(0,255),random(0,255),random(0,255))
     61 | 
     62 | combos = (#825854 #3de7e4) (#29519e #94eced) (#e26ab5 #1e4823) (#11feec #FF00E0) (#006C8D #006C8D) (#3a5c8e #eb821b) (#ca5723 #7d5794) (#6aa4e1 #ae5ceb) (#226443 #5e9ccd) (#6d6528 #3768f7) (#2decf5 #52aeea) (#d07dd7 #e1e34a) (#3456fd #14e839) (#f8078c #17156b)
     63 | 
     64 | randomGradient()
     65 |   n = random(1,13)
     66 |   combo = combos[n]
     67 |   return combo
     68 | 
     69 | for num in 1...40
     70 |   path = "images/"+ random(1,10)+".jpg"
     71 |   combo = randomGradient();
     72 |   .slides > article:nth-child(39n+{num})
     73 |     &:before
     74 |       background-image url(path)
     75 |       content combo[1]
     76 |     &:after
     77 |       background-image linear-gradient(random(0,360)deg, combo[1] 0%, combo[0] 100%);
     78 | 
     79 | 
     80 | // .slides > article:nth-child(1n):after
     81 | //     background-image: linear-gradient(-173deg, #007EFF 0%, #6DD7BA 51%);
     82 | 
     83 | h1,h2,h3,h4,h5,h6
     84 |   font-family bangers
     85 |   font-weight 100
     86 |   letter-spacing 1px
     87 |   line-height 1
     88 |   text-shadow 5px 5px 0 rgba(0,0,0,0.3)
     89 |   margin 15px 0
     90 | 
     91 | p, a, li
     92 |   text-shadow 4px 3px 0 rgba(0,0,0,0.3)
     93 |   margin 15px 0
     94 | 
     95 | p, li, small
     96 |   color white
     97 |   font-size 50px
     98 |   font-weight 700
     99 | 
    100 | small
    101 |   font-size 25px
    102 | 
    103 | h1
    104 |   transform skew(0, -8deg)
    105 |   font-size 120px
    106 |   color yellow
    107 |   a
    108 |     text-decoration none
    109 |     font-size 20px
    110 |     color black
    111 |     padding-left 10px
    112 | 
    113 | h2
    114 |   font-size 200px
    115 |   color yellow
    116 |   line-height 1
    117 |   margin 0
    118 | 
    119 | .large
    120 |   font-size 200px
    121 | 
    122 | .big
    123 |   font-size 250px
    124 | 
    125 | .huge
    126 |   font-size 450px
    127 | 
    128 | h3
    129 |   font-size 70px
    130 |   color yellow
    131 |   line-height 1.5
    132 | 
    133 | h4
    134 |   color yellow
    135 |   font-size 50px
    136 | 
    137 | .halves
    138 |   width 100%
    139 | 
    140 | .half
    141 |   width 50%
    142 | 
    143 | 
    144 | img.wide
    145 |   width 100%
    146 |   height auto
    147 |   max-height none
    148 | 
    149 | img.high
    150 |   width auto
    151 |   height 100%
    152 |   max-height 1200px
    153 | 
    154 | .hl
    155 |   color yellow
    156 | 
    157 | article.treats
    158 |   h1
    159 |     margin-bottom 90px
    160 |     font-size 250px
    161 |     // color orange
    162 | 
    163 | 
    164 | .flex
    165 |   display flex !important
    166 |   & > *
    167 |     flex 1
    168 |     &:before
    169 |       content  ''
    170 | 
    171 | 
    172 | .emoji
    173 |   font-size 116px
    174 |   font-family serif
    175 | 
    176 | 
    177 | // Smaller Screens
    178 | body.square
    179 |   .slides
    180 |     & > article
    181 |       w = 1024
    182 |       h = 768
    183 |       width w px
    184 |       height h px
    185 |       margin-left (w/2) * -1px
    186 |       margin-top (h/2) * -1px
    187 |       border-radius 0
    188 |       h2
    189 |         font-size 110px
    190 |       p, li, small
    191 |         font-size 30px
    192 |       .big
    193 |         font-size 180px
    194 |       .large
    195 |         font-size 140px
    196 |       .huge
    197 |         font-size 220px
    198 | 
    
    
    --------------------------------------------------------------------------------