jQuery Conference Slides and Demo
By Ryan Florence, published 2010-04-24
Part of the issue Migrated Articles From Original Site Structure..
Just spoke at jQuery conference, the largest javascript conference to date (as stated by Resig.) It really was a great turnout and I’ve met tons of cool people.
I only had a half an hour, which flew by and is hardly enough time to talk about MooTools Class
AND the jQuery mutator. Had several people tell me I convinced them to give MooTools a shot with their apps since they can keep jQuery for the DOM. That’s always good news.
Update: moo4q.com
There’s now a site dedicated to this technique: http://moo4q.com, check it out.
Slides and Demo
And now for your viewing pleasure:
Source of the demo
Mutator
Class.Mutators.jQuery = function(name){
var self = this;
jQuery.fn[name] = function(arg){
var instance = this.data(name);
if ($type(arg) == 'string'){
var prop = instance[arg];
if ($type(prop) == 'function'){
var returns = prop.apply(instance, Array.slice(arguments, 1));
return (returns == instance) ? this : returns;
} else if (arguments.length == 1){
return prop;
}
instance[arg] = arguments[1];
} else {
if (instance) return instance;
this.data(name, new self(this.selector, arg));
}
return this;
};
};
Human
var Human = new Class({
Implements: [Options, Events, Loop],
options: {
rest: {
frames: 14,
fileName: 'images/rest/rest_',
speed: 100
}
},
jQuery: 'human',
energy: 5,
isAlive: true,
animatingOnce: false,
initialize: function(selector, options){
this.setOptions(options);
this.image = $(selector);
this.name = this.image.attr('name') || 'unkown';
this.currentAnimation = this.options.rest;
this.loadImages(this.options.rest);
this.setLoop(this.animate, this.currentAnimation.speed).startLoop();
},
eat: function(){
if(this.isAlive){
this.energy++;
this.fireEvent('eat');
}
},
die: function(killer){
if(this.isAlive){
this.energy = 0;
this.isAlive = false;
killer.kill(this);
this.fireEvent('die', killer);
}
return this;
},
revive: function(){
if(!this.isAlive){
this.energy = 1;
this.isAlive = true;
this.fireEvent('revive');
}
return this;
},
animate: function(){
if(this.loopCount > this.currentAnimation.frames) this.loopCount = 1;
this.image.attr('src', this.currentAnimation.fileName + this.loopCount.zeroPad(4) + '.png');
return this;
},
changeAnimation: function(animation){
if(!this.animatingOnce){
this.currentAnimation = animation;
this.resetLoop().setLoop(this.animate, animation.speed);
}
return this;
},
animateOnce: function(animation, stop){
if(!this.animatingOnce){
var original = this.currentAnimation;
this.changeAnimation(animation);
this.animatingOnce = true;
var delay = this.currentAnimation.frames * this.currentAnimation.speed;
var self = this;
(function(){
self.animatingOnce = false;
(stop) ? self.stopLoop() : self.changeAnimation(original);
}).delay(delay);
}
return this;
},
loadImages: function(animation){
(animation.frames - 1).times(function(num){
$('<img/>', { src: animation.fileName + (num + 1).zeroPad(4) + '.png'});
}, this);
return this;
}
});
Number.implement({
zeroPad: function(length){
var str = '' + this;
while (str.length < length) str = '0' + str;
return str;
}
});
Warrior
var Warrior = new Class({
energy: 1000,
kills: 0,
attack: function(target){
if(!this.animatingOnce) {
this.fireEvent('attack', target);
this.animateOnce(this.options.attack);
if(target.isAlive){
var delay = this.options.attack.frames / 2.3 * this.options.attack.speed;
target.getAttacked.delay(delay, target, this);
this.fireEvent.delay(delay, this, ['attackComplete', target]);
}
}
return this;
},
kill: function(victim){
this.kills++;
this.fireEvent('kill', victim);
return this;
}
});
Ninja
var Ninja = new Class({
Extends: Human,
Implements: Warrior,
options: {
side: 'evil',
attack: {
frames: 44,
fileName: 'images/attack/attack_',
speed: 40
},
rest:{
frames: 14,
fileName: 'images/ninja-rest/rest_',
speed: 75
}
},
jQuery: 'ninja',
initialize: function(selector, options){
this.parent(selector, options);
this.loadImages(this.options.attack);
this.side = this.options.side;
}
});
Victim
var Victim = new Class({
getAttacked: function(attacker){
this.stopLoop();
this.image.attr('src', this.options.attackedSrc);
this.startLoop.delay(100, this);
this.energy--;
this.fireEvent('getAttacked', attacker);
if(this.energy == 0) this.die(attacker);
},
die: function(killer){
if(this.isAlive){
this.parent(killer);
this.animateOnce(this.options.die, true);
}
return this;
},
revive: function(){
if(!this.isAlive){
this.parent();
this.changeAnimation(this.options.rest).startLoop();
}
return this;
}
});
Civilian
var Civilian = new Class({
Extends: Human,
Implements: Victim,
options: {
die: {
frames: 29,
fileName: 'images/die/die_',
speed: 75
},
attackedSrc: 'images/die/die_0000.png'
},
jQuery: 'civilian',
initialize: function(selector, options){
this.parent(selector, options);
this.loadImages(this.options.die);
}
});
Domready
$(document).ready(function(){
// create our humans
$('#bob').civilian(); // creates instance
$('#ryu').ninja({side: 'evil'}); // pass in some options
// keyboard events
$(document).bind('keydown',function(event){
if(event.keyCode == 65)
$('#ryu').ninja('attack', $('#bob').civilian());
// $('#bob').civilian() a second time returns the instance
// just like `var bob = new Civilian('#bob')`, it returns a object like `bob`
// so `$('#bob').civilian().die() ~= $('#bob').civilian('die')`
// except the first returns the object and the second returns the jquery object
if(event.keyCode == 82) $('#bob').civilian('revive');
if(event.keyCode == 69) $('#bob').civilian('eat');
});
// log function
var setLog = function(message, className){
className = className || 'message';
var top = $('#log')[0].scrollHeight;
$('#log').append('<p class=' + className + '>' + message + '</p>').stop().animate({scrollTop: top}, 1000);
}
// instance events
// when things happen we can do stuff, like the callbacks you're used to
// Here we get the instance of civilian by calling the civilian on bob again
// and add the events to it, but you can also ...
$('#bob').civilian().addEvents({
onDie: function(killer){
// events can have arguments, killer is the instance of $('#ryu').ninja()
// and `this` is the instance of $('#bob').civilian() in here.
setLog(this.name + ' dies by the hand of ' + killer.name, 'die');
},
onRevive: function(){
setLog(this.name + ' is Revived', 'revive');
},
onGetAttacked: function(attacker){
setLog(this.name + ' is hit! He has ' + this.energy + ' energy left after ' + attacker.name + "'s attack!", 'getAttacked');
},
onEat: function(){
setLog(this.name + ' ate some food. His energy is now ' + this.energy, 'eat');
}
});
// ... just use 'addEvents' as an argument to the class to add the events
$('#ryu').ninja('addEvents',{
onAttack: function(target){
setLog(this.name + ' attacks ' + target.name + '!', 'attack');
},
onKill: function(target){
var plural = (this.kills == 1) ? 'kill' : 'kills';
setLog(this.name + ' killed ' + target.name + '!! Ryu now has ' + this.kills + ' ' + plural, 'kill');
setLog.delay(3000, this, ["You find an <span>Emporer's Hairpin</span> on " + target.name, 'find']);
setLog.delay(3000, this, ["You find a <span>handful of paint brushes</span> on " + target.name, 'find']);
setLog.delay(3000, this, ["You find <span>4 " + target.name + " skins</span> on " + target.name, 'find']);
}
});
});