Skip to content

Instantly share code, notes, and snippets.

@stefanpenner
Created April 30, 2010 01:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stefanpenner/384554 to your computer and use it in GitHub Desktop.
Save stefanpenner/384554 to your computer and use it in GitHub Desktop.
// Set up simple-inheritance (Classical)
// Inspired by base2 and Prototype
(function(){
var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
// The base Class implementation (does nothing)
this.Class = function(){};
// Create a new Class that inherits from this class
Class.extend = function(prop) {
var _super = this.prototype;
// Instantiate a base class (but only create the instance,
// don't run the init constructor)
initializing = true;
var prototype = new this();
initializing = false;
// Copy the properties over onto the new prototype
for (var name in prop) {
// Check if we're overwriting an existing function
prototype[name] = typeof prop[name] == "function" &&
typeof _super[name] == "function" && fnTest.test(prop[name]) ?
(function(name, fn){
return function() {
var tmp = this._super;
// Add a new ._super() method that is the same method
// but on the super-class
this._super = _super[name];
// The method only need to be bound temporarily, so we
// remove it when we're done executing
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name]) :
prop[name];
}
// The dummy class constructor
function Class() {
// All construction is actually done in the init method
if ( !initializing && this.init )
this.init.apply(this, arguments);
}
// Populate our constructed prototype object
Class.prototype = prototype;
// Enforce the constructor to be what we expect
Class.constructor = Class;
// And make this class extendable
Class.extend = arguments.callee;
return Class;
};
})();
// Create a closure for namespace safety
(function($){
//////////////////////////////////////////////////
// Define our Objects
//////////////////////////////////////////////////
/**
* Class Person
*
* A simple representation of a person.
*/
var Person = Class.extend({
init : function (options, elem) {
// Overload the passed in options with any default options
this.options = $.extend({}, this.options, options);
// Set the relative element as the passed in element
this.element = elem;
// Initiate the dom building method
this.buildDom();
},
options : {
className: "person",
smartz: 1
},
getName : function () {
return this.options.name;
},
buildDom : function() {
$(this.element)
.addClass(this.options.className)
.html(this.getName() + '<span class="smartz">('+this.options.smartz+')</span>');
}
});
/**
* Class Attendee
*
* A Person with the added interface for a conference attendee
*/
var Attendee = Person.extend((function(){
// A private function via the module pattern
var addSmartz = function(points) {
if (this.options.smartz) {
this.options.smartz += points;
}
else {
this.options.smartz = points;
}
// Update the indicator
$(this.element).find('.smartz').text('('+this.options.smartz+')');
};
// Return the object literal that we would normally just type
// if we weren't using the module pattern.
return {
// A function to add to the smartness level
listen : function() {
// Call our private function with the context of this object
addSmartz.call(this, 1);
},
options: {
className: "attendee"
}
};
// Notice that this is a self executing function
})());
/**
* Class Speaker
*
* An Attendee that has speaking powers
*/
var Speaker = Attendee.extend({
// The speaking function only decreases Smartz
speak : function(attendees) {
// Decrement smartz
this.options.smartz -= 1;
// Loop through attendees and make them listen
for (var i in attendees) {
attendees[i].listen();
}
// Update the indicator
$(this.element).find('.smartz').text('('+this.options.smartz+')');
// This makes this method chainable
return this;
},
options: {
className: "speaker"
},
buildDom : function() {
// Use the super class!
this._super();
$(this.element).append('<a href="#speak" class="spk">[speak]</a>');
}
});
/////////////////////////////////////////////////
// Pluginify!
/////////////////////////////////////////////////
$.fn.extend({
'person': function(options) {
return this.each(function(){
// Create a new person
var myPerson = new Person(options, this);
// Save the reference to the person in the elem's data object
$(this).data('person', myPerson);
});
},
// Do the same for attendee and speaker
'attendee': function(options) {
return this.each(function(){
var myAttendee = new Attendee(options, this);
$(this).data('attendee', myAttendee);
});
},
'speaker': function(options) {
return this.each(function(){
var mySpeaker = new Speaker(options, this);
$(this).data('speaker', mySpeaker);
});
}
});
////////////////////////////////////////////////
// Use our new plugins for our website! - NOTHING CHANGES IN OUR INTERFACE TO OUR APP
////////////////////////////////////////////////
// Run when the document is ready
$(document).ready(function(){
// Cache the elements where our people will be placed
var personList = $('#personList'),
attendeeList = $('#attendeeList'),
speakerList = $('#speakerList');
// Create storage for our people
var everyone = {
persons: [],
attendees: [],
speakers: []
};
// Handle the add Person button
$('#add_person').click(function(e){
// Generate a person
var generatedPerson = $('<li />').person({
name : "Person " + everyone.persons.length,
smartz : 20
});
// Add the Person Object to the everyone storage
everyone.persons.push(generatedPerson.data('person'));
// Append the dom element to the list of persons
personList.append(generatedPerson);
// Update the count
personList.prev().text(everyone.persons.length);
// Prevent the link from following
return false;
});
// Handle the add Attendee button
$('#add_attendee').click(function(e){
// Generate an attendee
var generatedAttendee = $('<li />').attendee({
name : "Attendee " + everyone.attendees.length,
smartz : 50
});
// Add the Attendee Object to the everyone storage
everyone.attendees.push(generatedAttendee.data('attendee'));
// Append the dom element to the list of attendees
attendeeList.append(generatedAttendee);
// Update the count
attendeeList.prev().text(everyone.attendees.length);
// Prevent the link from following
return false;
});
// Handle the add Speaker button
$('#add_speaker').click(function(e){
// Generate an speaker
var generatedSpeaker = $('<li />').speaker({
name : "Speaker " + everyone.speakers.length,
smartz : 10
});
// Add the Attendee Object to the everyone storage
everyone.speakers.push(generatedSpeaker.data('speaker'));
// Append the dom element to the list of speakers
speakerList.append(generatedSpeaker);
// Update the count
speakerList.prev().text(everyone.speakers.length);
// Prevent the link from following
return false;
});
speakerList.find('a.spk').live('click', function(e){
// Get our speaker instance from the data cache
var speakingSpeaker = $(this).closest('li').data('speaker');
// Invoke the speaking function on our object
speakingSpeaker.speak(everyone.attendees);
// Prevent the link from following
return false;
});
});
})(jQuery);
// Set up prototypal inheritance - if it doesn't exist
if (typeof Object.create !== 'function') {
Object.create = function (o) {
function F() {}
F.prototype = o;
return new F();
};
}
// Create a closure for namespace safety
(function($){
//////////////////////////////////////////////////
// Define our Objects
//////////////////////////////////////////////////
/**
* Object Person
*
* A simple representation of a person.
*/
var Person = {
init : function (options, elem) {
// Overload the passed in options with any default options
this.options = $.extend({}, this.options, options);
// Set the relative element as the passed in element
this.element = elem;
// Initiate the dom building method
this.buildDom();
},
options : {
className: "person",
smartz: 1
},
getName : function () {
return this.options.name;
},
buildDom : function() {
$(this.element)
.addClass(this.options.className)
.html(this.getName() + '<span class="smartz">('+this.options.smartz+')</span>');
}
};
/**
* Object Attendee
*
* A Person with the added interface for a conference attendee
*/
var Attendee = $.extend(Object.create(Person), (function(){
// A private function via the module pattern
var addSmartz = function(points) {
if (this.options.smartz) {
this.options.smartz += points;
}
else {
this.options.smartz = points;
}
// Update the indicator
$(this.element).find('.smartz').text('('+this.options.smartz+')');
};
// Return the object literal that we would normally just type
// if we weren't using the module pattern.
return {
// A function to add to the smartness level
listen : function() {
// Call our private function with the context of this object
addSmartz.call(this, 1);
},
options: {
className: "attendee"
}
};
// Notice that this is a self executing function
})());
/**
* Object Speaker
*
* An Attendee that has speaking powers
*/
var Speaker = $.extend(Object.create(Attendee), {
// The speaking function only decreases Smartz
speak : function(attendees) {
// Decrement smartz
this.options.smartz -= 1;
// Loop through attendees and make them listen
for (var i in attendees) {
attendees[i].listen();
}
// Update the indicator
$(this.element).find('.smartz').text('('+this.options.smartz+')');
// This makes this method chainable
return this;
},
options: {
className: "speaker"
},
buildDom : function() {
$(this.element)
.addClass(this.options.className)
.html(this.getName() + '<span class="smartz">('+this.options.smartz+')</span>')
.append('<a href="#speak" class="spk">[speak]</a>');
}
});
// Redefine the getName function for Person to show that differential inheritance is retroactive
Person.getName = function() {
return this.options.name + '!';
};
/////////////////////////////////////////////////
// Pluginify!
/////////////////////////////////////////////////
$.fn.extend({
'person': function(options) {
return this.each(function(){
// Create a new person
var myPerson = Object.create(Person);
// Set initialize that person with the plugin options
myPerson.init(options, this);
// Save the reference to the person in the elem's data object
$(this).data('person', myPerson);
});
},
// Do the same for attendee and speaker
'attendee': function(options) {
return this.each(function(){
var myAttendee = Object.create(Attendee);
myAttendee.init(options, this);
$(this).data('attendee', myAttendee);
});
},
'speaker': function(options) {
return this.each(function(){
var mySpeaker = Object.create(Speaker);
mySpeaker.init(options, this);
$(this).data('speaker', mySpeaker);
});
}
});
////////////////////////////////////////////////
// Use our new plugins for our website!
////////////////////////////////////////////////
// Run when the document is ready
$(document).ready(function(){
// Cache the elements where our people will be placed
var personList = $('#personList'),
attendeeList = $('#attendeeList'),
speakerList = $('#speakerList');
// Create storage for our people
var everyone = {
persons: [],
attendees: [],
speakers: []
};
// Handle the add Person button
$('#add_person').click(function(e){
// Generate a person
var generatedPerson = $('<li />').person({
name : "Person " + everyone.persons.length,
smartz : 20
});
// Add the Person Object to the everyone storage
everyone.persons.push(generatedPerson.data('person'));
// Append the dom element to the list of persons
personList.append(generatedPerson);
// Update the count
personList.prev().text(everyone.persons.length);
// Prevent the link from following
return false;
});
// Handle the add Attendee button
$('#add_attendee').click(function(e){
// Generate an attendee
var generatedAttendee = $('<li />').attendee({
name : "Attendee " + everyone.attendees.length,
smartz : 50
});
// Add the Attendee Object to the everyone storage
everyone.attendees.push(generatedAttendee.data('attendee'));
// Append the dom element to the list of attendees
attendeeList.append(generatedAttendee);
// Update the count
attendeeList.prev().text(everyone.attendees.length);
// Prevent the link from following
return false;
});
// Handle the add Speaker button
$('#add_speaker').click(function(e){
// Generate an speaker
var generatedSpeaker = $('<li />').speaker({
name : "Speaker " + everyone.speakers.length,
smartz : 10
});
// Add the Attendee Object to the everyone storage
everyone.speakers.push(generatedSpeaker.data('speaker'));
// Append the dom element to the list of speakers
speakerList.append(generatedSpeaker);
// Update the count
speakerList.prev().text(everyone.speakers.length);
// Prevent the link from following
return false;
});
speakerList.find('a.spk').live('click', function(e){
// Get our speaker instance from the data cache
var speakingSpeaker = $(this).closest('li').data('speaker');
// Invoke the speaking function on our object
speakingSpeaker.speak(everyone.attendees);
// Prevent the link from following
return false;
});
});
})(jQuery);
// No extras for Pseudo-Classical - we're native!
// Create a closure for namespace safety
(function($){
//////////////////////////////////////////////////
// Define our Objects
//////////////////////////////////////////////////
/**
* Person
*
* A simple representation of a person.
*/
var Person = function(options, elem) {
// Call init - to initialize the object
this.init(options, elem);
};
Person.prototype.init = function(options, elem) {
// Overload the passed in options with any default options
this.options = $.extend({}, this.options, options);
// Set the relative element as the passed in element
this.element = elem;
// Initiate the dom building method
this.buildDom();
};
// Prototype members can be declared after the fact
Person.prototype.options = {
className: "person",
smartz: 1
};
Person.prototype.getName = function () {
return this.options.name;
};
Person.prototype.buildDom = function() {
$(this.element)
.addClass(this.options.className)
.html(this.getName() + '<span class="smartz">('+this.options.smartz+')</span>');
};
/**
* Attendee
*
* A Person with the added interface for a conference attendee
*/
var Attendee = function(options, elem){
this.init(options, elem);
};
Attendee.prototype = new Person();
Attendee.prototype.options = {className: "attendee"};
// A private function via the module pattern
Attendee.prototype.listen = (function() {
// Use a self executing function to return a function with access to a private function
var addSmartz = function(points) {
if (this.options.smartz) {
this.options.smartz += points;
}
else {
this.options.smartz = points;
}
// Update the indicator
$(this.element).find('.smartz').text('('+this.options.smartz+')');
};
// Return the object literal instead of 'this' which usually happens automatically
return function() {
// Call our private function with the context of this object
addSmartz.call(this, 1);
};
})();
/**
* Speaker
*
* An Attendee that has speaking powers
*/
var Speaker = function(options, elem) {
// You can specifically invoke your parent constructor function
this.constructor.apply(this, arguments);
};
Speaker.prototype = new Attendee();
Speaker.prototype.options = {className: "speaker"};
// The speaking function only decreases Smartz
Speaker.prototype.speak = function(attendees) {
// Decrement smartz
this.options.smartz -= 1;
// Loop through attendees and make them listen
for (var i in attendees) {
attendees[i].listen();
}
// Update the indicator
$(this.element).find('.smartz').text('('+this.options.smartz+')');
// This makes this method chainable
return this;
};
Speaker.prototype.buildDom = function() {
// Use the super class!
this.constructor.prototype.buildDom.call(this);
$(this.element).append('<a href="#speak" class="spk">[speak]</a>');
};
/////////////////////////////////////////////////
// Pluginify!
/////////////////////////////////////////////////
$.fn.extend({
'person': function(options) {
return this.each(function(){
// Create a new person
var myPerson = new Person(options, this);
// Save the reference to the person in the elem's data object
$(this).data('person', myPerson);
});
},
// Do the same for attendee and speaker
'attendee': function(options) {
return this.each(function(){
var myAttendee = new Attendee(options, this);
$(this).data('attendee', myAttendee);
});
},
'speaker': function(options) {
return this.each(function(){
var mySpeaker = new Speaker(options, this);
$(this).data('speaker', mySpeaker);
});
}
});
////////////////////////////////////////////////
// Use our new plugins for our website! - NOTHING CHANGES IN OUR INTERFACE TO OUR APP
////////////////////////////////////////////////
// Run when the document is ready
$(document).ready(function(){
// Cache the elements where our people will be placed
var personList = $('#personList'),
attendeeList = $('#attendeeList'),
speakerList = $('#speakerList');
// Create storage for our people
var everyone = {
persons: [],
attendees: [],
speakers: []
};
// Handle the add Person button
$('#add_person').click(function(e){
// Generate a person
var generatedPerson = $('<li />').person({
name : "Person " + everyone.persons.length,
smartz : 20
});
// Add the Person Object to the everyone storage
everyone.persons.push(generatedPerson.data('person'));
// Append the dom element to the list of persons
personList.append(generatedPerson);
// Update the count
personList.prev().text(everyone.persons.length);
// Prevent the link from following
return false;
});
// Handle the add Attendee button
$('#add_attendee').click(function(e){
// Generate an attendee
var generatedAttendee = $('<li />').attendee({
name : "Attendee " + everyone.attendees.length,
smartz : 50
});
// Add the Attendee Object to the everyone storage
everyone.attendees.push(generatedAttendee.data('attendee'));
// Append the dom element to the list of attendees
attendeeList.append(generatedAttendee);
// Update the count
attendeeList.prev().text(everyone.attendees.length);
// Prevent the link from following
return false;
});
// Handle the add Speaker button
$('#add_speaker').click(function(e){
// Generate an speaker
var generatedSpeaker = $('<li />').speaker({
name : "Speaker " + everyone.speakers.length,
smartz : 10
});
// Add the Attendee Object to the everyone storage
everyone.speakers.push(generatedSpeaker.data('speaker'));
// Append the dom element to the list of speakers
speakerList.append(generatedSpeaker);
// Update the count
speakerList.prev().text(everyone.speakers.length);
// Prevent the link from following
return false;
});
speakerList.find('a.spk').live('click', function(e){
// Get our speaker instance from the data cache
var speakingSpeaker = $(this).closest('li').data('speaker');
// Invoke the speaking function on our object
speakingSpeaker.speak(everyone.attendees);
// Prevent the link from following
return false;
});
});
})(jQuery);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment