/
funcs.js
137 lines (125 loc) · 4.1 KB
/
funcs.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
var types = require('org.startpad.types');
exports.extend({
'VERSION': '0.2.1',
'methods': methods,
'bind': bind,
'decorate': decorate,
'shadow': shadow,
'subclass': subclass,
'numericVersion': numericVersion,
'monkeyPatch': monkeyPatch,
'patch': patch
});
// Convert 3-part version number to comparable integer.
// Note: No part should be > 99.
function numericVersion(s) {
if (!s) {
return 0;
}
var a = s.split('.');
return 10000 * parseInt(a[0]) + 100 * parseInt(a[1]) + parseInt(a[2]);
}
// Monkey patch additional methods to constructor prototype, but only
// if patch version is newer than current patch version.
function monkeyPatch(ctor, by, version, patchMethods) {
if (ctor._patches) {
var patchVersion = ctor._patches[by];
if (numericVersion(patchVersion) >= numericVersion(version)) {
return;
}
}
ctor._patches = ctor._patches || {};
ctor._patches[by] = version;
methods(ctor, patchMethods);
}
function patch() {
monkeyPatch(Function, 'org.startpad.funcs', exports.VERSION, {
'methods': function (obj) { methods(this, obj); },
'curry': function () {
var args = [this, undefined].concat(types.copyArray(arguments));
return bind.apply(undefined, args);
},
'curryThis': function (self) {
var args = types.copyArray(arguments);
args.unshift(this);
return bind.apply(undefined, args);
},
'decorate': function (decorator) {
return decorate(this, decorator);
},
'subclass': function(parent, extraMethods) {
return subclass(this, parent, extraMethods);
}
});
return exports;
}
// Copy methods to a Constructor Function's prototype
function methods(ctor, obj) {
types.extend(ctor.prototype, obj);
}
// Bind 'this' and/or arguments and return new function.
// Differs from native bind (if present) in that undefined
// parameters are merged.
function bind(fn, self) {
var presets;
// Handle the monkey-patched and in-line forms of curry
if (arguments.length == 3 && types.isArguments(arguments[2])) {
presets = Array.prototype.slice.call(arguments[2], self1);
} else {
presets = Array.prototype.slice.call(arguments, 2);
}
function merge(a1, a2) {
var merged = types.copyArray(a1);
a2 = types.copyArray(a2);
for (var i = 0; i < merged.length; i++) {
if (merged[i] === undefined) {
merged[i] = a2.shift();
}
}
return merged.concat(a2);
}
return function curried() {
return fn.apply(self || this, merge(presets, arguments));
};
}
// Wrap the fn function with a generic decorator like:
//
// function decorator(fn, arguments, wrapper) {
// if (fn == undefined) { ... init ...; return;}
// ...
// result = fn.apply(this, arguments);
// ...
// return result;
// }
//
// The decorated function is created for each call
// of the decorate function. In addition to wrapping
// the decorated function, it can be used to save state
// information between calls by adding properties to it.
function decorate(fn, decorator) {
function decorated() {
return decorator.call(this, fn, arguments, decorated);
}
// Init call - pass undefined fn - but available in this
// if needed.
decorator.call(fn, undefined, arguments, decorated);
return decorated;
}
// Create an empty object whose __proto__ points to the given object.
// It's properties will "shadow" those of the given object until modified.
function shadow(obj) {
function Dummy() {}
Dummy.prototype = obj;
return new Dummy();
}
// Classical JavaScript single-inheritance pattern.
// Call super constructor via this._super(args);
// Call super methods via this._proto.method.call(this, args)
function subclass(ctor, parent, extraMethods) {
ctor.prototype = shadow(parent.prototype);
ctor.prototype.constructor = ctor;
ctor.prototype._super = parent;
ctor.prototype._proto = parent.prototype;
methods(ctor, extraMethods);
return ctor;
}