Skip to content

Instantly share code, notes, and snippets.

@cpojer
Created July 6, 2010 23:42
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 cpojer/466097 to your computer and use it in GitHub Desktop.
Save cpojer/466097 to your computer and use it in GitHub Desktop.
CAFE
0010
0007
7100
8AFF
CA15
11A1
C011
91FF
0000
1234
2341
3412
4123
1111
1111
1111
1111
1111
0001
0001
0000
/*
JavaScript Implementation of the TOY CPU which we used in the
lecture RECHNERNETZE UND -ORGANISATION (Computer Networks and Organization)
More Information: http://www.cs.princeton.edu/introcs/52toy/
Instruction Set: http://www.cs.princeton.edu/introcs/53isa/
This implementation comes with a built-in boot loader.
It expects files in the following format:
line 1: CAFE
line 2: Memory Address on where to store the program
line 3: Amount of Instructions
line 4 .. n: Instructions, terminated by 0000
line n+1 .. EOF: stdin data
Run:
node Toy.js test1.in // Expected output: 0001
Notes:
This hasn't been thoroughly tested and was done just for the fun of it.
SHR likely does not work correctly.
(c) [Christoph Pojer](http://cpojer.net)
*/
require('./MooTools').apply(GLOBAL);
var sys = require('sys'),
puts = sys.puts,
print = sys.print;
String.implement({
repeat: function(times){
var s = '';
while (times--) s += this;
return s;
},
pad: function(length, left){
if (!left) left = '0';
if (this.length < length) return (left).repeat(length - this.length) + this;
return this;
},
toAddress: function(){
return parseInt(this, 16).toAddress();
},
toWord: function(){
return this.pad(4);
}
});
Number.implement({
toAddress: function(){
return (this & 0xFF).toString(16).toUpperCase().pad(2);
},
toWord: function(){
return (this & 0xFFFF).toString(16).toUpperCase().pad(4);
}
});
var Register = new Class({
value: 0,
read: function(){
return this.value.toWord();
},
write: function(value){
this.value = value.toWord();
},
raw: function(){
return parseInt(this.read(), 16) & 0xFFFF;
}
});
var Register0 = new Class({
Extends: Register,
write: function(){}
});
var Memory = Register;
var IO = new Class({
Extends: Memory,
initialize: function(stdin, stdout){
this.stdin = stdin;
this.stdout = stdout;
},
read: function(){
return (this.stdin.shift() || '0').toWord();
},
write: function(value){
this.stdout.push(value.toWord());
}
});
var Toy = new Class({
pc: 0x10,
registers: {},
memory: {},
stdout: [],
initialize: function(stdin){
var i;
this.registers[0] = new Register0;
for (i = 1; i <= 15; i++)
this.registers[i.toString(16).toUpperCase()] = new Register;
for (i = 0; i <= 254; i++)
this.memory[i.toAddress()] = new Memory;
this.memory.FF = new IO(stdin, this.stdout);
// Bootloader
this.memory['00'].write('CAFE'); // A DW 0xCAFE
this.memory['10'].write('FFE0'); // jl RF, start
this.memory['E0'].write('7101'); // start lda R1, 0x1 // load constant 1
this.memory['E1'].write('8200'); // ld R2, A // load CAFE
this.memory['E2'].write('83FF'); // ld R3, 0xFF // read
this.memory['E3'].write('2332'); // sub R3, R3, R2 // check for CAFE
this.memory['E4'].write('C3E6'); // bz R3, go
this.memory['E5'].write('0000'); // hlt // halt, no exe-file
this.memory['E6'].write('84FF'); // go ld R4, 0xFF // start addr in R4
this.memory['E7'].write('1540'); // add R5, R4, R0 // temp copy of start addr
this.memory['E8'].write('86FF'); // ld R6, 0xFF // amount of words
this.memory['E9'].write('C6EF'); // loop bz R6, exit // goto exit if finished
this.memory['EA'].write('2661'); // sub R6, R6, R1 // decrement R6
this.memory['EB'].write('87FF'); // ld R7, 0xFF // read a word
this.memory['EC'].write('B705'); // st R7, R5
this.memory['ED'].write('1551'); // add R5, R5, R1 // increment code address
this.memory['EE'].write('FFE9'); // jl RF, loop
this.memory['EF'].write('E400'); // exit jr R4 // jump to program's start addr
this.memory['F0'].write('0000'); // hlt // this line should not be reached
},
execute: function(){
while (true){
var pc = this.getPC(),
instruction = this.memory[pc].read();
this.pc++;
puts('PC: 0x' + pc + '\tInst: 0x' + instruction);
if (!instruction.test(/^[0-9A-F]{4}$/)){
puts(' Abort! Instruction mismatch');
return this;
}
var opcode = instruction.charAt(0),
d = this.registers[instruction.charAt(1)],
s = this.registers[instruction.charAt(2)],
t = this.registers[instruction.charAt(3)],
addr = parseInt(instruction.charAt(2) + instruction.charAt(3), 16);
switch (opcode){
case '0': return this;
case '1': d.write(s.raw() + t.raw()); break;
case '2': d.write(s.raw() - t.raw()); break;
case '3': d.write(s.raw() & t.raw()); break;
case '4': d.write(s.raw() ^ t.raw()); break;
case '5': d.write(s.raw() << t.raw()); break;
case '6': d.write(s.raw() >> t.raw()); break;
case '7': d.write(addr); break;
case '8': d.write(this.memory[addr.toAddress()].read()); break;
case '9': this.memory[addr.toAddress()].write(d.read()); break;
case 'A': d.write(this.memory[t.read().toAddress()].read()); break;
case 'B': this.memory[t.read().toAddress()].write(d.read()); break;
case 'C': if (!d.raw()) this.pc = addr; break;
case 'D': if (d.raw()) this.pc = addr; break;
case 'E': this.pc = (d.raw() & 0xFF); break;
case 'F': d.write(this.pc); this.pc = addr; break;
}
}
return this;
},
getPC: function(){
return (this.pc & 0xFF).toAddress();
},
dump: function(){
var key;
puts('------------');
puts('## Registers');
for (key in this.registers)
puts('0x' + key + ': ' + this.registers[key].read());
puts('------------');
puts('## Memory Dump');
for (var i = 0; i <= 255; i++){
key = i.toAddress();
print('0x' + key + ': ' + this.memory[key].read() + '\t');
}
print('\n');
puts('------------');
puts('## Stdout');
this.stdout.forEach(function(value){
puts(value);
});
return this;
}
});
if (!process.argv[2]){
puts('Please specify a test to run.');
return;
}
var file = process.argv[2];
require('fs').readFile(file, function(err, data){
if (err) return;
var stdin = data.toString().split('\n');
puts('## Stdin (' + file + ')');
stdin.forEach(function(value){
puts(value);
});
puts('------------');
(new Toy(stdin)).execute().dump();
});
/*
---
name: Prefix
description: Loads MooTools as a CommonJS Module.
license: MIT-style license.
copyright: Copyright (c) 2010 [Christoph Pojer](http://cpojer.net/).
authors: Christoph Pojer
provides: [Prefix]
...
*/
var GLOBAL_ITEMS = function(){
var items = [];
for (var key in this)
items.push(key);
return items;
}();
/*
---
name: Core
description: The heart of MooTools.
license: MIT-style license.
copyright: Copyright (c) 2006-2010 [Valerio Proietti](http://mad4milk.net/).
authors: The MooTools production team (http://mootools.net/developers/)
inspiration:
- Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)
- Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php)
provides: [Core, MooTools, Type, typeOf, instanceOf]
...
*/
(function(){
this.MooTools = {
version: '1.3dev',
build: 'd29f41a0e1d9fcf80cdd6894317d393e2885bab5'
};
// typeOf, instanceOf
var typeOf = this.typeOf = function(item){
if (item == null) return 'null';
if (item.$family) return item.$family();
if (item.nodeName){
if (item.nodeType == 1) return 'element';
if (item.nodeType == 3) return (/\S/).test(item.nodeValue) ? 'textnode' : 'whitespace';
} else if (typeof item.length == 'number'){
if (item.callee) return 'arguments';
if ('item' in item) return 'collection';
}
return typeof item;
};
var instanceOf = this.instanceOf = function(item, object){
if (item == null) return false;
var constructor = item.$constructor || item.constructor;
while (constructor){
if (constructor === object) return true;
constructor = constructor.parent;
}
return item instanceof object;
};
// Function overloading
var Function = this.Function;
var enumerables = true;
for (var i in {toString: 1}) enumerables = null;
if (enumerables) enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'constructor'];
Function.prototype.overloadSetter = function(usePlural){
var self = this;
return function(a, b){
if (a == null) return this;
if (usePlural || typeof a != 'string'){
for (var k in a) self.call(this, k, a[k]);
if (enumerables) for (var i = enumerables.length; i--;){
k = enumerables[i];
if (a.hasOwnProperty(k)) self.call(this, k, a[k]);
}
} else {
self.call(this, a, b);
}
return this;
};
};
Function.prototype.overloadGetter = function(usePlural){
var self = this;
return function(a){
var args, result;
if (usePlural || typeof a != 'string') args = a;
else if (arguments.length > 1) args = arguments;
if (args){
result = {};
for (var i = 0; i < args.length; i++) result[args[i]] = self.call(this, args[i]);
} else {
result = self.call(this, a);
}
return result;
};
};
Function.prototype.extend = function(key, value){
this[key] = value;
}.overloadSetter();
Function.prototype.implement = function(key, value){
this.prototype[key] = value;
}.overloadSetter();
// From
Function.from = function(item){
return (typeOf(item) == 'function') ? item : function(){
return item;
};
};
Array.from = function(item){
if (item == null) return [];
return (Type.isEnumerable(item) && typeof item != 'string') ? (typeOf(item) == 'array') ? item : Array.prototype.slice.call(item) : [item];
};
Number.from = function(item){
var number = parseFloat(item);
return isFinite(number) ? number : null;
};
String.from = function(item){
return item + '';
};
// hide, protect
Function.implement({
hide: function(){
this.$hidden = true;
return this;
},
protect: function(){
this.$protected = true;
return this;
}
});
// Type
var Type = this.Type = function(name, object){
if (name){
var lower = name.toLowerCase();
var typeCheck = function(item){
return (typeOf(item) == lower);
};
Type['is' + name] = typeCheck;
if (object != null){
object.prototype.$family = (function(){
return lower;
}).hide();
}
}
if (object == null) return null;
object.extend(this);
object.$constructor = Type;
object.prototype.$constructor = object;
return object;
};
var toString = Object.prototype.toString;
Type.isEnumerable = function(item){
return (item != null && typeof item.length == 'number' && toString.call(item) != '[object Function]' );
};
var hooks = {};
var hooksOf = function(object){
var type = typeOf(object.prototype);
return hooks[type] || (hooks[type] = []);
};
var implement = function(name, method){
if (method && method.$hidden) return this;
var hooks = hooksOf(this);
for (var i = 0; i < hooks.length; i++){
var hook = hooks[i];
if (typeOf(hook) == 'type') implement.call(hook, name, method);
else hook.call(this, name, method);
}
var previous = this.prototype[name];
if (previous == null || !previous.$protected) this.prototype[name] = method;
if (this[name] == null && typeOf(method) == 'function') extend.call(this, name, function(item){
return method.apply(item, Array.prototype.slice.call(arguments, 1));
});
return this;
};
var extend = function(name, method){
if (method && method.$hidden) return this;
var previous = this[name];
if (previous == null || !previous.$protected) this[name] = method;
return this;
};
Type.implement({
implement: implement.overloadSetter(),
extend: extend.overloadSetter(),
alias: function(name, existing){
implement.call(this, name, this.prototype[existing]);
}.overloadSetter(),
mirror: function(hook){
hooksOf(this).push(hook);
return this;
}
});
new Type('Type', Type);
// Default Types
var force = function(name, type, methods){
var object = new Type(name, type),
prototype = object.prototype;
for (var i = 0, l = methods.length; i < l; i++){
var key = methods[i],
generic = object[key],
proto = prototype[key];
if (generic) generic.protect();
if (proto){
delete prototype[key];
prototype[key] = proto.protect();
}
}
object.implement(object.prototype);
return force;
};
force('String', String, [
'charAt', 'charCodeAt', 'concat', 'indexOf', 'lastIndexOf', 'match', 'quote', 'replace', 'search',
'slice', 'split', 'substr', 'substring', 'toLowerCase', 'toUpperCase'
])('Array', Array, [
'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice',
'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight'
])('Number', Number, [
'toExponential', 'toFixed', 'toLocaleString', 'toPrecision'
])('Function', Function, [
'apply', 'call'
])('RegExp', RegExp, ['exec', 'test'])('Date', Date, ['now']);
Date.extend('now', function(){
return +(new Date);
});
new Type('Boolean', Boolean);
// fixes NaN returning as Number
Number.prototype.$family = function(){
return isFinite(this) ? 'number' : 'null';
}.hide();
// Number.random
Number.extend('random', function(min, max){
return Math.floor(Math.random() * (max - min + 1) + min);
});
// forEach, each
Object.extend('forEach', function(object, fn, bind){
for (var key in object){
if (object.hasOwnProperty(key)) fn.call(bind, object[key], key, object);
}
});
Object.each = Object.forEach;
Array.implement({
forEach: function(fn, bind){
for (var i = 0, l = this.length; i < l; i++){
if (i in this) fn.call(bind, this[i], i, this);
}
},
each: function(fn, bind){
Array.forEach(this, fn, bind);
return this;
}
});
// Array & Object cloning, Object merging and appending
var cloneOf = function(item){
switch (typeOf(item)){
case 'array': return item.clone();
case 'object': return Object.clone(item);
default: return item;
}
};
Array.implement('clone', function(){
var i = this.length, clone = new Array(i);
while (i--) clone[i] = cloneOf(this[i]);
return clone;
});
var mergeOne = function(source, key, current){
switch (typeOf(current)){
case 'object':
if (typeOf(source[key]) == 'object') Object.merge(source[key], current);
else source[key] = Object.clone(current);
break;
case 'array': source[key] = current.clone(); break;
default: source[key] = current;
}
return source;
};
Object.extend({
merge: function(source, k, v){
if (typeOf(k) == 'string') return mergeOne(source, k, v);
for (var i = 1, l = arguments.length; i < l; i++){
var object = arguments[i];
for (var key in object) mergeOne(source, key, object[key]);
}
return source;
},
clone: function(object){
var clone = {};
for (var key in object) clone[key] = cloneOf(object[key]);
return clone;
},
append: function(original){
for (var i = 1, l = arguments.length; i < l; i++){
var extended = arguments[i] || {};
for (var key in extended) original[key] = extended[key];
}
return original;
}
});
// Object-less types
['Object', 'WhiteSpace', 'TextNode', 'Collection', 'Arguments'].each(function(name){
new Type(name);
});
})();
/*
---
name: Array
description: Contains Array Prototypes like each, contains, and erase.
license: MIT-style license.
requires: Type
provides: Array
...
*/
Array.implement({
invoke: function(methodName){
var args = Array.slice(arguments, 1);
return this.map(function(item){
return item[methodName].apply(item, args);
});
},
every: function(fn, bind){
for (var i = 0, l = this.length; i < l; i++){
if ((i in this) && !fn.call(bind, this[i], i, this)) return false;
}
return true;
},
filter: function(fn, bind){
var results = [];
for (var i = 0, l = this.length; i < l; i++){
if ((i in this) && fn.call(bind, this[i], i, this)) results.push(this[i]);
}
return results;
},
clean: function(){
return this.filter(function(item){
return item != null;
});
},
indexOf: function(item, from){
var len = this.length;
for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++){
if (this[i] === item) return i;
}
return -1;
},
map: function(fn, bind){
var results = [];
for (var i = 0, l = this.length; i < l; i++){
if (i in this) results[i] = fn.call(bind, this[i], i, this);
}
return results;
},
some: function(fn, bind){
for (var i = 0, l = this.length; i < l; i++){
if ((i in this) && fn.call(bind, this[i], i, this)) return true;
}
return false;
},
associate: function(keys){
var obj = {}, length = Math.min(this.length, keys.length);
for (var i = 0; i < length; i++) obj[keys[i]] = this[i];
return obj;
},
link: function(object){
var result = {};
for (var i = 0, l = this.length; i < l; i++){
for (var key in object){
if (object[key](this[i])){
result[key] = this[i];
delete object[key];
break;
}
}
}
return result;
},
contains: function(item, from){
return this.indexOf(item, from) != -1;
},
append: function(array){
this.push.apply(this, array);
return this;
},
getLast: function(){
return (this.length) ? this[this.length - 1] : null;
},
getRandom: function(){
return (this.length) ? this[Number.random(0, this.length - 1)] : null;
},
include: function(item){
if (!this.contains(item)) this.push(item);
return this;
},
combine: function(array){
for (var i = 0, l = array.length; i < l; i++) this.include(array[i]);
return this;
},
erase: function(item){
for (var i = this.length; i--;){
if (this[i] === item) this.splice(i, 1);
}
return this;
},
empty: function(){
this.length = 0;
return this;
},
flatten: function(){
var array = [];
for (var i = 0, l = this.length; i < l; i++){
var type = typeOf(this[i]);
if (type == 'null') continue;
array = array.concat((type == 'array' || type == 'collection' || type == 'arguments' || instanceOf(this[i], Array)) ? Array.flatten(this[i]) : this[i]);
}
return array;
},
pick: function(){
for (var i = 0, l = this.length; i < l; i++){
if (this[i] != null) return this[i];
}
return null;
},
hexToRgb: function(array){
if (this.length != 3) return null;
var rgb = this.map(function(value){
if (value.length == 1) value += value;
return value.toInt(16);
});
return (array) ? rgb : 'rgb(' + rgb + ')';
},
rgbToHex: function(array){
if (this.length < 3) return null;
if (this.length == 4 && this[3] == 0 && !array) return 'transparent';
var hex = [];
for (var i = 0; i < 3; i++){
var bit = (this[i] - 0).toString(16);
hex.push((bit.length == 1) ? '0' + bit : bit);
}
return (array) ? hex : '#' + hex.join('');
}
});
/*
---
name: String
description: Contains String Prototypes like camelCase, capitalize, test, and toInt.
license: MIT-style license.
requires: Type
provides: String
...
*/
String.implement({
test: function(regex, params){
return ((typeOf(regex) == 'regexp') ? regex : new RegExp('' + regex, params)).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;
},
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] : '';
});
}
});
/*
---
name: Function
description: Contains Function Prototypes like create, bind, pass, and delay.
license: MIT-style license.
requires: Type
provides: Function
...
*/
Function.extend({
attempt: function(){
for (var i = 0, l = arguments.length; i < l; i++){
try {
return arguments[i]();
} catch (e){}
}
return null;
}
});
Function.implement({
attempt: function(args, bind){
try {
return this.apply(bind, Array.from(args));
} catch (e){
return null;
}
},
bind: function(bind, args){
var self = this;
if (args != null) args = Array.from(args);
return function(){
return self.apply(bind, args || arguments);
};
},
delay: function(delay, bind, args){
return setTimeout(this.bind(bind, args || []), delay);
},
pass: function(args, bind){
return this.bind(bind, args);
},
periodical: function(periodical, bind, args){
return setInterval(this.bind(bind, args || []), periodical);
},
run: function(args, bind){
return this.apply(bind, Array.from(args));
}
});
/*
---
name: Number
description: Contains Number Prototypes like limit, round, times, and ceil.
license: MIT-style license.
requires: Type
provides: Number
...
*/
Number.implement({
limit: function(min, max){
return Math.min(max, Math.max(min, this));
},
round: function(precision){
precision = Math.pow(10, precision || 0).toFixed(precision < 0 ? -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('each', 'times');
(function(math){
var methods = {};
math.each(function(name){
if (!Number[name]) methods[name] = function(){
return Math[name].apply(null, [this].concat(Array.from(arguments)));
};
});
Number.implement(methods);
})(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']);
/*
---
name: Class
description: Contains the Class Function for easily creating, extending, and implementing reusable Classes.
license: MIT-style license.
requires: [Array, String, Function, Number]
provides: Class
...
*/
(function(){
var Class = this.Class = new Type('Class', function(params){
if (instanceOf(params, Function)) params = {'initialize': params};
var newClass = function(){
reset(this);
if (newClass.$prototyping) return this;
this.$caller = null;
var value = (this.initialize) ? this.initialize.apply(this, arguments) : this;
this.$caller = this.caller = null;
return value;
}.extend(this);
newClass.implement(params);
newClass.$constructor = Class;
newClass.prototype.$constructor = newClass;
newClass.prototype.parent = parent;
return newClass;
});
var parent = function(){
if (!this.$caller) throw new Error('The method "parent" cannot be called.');
var name = this.$caller.$name, parent = this.$caller.$owner.parent;
var previous = (parent) ? parent.prototype[name] : null;
if (!previous) throw new Error('The method "' + name + '" has no parent.');
return previous.apply(this, arguments);
};
var reset = function(object){
for (var key in object){
var value = object[key];
switch (typeOf(value)){
case 'object':
var F = function(){};
F.prototype = value;
var instance = new F;
object[key] = reset(instance);
break;
case 'array': object[key] = value.clone(); break;
}
}
return object;
};
var wrap = function(self, key, method){
if (method.$origin) method = method.$origin;
var wrapper = function(){
if (method.$protected && this.$caller == null) throw new Error('The method "' + key + '" cannot be called.');
var caller = this.caller, current = this.$caller;
this.caller = current; this.$caller = wrapper;
var result = method.apply(this, arguments);
this.$caller = current; this.caller = caller;
return result;
}.extend({$owner: self, $origin: method, $name: key});
return wrapper;
};
var implement = function(key, value, retain){
if (Class.Mutators.hasOwnProperty(key)){
value = Class.Mutators[key].call(this, value);
if (value == null) return this;
}
if (typeOf(value) == 'function'){
if (value.$hidden) return this;
this.prototype[key] = (retain) ? value : wrap(this, key, value);
} else {
Object.merge(this.prototype, key, value);
}
return this;
};
var getInstance = function(klass){
klass.$prototyping = true;
var proto = new klass;
delete klass.$prototyping;
return proto;
};
Class.implement('implement', implement.overloadSetter());
Class.Mutators = {
Extends: function(parent){
this.parent = parent;
this.prototype = getInstance(parent);
},
Implements: function(items){
Array.from(items).each(function(item){
var instance = new item;
for (var key in instance) implement.call(this, key, instance[key], true);
}, this);
}
};
})();
/*
---
name: Class.Extras
description: Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks.
license: MIT-style license.
requires: Class
provides: [Class.Extras, Chain, Events, Options]
...
*/
(function(){
this.Chain = new Class({
$chain: [],
chain: function(){
this.$chain.append(Array.flatten(arguments));
return this;
},
callChain: function(){
return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false;
},
clearChain: function(){
this.$chain.empty();
return this;
}
});
var Events = this.Events = new Class({
$events: {},
addEvent: function(type, fn, internal){
type = Events.removeOn(type);
if (fn){
this.$events[type] = this.$events[type] || [];
this.$events[type].include(fn);
if (internal) fn.internal = true;
}
return this;
},
addEvents: function(events){
for (var type in events) this.addEvent(type, events[type]);
return this;
},
fireEvent: function(type, args, delay){
type = Events.removeOn(type);
if (!this.$events || !this.$events[type]) return this;
this.$events[type].each(function(fn){
(delay) ? fn.delay(delay, this, args) : fn.run(args, this);
}, this);
return this;
},
removeEvent: function(type, fn){
type = Events.removeOn(type);
if (!this.$events[type]) return this;
if (!fn.internal) this.$events[type].erase(fn);
return this;
},
removeEvents: function(events){
var type;
if (typeOf(events) == 'object'){
for (type in events) this.removeEvent(type, events[type]);
return this;
}
if (events) events = Events.removeOn(events);
for (type in this.$events){
if (events && events != type) continue;
var fns = this.$events[type];
for (var i = fns.length; i--;) this.removeEvent(type, fns[i]);
}
return this;
}
});
Events.removeOn = function(string){
return string.replace(/^on([A-Z])/, function(full, first){
return first.toLowerCase();
});
};
this.Options = new Class({
setOptions: function(){
var options = this.options = Object.merge.run([{}, this.options].append(arguments));
if (!this.addEvent) return this;
for (var option in options){
if (typeOf(options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue;
this.addEvent(option, options[option]);
delete options[option];
}
return this;
}
});
})();
/*
---
name: Object
description: Object generic methods
license: MIT-style license.
requires: Type
provides: [Object, Hash]
...
*/
Object.extend({
subset: function(object, keys){
var results = {};
for (var i = 0, l = keys.length; i < l; i++){
var k = keys[i];
results[k] = object[k];
}
return results;
},
map: function(object, fn, bind){
var results = {};
for (var key in object){
if (object.hasOwnProperty(key)) results[key] = fn.call(bind, object[key], key, object);
}
return results;
},
filter: function(object, fn, bind){
var results = {};
Object.each(object, function(value, key){
if (fn.call(bind, value, key, object)) results[key] = value;
});
return results;
},
every: function(object, fn, bind){
for (var key in object){
if (object.hasOwnProperty(key) && !fn.call(bind, object[key], key)) return false;
}
return true;
},
some: function(object, fn, bind){
for (var key in object){
if (object.hasOwnProperty(key) && fn.call(bind, object[key], key)) return true;
}
return false;
},
keys: function(object){
var keys = [];
for (var key in object){
if (object.hasOwnProperty(key)) keys.push(key);
}
return keys;
},
values: function(object){
var values = [];
for (var key in object){
if (object.hasOwnProperty(key)) values.push(object[key]);
}
return values;
},
getLength: function(object){
return Object.keys(object).length;
},
keyOf: function(object, value){
for (var key in object){
if (object.hasOwnProperty(key) && object[key] === value) return key;
}
return null;
},
contains: function(object, value){
return Object.keyOf(object, value) != null;
},
toQueryString: function(object, base){
var queryString = [];
Object.each(object, function(value, key){
if (base) key = base + '[' + key + ']';
var result;
switch (typeOf(value)){
case 'object': result = Object.toQueryString(value, key); break;
case 'array':
var qs = {};
value.each(function(val, i){
qs[i] = val;
});
result = Object.toQueryString(qs, key);
break;
default: result = key + '=' + encodeURIComponent(value);
}
if (value != undefined) queryString.push(result);
});
return queryString.join('&');
}
});
/*
---
name: Loader
description: Loads MooTools as a CommonJS Module.
license: MIT-style license.
copyright: Copyright (c) 2010 [Christoph Pojer](http://cpojer.net/).
authors: Christoph Pojer
requires: [Core/Core, Core/Object]
provides: [Loader]
...
*/
if (typeof exports != 'undefined') (function(){
for (var key in this) if (!GLOBAL_ITEMS.contains(key)){
exports[key] = this[key];
}
exports.apply = function(object){
Object.append(object, exports);
};
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment