forked from tnhu/jsface
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjsface.js
185 lines (160 loc) · 6.39 KB
/
jsface.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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/*
* JSFace Object Oriented Programming Library
* https://github.com/tnhu/jsface
*
* Copyright (c) 2009-2013 Tan Nhu
* Licensed under MIT license (https://github.com/tnhu/jsface/blob/master/LICENSE.txt)
*/
(function(context, OBJECT, NUMBER, LENGTH, toString, undefined, oldClass, jsface) {
/**
* Return a map itself or null. A map is a set of { key: value }
* @param obj object to be checked
* @return obj itself as a map or false
*/
function mapOrNil(obj) { return (obj && typeof obj === OBJECT && !(typeof obj.length === NUMBER && !(obj.propertyIsEnumerable(LENGTH))) && obj) || null; }
/**
* Return an array itself or null
* @param obj object to be checked
* @return obj itself as an array or null
*/
function arrayOrNil(obj) { return (obj && typeof obj === OBJECT && typeof obj.length === NUMBER && !(obj.propertyIsEnumerable(LENGTH)) && obj) || null; }
/**
* Return a function itself or null
* @param obj object to be checked
* @return obj itself as a function or null
*/
function functionOrNil(obj) { return (obj && typeof obj === "function" && obj) || null; }
/**
* Return a string itself or null
* @param obj object to be checked
* @return obj itself as a string or null
*/
function stringOrNil(obj) { return (toString.apply(obj) === "[object String]" && obj) || null; }
/**
* Return a class itself or null
* @param obj object to be checked
* @return obj itself as a class or false
*/
function classOrNil(obj) { return (functionOrNil(obj) && (obj.prototype && obj === obj.prototype.constructor) && obj) || null; }
/**
* Util for extend() to copy a map of { key:value } to an object
* @param key key
* @param value value
* @param ignoredKeys ignored keys
* @param object object
* @param iClass true if object is a class
* @param oPrototype object prototype
*/
function copier(key, value, ignoredKeys, object, iClass, oPrototype) {
if ( !ignoredKeys || !ignoredKeys.hasOwnProperty(key)) {
object[key] = value;
if (iClass) { oPrototype[key] = value; } // class? copy to prototype as well
}
}
/**
* Extend object from subject, ignore properties in ignoredKeys
* @param object the child
* @param subject the parent
* @param ignoredKeys (optional) keys should not be copied to child
*/
function extend(object, subject, ignoredKeys) {
if (arrayOrNil(subject)) {
for (var len = subject.length; --len >= 0;) { extend(object, subject[len], ignoredKeys); }
} else {
ignoredKeys = ignoredKeys || { constructor: 1, $super: 1, prototype: 1, $superp: 1 };
var iClass = classOrNil(object),
isSubClass = classOrNil(subject),
oPrototype = object.prototype, supez, key, proto;
// copy static properties and prototype.* to object
if (mapOrNil(subject)) {
for (key in subject) {
copier(key, subject[key], ignoredKeys, object, iClass, oPrototype);
}
}
if (isSubClass) {
proto = subject.prototype;
for (key in proto) {
copier(key, proto[key], ignoredKeys, object, iClass, oPrototype);
}
}
// prototype properties
if (iClass && isSubClass) { extend(oPrototype, subject.prototype, ignoredKeys); }
}
}
/**
* Create a class.
* @param parent parent class(es)
* @param api class api
* @return class
*/
function Class(parent, api) {
if ( !api) {
parent = (api = parent, 0); // !api means there's no parent
}
var clazz, constructor, singleton, statics, key, bindTo, len, i = 0, p,
ignoredKeys = { constructor: 1, $singleton: 1, $statics: 1, prototype: 1, $super: 1, $superp: 1, main: 1, toString: 0 },
plugins = Class.plugins;
api = (typeof api === "function" ? api() : api) || {}; // execute api if it's a function
constructor = api.hasOwnProperty("constructor") ? api.constructor : 0; // hasOwnProperty is a must, constructor is special
singleton = api.$singleton;
statics = api.$statics;
// add plugins' keys into ignoredKeys
for (key in plugins) { ignoredKeys[key] = 1; }
// construct constructor
clazz = singleton ? {} : (constructor ? constructor : function(){});
// determine bindTo: where api should be bound
bindTo = singleton ? clazz : clazz.prototype;
// make sure parent is always an array
parent = !parent || arrayOrNil(parent) ? parent : [ parent ];
// do inherit
len = parent && parent.length;
while (i < len) {
p = parent[i++];
for (key in p) {
if ( !ignoredKeys[key]) {
bindTo[key] = p[key];
if ( !singleton) { clazz[key] = p[key]; }
}
}
for (key in p.prototype) { if ( !ignoredKeys[key]) { bindTo[key] = p.prototype[key]; } }
}
// copy properties from api to bindTo
for (key in api) {
if ( !ignoredKeys[key]) {
bindTo[key] = api[key];
}
}
// copy static properties from statics to both clazz and bindTo
for (key in statics) { clazz[key] = bindTo[key] = statics[key]; }
// if class is not a singleton, add $super and $superp
if ( !singleton) {
p = parent && parent[0] || parent;
clazz.$super = p;
clazz.$superp = p && p.prototype ? p.prototype : p;
bindTo.$class = clazz;
}
for (key in plugins) { plugins[key](clazz, parent, api); } // pass control to plugins
if (functionOrNil(api.main)) { api.main.call(clazz, clazz); } // execute main()
return clazz;
}
/* Class plugins repository */
Class.plugins = {};
/* Initialization */
jsface = {
Class : Class,
extend : extend,
mapOrNil : mapOrNil,
arrayOrNil : arrayOrNil,
functionOrNil: functionOrNil,
stringOrNil : stringOrNil,
classOrNil : classOrNil
};
if (typeof module !== "undefined" && module.exports) { // NodeJS/CommonJS
module.exports = jsface;
} else {
oldClass = context.Class; // save current Class namespace
context.Class = Class; // bind Class and jsface to global scope
context.jsface = jsface;
jsface.noConflict = function() { context.Class = oldClass; }; // no conflict
}
})(this, "object", "number", "length", Object.prototype.toString);