mooTools No.2
Function.implement({

extend: function(properties){
for (var property in properties) this[property] = properties[property];
return this;
},

create: function(options){
var self = this;
options = options || {};
return function(event){
var args = options.arguments;
args = (args != undefined) ? $splat(args) : Array.slice(arguments, (options.event) ? 1 : 0);
if (options.event) args = [event || window.event].extend(args);
var returns = function(){
return self.apply(options.bind || null, args);
};
if (options.delay) return setTimeout(returns, options.delay);
if (options.periodical) return setInterval(returns, options.periodical);
if (options.attempt) return $try(returns);
return returns();
};
},

run: function(args, bind){
return this.apply(bind, $splat(args));
},

pass: function(args, bind){
return this.create({bind: bind, arguments: args});
},

bind: function(bind, args){
return this.create({bind: bind, arguments: args});
},

bindWithEvent: function(bind, args){
return this.create({bind: bind, arguments: args, event: true});
},

attempt: function(args, bind){
return this.create({bind: bind, arguments: args, attempt: true})();
},

delay: function(delay, bind, args){
return this.create({bind: bind, arguments: args, delay: delay})();
},

periodical: function(periodical, bind, args){
return this.create({bind: bind, arguments: args, periodical: periodical})();
}

});


Number.implement({

limit: function(min, max){
return Math.min(max, Math.max(min, this));
},

round: function(precision){
precision = Math.pow(10, precision || 0);
return Math.round(this * precision) / precision;
},

times: function(fn, bind){
for (var i = 0; i < this; i++) fn.call(bind, i, this);
},

toFloat: function(){
return parseFloat(this);
},

toInt: function(base){
return parseInt(this, base || 10);
}

});

Number.alias('times', 'each');

(function(math){
var methods = {};
math.each(function(name){
if (!Number[name]) methods[name] = function(){
return Math[name].apply(null, [this].concat($A(arguments)));
};
});
Number.implement(methods);
})(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']);



String.implement({

test: function(regex, params){
return ((typeof regex == 'string') ? new RegExp(regex, params) : regex).test(this);
},

contains: function(string, separator){
return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : this.indexOf(string) > -1;
},

trim: function(){
return this.replace(/^\s+|\s+$/g, '');
},

clean: function(){
return this.replace(/\s+/g, ' ').trim();
},

camelCase: function(){
return this.replace(/-\D/g, function(match){
return match.charAt(1).toUpperCase();
});
},

hyphenate: function(){
return this.replace(/[A-Z]/g, function(match){
return ('-' + match.charAt(0).toLowerCase());
});
},

capitalize: function(){
return this.replace(/\b[a-z]/g, function(match){
return match.toUpperCase();
});
},

escapeRegExp: function(){
return this.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
},

toInt: function(base){
return parseInt(this, base || 10);
},

toFloat: function(){
return parseFloat(this);
},

hexToRgb: function(array){
var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
return (hex) ? hex.slice(1).hexToRgb(array) : null;
},

rgbToHex: function(array){
var rgb = this.match(/\d{1,3}/g);
return (rgb) ? rgb.rgbToHex(array) : null;
},

stripScripts: function(option){
var scripts = '';
var text = this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(){
scripts += arguments[1] + '\n';
return '';
});
if (option === true) $exec(scripts);
else if ($type(option) == 'function') option(scripts, text);
return text;
},

substitute: function(object, regexp){
return this.replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){
if (match.charAt(0) == '\\') return match.slice(1);
return (object[name] != undefined) ? object[name] : '';
});
}

});


Hash.implement({

has: Object.prototype.hasOwnProperty,

keyOf: function(value){
for (var key in this){
if (this.hasOwnProperty(key) && this[key] === value) return key;
}
return null;
},

hasValue: function(value){
return (Hash.keyOf(this, value) !== null);
},

extend: function(properties){
Hash.each(properties, function(value, key){
Hash.set(this, key, value);
}, this);
return this;
},

combine: function(properties){
Hash.each(properties, function(value, key){
Hash.include(this, key, value);
}, this);
return this;
},

erase: function(key){
if (this.hasOwnProperty(key)) delete this[key];
return this;
},

get: function(key){
return (this.hasOwnProperty(key)) ? this[key] : null;
},

set: function(key, value){
if (!this[key] || this.hasOwnProperty(key)) this[key] = value;
return this;
},

empty: function(){
Hash.each(this, function(value, key){
delete this[key];
}, this);
return this;
},

include: function(key, value){
var k = this[key];
if (k == undefined) this[key] = value;
return this;
},

map: function(fn, bind){
var results = new Hash;
Hash.each(this, function(value, key){
results.set(key, fn.call(bind, value, key, this));
}, this);
return results;
},

filter: function(fn, bind){
var results = new Hash;
Hash.each(this, function(value, key){
if (fn.call(bind, value, key, this)) results.set(key, value);
}, this);
return results;
},

every: function(fn, bind){
for (var key in this){
if (this.hasOwnProperty(key) && !fn.call(bind, this[key], key)) return false;
}
return true;
},

some: function(fn, bind){
for (var key in this){
if (this.hasOwnProperty(key) && fn.call(bind, this[key], key)) return true;
}
return false;
},

getKeys: function(){
var keys = [];
Hash.each(this, function(value, key){
keys.push(key);
});
return keys;
},

getValues: function(){
var values = [];
Hash.each(this, function(value){
values.push(value);
});
return values;
},

toQueryString: function(base){
var queryString = [];
Hash.each(this, function(value, key){
if (base) key = base + '[' + key + ']';
var result;
switch ($type(value)){
case 'object': result = Hash.toQueryString(value, key); break;
case 'array':
var qs = {};
value.each(function(val, i){
qs[i] = val;
});
result = Hash.toQueryString(qs, key);
break;
default: result = key + '=' + encodeURIComponent(value);
}
if (value != undefined) queryString.push(result);
});

return queryString.join('&');
}

});

