Bespoke.js: The Road to 1KB

What?

Why?

< = 5KB No dependencies

/*! Bespoke.js v0.3 © 2013 Mark Dalgleish, Licensed MIT */ !function(a, b){var c=function(a, b){var c=document.querySelector(a), g=[].slice.call(c.children, 0), h=g[0], i={}, k=function(a, b){g[a]&&(p("deactivate", q(h, b)), h=g[a], g.map(l), p("activate", q(h, b)), e(h, "active"), f(h, "inactive"))}, l=function(a, b){var c=b-g.indexOf(h), d=c>0?"after":"before";["before(-\\d+)?", "after(-\\d+)?", "active", "inactive"].map(f.bind(0, a)), a!=h&&["inactive", d, d+"-"+Math.abs(c)].map(e.bind(0, a))}, m=function(a, b){p("slide", q(g[a], b))&&k(a, b)}, n=function(a, b){var c=g.indexOf(h)+a;p(a>0?"next":"prev", q(h, b))&&k(c, b)}, o=function(a, b){return(i[a]||(i[a]=[])).push(b), function(){i[a]=i[a].filter(function(a){return a!=b})}}, p=function(a, b){return(i[a]||[]).reduce(function(a, c){return a&&c(b)!==!1}, !0)}, q=function(a, b){return b=b||{}, b.index=g.indexOf(a), b.slide=a, b}, r={on:o, fire:p, slide:m, next:n.bind(0, 1), prev:n.bind(0, -1), parent:c, slides:g};e(c, "parent"), g.map(function(a){e(a, "slide")});for(var s in b)j[s](r, b[s]);return k(0), d.push(r), r}, d=[], e=function(b, c){b.classList.add(a+"-"+c)}, f=function(b, c){b.className=b.className.replace(RegExp(a+"-"+c+"(\\s|$)", "g"), " ").trim()}, g=function(a){return function(b){d.map(function(c){c[a](b)})}}, h=function(a){return{from:function(b, d){return(d=d||{})[a]=!0, c(b, d)}}}, i=function(a){return function(b){var c, d;document.addEventListener("keydown", function(c){(34==c.which||32==c.which||"X"==a&&39==c.which||"Y"==a&&40==c.which)&&b.next(), (33==c.which||"X"==a&&37==c.which||"Y"==a&&38==c.which)&&b.prev()}), b.parent.addEventListener("touchstart", function(b){1==b.touches.length&&(c=b.touches[0]["page"+a], d=0)}), b.parent.addEventListener("touchmove", function(b){1==b.touches.length&&(b.preventDefault(), d=b.touches[0]["page"+a]-c)}), b.parent.addEventListener("touchend", function(){Math.abs(d)>50&&(d>0?b.prev():b.next())})}}, j={horizontal:i("X"), vertical:i("Y")};b[a]={from:c, slide:g("slide"), next:g("next"), prev:g("prev"), horizontal:h("horizontal"), vertical:h("vertical"), plugins:j}}("bespoke", this);

Feature request? Just say no.

Want features? Make it modular

grunt-micro (by me)

JS too large?

Break the build!

Limit that size ES5 / Modern Browser isn't a "dependency"

Bonus: ES5 === expressive

Code time!

forEach for(var i=0;i<things.length;i++){ doSomething(things[i]); } Vs. things.forEach(doSomething);

Partial application function(cls){ addClass(slide, cls); } Vs. addClass.bind(null, slide); Function#bind returns a new function

with 'this' and arguments applied.

Hack time!

forEach ~> map arr.forEach(doSomething); Vs. arr.map(doSomething); Array#map returns an array, just don't use it.

Undefined fallbacks if(obj)Object.keys(obj).forEach(foo); Vs. Object.keys(obj||{}).forEach(foo); Object.keys on {} returns [].

Array#forEach on [] does nothing.

RegExp doesn't need 'new' new RegExp(foobar); Vs. RegExp(foobar); Don't believe me? Read the spec!

Assignment returns value obj=obj||{};obj[prop] = true; Vs. (obj=obj||{})[prop] = true;

Double equals! obj1 === obj2 Vs. obj1 == obj2 Sorry Crockford, but

I know they're the same type.

Actual solution time!

Let's assume a lib like this

myLib({ foo: true, bar: false, baz: 'quz' });

Don't write it

like this

var myLib = function(options) { if (options.foo) doThis(); if (options.bar) doSomeOtherStuff(); if (options.baz) coverSomeWeirdEdgeCase(); };

Write it like this

var myLib = function(plugins) { for (var name in plugins) { var value = plugins[name]; myLib.plugins[name](value); }); }; myLib.plugins.baz = function() { coverSomeWeirdEdgeCase(); }; // etc...

Why?

Tiny core library Extremely modular Repo per feature

Bower package per feature.

Seriously, why?

Yeoman.

Introducing... generator-bespoke

$ npm install -g yo generator-bespoke $ yo bespoke

Generates bower.json (Among other things)

{ "name": "hello-world-bespoke", "version": "0.0.0", "dependencies": { "bespoke.js": "~0.2.0", "bespoke-bullets": "~0.2.0", "bespoke-hash": "~0.1.0", "bespoke-state": "~0.2.0", "prism": "gh-pages" } }

Plus heaps more Boilerplate presentation

Grunt build system

Preview server + LiveReload

Compiles Jade, Stylus, CoffeeScript

Syntax highlighting with Prism

Bonus!

GitHub Pages deploy task Publishing your deck is stupid easy.