jsface
Advanced tools
Comparing version 2.1.1 to 2.2.0
@@ -1,5 +0,9 @@ | ||
v2.x.0 TBD | ||
v2.2.0 Oct 11, 2013 | ||
---------------------------------------------------------------- | ||
* Add overloading plugin: support method overloading, type | ||
checking and arguments validation | ||
* Introduce $class (issue #14, thanks to Anton Baukin) | ||
* API change (remove isMap, isArray, isFunction, isString, | ||
isClass; add mapOrNil, arrayOrNil, functionOrNil, stringOrNil | ||
classOrNil) | ||
* Update jsface.ready | ||
* Update unit test | ||
@@ -53,3 +57,3 @@ v2.1.1 Apr 25, 2012 | ||
* Make jsFace private methods not exposed via jsFace namespace | ||
* Remove unneccessary code | ||
* Remove unnecessary code | ||
* Size optimization | ||
@@ -60,3 +64,2 @@ * More unit tests | ||
---------------------------------------------------------------- | ||
* More elegant syntax. | ||
@@ -67,4 +70,3 @@ * Remove unnecessary meta data copying on class inheritance. | ||
---------------------------------------------------------------- | ||
* Initial release. A complete new look from version 1.2a which | ||
was developed for browsers. | ||
was developed for browsers. |
157
jsface.js
@@ -5,41 +5,55 @@ /* | ||
* | ||
* Copyright (c) 2009-2012 Tan Nhu | ||
* 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) { | ||
"use strict"; | ||
/** | ||
* 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; } | ||
/** | ||
* Check an object is a map or not. A map is something like { key1: value1, key2: value2 }. | ||
* Return an array itself or null | ||
* @param obj object to be checked | ||
* @return obj itself as an array or null | ||
*/ | ||
function isMap(obj) { | ||
return (obj && typeof obj === OBJECT && !(typeof obj.length === NUMBER && !(obj.propertyIsEnumerable(LENGTH)))); | ||
} | ||
function arrayOrNil(obj) { return (obj && typeof obj === OBJECT && typeof obj.length === NUMBER && !(obj.propertyIsEnumerable(LENGTH)) && obj) || null; } | ||
/** | ||
* Check an object is an array or not. An array is something like []. | ||
* Return a function itself or null | ||
* @param obj object to be checked | ||
* @return obj itself as a function or null | ||
*/ | ||
function isArray(obj) { | ||
return (obj && typeof obj === OBJECT && typeof obj.length === NUMBER && !(obj.propertyIsEnumerable(LENGTH))); | ||
} | ||
function functionOrNil(obj) { return (obj && typeof obj === "function" && obj) || null; } | ||
/** | ||
* Check an object is a function or not. | ||
* Return a string itself or null | ||
* @param obj object to be checked | ||
* @return obj itself as a string or null | ||
*/ | ||
function isFunction(obj) { | ||
return (obj && typeof obj === "function"); | ||
} | ||
function stringOrNil(obj) { return (toString.apply(obj) === "[object String]" && obj) || null; } | ||
/** | ||
* Check an object is a string not. | ||
* Return a class itself or null | ||
* @param obj object to be checked | ||
* @return obj itself as a class or false | ||
*/ | ||
function isString(obj) { | ||
return toString.apply(obj) === "[object String]"; | ||
} | ||
function classOrNil(obj) { return (functionOrNil(obj) && (obj.prototype && obj === obj.prototype.constructor) && obj) || null; } | ||
/** | ||
* Check an object is a class (not an instance of a class, which is a map) or not. | ||
* 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 isClass(clazz) { | ||
return isFunction(clazz) && (clazz.prototype && clazz === clazz.prototype.constructor); | ||
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 | ||
} | ||
} | ||
@@ -49,26 +63,28 @@ | ||
* 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) { | ||
function copier(key, value) { | ||
if ( !ignoredKeys || !ignoredKeys.hasOwnProperty(key)) { // no copy ignored keys | ||
object[key] = value; // do copy | ||
if (iClass) { oPrototype[key] = value; } // class? copy to prototype as well | ||
} | ||
} | ||
if (isArray(subject)) { | ||
if (arrayOrNil(subject)) { | ||
for (var len = subject.length; --len >= 0;) { extend(object, subject[len], ignoredKeys); } | ||
} else { | ||
ignoredKeys = ignoredKeys || { constructor: 1, $super: 1, prototype: 1, $superb: 1 }; | ||
ignoredKeys = ignoredKeys || { constructor: 1, $super: 1, prototype: 1, $superp: 1 }; | ||
var iClass = isClass(object), | ||
isSubClass = isClass(subject), | ||
var iClass = classOrNil(object), | ||
isSubClass = classOrNil(subject), | ||
oPrototype = object.prototype, supez, key, proto; | ||
// copy static properties and prototype.* to object | ||
if (isMap(subject)) { for (key in subject) copier(key, subject[key]); } | ||
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]); } | ||
for (key in proto) { | ||
copier(key, proto[key], ignoredKeys, object, iClass, oPrototype); | ||
} | ||
} | ||
@@ -83,22 +99,33 @@ | ||
* Create a class. | ||
* @param parent parent class(es) | ||
* @param api class api | ||
* @return class | ||
*/ | ||
function Class(parent, api) { | ||
if ( !api) parent = (api = parent, 0); | ||
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 }, | ||
overload = Class.overload, | ||
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 | ||
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; | ||
for (key in plugins) { ignoredKeys[key] = 1; } // add plugins' keys into ignoredKeys | ||
// add plugins' keys into ignoredKeys | ||
for (key in plugins) { ignoredKeys[key] = 1; } | ||
clazz = singleton ? {} : (constructor ? (overload ? overload("constructor", constructor) : constructor) : function(){}); | ||
// construct constructor | ||
clazz = singleton ? {} : (constructor ? constructor : function(){}); | ||
// determine bindTo: where api should be bound | ||
bindTo = singleton ? clazz : clazz.prototype; | ||
parent = !parent || isArray(parent) ? parent : [ parent ]; | ||
// make sure parent is always an array | ||
parent = !parent || arrayOrNil(parent) ? parent : [ parent ]; | ||
// do inherit | ||
len = parent && parent.length; | ||
@@ -113,14 +140,16 @@ while (i < len) { | ||
} | ||
for (key in p.prototype) { | ||
if ( !ignoredKeys[key]) { bindTo[key] = p.prototype[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]; | ||
if ( !ignoredKeys[key]) { | ||
bindTo[key] = api[key]; | ||
} | ||
} | ||
for (key in statics) { | ||
clazz[key] = bindTo[key] = statics[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) { | ||
@@ -130,7 +159,7 @@ p = parent && parent[0] || parent; | ||
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 (isFunction(api.main)) { api.main.call(clazz, clazz); } // execute main() | ||
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; | ||
@@ -144,19 +173,19 @@ } | ||
jsface = { | ||
Class : Class, | ||
extend : extend, | ||
isMap : isMap, | ||
isArray : isArray, | ||
isFunction: isFunction, | ||
isString : isString, | ||
isClass : isClass | ||
Class : Class, | ||
extend : extend, | ||
mapOrNil : mapOrNil, | ||
arrayOrNil : arrayOrNil, | ||
functionOrNil: functionOrNil, | ||
stringOrNil : stringOrNil, | ||
classOrNil : classOrNil | ||
}; | ||
if (typeof module !== "undefined" && module.exports) { // NodeJS/CommonJS | ||
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 | ||
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 | ||
jsface.noConflict = function() { context.Class = oldClass; }; // no conflict | ||
} | ||
})(this, "object", "number", "length", Object.prototype.toString); | ||
})(this, "object", "number", "length", Object.prototype.toString); |
@@ -9,19 +9,17 @@ /* | ||
(function(context) { | ||
"use strict"; | ||
var jsface = context.jsface || require("./jsface"), | ||
Class = jsface.Class, | ||
classOrNil = jsface.classOrNil, | ||
functionOrNil = jsface.functionOrNil, | ||
extend = jsface.extend, | ||
mapOrNil = jsface.mapOrNil, | ||
WRAPPER = "___wrapper___", | ||
BEFORE = "___before_fns___", | ||
AFTER = "___after_fns___", | ||
ORIGIN = "___origin_fn___", | ||
ADVISOR = "___advisors___", | ||
INVALID = "Invalid ", | ||
NON_FUNC = "Non-function property named ", | ||
noop = function(){}; | ||
var jsface = context.jsface || require("./jsface"), | ||
Class = jsface.Class, | ||
isClass = jsface.isClass, | ||
isFunction = jsface.isFunction, | ||
extend = jsface.extend, | ||
isMap = jsface.isMap, | ||
WRAPPER = "___wrapper___", | ||
BEFORE = "___before_fns___", | ||
AFTER = "___after_fns___", | ||
ORIGIN = "___origin_fn___", | ||
ADVISOR = "___advisors___", | ||
INVALID = "Invalid ", | ||
NON_FUNC = "Non-function property named ", | ||
noop = function(){}; | ||
/** | ||
@@ -44,3 +42,3 @@ * Wrap a function with before & after. | ||
r = _before[bLen].apply(this, arguments); | ||
if (isMap(r) && r.$skip === true) { | ||
if (mapOrNil(r) && r.$skip === true) { | ||
return r.$data; | ||
@@ -55,3 +53,3 @@ } | ||
r = _after[aLen].apply(this, arguments); | ||
if (isMap(r) && r.$skip === true) { | ||
if (mapOrNil(r) && r.$skip === true) { | ||
return ret; | ||
@@ -73,3 +71,3 @@ } | ||
extend(wrapper, fn, 0, true); | ||
if (isClass(fn)) { | ||
if (classOrNil(fn)) { | ||
wrapper.prototype = fn.prototype; | ||
@@ -182,4 +180,4 @@ | ||
jsface.pointcut = function pointcut(clazz, opts) { | ||
var iClass = isFunction(clazz), | ||
iInstance = isMap(clazz), | ||
var iClass = functionOrNil(clazz), | ||
iInstance = mapOrNil(clazz), | ||
iRemove = (/^remove ?/.exec(opts) !== null), | ||
@@ -189,3 +187,3 @@ advisor = iRemove && arguments[2], | ||
if ( !(iClass || iInstance) || !(isMap(opts) || iRemove)) { | ||
if ( !(iClass || iInstance) || !(mapOrNil(opts) || iRemove)) { | ||
throw INVALID + "params"; | ||
@@ -201,13 +199,13 @@ } | ||
pointcuts = opts[method]; | ||
pointcuts = isFunction(pointcuts) ? { before: pointcuts } : pointcuts; // sugar syntax | ||
pointcuts = functionOrNil(pointcuts) ? { before: pointcuts } : pointcuts; // sugar syntax | ||
var before = isMap(pointcuts) && !pointcuts.before ? noop : pointcuts.before, | ||
after = isMap(pointcuts) && !pointcuts.after ? noop : pointcuts.after, | ||
var before = mapOrNil(pointcuts) && !pointcuts.before ? noop : pointcuts.before, | ||
after = mapOrNil(pointcuts) && !pointcuts.after ? noop : pointcuts.after, | ||
isStatic; | ||
// check if before & after are valid | ||
if ( !isFunction(before)) { | ||
if ( !functionOrNil(before)) { | ||
throw INVALID + method + ":before"; | ||
} | ||
if ( !isFunction(after)) { | ||
if ( !functionOrNil(after)) { | ||
throw INVALID + method + ":after"; | ||
@@ -217,3 +215,3 @@ } | ||
if (iInstance) { | ||
if (isFunction(bindTo[method])) { | ||
if (functionOrNil(bindTo[method])) { | ||
bindTo[method] = wrap(bindTo[method], before, after, opts); | ||
@@ -227,3 +225,3 @@ } else { | ||
} else { | ||
if (isFunction(bindTo[method])) { | ||
if (functionOrNil(bindTo[method])) { | ||
isStatic = iClass && bindTo[method] === clazz[method]; | ||
@@ -230,0 +228,0 @@ bindTo[method] = wrap(bindTo[method], before, after, opts); |
@@ -9,16 +9,16 @@ /* | ||
(function(context) { | ||
"use strict"; | ||
var jsface = context.jsface || require("./jsface"), | ||
Class = jsface.Class, | ||
functionOrNil = jsface.functionOrNil, | ||
readyFns = [], | ||
readyCount = 0; | ||
var jsface = context.jsface || require("./jsface"), | ||
Class = jsface.Class, | ||
isFunction = jsface.isFunction, | ||
readyFns = [], | ||
readyCount = 0; | ||
Class.plugins.$ready = function(clazz, parent, api) { | ||
var r = api.$ready, | ||
len = parent ? parent.length : 0, | ||
count = len, | ||
Class.plugins.$ready = function invoke(clazz, parent, api, loop) { | ||
var r = api.$ready, | ||
len = parent ? parent.length : 0, | ||
count = len, | ||
_super = len && parent[0].$super, | ||
pa, i, entry; | ||
// find and invoke $ready from parent(s) | ||
while (len--) { | ||
@@ -38,6 +38,11 @@ for (i = 0; i < readyCount; i++) { | ||
// call $ready from grandparent(s), if any | ||
if (_super) { | ||
invoke(clazz, [ _super ], api, true); | ||
} | ||
// in an environment where there are a lot of class creating/removing (rarely) | ||
// this implementation might cause a leak (saving pointers to clazz and $ready) | ||
if (isFunction(r)) { | ||
r.call(clazz, clazz, parent, api); | ||
if ( !loop && functionOrNil(r)) { | ||
r.call(clazz, clazz, parent, api); // invoke ready from current class | ||
readyFns.push([ clazz, r ]); | ||
@@ -44,0 +49,0 @@ readyCount++; |
@@ -1,2 +0,2 @@ | ||
Copyright (c) 2009-2012 Tan Nhu | ||
Copyright (c) 2009-2013 Tan Nhu | ||
@@ -3,0 +3,0 @@ Permission is hereby granted, free of charge, to any person obtaining a copy |
{ | ||
"name" : "jsface", | ||
"main" : "jsface.js", | ||
"description" : "Small, fast, elegant, powerful, and cross platform JavaScript OOP library. Support singleton, super call, private, mixins, plugins, AOP and more.", | ||
"author" : "Tan Nhu <tnhu AT me . com>", | ||
"version" : "2.1.1", | ||
"keywords" : [ "jsface", "JSFace", "OOP", "JavaScript OOP", "JavaScript Object Oriented Programming", "AOP", "Aspect Oriented Programming" ], | ||
"homepage" : "https://github.com/tnhu/jsface", | ||
"licenses" : [{ | ||
"type" : "MIT", | ||
"url" : "https://github.com/tnhu/jsface/blob/master/LICENSE.txt" | ||
}], | ||
"repository" : { | ||
"type" : "git", | ||
"url" : "git://github.com/tnhu/jsface.git" | ||
}, | ||
"engines" : { | ||
"node" : ">=0.4.0" | ||
}, | ||
"dependencies" : {}, | ||
"devDependencies": {} | ||
"name": "jsface", | ||
"main": "jsface.js", | ||
"description": "Small, fast, elegant, powerful, and cross platform JavaScript OOP library. Support main, singleton, super call, private, mixins, plugins, AOP and more.", | ||
"author": "Tan Nhu <tnhu AT me . com>", | ||
"version": "2.2.0", | ||
"keywords": [ | ||
"jsface", | ||
"JSFace", | ||
"OOP", | ||
"JavaScript OOP", | ||
"JavaScript Object Oriented Programming", | ||
"AOP", | ||
"Aspect Oriented Programming" | ||
], | ||
"homepage": "https://github.com/tnhu/jsface", | ||
"licenses": [ | ||
{ | ||
"type": "MIT", | ||
"url": "https://github.com/tnhu/jsface/blob/master/LICENSE.txt" | ||
} | ||
], | ||
"repository": { | ||
"type": "git", | ||
"url": "git://github.com/tnhu/jsface.git" | ||
}, | ||
"engines": { | ||
"node": ">=0.4.0" | ||
}, | ||
"dependencies": {}, | ||
"devDependencies": { | ||
"grunt": "~0.4.1", | ||
"grunt-contrib-qunit": ">=0.2.1", | ||
"grunt-contrib-watch": ">=0.3.1", | ||
"grunt-contrib-uglify": ">=0.2.0", | ||
"grunt-contrib-copy": ">=0.4.0", | ||
"grunt-contrib-clean": "~0.5.0", | ||
"grunt-contrib-concat": "~0.3.0" | ||
} | ||
} |
![Benchmark result](https://lh5.googleusercontent.com/-2dQo8ttjn48/T2KVyppgd2I/AAAAAAAADQw/GvEpE5MIYUo/s956/Screen%2520Shot%25202012-03-15%2520at%25206.21.04%2520PM.png "Benchmark") | ||
[![Build Status](https://secure.travis-ci.org/tannhu/jsface.png?branch=master)](http://travis-ci.org/tannhu/jsface) | ||
# Features | ||
## Features | ||
@@ -13,3 +13,3 @@ * Small footprint, no dependency, 0.7K minimized+gzip! | ||
# Setup | ||
## Setup | ||
@@ -24,8 +24,4 @@ JSFace supports both server side (CommonJS) and client side JavaScript (browser). | ||
JSFace introduces two variables in browser global scope: jsface and Class. In case you want to use other APIs such as extend, you need to create aliases, for example: | ||
JSFace introduces two global variables: jsface and Class. Other APIs are under jsface namespace. | ||
``` javascript | ||
var extend = jsface.extend; | ||
``` | ||
In NodeJS environment, first install JSFace via npm: | ||
@@ -45,5 +41,5 @@ | ||
# API | ||
## API | ||
## Define a class | ||
### Define a class | ||
@@ -66,3 +62,3 @@ ``` javascript | ||
## Define a sub-class | ||
### Define a sub-class | ||
@@ -73,9 +69,7 @@ ``` javascript | ||
this.id = id; | ||
Student.$super.call(this, name, age); // Invoke parent's constructor | ||
// this.$super(name, age); // This api is removed since v2.1.0 | ||
this.$class.$super.call(this, name, age); // Invoke parent's constructor | ||
}, | ||
toString: function() { | ||
return this.id + "/" + Student.$superp.toString.call(this); // Invoke parent's toString method | ||
// return this.id + "/" + this.$super(); // This api is removed since v2.1.0 | ||
return this.id + "/" + this.$class.$superp.toString.call(this); // Invoke parent's toString method | ||
} | ||
@@ -88,5 +82,5 @@ }); | ||
## main | ||
### main | ||
JSFace supports a special method named main(). It works just similar to Java's main method. | ||
JSFace supports a special method named main(). main() is executed right after the class is created. | ||
@@ -111,3 +105,3 @@ ``` javascript | ||
## Singleton class | ||
### Singleton class | ||
@@ -126,3 +120,3 @@ ``` javascript | ||
## Static properties | ||
### Static properties | ||
@@ -156,3 +150,3 @@ JSFace supports Java-style static properties. Meaning they are accessible on both class and instance levels. | ||
## Private properties | ||
### Private properties | ||
@@ -183,3 +177,3 @@ JSFace supports private static properties, meaning the properties are shared over instances. | ||
## Mixins | ||
### Mixins | ||
@@ -253,3 +247,3 @@ JSFace provides a powerful mechanism to support mixins. Reusable code can be mixed into almost anything. | ||
``` | ||
## No conflict | ||
### No conflict | ||
@@ -262,11 +256,4 @@ In browser environment, you might be using another library which also introduces the global namespace Class. JSFace can return the original Class back to the library claims it with a call to jsface.noConflict(). | ||
// Code that uses other library's Class can follow here | ||
``` | ||
// ... | ||
Actually, Class is an alias of jsface.Class: | ||
``` javascript | ||
jsface.noConflict(); | ||
// Code that uses other library's Class can follow here | ||
// Define classes by using jsface.Class directly | ||
@@ -277,5 +264,5 @@ var Person = jsface.Class({ | ||
# Plugins | ||
## Plugins | ||
## Plug and Play pointcut | ||
### Plug and Play pointcut | ||
@@ -286,3 +273,3 @@ JSFace supports Aspect Oriented Programming (AOP) via simple before/after mechanism. You can apply pointcuts over class constructors, class methods, singleton methods, instance methods. You can even apply pointcuts over native classes. | ||
### Setup | ||
#### Setup | ||
@@ -307,3 +294,3 @@ Browser: | ||
### Applying pointcuts | ||
#### Applying pointcuts | ||
@@ -359,3 +346,3 @@ In JSFace, an advisor is a set of pointcuts you want to apply to a subject. You can apply as many advisors as you want. | ||
### Removing pointcuts | ||
#### Removing pointcuts | ||
@@ -375,3 +362,3 @@ Using previous apply pointcut example: | ||
## $ready | ||
### $ready | ||
@@ -381,3 +368,3 @@ $ready plugin is designed to help parent classes to intercept their subclasses' creation. If a class uses $ready, | ||
### Setup | ||
#### Setup | ||
@@ -396,7 +383,7 @@ Browser: | ||
### Sample | ||
#### Sample | ||
``` javascript | ||
var Service = Class({ | ||
$ready: function(clazz, api, parent) { | ||
$ready: function(clazz, parent, api) { | ||
var type = (this !== clazz) && api.type; | ||
@@ -424,17 +411,10 @@ | ||
# Bug tracker | ||
## Bug tracker | ||
Have a bug? Please [create an issue here](https://github.com/tannhu/jsface/issues) on GitHub! | ||
# Some notes | ||
## License | ||
Method overloadings, type checking, and arguments validation (available in versions prior to 2.0.0) are being implemented as plugins. | ||
Copyright (c) 2009-2013 Tan Nhu | ||
More use cases are covered in [unit tests](https://github.com/tannhu/jsface/tree/master/test) | ||
(I'm using [QUnit](https://github.com/jquery/qunit)). | ||
# License | ||
Copyright (c) 2009-2012 Tan Nhu | ||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
@@ -456,2 +436,2 @@ of this software and associated documentation files (the "Software"), to deal | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
THE SOFTWARE. | ||
THE SOFTWARE. |
@@ -1,24 +0,27 @@ | ||
var context = this, | ||
extend = jsface.extend, | ||
isMap = jsface.isMap, | ||
isArray = jsface.isArray, | ||
isFunction = jsface.isFunction, | ||
isString = jsface.isString, | ||
isClass = jsface.isClass; | ||
var context = this, | ||
extend = jsface.extend, | ||
mapOrNil = jsface.mapOrNil, | ||
arrayOrNil = jsface.arrayOrNil, | ||
functionOrNil = jsface.functionOrNil, | ||
stringOrNil = jsface.stringOrNil, | ||
classOrNil = jsface.classOrNil; | ||
// --------- UTILITIES --------- // | ||
test("Check type with isMap", function() { | ||
equal(isMap({}), true, "jsface.isMap works incorrectly"); | ||
equal(isMap({ one: 1, two: 2 }), true, "jsface.isMap works incorrectly"); | ||
test("Check type with mapOrNil", function() { | ||
var emptyMap = {}, | ||
fooMap = { one: 1, two: 2 }; | ||
equal( !isMap(), true, "jsface.isMap works incorrectly"); | ||
equal( !isMap(""), true, "jsface.isMap works incorrectly"); | ||
equal( !isMap("Hello"), true, "jsface.isMap works incorrectly"); | ||
equal( !isMap([]), true, "jsface.isMap works incorrectly"); | ||
equal( !isMap([ 1, 2, 3 ]), true, "jsface.isMap works incorrectly"); | ||
equal( !isMap(1234), true, "jsface.isMap works incorrectly"); | ||
equal(mapOrNil(emptyMap), emptyMap, "jsface.mapOrNil works incorrectly"); | ||
equal(mapOrNil(fooMap), fooMap, "jsface.mapOrNil works incorrectly"); | ||
equal(mapOrNil(), null, "jsface.mapOrNil works incorrectly"); | ||
equal(mapOrNil(""), null, "jsface.mapOrNil works incorrectly"); | ||
equal(mapOrNil("Hello"), null, "jsface.mapOrNil works incorrectly"); | ||
equal(mapOrNil([]), null, "jsface.mapOrNil works incorrectly"); | ||
equal(mapOrNil([ 1, 2, 3 ]), null, "jsface.mapOrNil works incorrectly"); | ||
equal(mapOrNil(1234), null, "jsface.mapOrNil works incorrectly"); | ||
}); | ||
test("Check type with jsface.isMap on iframe", function() { | ||
test("Check type with jsface.mapOrNil on iframe", function() { | ||
var frame, iframe, IObject; | ||
@@ -42,20 +45,22 @@ | ||
equal(isMap(map), true, "jsface.isMap works incorrectly in iframe"); | ||
equal(mapOrNil(map), map, "jsface.mapOrNil works incorrectly in iframe"); | ||
document.body.removeChild(iframe); | ||
}); | ||
test("Check type with jsface.isArray", function() { | ||
equal(isArray([]), true, "jsface.isArray works incorrectly"); | ||
equal(isArray([ 1, 2, 3, 4 ]), true, "jsface.isArray works incorrectly"); | ||
equal( !isArray(), true, "jsface.isArray works incorrectly"); | ||
test("Check type with jsface.arrayOrNil", function() { | ||
var emptyArray = [], | ||
fooArray = [ 1, 2, 3, 4 ]; | ||
equal(arrayOrNil(emptyArray), emptyArray, "jsface.arrayOrNil works incorrectly"); | ||
equal(arrayOrNil(fooArray), fooArray, "jsface.arrayOrNil works incorrectly"); | ||
equal( !arrayOrNil(), true, "jsface.arrayOrNil works incorrectly"); | ||
// jsface.isArray does not consider String as Array of characters | ||
equal( !isArray(""), true, "jsface.isArray works incorrectly"); | ||
equal( !isArray("Hello"), true, "jsface.isArray works incorrectly"); | ||
equal( !isArray({}), true, "jsface.isArray works incorrectly"); | ||
equal( !isArray({ one: 1, two: 2 }), true, "jsface.isArray works incorrectly"); | ||
equal( !isArray(1), true, "jsface.isArray works incorrectly"); | ||
// jsface.arrayOrNil does not consider String as Array of characters | ||
equal( !arrayOrNil(""), true, "jsface.arrayOrNil works incorrectly"); | ||
equal( !arrayOrNil("Hello"), true, "jsface.arrayOrNil works incorrectly"); | ||
equal( !arrayOrNil({}), true, "jsface.arrayOrNil works incorrectly"); | ||
equal( !arrayOrNil({ one: 1, two: 2 }), true, "jsface.arrayOrNil works incorrectly"); | ||
equal( !arrayOrNil(1), true, "jsface.arrayOrNil works incorrectly"); | ||
}); | ||
test("Check type with jsface.isArray on iframe", function() { | ||
test("Check type with jsface.arrayOrNil on iframe", function() { | ||
var frame, iframe, IArray, array; | ||
@@ -75,19 +80,21 @@ | ||
equal(isArray(array), true, "jsface.isArray works incorrectly in iframe"); | ||
equal(arrayOrNil(array), array, "jsface.arrayOrNil works incorrectly in iframe"); | ||
document.body.removeChild(iframe); | ||
}); | ||
test("Check type with jsface.isFunction", function() { | ||
equal(isFunction(function(){}), true, "jsface.isFunction works incorrectly"); | ||
equal( !isFunction([]), true, "jsface.isFunction works incorrectly"); | ||
equal( !isFunction([ 1, 2, 3, 4 ]), true, "jsface.isFunction works incorrectly"); | ||
equal( !isFunction(), true, "jsface.isFunction works incorrectly"); | ||
equal( !isFunction(""), true, "jsface.isFunction works incorrectly"); | ||
equal( !isFunction("Hello"), true, "jsface.isFunction works incorrectly"); | ||
equal( !isFunction({}), true, "jsface.isFunction works incorrectly"); | ||
equal( !isFunction({ one: 1, two: 2 }), true, "jsface.isFunction works incorrectly"); | ||
equal( !isFunction(1), true, "jsface.isFunction works incorrectly"); | ||
test("Check type with jsface.functionOrNil", function() { | ||
var fn = function(){}; | ||
equal(functionOrNil(fn), fn, "jsface.functionOrNil works incorrectly"); | ||
equal(functionOrNil([]), null, "jsface.functionOrNil works incorrectly"); | ||
equal(functionOrNil([ 1, 2, 3, 4 ]), null, "jsface.functionOrNil works incorrectly"); | ||
equal(functionOrNil(), null, "jsface.functionOrNil works incorrectly"); | ||
equal(functionOrNil(""), null, "jsface.functionOrNil works incorrectly"); | ||
equal(functionOrNil("Hello"), null, "jsface.functionOrNil works incorrectly"); | ||
equal(functionOrNil({}), null, "jsface.functionOrNil works incorrectly"); | ||
equal(functionOrNil({ one: 1, two: 2 }), null, "jsface.functionOrNil works incorrectly"); | ||
equal(functionOrNil(1), null, "jsface.functionOrNil works incorrectly"); | ||
}); | ||
test("Check type with jsface.isFunction on iframe", function() { | ||
test("Check type with jsface.functionOrNil on iframe", function() { | ||
var frame, iframe, IFunction, fn; | ||
@@ -106,23 +113,25 @@ | ||
fn = new IFunction(); | ||
equal(isFunction(fn), true, "jsface.isFunction works incorrectly"); | ||
equal(functionOrNil(fn), fn, "jsface.functionOrNil works incorrectly"); | ||
document.body.removeChild(iframe); | ||
}); | ||
test("Check type with jsface.isClass", function() { | ||
equal(isClass(function(){}), true, "jsface.isClass works incorrectly on function"); | ||
equal( !isClass(), true, "jsface.isClass works incorrectly passing empty param"); | ||
equal( !isClass(undefined), true, "jsface.isClass works incorrectly undefined"); | ||
equal( !isClass(null), true, "jsface.isClass works incorrectly on null"); | ||
equal( !isClass(""), true, "jsface.isClass works incorrectly empty string"); | ||
equal( !isClass(" "), true, "jsface.isClass works incorrectly blank string"); | ||
equal( !isClass({}), true, "jsface.isClass works incorrectly empty map"); | ||
equal( !isClass([]), true, "jsface.isClass works incorrectly empty array"); | ||
equal( !isClass(0), true, "jsface.isClass works incorrectly on 0"); | ||
equal( !isClass(1), true, "jsface.isClass works incorrectly on 1"); | ||
equal( !isClass(true), true, "jsface.isClass works incorrectly on true"); | ||
equal( !isClass(false), true, "jsface.isClass works incorrectly on false"); | ||
equal( !isClass([ 1, 2, 3, 4 ]), true, "jsface.isClass works incorrectly on array"); | ||
equal( !isClass("Hello"), true,"jsface.isClass works incorrectly on string"); | ||
equal( !isClass({ one: 1, two: 2 }), true, "jsface.isClass works incorrectly on map"); | ||
test("Check type with jsface.classOrNil", function() { | ||
var fn = function(){}; | ||
equal(classOrNil(fn), fn, "jsface.classOrNil works incorrectly on function"); | ||
equal( !classOrNil(), true, "jsface.classOrNil works incorrectly passing empty param"); | ||
equal( !classOrNil(undefined), true, "jsface.classOrNil works incorrectly undefined"); | ||
equal( !classOrNil(null), true, "jsface.classOrNil works incorrectly on null"); | ||
equal( !classOrNil(""), true, "jsface.classOrNil works incorrectly empty string"); | ||
equal( !classOrNil(" "), true, "jsface.classOrNil works incorrectly blank string"); | ||
equal( !classOrNil({}), true, "jsface.classOrNil works incorrectly empty map"); | ||
equal( !classOrNil([]), true, "jsface.classOrNil works incorrectly empty array"); | ||
equal( !classOrNil(0), true, "jsface.classOrNil works incorrectly on 0"); | ||
equal( !classOrNil(1), true, "jsface.classOrNil works incorrectly on 1"); | ||
equal( !classOrNil(true), true, "jsface.classOrNil works incorrectly on true"); | ||
equal( !classOrNil(false), true, "jsface.classOrNil works incorrectly on false"); | ||
equal( !classOrNil([ 1, 2, 3, 4 ]), true, "jsface.classOrNil works incorrectly on array"); | ||
equal( !classOrNil("Hello"), true,"jsface.classOrNil works incorrectly on string"); | ||
equal( !classOrNil({ one: 1, two: 2 }), true, "jsface.classOrNil works incorrectly on map"); | ||
// jsface's Class/Singleton | ||
@@ -137,5 +146,5 @@ var Foo = Class({}), | ||
equal(isClass(Foo), true, "jsface.isClass works incorrectly on an empty class"); | ||
equal(isClass(Bar), true, "jsface.isClass works incorrectly on a simple class"); | ||
equal( !isClass(Util), true, "jsface.isClass works incorrectly a singleton class (singleton is a map, not a class)"); | ||
equal(classOrNil(Foo), Foo, "jsface.classOrNil works incorrectly on an empty class"); | ||
equal(classOrNil(Bar), Bar, "jsface.classOrNil works incorrectly on a simple class"); | ||
equal(classOrNil(Util), null, "jsface.classOrNil works incorrectly a singleton class (singleton is a map, not a class)"); | ||
}); | ||
@@ -147,9 +156,9 @@ | ||
var Test = Class(); | ||
equal(isClass(Test), true, "Class defination must be a class"); | ||
equal(classOrNil(Test), Test, "Class defination must be a class"); | ||
var Foo = Class({}); | ||
equal(isClass(Foo), true, "Class defination must be a class"); | ||
equal(classOrNil(Foo), Foo, "Class defination must be a class"); | ||
var Bar = Class("Hello World", {}); | ||
equal(isClass(Bar), true, "Class defination must be a class"); | ||
equal(classOrNil(Bar), Bar, "Class defination must be a class"); | ||
}); | ||
@@ -170,5 +179,5 @@ | ||
equal(isFunction(Foo), true, "Class defination must be a function"); | ||
equal(isClass(Foo), true, "Class defination must be a class"); | ||
equal(isMap(foo), true, "Class instance must be a map"); | ||
equal(functionOrNil(Foo), Foo, "Class defination must be a function"); | ||
equal(classOrNil(Foo), Foo, "Class defination must be a class"); | ||
equal(mapOrNil(foo), foo, "Class instance must be a map"); | ||
equal(foo.sayHi(), "Hello World John Rambo", "Error invoking method on class instance"); | ||
@@ -187,3 +196,3 @@ equal(foo.name, "John Rambo", "Invalid constructor initialization"); | ||
equal(isFunction(Foo), true, "Default constructor must be a function"); | ||
equal(functionOrNil(Foo), Foo, "Default constructor must be a function"); | ||
equal(foo.sayBye(), "Bye!", "Error invoking method on class instance"); | ||
@@ -211,3 +220,3 @@ }); | ||
equal(isFunction(Foo), true, "Default constructor must be a function"); | ||
equal(functionOrNil(Foo), Foo, "Default constructor must be a function"); | ||
equal(foo.sayBye(), "Bye!", "Error invoking method on class instance"); | ||
@@ -322,3 +331,3 @@ equal(foo.sayHi(), "Hi!", "Invalid private implementation"); | ||
equal(isMap(Foo), true, "Singleton class must be a map object"); | ||
equal(mapOrNil(Foo), Foo, "Singleton class must be a map object"); | ||
equal(Foo.sayHi(), "Hello World", "Error invoking method on singleton class"); | ||
@@ -344,3 +353,3 @@ }); | ||
equal(isMap(Bar), true, "Singleton class must be a map object"); | ||
equal(mapOrNil(Bar), Bar, "Singleton class must be a map object"); | ||
equal(Bar.sayHi(), "Hello World", "Error invoking method on singleton class"); | ||
@@ -367,3 +376,3 @@ equal(Bar.sayBye(), "Bye!", "Error invoking method on singleton class"); | ||
equal(isClass(Bar), true, "Class defination must be a class"); | ||
equal(classOrNil(Bar), Bar, "Class defination must be a class"); | ||
equal(bar.sayHi, Bar.sayHi, "Static method must be the same on both class and class instance"); | ||
@@ -789,5 +798,5 @@ equal(bar.sayHi, Foo.sayHi, "Static method must be the same on both class and class instance"); | ||
equal(isFunction(Foo.prototype.log), true, "Class plugins mechanism works incorrectly"); | ||
equal(isFunction(foo.log), true, "Class plugins mechanism works incorrectly"); | ||
equal(functionOrNil(Foo.prototype.log), Foo.prototype.log, "Class plugins mechanism works incorrectly"); | ||
equal(functionOrNil(foo.log), foo.log, "Class plugins mechanism works incorrectly"); | ||
delete Class.plugins.$log; | ||
}); |
@@ -15,3 +15,3 @@ var context = this; | ||
done = true; | ||
isFunction(callback) && callback(); | ||
functionOrNil(callback) && callback(); | ||
script.onload = script.onreadystatechange = null; | ||
@@ -35,7 +35,7 @@ head.removeChild(script); | ||
ok(exports.Class, "Class must be available in exports"); | ||
ok(exports.isMap, "isMap must be available in exports"); | ||
ok(exports.isArray, "isArray must be available in exports"); | ||
ok(exports.isFunction, "isFunction must be available in exports"); | ||
ok(exports.isString, "isString must be available in exports"); | ||
ok(exports.isClass, "isClass must be available in exports"); | ||
ok(exports.mapOrNil, "mapOrNil must be available in exports"); | ||
ok(exports.arrayOrNil, "arrayOrNil must be available in exports"); | ||
ok(exports.functionOrNil, "functionOrNil must be available in exports"); | ||
ok(exports.stringOrNil, "stringOrNil must be available in exports"); | ||
ok(exports.classOrNil, "classOrNil must be available in exports"); | ||
delete context.module; | ||
@@ -42,0 +42,0 @@ }); |
var context = this, | ||
Class = jsface.Class, | ||
extend = jsface.extend, | ||
isMap = jsface.isMap, | ||
isArray = jsface.isArray, | ||
isFunction = jsface.isFunction, | ||
isString = jsface.isString, | ||
isClass = jsface.isClass, | ||
pointcut = jsface.pointcut; | ||
@@ -10,0 +5,0 @@ |
@@ -1,8 +0,5 @@ | ||
var context = this, | ||
extend = jsface.extend, | ||
isMap = jsface.isMap, | ||
isArray = jsface.isArray, | ||
isFunction = jsface.isFunction, | ||
isString = jsface.isString, | ||
isClass = jsface.isClass; | ||
var context = this, | ||
extend = jsface.extend, | ||
isMap = jsface.isMap, | ||
functionOrNil = jsface.functionOrNil; | ||
@@ -16,5 +13,5 @@ test("$ready plugin: class notifies itself", function() { | ||
equal(this, clazz, "clazz must be equal to this"); | ||
ok(isFunction(api.$ready), "$ready works incorrectly"); | ||
ok(isFunction(api.echo), "$ready works incorrectly"); | ||
ok(isFunction(clazz.prototype.echo), "$ready works incorrectly"); | ||
ok(functionOrNil(api.$ready), "$ready works incorrectly"); | ||
ok(functionOrNil(api.echo), "$ready works incorrectly"); | ||
ok(functionOrNil(clazz.prototype.echo), "$ready works incorrectly"); | ||
ok( !parent, "$ready works incorrectly"); | ||
@@ -40,3 +37,3 @@ }, | ||
ok( !api.$ready, "$ready works incorrectly"); | ||
ok(isFunction(clazz.prototype.echo2), "$ready works incorrectly"); | ||
ok(functionOrNil(clazz.prototype.echo2), "$ready works incorrectly"); | ||
} | ||
@@ -57,1 +54,19 @@ }, | ||
}); | ||
test("$ready plugin: class is notified when its subclasses are ready (multiple levels)", function() { | ||
var count = 0; | ||
var Foo = Class({ | ||
$ready: function(clazz, parent, api) { | ||
if (this !== clazz) { | ||
count++; | ||
} | ||
} | ||
}); | ||
var Bar1 = Class(Foo, {}); | ||
var Bar2 = Class(Bar1, {}); | ||
var Bar3 = Class(Bar2, {}); | ||
ok(count === 3, "$ready must be executed in multiple level inheritance"); | ||
}); |
Sorry, the diff of this file is not supported yet
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 2 instances in 1 package
161941
33
4288
7
3
416