-
Notifications
You must be signed in to change notification settings - Fork 4
/
jquery.class.js
365 lines (315 loc) · 11.7 KB
/
jquery.class.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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
/*--------------------------------------------*
* Info: https://github.com/kilhage/class.js
*--------------------------------------------*
* Copyright 2011, Emil Kilhage
* Released under the MIT License
*--------------------------------------------*
* Environment-release: jQuery
* Last Update: 2011-04-29 18:10:51
* Version 1.1.0
*--------------------------------------------*/
/*jslint forin: true, onevar: true, debug: false, indent: 4
white: true, strict: true, undef: true, newcap: true
maxlen: 85, evil: false, nomen: false, regexp: false
browser: true */
/*global jQuery */
jQuery.Class = (function (undefined) {
"use strict";
var initializing = false,
prefix = "Class",
__self__ = prefix + (new Date()).getTime(),
testFn = function () {
this._parent();
},
fnSearch = /\b_parent\b/,
parentFnSearch = fnSearch.test(testFn) ? (/\b_parent\b\./) : fnSearch = /.*/,
toString = Object.prototype.toString,
hasOwn = Object.prototype.hasOwnProperty,
functionToString = toString.call(testFn),
objectToString = toString.call({}),
StdError = Error,
tmpProto,
errors = {};
/* Define some used Errors */
/**
* This is thrown whenever the input in any of the class building
* functions conatins something wiered.
*/
function InvalidClassDefinition(msg) {
StdError.apply(this, arguments);
this.message = prefix + "::" + msg;
}
tmpProto = InvalidClassDefinition.prototype = new StdError();
errors[tmpProto.name = "InvalidClassDefinition"] = InvalidClassDefinition;
/**
* Gets thrown when someone calls a parent method that don't exists
*/
function InvalidParentMethodCall(name) {
StdError.apply(this, arguments);
this.message = prefix + "::Parent Class doesn't have the method: " + name;
}
tmpProto = InvalidParentMethodCall.prototype = new StdError();
errors[tmpProto.name = "InvalidParentMethodCall"] = InvalidParentMethodCall;
/**
* The base Class implementation that all
* classes created by this library will be extended from
*/
function Base() {}
/**
* Simple JavaScript Inheritance
* By John Resig http://ejohn.org/
* MIT Licensed.
*
* @param <object> prop: The prototype that you want the object to have
* @return <function>: Created class
*/
function Class(properties) {
return Base.extend(properties);
}
/**
* @param <mixed> fn
* @return <boolean>: if fn is created by this library
*/
function is(fn) {
return !!(fn && fn.extend === Base.extend);
}
/**
* makeClass - By John Resig (MIT Licensed)
* http://ejohn.org/
*
* Makes it possible to instantiate a
* class both with or without the new keyword.
* It also moves the constructor to a function
* on the prototype called "init"
*
* @return <function>
*/
function makeClass() {
// The constructor will be cached
// here and updated each time it changes
var init;
function Awesome(args) {
var self = this;
// Where the new keyword used?
if (self instanceof Awesome) {
if (initializing === false) {
// Have the constructor property changed since
// last time a new instance where made?
if (init !== self.init) {
// Is the constructor property a function?
if ("init" in self &&
toString.call(self.init) === functionToString) {
// Update the cached constructor.
init = self.init;
} else {
// The not a valid constructor.
init = undefined;
}
}
if (init !== undefined) {
// Call the "real" constructor and apply the arguments
init.apply(self, args && args.callee === Awesome ?
args : arguments);
}
}
} else {
// Instantiate the class and pass the aruments
return new Awesome(arguments);
}
}
return Awesome;
}
function rewrite(name, current, parent, populator) {
// Should this._parent be
// populated with any properties
// from the parent class?
var populate = parentFnSearch.test(current),
setSelf = populate,
// Needed to wrap the original function
// inside a new function to avoid adding
// properties to the original function
// when calling 'this._parent.<method name>()'
realParent = toString.call(parent) === functionToString ? function () {
return parent.apply(this, arguments);
} : // Make sure to throw an error
// when calling a method that don't exists
function () {
throw new InvalidParentMethodCall(name);
};
return function () {
var self = this,
// Store the content in the ._parent property
// so we can revert the object after
// we're done if it's needed
tmp = self._parent,
ret,
name,
fns;
// Add a new ._parent() method that points to the parent
// class's method with the same name
self._parent = realParent;
if (setSelf) {
// Add the parent class's methods to
// 'this._parent' which enables you
// to call 'this._parent<method name>()'
if (populate) {
// We only need to do this once
populate = false;
// Get the parent functions and add'em
fns = populator();
for (name in fns) {
if (hasOwn.call(fns, name)) {
// Add the parent functions
realParent[name] = fns[name];
}
}
}
// Save a reference to the class instance on the parent
// function so the other methods from the
// instance parent class can be called.
// Only do this when needed, to optimize the performace
realParent[__self__] = self;
}
// Execute the original function
ret = current.apply(self, arguments);
// Restore the context
self._parent = tmp;
return ret;
};
}
function rewriteFn(fn) {
return function () {
return fn.apply(this[__self__], arguments);
};
}
function addProperties(from, reference, target) {
var name, current, fns,
populator = function () {
if (fns === undefined) {
var key;
fns = {};
for (key in reference) {
if (toString.call(reference[key]) === functionToString) {
fns[key] = rewriteFn(reference[key]);
}
}
}
return fns;
};
if (target === undefined) {
target = reference;
}
for (name in from) {
if (hasOwn.call(from, name)) {
current = from[name];
target[name] = toString.call(current) === functionToString &&
fnSearch.test(current) ?
rewrite(name, current, reference[name], populator) : current;
}
}
}
/**
* Creates a new class based on the current class
*
* @param properties
* @return <function>
*/
Base.extend = function (properties) {
// Create the new class
var Awesome = makeClass(), name, Src = this,
prototype, parent = Src.prototype, m;
if (!properties || toString.call(properties) !== objectToString) {
m = "Unable to " + (Src === Base ? "extend" : "create") + " class";
throw new InvalidClassDefinition(m);
}
// Move all static properties
for (name in Src) {
if (hasOwn.call(Src, name)) {
Awesome[name] = Src[name];
}
}
/**
* Does the input contains any static properties that should be added?
*/
if (properties.hasOwnProperty("prototype")) {
prototype = properties.prototype;
if (prototype && toString.call(prototype) === objectToString) {
delete properties.prototype;
addProperties(properties, Src, Awesome);
properties = properties.prototype = prototype;
} else {
m = "Invalid type on properties.prototype(" +
prototype + "), literal object expected";
throw new InvalidClassDefinition(m);
}
}
// Create a shallow copy of the source prototype
initializing = true;
prototype = new Src();
initializing = false;
// Copy the properties over onto the new prototype
addProperties(properties, parent, prototype);
// Enforce the constructor to be what we expect
Awesome.constructor = prototype.constructor = Awesome;
// Add the final prototype to the created class
Awesome.prototype = prototype;
/**
* Checks if a class inherits from another class
*
* @param <function> parent
* @return <boolean>
*/
Awesome.inherits = function (parent) {
return parent === Src || Src.inherits(parent);
};
return Awesome;
};
Base.inherits = function () {
return false;
};
/**
* Adds properties to a Class
* @param <object> prop
*/
Base.addMethods = function (properties, proto, own_proto) {
if (properties && toString.call(properties) === objectToString) {
proto = properties.prototype;
own_proto = this.prototype;
if (proto && toString.call(proto) === objectToString) {
addProperties(proto, own_proto);
delete properties.prototype;
addProperties(properties, this);
properties.prototype = proto;
} else {
addProperties(properties, own_proto);
}
} else {
throw new InvalidClassDefinition("Unable to add methods to class");
}
};
/**
* A default function on all classes that are created.
*
* Makes in possible to extend already initalized
* objects in an easy way
*
* @param <object> properties
*/
Base.prototype.addMethods = function (properties) {
if (properties && toString.call(properties) === objectToString) {
addProperties(properties, this);
} else {
throw new InvalidClassDefinition("Unable to add methods to instance");
}
};
// Public helper methods
Class.is = is;
Class.makeClass = makeClass;
// These are exposed to simplify the unit-testing
// I will probably remove them later...
Class.fnSearch = fnSearch;
Class.parentFnSearch = parentFnSearch;
Class.errors = errors;
Class.version = "1.1.0";
return Class;
}());