Hash.alias({keyOf: 'indexOf', hasValue: 'contains'});



var Event = new Native({

name: 'Event',

initialize: function(event, win){
win = win || window;
var doc = win.document;
event = event || win.event;
if (event.$extended) return event;
this.$extended = true;
var type = event.type;
var target = event.target || event.srcElement;
while (target && target.nodeType == 3) target = target.parentNode;

if (type.test(/key/)){
var code = event.which || event.keyCode;
var key = Event.Keys.keyOf(code);
if (type == 'keydown'){
var fKey = code - 111;
if (fKey > 0 && fKey < 13) key = 'f' + fKey;
}
key = key || String.fromCharCode(code).toLowerCase();
} else if (type.match(/(click|mouse|menu)/i)){
doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
var page = {
x: event.pageX || event.clientX + doc.scrollLeft,
y: event.pageY || event.clientY + doc.scrollTop
};
var client = {
x: (event.pageX) ? event.pageX - win.pageXOffset : event.clientX,
y: (event.pageY) ? event.pageY - win.pageYOffset : event.clientY
};
if (type.match(/DOMMouseScroll|mousewheel/)){
var wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3;
}
var rightClick = (event.which == 3) || (event.button == 2);
var related = null;
if (type.match(/over|out/)){
switch (type){
case 'mouseover': related = event.relatedTarget || event.fromElement; break;
case 'mouseout': related = event.relatedTarget || event.toElement;
}
if (!(function(){
while (related && related.nodeType == 3) related = related.parentNode;
return true;
}).create({attempt: Browser.Engine.gecko})()) related = false;
}
}

return $extend(this, {
event: event,
type: type,

page: page,
client: client,
rightClick: rightClick,

wheel: wheel,

relatedTarget: related,
target: target,

code: code,
key: key,

shift: event.shiftKey,
control: event.ctrlKey,
alt: event.altKey,
meta: event.metaKey
});
}

});

Event.Keys = new Hash({
'enter': 13,
'up': 38,
'down': 40,
'left': 37,
'right': 39,
'esc': 27,
'space': 32,
'backspace': 8,
'tab': 9,
'delete': 46
});

Event.implement({

stop: function(){
return this.stopPropagation().preventDefault();
},

stopPropagation: function(){
if (this.event.stopPropagation) this.event.stopPropagation();
else this.event.cancelBubble = true;
return this;
},

preventDefault: function(){
if (this.event.preventDefault) this.event.preventDefault();
else this.event.returnValue = false;
return this;
}

});


var Class = new Native({

name: 'Class',

initialize: function(properties){
properties = properties || {};
var klass = function(){
for (var key in this){
if ($type(this[key]) != 'function') this[key] = $unlink(this[key]);
}
this.constructor = klass;
if (Class.prototyping) return this;
var instance = (this.initialize) ? this.initialize.apply(this, arguments) : this;
if (this.options && this.options.initialize) this.options.initialize.call(this);
return instance;
};

for (var mutator in Class.Mutators){
if (!properties[mutator]) continue;
properties = Class.Mutators[mutator](properties, properties[mutator]);
delete properties[mutator];
}

$extend(klass, this);
klass.constructor = Class;
klass.prototype = properties;
return klass;
}

});

Class.Mutators = {

Extends: function(self, klass){
Class.prototyping = klass.prototype;
var subclass = new klass;
delete subclass.parent;
subclass = Class.inherit(subclass, self);
delete Class.prototyping;
return subclass;
},

Implements: function(self, klasses){
$splat(klasses).each(function(klass){
Class.prototying = klass;
$extend(self, ($type(klass) == 'class') ? new klass : klass);
delete Class.prototyping;
});
return self;
}

};

Class.extend({

inherit: function(object, properties){
var caller = arguments.callee.caller;
for (var key in properties){
var override = properties[key];
var previous = object[key];
var type = $type(override);
if (previous && type == 'function'){
if (override != previous){
if (caller){
override.<parent = previous;
object[key] = override;
} else {
Class.override(object, key, override);
}
}
} else if(type == 'object'){
object[key] = $merge(previous, override);
} else {
object[key] = override;
}
}

if (caller) object.parent = function(){
return arguments.callee.caller.<parent.apply(this, arguments);
};

return object;
},

override: function(object, name, method){
var parent = Class.prototyping;
if (parent && object[name] != parent[name]) parent = null;
var override = function(){
var previous = this.parent;
this.parent = parent ? parent[name] : object[name];
var value = method.apply(this, arguments);
this.parent = previous;
return value;
};
object[name] = override;
}

});

Class.implement({

implement: function(){
var proto = this.prototype;
$each(arguments, function(properties){
Class.inherit(proto, properties);
});
return this;
}

});