Bespoke.js: The Road to 1KB
What?
bit.ly/diyslides
Google "bespoke.js"
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!
Google
"grunt-micro"
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