-
Notifications
You must be signed in to change notification settings - Fork 3
/
index.js
140 lines (119 loc) · 3.43 KB
/
index.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
138
139
140
// 'use strict'; //<-- Root of all evil, causes thrown errors on readyOnly props.
var has = Object.prototype.hasOwnProperty
, slice = Array.prototype.slice;
/**
* Copy all readable properties from an Object or function and past them on the
* object.
*
* @param {Object} obj The object we should paste everything on.
* @returns {Object} obj
* @api private
*/
function copypaste(obj) {
var args = slice.call(arguments, 1)
, i = 0
, prop;
for (; i < args.length; i++) {
if (!args[i]) continue;
for (prop in args[i]) {
if (!has.call(args[i], prop)) continue;
obj[prop] = args[i][prop];
}
}
return obj;
}
/**
* A proper mixin function that respects getters and setters.
*
* @param {Object} obj The object that should receive all properties.
* @returns {Object} obj
* @api private
*/
function mixin(obj) {
if (
'function' !== typeof Object.getOwnPropertyNames
|| 'function' !== typeof Object.defineProperty
|| 'function' !== typeof Object.getOwnPropertyDescriptor
) {
return copypaste.apply(null, arguments);
}
//
// We can safely assume that if the methods we specify above are supported
// that it's also save to use Array.forEach for iteration purposes.
//
slice.call(arguments, 1).forEach(function forEach(o) {
Object.getOwnPropertyNames(o).forEach(function eachAttr(attr) {
Object.defineProperty(obj, attr, Object.getOwnPropertyDescriptor(o, attr));
});
});
return obj;
}
/**
* Detect if a given parent is constructed in strict mode so we can force the
* child in to the same mode. It detects the strict mode by accessing properties
* on the function that are forbidden in strict mode:
*
* - `caller`
* - `callee`
* - `arguments`
*
* Forcing the a thrown TypeError.
*
* @param {Function} parent Parent constructor
* @returns {Function} The child constructor
* @api private
*/
function mode(parent) {
try {
var e = parent.caller || parent.arguments || parent.callee;
return function child() {
return parent.apply(this, arguments);
};
} catch(e) {}
return function child() {
'use strict';
return parent.apply(this, arguments);
};
}
//
// Helper function to correctly set up the prototype chain, for subclasses.
// Similar to `goog.inherits`, but uses a hash of prototype properties and
// class properties to be extended.
//
module.exports = function extend(protoProps, staticProps) {
var parent = this
, child;
//
// The constructor function for the new subclass is either defined by you
// (the "constructor" property in your `extend` definition), or defaulted
// by us to simply call the parent's constructor.
//
if (protoProps && has.call(protoProps, 'constructor')) {
child = protoProps.constructor;
} else {
child = mode(parent);
}
//
// Set the prototype chain to inherit from `parent`, without calling
// `parent`'s constructor function.
//
function Surrogate() {
this.constructor = child;
}
Surrogate.prototype = parent.prototype;
child.prototype = new Surrogate;
//
// Add prototype properties (instance properties) to the subclass,
// if supplied.
//
if (protoProps) mixin(child.prototype, protoProps);
//
// Add static properties to the constructor function, if supplied.
//
copypaste(child, parent, staticProps);
//
// Set a convenience property in case the parent's prototype is needed later.
//
child.__super__ = parent.prototype;
return child;
};