Added: javascript/base.html =================================================================== --- javascript/base.html (rev 0) +++ javascript/base.html 2007-03-18 09:59:40 UTC (rev 17) @@ -0,0 +1,41 @@ + + + + Base class for JavaScript + + + +

Base class for JavaScript

+ + + + Added: javascript/base.js =================================================================== --- javascript/base.js (rev 0) +++ javascript/base.js 2007-03-18 09:59:40 UTC (rev 17) @@ -0,0 +1,143 @@ +/* +** base.js -- base class for better OO inheritance in JavaScript +** Copyright (c) 2006 Dean Edwards +** Copyright (c) 2007 Ralf S. Engelschall +** Licensed under LGPL +** +** This is derived from Dean Edwards' "Base" implementation, version +** 1.0.2 as available on http://dean.edwards.name/base/Base.js. +** It was cleaned up, modified and extended with the feedback +** under http://dean.edwards.name/weblog/2006/03/base/ by Ralf S. +** Engelschall. +** +** $LastChangedDate: $ +** $LastChangedRevision: $ +*/ + +/* class constructor */ +var Base = function() { + if (arguments.length) { + if (this == window) { + // cast an object to this class + Base.prototype.extend.call(arguments[0], arguments.callee.prototype); + } else { + this.extend(arguments[0]); + } + } +}; + +/* class version */ +Base.version = "1.0.2+FIXES+RSE"; + +/* class method for extending */ +Base.extend = function(_instance, _static) { + var extend = Base.prototype.extend; + if (!_instance) + _instance = {}; + + /* build the prototype */ + Base._prototyping = true; + var _prototype = new this; + extend.call(_prototype, _instance); + var constructor = _prototype.constructor; + _prototype.constructor = this; + delete Base._prototyping; + + /* create the wrapper for the constructor function */ + var klass = function() { + if (!Base._prototyping) { + if (this instanceof klass) { + constructor.apply(this, arguments); + } else { + if (arguments.length) + return extend.call(arguments[0], _prototype); + } + } + }; + klass.prototype = _prototype; + + /* build the class interface */ + klass.extend = this.extend; + klass.implement = this.implement; + klass.toString = function() { + return String(constructor); + }; + extend.call(klass, _static); + + /* single instance */ + var object = constructor ? klass : _prototype; + + /* class initialisation */ + if (object.init instanceof Function) + object.init(); + + return object; +}; + +/* class method for multipe inheritance support */ +Base.implement = function() { + for (var i = 0; i < arguments.length; i++) { + _interface = arguments[i]; + if (_interface instanceof Function) + _interface = _interface.prototype; + this.prototype.extend(_interface); + } +}; + +/* instance method for extending */ +Base.prototype.extend = function(source, value) { + var extend = Base.prototype.extend; + if (arguments.length == 2) { + var ancestor = this[source]; + if ( (ancestor instanceof Function) + && (value instanceof Function) + && ancestor.valueOf() != value.valueOf() + && /\bbase\b/.test(value) ) { + var method = value; + value = function() { + var previous = this.base; + this.base = ancestor; + try { + var returnValue = method.apply(this, arguments); + } catch (ex) { + throw ex; + } + finally { + this.base = previous; + } + return returnValue; + }; + // point to the underlying method + value.valueOf = function() { + return method; + }; + value.toString = function() { + return String(method); + }; + } + return this[source] = value; + } else if (source) { + var _prototype = {toSource: null}; + // do the "toString" and other methods manually + var _protected = ["toString", "valueOf"]; + // if we are prototyping then include the constructor + if (Base._prototyping) + _protected[2] = "constructor"; + for (var i = 0; (name = _protected[i]); i++) { + if (source[name] != _prototype[name]) + extend.call(this, name, source[name]); + } + // copy each of the source object's properties to this object + for (var name in source) { + if (!_prototype[name]) + extend.call(this, name, source[name]); + } + } + return this; +}; + +/* instance method for invoking ancestor method */ +Base.prototype.base = function() { + /* call this method from any other method to invoke that method's ancestor */ +}; + Added: javascript/ruby.html =================================================================== --- javascript/ruby.html (rev 0) +++ javascript/ruby.html 2007-03-18 09:59:40 UTC (rev 17) @@ -0,0 +1,14 @@ + + + + Ruby for JavaScript + + + +

Ruby for JavaScript

+ + + Added: javascript/ruby.js =================================================================== --- javascript/ruby.js (rev 0) +++ javascript/ruby.js 2007-03-18 09:59:40 UTC (rev 17) @@ -0,0 +1,892 @@ +/* +** ruby.js -- Ruby-style object extensions for JavaScript +** Copyright (c) 2006 Florian Gross +** Copyright (c) 2007 Ralf S. Engelschall +** Licensed under GPL +** +** This is derived from the Florian Gross' "ruby.js", version 0.4.0 as +** of 2005-09-05 from http://flgr.0x42.net/ruby.js/, which implements +** some of Ruby's standard library functions in JavaScript. It was +** reformatted and cleaned up (especially semicolons additions to allow +** code packing) by Ralf S. Engelschall. +** +** $LastChangedDate: $ +** $LastChangedRevision: $ +*/ + +/* + * Notice: This library heavily touches the global JavaScript namespace + * and actually doesn't create a custom namespace at all. It is + * expected that this library will become obsolete when the next + * revision of the ECMA-262 standard is released as there is plans to + * offer more functionality as part of the official language's standard + * library. Note that this implementation does not exactly mimic all + * of Ruby's standard library -- ".each" in general always yields + * value,key where key is the index for sequential containers and the + * Range implementation is known to have bugs. + */ + +(function(){ + /* + * "Object" extensions + */ + + Object.prototype.clone = function (deepClone) { + var result = new this.constructor(); + for (var property in this) { + if (deepClone && typeof this[property] === 'object') + result[property] = this[property].clone(deepClone); + else + result[property] = this[property]; + } + return result; + }; + + Object.prototype.extend = function (other) { + if (!this.mixins) + this.mixins = []; + this.mixins.push(other); + for (var property in other) + if (!this.hasOwnProperty(property)) + this[property] = other[property]; + return; + }; + + Object.prototype.cmp = function (other) { + if (this < other) + return -1; + if (this > other) + return +1; + return 0; + }; + + Object.prototype.valuesAt = function () { + var obj = this; + return(arguments.toArray().map(function(index) { + return obj[index]; + })); + }; + + Object.prototype.toArray = function () { + if (!this.length) + throw "Can't convert"; + var result = []; + for (var i = 0; i < this.length; i++) + result.push(this[i]); + return result; + }; + + Object.prototype.hash = function () { + return this.toSource().hash(); + }; + + Object.prototype.instanceOf = function (klass) { + return (this.constructor == klass); + }; + + Object.prototype.isA = + Object.prototype.kindOf = function (klass) { + if (this.instanceOf(klass)) + return true; + if (this["mixins"] != undefined && this.mixins.includes(klass)) + return true; + return false; + }; + + Object.prototype.methods = function () { + var result = []; + for (var property in this) + if (typeof this[property] === "function") + result.push(property); + return result; + }; + + Object.prototype.respondTo = function (method) { + return this.methods().includes(method); + }; + + Object.prototype.send = function(method) { + var rest = arguments.toArray().last(-1); + if (!this.respondTo(method)) + throw "undefined method"; + return this[method].apply(this, rest); + }; + + Object.prototype.instanceEval = function (code) { + if (code.isA(Function)) + return code.apply(this); + return eval(code.toString()); + }; + + /* + * "Number" extensions + */ + + Number.prototype.times = function (block) { + for (var i = 0; i < this; i++) + block(i); + }; + + Number.prototype.upto = function (other, block) { + for (var i = this; i <= other; i++) + block(i); + }; + + Number.prototype.downto = function (other, block) { + for (var i = this; i >= other; i--) + block(i); + }; + + Number.prototype.towards = function (other, block) { + var step = this.cmp(other); + for (var i = this; i !== other - step; i -= step) + block(i); + }; + + Number.prototype.succ = function () { + return this + 1; + }; + + Number.prototype.pred = function () { + return this - 1; + }; + + Number.prototype.chr = function () { + return String.fromCharCode(this); + }; + + /* + * "Enumerable" addition + */ + + var Enumerable = new Object(); + + Enumerable.eachWindow = function (window, block) { + if (!window.isA(Range)) + window = range(0, window); + var elements = [], pushed = 0; + this.each(function (item, index) { + elements.push(item); + pushed += 1; + if (pushed % window.rend == 0) { + var start = [0, window.start - window.rend + pushed].max(); + var end = [0, window.rend + pushed].max(); + block(elements.fetch(xrange(start, end)), index); + } + }); + }; + + Enumerable.collect = + Enumerable.map = function (block) { + var result = []; + this.each(function (item, index) { + result.push(block(item, index)); + }); + return result; + }; + + Enumerable.toArray = Enumerable.entries = function () { + return this.map(function (item) { + return(item); + }); + }; + + Enumerable.inject = function (firstArg) { + var state, block, first = true; + if (arguments.length == 1) + block = firstArg; + else { + state = firstArg; + block = arguments[1]; + } + this.each(function (item, index) { + if (first && typeof state === "undefined") + state = item, first = false; + else + state = block(state, item, index); + }); + return state; + }; + + Enumerable.find = + Enumerable.detect = function (block) { + var result, done; + this.each(function (item, index) { + if (!done && block(item, index)) { + result = item; + done = true; + } + }); + return result; + }; + + Enumerable.findAll = + Enumerable.select = function (block) { + return this.inject([], function (result, item, index) { + return (block(item, index) ? result.add(item) : result); + }); + }; + + Enumerable.grep = function (obj) { + return this.findAll(function (item) { + return obj.test(item); + }); + }; + + Enumerable.reject = function (block) { + return this.select(function (item, index) { + return !block(item, index); + }); + }; + + Enumerable.compact = function () { + return this.select(function (item) { + return (typeof item !== "undefined") + }); + }; + + Enumerable.nitems = function () { + return this.compact().length; + }; + + Enumerable.sortBy = function (block) { + return this.map(function(item, index) { + return [ block(item, index), item ]; + }).sort(function (a, b) { + return a[0].cmp(b[0]); + }).map(function (item) { + return item[1]; + }); + }; + + Enumerable.all = function (block) { + return (this.findAll(block).length == this.length); + }; + + Enumerable.any = function (block) { + return (typeof this.find(block) !== "undefined"); + }; + + Enumerable.includes = function (obj) { + return this.any(function (item) { + return (item === obj); + }); + }; + + Enumerable.index = function (obj) { + var result; + this.find(function (item, index) { + if (obj == item) { + result = index; + return true; + } else + return false; + }); + return result; + }; + + Enumerable.uniq = function () { + return this.inject([], function (result, item) { + return (result.includes(item) ? result : result.add(item)); + }); + }; + + Enumerable.max = function (block) { + if (!block) + block = function (a, b) { return a.cmp(b); }; + return this.sort(block).last(); + }; + + Enumerable.min = function (block) { + if (!block) { + block = function (a, b) { return a.cmp(b); }; + return this.sort(block).first(); + }; + + Enumerable.partition = function (block) { + var positives = [], negatives = []; + this.each(function (item, index) { + if (block(item, index)) + positives.push(item); + else + negatives.push(item); + }); + return [positives, negatives]; + }; + + Enumerable.zip = function () { + var ary = arguments.toArray(); + ary.unshift(this); + return ary.transpose(); + }; + + Enumerable.flatten = function (depth) { + if (depth == undefined) + depth = -1; + if (!depth) + return this; + return this.inject([], function(result, item) { + var flatItem = item.respondTo("flatten") ? item.flatten(depth - 1) : [item]; + return result.merge(flatItem); + }); + }; + + /* + * "Array" extension + */ + + Array.fromObject = function (obj) { + if (!obj.length) + throw "Can't convert"; + var result = []; + for (var i = 0; i < obj.length; i++) + result.push(obj[i]); + return result; + }; + + Array.prototype.transpose = function () { + var result, length = -1; + this.each(function (item, index) { + if (length < 0) { /* first element */ + length = item.length; + result = Array.withLength(length, function () { + return new Array(this.length); + }); + } else if (length != item.length) { + throw "Element sizes differ"; + } + item.each(function (iitem, iindex) { + result[iindex][index] = iitem; + }); + }); + return result; + }; + + Array.withLength = function (length, fallback) { + var result = [null].mul(length); + result.fill(fallback); + return result; + }; + + Array.prototype.each = function (block) { + for (var index = 0; index < this.length; index++) { + var item = this[index]; + block(item, index); + } + return this; + }; + + Array.prototype.extend(Enumerable); + + Array.prototype.isEmpty = function () { + return (this.length == 0); + }; + + Array.prototype.at = + Array.prototype.fetch = function (index, length) { + if (index.isA(Range)) { + var end = index.rend + (index.rend < 0 ? this.length : 0); + index = index.start; + length = end - index + 1; + } + if (length == undefined) + length = 1; + if (index < 0) + index += this.length; + var result = this.slice(index, index + length); + return (result.length == 1 ? result[0] : result); + }; + + Array.prototype.first = function (amount) { + if (amount == undefined) + amount = 1; + return this.at(xrange(0, amount)); + }; + + Array.prototype.last = function (amount) { + if (amount == undefined) + amount = 1; + return this.at(range(-amount, -1)); + }; + + Array.prototype.store = function (index) { + var length = 1, obj; + arguments = arguments.toArray(); + arguments.shift(); + if (arguments.length == 2) + length = arguments.shift(); + obj = arguments.shift(); + if (!obj.isA(Array)) + obj = [obj]; + if (index.isA(Range)) { + var end = index.rend + (index.rend < 0 ? this.length : 0); + index = index.start; + length = end - index + 1; + } + if (index < 0) + index += this.length; + this.replace(this.slice(0, index).merge(obj).merge(this.slice(index + length))); + return this; + }; + + Array.prototype.insert = function (index) { + var values = arguments.toArray().last(-1); + if (index < 0) + index += this.length + 1; + return this.store(index, 0, values); + }; + + Array.prototype.update = function (other) { + var obj = this; + other.each(function(item) { obj.push(item) }); + return obj; + }; + + Array.prototype.merge = Array.prototype.concat; + + Array.prototype.add = function (item) { + return this.merge([item]); + }; + + Array.prototype.clear = function () { + var obj = this; + this.length.times(function (index) { + delete obj[index]; + }); + this.length = 0; + }; + + Array.prototype.replace = function (obj) { + this.clear(); + this.update(obj); + }; + + Array.prototype.mul = function (count) { + var result = []; + var obj = this; + count.times(function() { + result = result.merge(obj); + }); + return result; + }; + + Array.prototype.fill = function (value) { + var old_length = this.length; + var obj = this; + this.clear(); + var block; + if (typeof value !== "function") + block = function() { return(value) }; + else + block = value; + old_length.times(function (i) { + obj.push(block(i)); + }); + }; + + Array.prototype.removeAt = function (targetIndex) { + var result = this[targetIndex]; + var newArray = this.reject(function (item, index) { + return (index == targetIndex); + }); + this.replace(newArray); + return result; + }; + + Array.prototype.remove = function (obj) { + this.removeAt(this.index(obj)); + }; + + Array.prototype.removeIf = function (block) { + this.replace(this.reject(block)); + }; + + /* + * "Range" addition + */ + + function Range (start, end, excludeEnd) { + this.begin = this.start = start; + this.end = end; + this.excludeEnd = excludeEnd; + this.rend = excludeEnd ? end.pred() : end; + this.length = this.toArray().length; + }; + + function range (start, end) { + return (new Range(start, end)); + } + function xrange(start, end) { + return (new Range(start, end, true)); + } + + Range.prototype.toString = function () { + return ("" + this.start + (this.excludeEnd ? "..." : "..") + this.end); + } + + Range.prototype.each = function (block) { + var index = 0; + this.start.towards(this.rend, function (i) { + return block(i, index++); + }); + } + + Range.prototype.extend(Enumerable); + + Range.prototype.includes = function (item) { + return (this.start.cmp(item) == -1 && this.rend.cmp(item) == +1); + }; + + /* + * "Hash" addition + */ + + function Hash (defaultBlock) { + this.defaultBlock = defaultBlock; + this.keys = []; + this.values = []; + this.length = 0; + } + + Hash.fromArray = function (array) { + var result = new Hash(); + array.each(function (item) { + var key = item[0], value = item[1]; + result.store(key, value); + }); + return result; + }; + + Hash.prototype.at = + Hash.prototype.fetch = function (key, block) { + if (this.hasKey(key)) + return this["item_" + key.hash()]; + else if (block) + return block(key); + else + return defaultBlock(key); + }; + + Hash.prototype.store = function (key, value) { + this.keys.push(key); + this.values.push(value); + this.length++; + return (this["item_" + key.hash()] = value); + }; + + Hash.prototype.toA = function () { + return this.keys.zip(this.values); + }; + + Hash.prototype.isEmpty = function () { + return (this.length == 0); + }; + + Hash.prototype.has = + Hash.prototype.includes = + Hash.prototype.hasKey = function (key) { + return hasOwnProperty("item_" + key.hash()); + }; + + Hash.prototype.hasValue = function (value) { + return this.values.includes(value); + }; + + Hash.prototype.each = function (block) { + this.toA().each(function (pair) { + return block(pair[1], pair[0]); + }); + }; + + Hash.prototype.extend(Enumerable); + + Hash.prototype.merge = function (other) { + other.each(function (value, key) { + this.store(key, value); + }); + }; + + Hash.prototype.remove = function (key) { + var valueIndex = this.keys.index(key); + var value = this.values[valueIndex]; + this.keys.remove(key); + this.values.removeAt(valueIndex); + delete(this["item_" + key.hash()]); + this.length--; + return [key, value]; + }; + + Hash.prototype.removeIf = function (block) { + this.each(function (value, key) { + if (block(value, key)) + this.remove(key); + }); + }; + + Hash.prototype.shift = function () { + return this.remove(this.keys[0]); + }; + + Hash.prototype.clear = function () { + var obj = this; + this.length.times(function() {obj.shift()}); + }; + + Hash.prototype.replace = function (obj) { + this.clear(); + this.merge(obj); + }; + + Hash.prototype.invert = function () { + return Hash.fromArray(this.map(function (value, key) { + return [value, key]; + })); + }; + + Hash.prototype.rehash = function () { + var result = new Hash(this.defaultBlock); + this.each(function (value, key) { + result.store(key, value); + }); + this.replace(result); + }; + + /* + * "MatchData" addition + */ + + function MatchData (matches, str, pos) { + this.matches = matches, this.string = str; + this.begin = this.position = pos; + this.match = matches[0]; + this.captures = matches.slice(1); + this.end = pos + this.match.length; + this.length = matches.length; + this.preMatch = str.substr(0, pos); + this.postMatch = str.substr(this.end); + } + + MatchData.prototype.toString = function () { + return this.match; + }; + + MatchData.prototype.at = function (index) { + return this.matches.at(index); + }; + + MatchData.prototype.toArray = function () { + return this.matches; + }; + + /* + * "RegExp" extension + */ + + RegExp.prototype.match = function (str) { + var matches = this.exec(str); + if (matches) { + var pos = str.search(this); + return (new MatchData(matches, str, pos)); + } + }; + + /* + * "String" extension + */ + + String.prototype.clone = function () { + return (new String(this)); + }; + + String.prototype.each = function (block) { + this.split("\n").each(block); + }; + + String.prototype.extend(Enumerable); + + String.prototype.toArray = function () { + return this.split("\n"); + }; + + String.prototype.towards = function (other, block) { + var item = this; + while (item.cmp(other) <= 0) { + block(item); + item = item.succ(); + } + }; + + String.prototype.hash = function () { + var result = 0; + this.split("").each(function (item) { + result += item.charCodeAt(0); + result += (result << 10); + result ^= (result >> 6); + }) + result += (result << 3); + result ^= (result >> 11); + result += (result << 15); + return result; + } + + String.prototype.chars = function () { + return this.split(""); + }; + + String.prototype.at = + String.prototype.fetch = function (index, length) { + if (index.isA(Range)) { + var end = index.rend + (index.rend < 0 ? this.length : 0); + index = index.start; + length = end - index + 1; + } + if (length == undefined) + length = 1; + if (index < 0) + index += this.length; + return this.substr(index, length); + }; + + String.prototype.store = + String.prototype.change = function (index) { + var length = 1, obj; + arguments = arguments.toArray(); + arguments.shift(); + if (arguments.length == 2) + length = arguments.shift(); + obj = arguments.shift(); + if (index.isA(Range)) { + var end = index.rend + (index.rend < 0 ? this.length : 0); + index = index.start; + length = end - index + 1; + } + if (index < 0) + index += this.length; + return (this.substr(0, index) + obj + this.substr(index + length)); + }; + + String.prototype.reverse = function () { + return this.split("").reverse().join(""); + }; + + String.prototype.scan = function (pattern) { + var str = this, result = [], oldPos = -1, match, offset = 0; + while (match = pattern.match(str)) { + if (match.end == match.begin) + throw "Can't have null length matches with scan()"; + var newMatch = new MatchData(match.matches, match.string, match.position + offset); + result.push(newMatch); + str = match.postMatch; + offset += match.toString().length; + } + return result; + }; + + String.prototype.sub = function (what, by, global) { + var block = (typeof by === "function" ? by : function() { return(by) }); + var matches = this.scan(what), result = this, offset = 0; + if (!global && !by.global) + matches = matches.slice(0, 1); + matches.each(function (match) { + var replacement = block(match); + offset += replacement.length - match.toString().length; + result = result.change(match.begin + offset, match.toString().length, replacement); + }); + return result; + }; + + String.prototype.gsub = function (what, by) { + return this.sub(what, by, true); + }; + + String.prototype.tr = function (from, to) { + var map = Hash.fromArray(from.chars().zip(to.chars())); + return (this.chars().map(function (chr) { + return (map.includes(chr) ? map.fetch(chr) : chr); + }).join("")); + }; + + String.prototype.mul = function (other) { + var result = "", str = this; + other.times(function() { + result += str; + }); + return result; + }; + + String.prototype.isUpcase = function () { + return (this == this.upcase()); + }; + + String.prototype.isDowncase = function () { + return (this == this.downcase()); + } + + String.prototype.isCapitalized = function () { + return ( this.fetch(0).isUpcase() + && this.fetch(range(1, -1)).isDowncase()); + }; + + String.prototype.upcase = String.prototype.toUpperCase; + + String.prototype.downcase = String.prototype.toLowerCase; + + String.prototype.capitalize = function () { + return (this.fetch(0).upcase() + this.fetch(range(1, -1)).downcase()); + }; + + String.prototype.swapcase = function () { + return (this.chars().map(function (chr) { + if (chr.isUpcase()) + return chr.downcase(); + if (chr.isDowncase()) + return chr.upcase(); + return chr; + }).join("")); + }; + + String.prototype.ord = function () { + return this.charCodeAt(0); + }; + + String.prototype.isEmpty = function () { + return (this.length == 0); + } + + String.prototype.succ = function () { + if (this.isEmpty()) + return this; + /* numerics */ + if (/^\d+$/.test(this)) + return ((Number(this) + 1).toString()); + /* just one character */ + if (this.length == 1) { + /* letters */ + if (/[A-Za-z]/.test(this)) { + var lastLetter = this.isUpcase() ? 'Z' : 'z'; + var firstLetter = this.isUpcase() ? 'A' : 'a'; + return ((this == lastLetter) ? firstLetter.mul(2) : (this.ord() + 1).chr()); + } + return (this == (-1).chr() ? 0.0.chr().mul(2) : (this.ord() + 1).chr()); + } + /* multiple characters */ + var result = this; + for (var index = this.length; --index >= 0; ) { + var chr = this.at(index); + if (chr.succ().length == 1 || index == 0) + return result.change(index, chr.succ()); + result = result.change(index, chr.succ().at(-1)); + } + return result; + }; + + String.prototype.ljust = function (length, fill) { + if (!fill) + fill = " "; + if (fill.length > 1) + throw "TODO: Make fills with length > 1 work."; + return (this + fill.mul(length / fill.length - this.length)); + }; + +})(); +