jsface
Advanced tools
Comparing version 2.3.0 to 2.4.9
{ | ||
"name": "jsface" | ||
"name": "jsface", | ||
"version": "2.4.9", | ||
"main": "jsface.js", | ||
"ignore": [ | ||
".gitignore", | ||
".travis.yml", | ||
"CHANGELOG.txt", | ||
"package.json", | ||
"README.md", | ||
"Gruntfile.js", | ||
"dist", | ||
"samples", | ||
"test" | ||
] | ||
} |
@@ -0,1 +1,30 @@ | ||
v2.4.9 Jul 23, 2015 | ||
---------------------------------------------------------------- | ||
* Support getter/setter on mixins | ||
v2.4.6 Jun 24, 2015 | ||
---------------------------------------------------------------- | ||
* Implement $const plugin (thanks to Federico Budassi) | ||
v2.4.5 May 26, 2015 | ||
---------------------------------------------------------------- | ||
* Rename $static to $statics ($statics will be removed in 2.5.0) | ||
v2.4.2 May 22, 2015 | ||
---------------------------------------------------------------- | ||
* Fix undefined issue caused by getter/setter impl | ||
v2.4.1 Apr 25, 2015 | ||
---------------------------------------------------------------- | ||
* Merge Milos Zikic's getter/setter support | ||
v2.4.0 Mar 07, 2015 | ||
---------------------------------------------------------------- | ||
* Remove static properties from instance (#25). Class level only | ||
* Integrate jsface.$ready plugin into core | ||
* Remove jsface.stringOrNil, sorry if it breaks ;-) | ||
* Fix (#26) - Thanks Freddy Snijder! | ||
* Fix (#12) | ||
* More unit tests | ||
v2.3.0 Dec 02, 2014 | ||
@@ -74,2 +103,2 @@ ---------------------------------------------------------------- | ||
* Initial release. A complete new look from version 1.2a which | ||
was developed for browsers. | ||
was developed for browsers. |
@@ -5,6 +5,6 @@ /* | ||
* | ||
* Copyright (c) 2009-2013 Tan Nhu | ||
* Copyright (c) Tan Nhu | ||
* Licensed under MIT license (https://github.com/tnhu/jsface/blob/master/LICENSE.txt) | ||
*/ | ||
(function(context, OBJECT, NUMBER, LENGTH, toString, undefined, oldClass, jsface) { | ||
(function(context, OBJECT, NUMBER, LENGTH, toString, readyFns, readyCount, undefined, oldClass, jsface) { | ||
/** | ||
@@ -32,9 +32,2 @@ * Return a map itself or null. A map is a set of { key: value } | ||
/** | ||
* 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 | ||
@@ -58,3 +51,2 @@ * @param obj object to be checked | ||
object[key] = value; | ||
if (iClass) { oPrototype[key] = value; } // class? copy to prototype as well | ||
} | ||
@@ -80,3 +72,3 @@ } | ||
// copy static properties and prototype.* to object | ||
if (mapOrNil(subject)) { | ||
if (mapOrNil(subject) || iClass) { | ||
for (key in subject) { | ||
@@ -100,2 +92,22 @@ copier(key, subject[key], ignoredKeys, object, iClass, oPrototype); | ||
/** | ||
* To make object fully immutable, freeze each object inside it. | ||
* @param object to deep freeze | ||
*/ | ||
function deepFreeze(object) { | ||
var prop, propKey; | ||
Object.freeze(object); // first freeze the object | ||
for (propKey in object) { | ||
prop = object[propKey]; | ||
if (!object.hasOwnProperty(propKey) || !(typeof prop === 'object') || Object.isFrozen(prop)) { | ||
// If the object is on the prototype, not an object, or is already frozen, | ||
// skip it. Note that this might leave an unfrozen reference somewhere in the | ||
// object if there is an already frozen object containing an unfrozen object. | ||
continue; | ||
} | ||
deepFreeze(prop); // recursively call deepFreeze | ||
} | ||
} | ||
/** | ||
* Create a class. | ||
@@ -108,8 +120,10 @@ * @param parent parent class(es) | ||
if ( !api) { | ||
parent = (api = parent, 0); // !api means there's no parent | ||
parent = (api = parent, 0); // !api means there's no parent | ||
} | ||
// TODO remove $statics, use $static instead | ||
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; | ||
ignoredKeys = { constructor: 1, $singleton: 1, $static:1, $statics: 1, prototype: 1, $super: 1, $superp: 1, main: 1, toString: 0 }, | ||
plugins = Class.plugins, | ||
rootParent, parentClass, Stub; | ||
@@ -119,3 +133,3 @@ api = (typeof api === "function" ? api() : api) || {}; // execute api if it's a function | ||
singleton = api.$singleton; | ||
statics = api.$statics; | ||
statics = api.$statics || api.$static; | ||
@@ -126,11 +140,24 @@ // add plugins' keys into ignoredKeys | ||
// construct constructor | ||
clazz = singleton ? {} : (constructor ? constructor : function(){}); | ||
clazz = singleton ? function(){} : (constructor ? constructor : function(){}); | ||
// make sure parent is always an array | ||
parent = !parent || arrayOrNil(parent) ? parent : [ parent ]; | ||
parent = !parent || parent instanceof Array ? parent : [ parent ]; | ||
len = parent && parent.length; | ||
rootParent = parent[0]; | ||
if ( !singleton && len) { | ||
clazz.prototype = classOrNil(parent[0]) ? new parent[0] : parent[0]; | ||
clazz.prototype.constructor = clazz; | ||
parentClass = rootParent.prototype && rootParent === rootParent.prototype.constructor && rootParent; | ||
if ( !parentClass) { | ||
clazz.prototype = rootParent; | ||
} else { | ||
// Constributed by Freddy Snijder (https://github.com/tnhu/jsface/issues/26) | ||
Stub = function(){}; | ||
Stub.prototype = parentClass.prototype; | ||
Stub.prototype.constructor = Stub; | ||
clazz.prototype = new Stub(); | ||
clazz.prototype.constructor = clazz; // restoring proper constructor for child class | ||
parentClass.prototype.constructor = parentClass; // restoring proper constructor for parent class | ||
} | ||
} | ||
@@ -141,12 +168,19 @@ | ||
// do inherit | ||
// do inherit static properties and extentions (parents other than the first one) | ||
while (i < len) { | ||
p = parent[i++]; | ||
// copy static properties | ||
for (key in p) { | ||
if ( !ignoredKeys[key]) { | ||
bindTo[key] = p[key]; | ||
if ( !singleton) { clazz[key] = p[key]; } | ||
clazz[key] = p[key]; | ||
} | ||
} | ||
for (key in p.prototype) { if ( !ignoredKeys[key]) { bindTo[key] = p.prototype[key]; } } | ||
if ( !singleton && i !== 0) { | ||
for (key in p.prototype) { | ||
if ( !ignoredKeys[key]) { | ||
bindTo[key] = p.prototype[key]; | ||
} | ||
} | ||
} | ||
} | ||
@@ -157,18 +191,26 @@ | ||
if ( !ignoredKeys[key]) { | ||
bindTo[key] = api[key]; | ||
var prop = api[key]; | ||
if (prop && (prop.get || prop.set)) { // check if it is a property descriptor | ||
prop.enumerable = true; | ||
Object.defineProperty(bindTo, key, prop); | ||
} else { | ||
bindTo[key] = prop; | ||
} | ||
} | ||
} | ||
// 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; | ||
// copy static properties from statics to clazz | ||
for (key in statics) { | ||
clazz[key] = statics[key]; | ||
} | ||
for (key in plugins) { plugins[key](clazz, parent, api); } // pass control to plugins | ||
if (functionOrNil(api.main)) { api.main.call(clazz, clazz); } // execute main() | ||
// add $super and $superp to refer to parent class and parent.prototype (if applied) | ||
p = parent && rootParent || parent; | ||
clazz.$super = p; | ||
clazz.$superp = p && p.prototype || p; | ||
for (key in plugins) { plugins[key](clazz, parent, api); } // pass control to plugins | ||
if (typeof api.main === "function") { api.main.call(clazz, clazz); } // execute main() | ||
return clazz; | ||
@@ -178,4 +220,54 @@ } | ||
/* Class plugins repository */ | ||
Class.plugins = {}; | ||
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--) { | ||
for (i = 0; i < readyCount; i++) { | ||
entry = readyFns[i]; | ||
pa = parent[len]; | ||
if (pa === entry[0]) { | ||
entry[1].call(pa, clazz, parent, api); | ||
count--; | ||
} | ||
if ( !count) { break; } | ||
} | ||
} | ||
// 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 ( !loop && functionOrNil(r)) { | ||
r.call(clazz, clazz, parent, api); // invoke ready from current class | ||
readyFns.push([ clazz, r ]); | ||
readyCount++; | ||
} | ||
}, | ||
$const: function (clazz, parent, api) { | ||
var key, | ||
consts = api.$const; | ||
// copy immutable properties from consts to clazz and freeze them recursively | ||
for (key in consts) { | ||
Object.defineProperty(clazz, key, { enumerable: true, value: consts[key] }); // enumerable for proper inheritance | ||
if ((typeof clazz[key] === 'object') && !Object.isFrozen(clazz[key])) { | ||
deepFreeze(clazz[key]); // if property is an unfrozen object, freeze it recursively | ||
} | ||
} | ||
} | ||
}; | ||
/* Initialization */ | ||
@@ -188,3 +280,2 @@ jsface = { | ||
functionOrNil: functionOrNil, | ||
stringOrNil : stringOrNil, | ||
classOrNil : classOrNil | ||
@@ -201,2 +292,2 @@ }; | ||
} | ||
})(this, "object", "number", "length", Object.prototype.toString); | ||
})(this, "object", "number", "length", Object.prototype.toString, [], 0); |
@@ -1,5 +0,1 @@ | ||
/* | ||
* JSFace Object Oriented Programming Library. | ||
* Copyright (c) 2009-2013 Tan Nhu, http://lnkd.in/tnhu | ||
*/ | ||
!function(a,b,c,d,e,f,g,h){function i(a){return a&&typeof a===b&&!(typeof a.length===c&&!a.propertyIsEnumerable(d))&&a||null}function j(a){return a&&typeof a===b&&typeof a.length===c&&!a.propertyIsEnumerable(d)&&a||null}function k(a){return a&&"function"==typeof a&&a||null}function l(a){return"[object String]"===e.apply(a)&&a||null}function m(a){return k(a)&&a.prototype&&a===a.prototype.constructor&&a||null}function n(a,b,c,d,e,f){c&&c.hasOwnProperty(a)||(d[a]=b,e&&(f[a]=b))}function o(a,b,c){if(j(b))for(var d=b.length;--d>=0;)o(a,b[d],c);else{c=c||{constructor:1,$super:1,prototype:1,$superp:1};var e,f,g=m(a),h=m(b),k=a.prototype;if(i(b))for(e in b)n(e,b[e],c,a,g,k);if(h){f=b.prototype;for(e in f)n(e,f[e],c,a,g,k)}g&&h&&o(k,b.prototype,c)}}function p(a,b){b||(b=a,a=0);var c,d,e,f,g,h,i,l,n=0,o={constructor:1,$singleton:1,$statics:1,prototype:1,$super:1,$superp:1,main:1,toString:0},q=p.plugins;b=("function"==typeof b?b():b)||{},d=b.hasOwnProperty("constructor")?b.constructor:0,e=b.$singleton,f=b.$statics;for(g in q)o[g]=1;for(c=e?{}:d?d:function(){},a=!a||j(a)?a:[a],i=a&&a.length,!e&&i&&(c.prototype=m(a[0])?new a[0]:a[0],c.prototype.constructor=c),h=e?c:c.prototype;i>n;){l=a[n++];for(g in l)o[g]||(h[g]=l[g],e||(c[g]=l[g]));for(g in l.prototype)o[g]||(h[g]=l.prototype[g])}for(g in b)o[g]||(h[g]=b[g]);for(g in f)c[g]=h[g]=f[g];e||(l=a&&a[0]||a,c.$super=l,c.$superp=l&&l.prototype?l.prototype:l);for(g in q)q[g](c,a,b);return k(b.main)&&b.main.call(c,c),c}p.plugins={},h={Class:p,extend:o,mapOrNil:i,arrayOrNil:j,functionOrNil:k,stringOrNil:l,classOrNil:m},"undefined"!=typeof module&&module.exports?module.exports=h:(g=a.Class,a.Class=p,a.jsface=h,h.noConflict=function(){a.Class=g})}(this,"object","number","length",Object.prototype.toString); | ||
!function(t,o,r,e,n,p,c,i,u,f){function s(t){return t&&typeof t===o&&!(typeof t.length===r&&!t.propertyIsEnumerable(e))&&t||null}function l(t){return t&&typeof t===o&&typeof t.length===r&&!t.propertyIsEnumerable(e)&&t||null}function y(t){return t&&"function"==typeof t&&t||null}function a(t){return y(t)&&t.prototype&&t===t.prototype.constructor&&t||null}function $(t,o,r,e,n,p){r&&r.hasOwnProperty(t)||(e[t]=o)}function b(t,o,r){if(l(o))for(var e=o.length;--e>=0;)b(t,o[e],r);else{r=r||{constructor:1,$super:1,prototype:1,$superp:1};var n,p,c=a(t),i=a(o),u=t.prototype;if(s(o)||c)for(n in o)$(n,o[n],r,t,c,u);if(i){p=o.prototype;for(n in p)$(n,p[n],r,t,c,u)}c&&i&&b(u,o.prototype,r)}}function g(t){var o,r;Object.freeze(t);for(r in t)o=t[r],t.hasOwnProperty(r)&&"object"==typeof o&&!Object.isFrozen(o)&&g(o)}function O(t,o){o||(o=t,t=0);var r,e,n,p,c,i,u,f,s,l,y,a=0,$={constructor:1,$singleton:1,$static:1,$statics:1,prototype:1,$super:1,$superp:1,main:1,toString:0},b=O.plugins;o=("function"==typeof o?o():o)||{},e=o.hasOwnProperty("constructor")?o.constructor:0,n=o.$singleton,p=o.$statics||o.$static;for(c in b)$[c]=1;for(r=n?function(){}:e?e:function(){},t=!t||t instanceof Array?t:[t],u=t&&t.length,s=t[0],!n&&u&&(l=s.prototype&&s===s.prototype.constructor&&s,l?(y=function(){},y.prototype=l.prototype,y.prototype.constructor=y,r.prototype=new y,r.prototype.constructor=r,l.prototype.constructor=l):r.prototype=s),i=n?r:r.prototype;u>a;){f=t[a++];for(c in f)$[c]||(r[c]=f[c]);if(!n&&0!==a)for(c in f.prototype)$[c]||(i[c]=f.prototype[c])}for(c in o)if(!$[c]){var g=o[c];g&&(g.get||g.set)?(g.enumerable=!0,Object.defineProperty(i,c,g)):i[c]=g}for(c in p)r[c]=p[c];f=t&&s||t,r.$super=f,r.$superp=f&&f.prototype||f;for(c in b)b[c](r,t,o);return"function"==typeof o.main&&o.main.call(r,r),r}O.plugins={$ready:function m(t,o,r,e){for(var n,i,u,f=r.$ready,s=o?o.length:0,l=s,a=s&&o[0].$super;s--;)for(i=0;c>i&&(u=p[i],n=o[s],n===u[0]&&(u[1].call(n,t,o,r),l--),l);i++);a&&m(t,[a],r,!0),!e&&y(f)&&(f.call(t,t,o,r),p.push([t,f]),c++)},$const:function(t,o,r){var e,n=r.$const;for(e in n)Object.defineProperty(t,e,{enumerable:!0,value:n[e]}),"object"!=typeof t[e]||Object.isFrozen(t[e])||g(t[e])}},f={Class:O,extend:b,mapOrNil:s,arrayOrNil:l,functionOrNil:y,classOrNil:a},"undefined"!=typeof module&&module.exports?module.exports=f:(u=t.Class,t.Class=O,t.jsface=f,f.noConflict=function(){t.Class=u})}(this,"object","number","length",Object.prototype.toString,[],0); |
@@ -5,3 +5,3 @@ /* | ||
* | ||
* Copyright (c) 2009-2012 Tan Nhu | ||
* Copyright (c) Tan Nhu | ||
* Licensed under MIT license (https://github.com/tnhu/jsface/blob/master/LICENSE.txt). | ||
@@ -21,4 +21,2 @@ */ | ||
ADVISOR = "___advisors___", | ||
INVALID = "Invalid ", | ||
NON_FUNC = "Non-function property named ", | ||
noop = function(){}; | ||
@@ -135,11 +133,13 @@ | ||
iConstructor = (key === "constructor"); | ||
iStatic = iClass && bindTo[key] === clazz[key]; | ||
fn = restore(iConstructor && iClass && clazz || bindTo[key], advisor); | ||
iStatic = iClass && clazz[key]; | ||
constructor = constructor || (iConstructor && fn); | ||
if (fn && !iConstructor) { | ||
bindTo[key] = fn; | ||
if (iStatic) { | ||
clazz[key] = fn; | ||
if (key === "constructor" && iClass) { | ||
constructor = restore(iClass, advisor);; | ||
} else { | ||
if (clazz[key]) { | ||
clazz[key] = restore(clazz[key], advisor); | ||
} | ||
if (bindTo[key]) { | ||
bindTo[key] = restore(bindTo[key], advisor); | ||
} | ||
} | ||
@@ -153,12 +153,7 @@ } | ||
for (var name in bindTo) { | ||
var fn = bindTo[name], | ||
iStatic = iClass && bindTo[name] === clazz[name]; | ||
bindTo[name] = restore(fn) || bindTo[name]; | ||
// restore static method | ||
if (iStatic) { | ||
clazz[name] = bindTo[name]; | ||
} | ||
bindTo[name] = restore(bindTo[name]) || bindTo[name]; | ||
} | ||
for (var name in clazz) { | ||
clazz[name] = restore(clazz[name]) || clazz[name]; | ||
} | ||
} else { // type 2: "remove", advisor | ||
@@ -171,3 +166,3 @@ doRestore(advisor, advisor); | ||
} else { | ||
throw INVALID + "params"; | ||
throw "Invalid params"; | ||
} | ||
@@ -181,3 +176,3 @@ return constructor || clazz; | ||
jsface.pointcut = function pointcut(clazz, opts) { | ||
var iClass = functionOrNil(clazz), | ||
var iClass = classOrNil(clazz) || functionOrNil(clazz), | ||
iInstance = mapOrNil(clazz), | ||
@@ -189,3 +184,3 @@ iRemove = (/^remove ?/.exec(opts) !== null), | ||
if ( !(iClass || iInstance) || !(mapOrNil(opts) || iRemove)) { | ||
throw INVALID + "params"; | ||
throw "Invalid params"; | ||
} | ||
@@ -204,10 +199,10 @@ bindTo = iClass ? clazz.prototype : clazz; | ||
after = mapOrNil(pointcuts) && !pointcuts.after ? noop : pointcuts.after, | ||
isStatic; | ||
isStatic, found = false; | ||
// check if before & after are valid | ||
if ( !functionOrNil(before)) { | ||
throw INVALID + method + ":before"; | ||
throw "Invalid " + method + ":before"; | ||
} | ||
if ( !functionOrNil(after)) { | ||
throw INVALID + method + ":after"; | ||
throw "Invalid " + method + ":after"; | ||
} | ||
@@ -219,3 +214,3 @@ | ||
} else { | ||
throw NON_FUNC + method; | ||
throw "Non-function property named " + method + " on instance"; | ||
} | ||
@@ -226,9 +221,15 @@ } else { | ||
} else { | ||
if (functionOrNil(bindTo[method])) { | ||
isStatic = iClass && bindTo[method] === clazz[method]; | ||
if (functionOrNil(clazz[method])) { // static method | ||
clazz[method] = wrap(clazz[method], before, after, opts); | ||
found = true; | ||
} | ||
if (functionOrNil(bindTo[method])) { // normal (non-static) method | ||
bindTo[method] = wrap(bindTo[method], before, after, opts); | ||
if (isStatic) { clazz[method] = bindTo[method]; } | ||
} else { | ||
throw NON_FUNC + method; | ||
found = true; | ||
} | ||
if ( !found) { | ||
throw "Non-function property named " + method + " on class"; | ||
} | ||
} | ||
@@ -235,0 +236,0 @@ } |
@@ -1,5 +0,1 @@ | ||
/* | ||
* JSFace Object Oriented Programming Library. | ||
* Copyright (c) 2009-2013 Tan Nhu, http://lnkd.in/tnhu | ||
*/ | ||
!function(a){function b(a,b,c,d){function e(){for(var a,b,c=e[k],d=c.length,f=e[l],g=f.length,h=e[m];d--;)if(b=c[d].apply(this,arguments),i(b)&&b.$skip===!0)return b.$data;for(a=h.apply(this,arguments);g--;)if(b=f[g].apply(this,arguments),i(b)&&b.$skip===!0)return a;return a}return a[j]===a?(a[k].push(b),a[l].push(c),a[n].push(d),a):(h(e,a,0,!0),f(a)&&(e.prototype=a.prototype),e[k]=[b],e[l]=[c],e[m]=a,e[n]=[d],e[j]=e,e.$super=a.$super,e.$superp=a.$superp,e)}function c(a,b){var d,e,f;if(a&&a===a[j])if(b){for(e=f=a[n].length;e--&&a[n][e]!==b;);if(e>=0){if(1===f)return c(a);a[n].splice(e,1),a[k].splice(e,1),a[l].splice(e,1)}}else d=a[m],delete a[m],delete a[n],delete a[k],delete a[l],delete a[j];return d}function d(a,b,d,e,f,g){function h(b,d){var f,h,j,k,l;for(f in b)h=f/1==f?b[f]:f,j="constructor"===h,k=e&&g[h]===a[h],l=c(j&&e&&a||g[h],d),i=i||j&&l,l&&!j&&(g[h]=l,k&&(a[h]=l))}var i,k,l,n;if("remove"===b)if(d)h(d,d);else{i=e&&a===a[j]&&a[m];for(var p in g){var n=g[p],q=e&&g[p]===a[p];g[p]=c(n)||g[p],q&&(a[p]=g[p])}}else{if(null===(k=/^remove /.exec(b)))throw o+"params";l=b.replace(k,"").split(" "),h(l)}return i||a}var e=a.jsface||require("./jsface"),f=(e.Class,e.classOrNil),g=e.functionOrNil,h=e.extend,i=e.mapOrNil,j="___wrapper___",k="___before_fns___",l="___after_fns___",m="___origin_fn___",n="___advisors___",o="Invalid ",p="Non-function property named ",q=function(){};e.pointcut=function(a,c){var e,f,h,j=g(a),k=i(a),l=null!==/^remove ?/.exec(c),m=l&&arguments[2];if(!j&&!k||!i(c)&&!l)throw o+"params";if(e=j?a.prototype:a,l)return d(a,c,m,j,k,e);for(f in c){h=c[f],h=g(h)?{before:h}:h;var n,r=i(h)&&!h.before?q:h.before,s=i(h)&&!h.after?q:h.after;if(!g(r))throw o+f+":before";if(!g(s))throw o+f+":after";if(k){if(!g(e[f]))throw p+f;e[f]=b(e[f],r,s,c)}else if("constructor"===f)a=b(a,r,s,c);else{if(!g(e[f]))throw p+f;n=j&&e[f]===a[f],e[f]=b(e[f],r,s,c),n&&(a[f]=e[f])}}return a},"undefined"!=typeof module&&module.exports&&(module.exports=e.pointcut)}(this); | ||
!function(e){function r(e,r,t,n){function o(){for(var e,r,t=o[p],n=t.length,i=o[l],f=i.length,s=o[_];n--;)if(r=t[n].apply(this,arguments),u(r)&&r.$skip===!0)return r.$data;for(e=s.apply(this,arguments);f--;)if(r=i[f].apply(this,arguments),u(r)&&r.$skip===!0)return e;return e}return e[a]===e?(e[p].push(r),e[l].push(t),e[c].push(n),e):(s(o,e,0,!0),i(e)&&(o.prototype=e.prototype),o[p]=[r],o[l]=[t],o[_]=e,o[c]=[n],o[a]=o,o.$super=e.$super,o.$superp=e.$superp,o)}function t(e,r){var n,o,i;if(e&&e===e[a])if(r){for(o=i=e[c].length;o--&&e[c][o]!==r;);if(o>=0){if(1===i)return t(e);e[c].splice(o,1),e[p].splice(o,1),e[l].splice(o,1)}}else n=e[_],delete e[_],delete e[c],delete e[p],delete e[l],delete e[a];return n}function n(e,r,n,o,i,f){function s(r,n){var i,s,a,p;for(i in r)s=i/1==i?r[i]:i,a="constructor"===s,p=o&&e[s],"constructor"===s&&o?u=t(o,n):(e[s]&&(e[s]=t(e[s],n)),f[s]&&(f[s]=t(f[s],n)))}var u,p,l;if("remove"===r)if(n)s(n,n);else{u=o&&e===e[a]&&e[_];for(var c in f)f[c]=t(f[c])||f[c];for(var c in e)e[c]=t(e[c])||e[c]}else{if(null===(p=/^remove /.exec(r)))throw"Invalid params";l=r.replace(p,"").split(" "),s(l)}return u||e}var o=e.jsface||require("./jsface"),i=(o.Class,o.classOrNil),f=o.functionOrNil,s=o.extend,u=o.mapOrNil,a="___wrapper___",p="___before_fns___",l="___after_fns___",_="___origin_fn___",c="___advisors___",d=function(){};o.pointcut=function(e,t){var o,s,a,p=i(e)||f(e),l=u(e),_=null!==/^remove ?/.exec(t),c=_&&arguments[2];if(!p&&!l||!u(t)&&!_)throw"Invalid params";if(o=p?e.prototype:e,_)return n(e,t,c,p,l,o);for(s in t){a=t[s],a=f(a)?{before:a}:a;var v=u(a)&&!a.before?d:a.before,h=u(a)&&!a.after?d:a.after,m=!1;if(!f(v))throw"Invalid "+s+":before";if(!f(h))throw"Invalid "+s+":after";if(l){if(!f(o[s]))throw"Non-function property named "+s+" on instance";o[s]=r(o[s],v,h,t)}else if("constructor"===s)e=r(e,v,h,t);else if(f(e[s])&&(e[s]=r(e[s],v,h,t),m=!0),f(o[s])&&(o[s]=r(o[s],v,h,t),m=!0),!m)throw"Non-function property named "+s+" on class"}return e},"undefined"!=typeof module&&module.exports&&(module.exports=o.pointcut)}(this); |
165
jsface.js
@@ -5,6 +5,6 @@ /* | ||
* | ||
* Copyright (c) 2009-2013 Tan Nhu | ||
* Copyright (c) Tan Nhu | ||
* Licensed under MIT license (https://github.com/tnhu/jsface/blob/master/LICENSE.txt) | ||
*/ | ||
(function(context, OBJECT, NUMBER, LENGTH, toString, undefined, oldClass, jsface) { | ||
(function(context, OBJECT, NUMBER, LENGTH, toString, readyFns, readyCount, undefined, oldClass, jsface) { | ||
/** | ||
@@ -32,9 +32,2 @@ * Return a map itself or null. A map is a set of { key: value } | ||
/** | ||
* 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 | ||
@@ -58,3 +51,2 @@ * @param obj object to be checked | ||
object[key] = value; | ||
if (iClass) { oPrototype[key] = value; } // class? copy to prototype as well | ||
} | ||
@@ -80,3 +72,3 @@ } | ||
// copy static properties and prototype.* to object | ||
if (mapOrNil(subject)) { | ||
if (mapOrNil(subject) || iClass) { | ||
for (key in subject) { | ||
@@ -100,2 +92,22 @@ copier(key, subject[key], ignoredKeys, object, iClass, oPrototype); | ||
/** | ||
* To make object fully immutable, freeze each object inside it. | ||
* @param object to deep freeze | ||
*/ | ||
function deepFreeze(object) { | ||
var prop, propKey; | ||
Object.freeze(object); // first freeze the object | ||
for (propKey in object) { | ||
prop = object[propKey]; | ||
if (!object.hasOwnProperty(propKey) || !(typeof prop === 'object') || Object.isFrozen(prop)) { | ||
// If the object is on the prototype, not an object, or is already frozen, | ||
// skip it. Note that this might leave an unfrozen reference somewhere in the | ||
// object if there is an already frozen object containing an unfrozen object. | ||
continue; | ||
} | ||
deepFreeze(prop); // recursively call deepFreeze | ||
} | ||
} | ||
/** | ||
* Create a class. | ||
@@ -108,8 +120,10 @@ * @param parent parent class(es) | ||
if ( !api) { | ||
parent = (api = parent, 0); // !api means there's no parent | ||
parent = (api = parent, 0); // !api means there's no parent | ||
} | ||
// TODO remove $statics, use $static instead | ||
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; | ||
ignoredKeys = { constructor: 1, $singleton: 1, $static:1, $statics: 1, prototype: 1, $super: 1, $superp: 1, main: 1, toString: 0 }, | ||
plugins = Class.plugins, | ||
rootParent, parentClass, Stub; | ||
@@ -119,3 +133,3 @@ api = (typeof api === "function" ? api() : api) || {}; // execute api if it's a function | ||
singleton = api.$singleton; | ||
statics = api.$statics; | ||
statics = api.$statics || api.$static; | ||
@@ -126,11 +140,24 @@ // add plugins' keys into ignoredKeys | ||
// construct constructor | ||
clazz = singleton ? {} : (constructor ? constructor : function(){}); | ||
clazz = singleton ? function(){} : (constructor ? constructor : function(){}); | ||
// make sure parent is always an array | ||
parent = !parent || arrayOrNil(parent) ? parent : [ parent ]; | ||
parent = !parent || parent instanceof Array ? parent : [ parent ]; | ||
len = parent && parent.length; | ||
rootParent = parent[0]; | ||
if ( !singleton && len) { | ||
clazz.prototype = classOrNil(parent[0]) ? new parent[0] : parent[0]; | ||
clazz.prototype.constructor = clazz; | ||
parentClass = rootParent.prototype && rootParent === rootParent.prototype.constructor && rootParent; | ||
if ( !parentClass) { | ||
clazz.prototype = rootParent; | ||
} else { | ||
// Constributed by Freddy Snijder (https://github.com/tnhu/jsface/issues/26) | ||
Stub = function(){}; | ||
Stub.prototype = parentClass.prototype; | ||
Stub.prototype.constructor = Stub; | ||
clazz.prototype = new Stub(); | ||
clazz.prototype.constructor = clazz; // restoring proper constructor for child class | ||
parentClass.prototype.constructor = parentClass; // restoring proper constructor for parent class | ||
} | ||
} | ||
@@ -141,12 +168,19 @@ | ||
// do inherit | ||
// do inherit static properties and extentions (parents other than the first one) | ||
while (i < len) { | ||
p = parent[i++]; | ||
// copy static properties | ||
for (key in p) { | ||
if ( !ignoredKeys[key]) { | ||
bindTo[key] = p[key]; | ||
if ( !singleton) { clazz[key] = p[key]; } | ||
clazz[key] = p[key]; | ||
} | ||
} | ||
for (key in p.prototype) { if ( !ignoredKeys[key]) { bindTo[key] = p.prototype[key]; } } | ||
if ( !singleton && i !== 0) { | ||
for (key in p.prototype) { | ||
if ( !ignoredKeys[key]) { | ||
bindTo[key] = p.prototype[key]; | ||
} | ||
} | ||
} | ||
} | ||
@@ -157,18 +191,26 @@ | ||
if ( !ignoredKeys[key]) { | ||
bindTo[key] = api[key]; | ||
var prop = api[key]; | ||
if (prop && (prop.get || prop.set)) { // check if it is a property descriptor | ||
prop.enumerable = true; | ||
Object.defineProperty(bindTo, key, prop); | ||
} else { | ||
bindTo[key] = prop; | ||
} | ||
} | ||
} | ||
// 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; | ||
// copy static properties from statics to clazz | ||
for (key in statics) { | ||
clazz[key] = statics[key]; | ||
} | ||
for (key in plugins) { plugins[key](clazz, parent, api); } // pass control to plugins | ||
if (functionOrNil(api.main)) { api.main.call(clazz, clazz); } // execute main() | ||
// add $super and $superp to refer to parent class and parent.prototype (if applied) | ||
p = parent && rootParent || parent; | ||
clazz.$super = p; | ||
clazz.$superp = p && p.prototype || p; | ||
for (key in plugins) { plugins[key](clazz, parent, api); } // pass control to plugins | ||
if (typeof api.main === "function") { api.main.call(clazz, clazz); } // execute main() | ||
return clazz; | ||
@@ -178,4 +220,54 @@ } | ||
/* Class plugins repository */ | ||
Class.plugins = {}; | ||
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--) { | ||
for (i = 0; i < readyCount; i++) { | ||
entry = readyFns[i]; | ||
pa = parent[len]; | ||
if (pa === entry[0]) { | ||
entry[1].call(pa, clazz, parent, api); | ||
count--; | ||
} | ||
if ( !count) { break; } | ||
} | ||
} | ||
// 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 ( !loop && functionOrNil(r)) { | ||
r.call(clazz, clazz, parent, api); // invoke ready from current class | ||
readyFns.push([ clazz, r ]); | ||
readyCount++; | ||
} | ||
}, | ||
$const: function (clazz, parent, api) { | ||
var key, | ||
consts = api.$const; | ||
// copy immutable properties from consts to clazz and freeze them recursively | ||
for (key in consts) { | ||
Object.defineProperty(clazz, key, { enumerable: true, value: consts[key] }); // enumerable for proper inheritance | ||
if ((typeof clazz[key] === 'object') && !Object.isFrozen(clazz[key])) { | ||
deepFreeze(clazz[key]); // if property is an unfrozen object, freeze it recursively | ||
} | ||
} | ||
} | ||
}; | ||
/* Initialization */ | ||
@@ -188,3 +280,2 @@ jsface = { | ||
functionOrNil: functionOrNil, | ||
stringOrNil : stringOrNil, | ||
classOrNil : classOrNil | ||
@@ -201,2 +292,2 @@ }; | ||
} | ||
})(this, "object", "number", "length", Object.prototype.toString); | ||
})(this, "object", "number", "length", Object.prototype.toString, [], 0); |
@@ -5,3 +5,3 @@ /* | ||
* | ||
* Copyright (c) 2009-2012 Tan Nhu | ||
* Copyright (c) Tan Nhu | ||
* Licensed under MIT license (https://github.com/tnhu/jsface/blob/master/LICENSE.txt). | ||
@@ -21,4 +21,2 @@ */ | ||
ADVISOR = "___advisors___", | ||
INVALID = "Invalid ", | ||
NON_FUNC = "Non-function property named ", | ||
noop = function(){}; | ||
@@ -135,11 +133,13 @@ | ||
iConstructor = (key === "constructor"); | ||
iStatic = iClass && bindTo[key] === clazz[key]; | ||
fn = restore(iConstructor && iClass && clazz || bindTo[key], advisor); | ||
iStatic = iClass && clazz[key]; | ||
constructor = constructor || (iConstructor && fn); | ||
if (fn && !iConstructor) { | ||
bindTo[key] = fn; | ||
if (iStatic) { | ||
clazz[key] = fn; | ||
if (key === "constructor" && iClass) { | ||
constructor = restore(iClass, advisor);; | ||
} else { | ||
if (clazz[key]) { | ||
clazz[key] = restore(clazz[key], advisor); | ||
} | ||
if (bindTo[key]) { | ||
bindTo[key] = restore(bindTo[key], advisor); | ||
} | ||
} | ||
@@ -153,12 +153,7 @@ } | ||
for (var name in bindTo) { | ||
var fn = bindTo[name], | ||
iStatic = iClass && bindTo[name] === clazz[name]; | ||
bindTo[name] = restore(fn) || bindTo[name]; | ||
// restore static method | ||
if (iStatic) { | ||
clazz[name] = bindTo[name]; | ||
} | ||
bindTo[name] = restore(bindTo[name]) || bindTo[name]; | ||
} | ||
for (var name in clazz) { | ||
clazz[name] = restore(clazz[name]) || clazz[name]; | ||
} | ||
} else { // type 2: "remove", advisor | ||
@@ -171,3 +166,3 @@ doRestore(advisor, advisor); | ||
} else { | ||
throw INVALID + "params"; | ||
throw "Invalid params"; | ||
} | ||
@@ -181,3 +176,3 @@ return constructor || clazz; | ||
jsface.pointcut = function pointcut(clazz, opts) { | ||
var iClass = functionOrNil(clazz), | ||
var iClass = classOrNil(clazz) || functionOrNil(clazz), | ||
iInstance = mapOrNil(clazz), | ||
@@ -189,3 +184,3 @@ iRemove = (/^remove ?/.exec(opts) !== null), | ||
if ( !(iClass || iInstance) || !(mapOrNil(opts) || iRemove)) { | ||
throw INVALID + "params"; | ||
throw "Invalid params"; | ||
} | ||
@@ -204,10 +199,10 @@ bindTo = iClass ? clazz.prototype : clazz; | ||
after = mapOrNil(pointcuts) && !pointcuts.after ? noop : pointcuts.after, | ||
isStatic; | ||
isStatic, found = false; | ||
// check if before & after are valid | ||
if ( !functionOrNil(before)) { | ||
throw INVALID + method + ":before"; | ||
throw "Invalid " + method + ":before"; | ||
} | ||
if ( !functionOrNil(after)) { | ||
throw INVALID + method + ":after"; | ||
throw "Invalid " + method + ":after"; | ||
} | ||
@@ -219,3 +214,3 @@ | ||
} else { | ||
throw NON_FUNC + method; | ||
throw "Non-function property named " + method + " on instance"; | ||
} | ||
@@ -226,9 +221,15 @@ } else { | ||
} else { | ||
if (functionOrNil(bindTo[method])) { | ||
isStatic = iClass && bindTo[method] === clazz[method]; | ||
if (functionOrNil(clazz[method])) { // static method | ||
clazz[method] = wrap(clazz[method], before, after, opts); | ||
found = true; | ||
} | ||
if (functionOrNil(bindTo[method])) { // normal (non-static) method | ||
bindTo[method] = wrap(bindTo[method], before, after, opts); | ||
if (isStatic) { clazz[method] = bindTo[method]; } | ||
} else { | ||
throw NON_FUNC + method; | ||
found = true; | ||
} | ||
if ( !found) { | ||
throw "Non-function property named " + method + " on class"; | ||
} | ||
} | ||
@@ -235,0 +236,0 @@ } |
@@ -6,3 +6,3 @@ { | ||
"author": "Tan Nhu <tnhu AT me . com>", | ||
"version": "2.3.0", | ||
"version": "2.4.9", | ||
"keywords": [ | ||
@@ -33,10 +33,8 @@ "jsface", | ||
"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" | ||
"del": "^1.2.0", | ||
"gulp": "^3.9.0", | ||
"gulp-rename": "^1.2.2", | ||
"gulp-uglify": "^1.2.0", | ||
"run-sequence": "^1.1.2" | ||
} | ||
} | ||
} |
105
README.md
![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) | ||
[![Build Status](https://secure.travis-ci.org/tannhu/jsface.svg?branch=master)](http://travis-ci.org/tannhu/jsface) | ||
## Features | ||
@@ -10,3 +11,3 @@ | ||
* Support CommonJS. | ||
* Support main, singleton, mixin, private properties, Aspect Oriented Programming. | ||
* Support getter/setter, constant, main, singleton, mixin, private properties, Aspect Oriented Programming. | ||
* Plugins mechanism to extend itself. | ||
@@ -18,12 +19,30 @@ | ||
Browser: | ||
### Browser | ||
#### bower | ||
``` sh | ||
bower install jsface | ||
``` | ||
``` html | ||
<script src="jsface.js" type="text/javascript"></script> | ||
<script src="bower_components/jsface/jsface.js"></script> | ||
``` | ||
JSFace introduces two global variables: jsface and Class. Other APIs are under jsface namespace. | ||
#### dnsjs | ||
In NodeJS environment, first install JSFace via npm: | ||
``` javascript | ||
<script src="https://cdn.jsdelivr.net/jsface/2.4.8/jsface.min.js"></script> | ||
``` | ||
#### Manually | ||
``` html | ||
<script src="jsface.js"></script> | ||
``` | ||
### NodeJS environment | ||
First install JSFace via npm: | ||
``` sh | ||
@@ -41,3 +60,3 @@ npm install jsface | ||
## API | ||
## Usage | ||
@@ -53,2 +72,12 @@ ### Define a class | ||
// Getter/Setter | ||
address: { | ||
get: function() { | ||
return this._address; | ||
}, | ||
set: function(value) { | ||
this._address = value; | ||
} | ||
}, | ||
toString: function() { | ||
@@ -69,7 +98,7 @@ return this.name + "/" + this.age; | ||
this.id = id; | ||
Student.$super.call(this, name, age); // Invoke parent's constructor | ||
Student.$super.call(this, name, age); // Call parent's constructor | ||
}, | ||
toString: function() { | ||
return this.id + "/" + Student.$superp.toString.call(this); // Invoke parent's toString method | ||
return this.id + "/" + Student.$superp.toString.call(this); // Call parent's toString method | ||
} | ||
@@ -120,7 +149,5 @@ }); | ||
JSFace supports Java-style static properties. Meaning they are accessible on both class and instance levels. | ||
``` javascript | ||
var Person = Class({ | ||
$statics: { | ||
$static: { | ||
MIN_AGE: 1, | ||
@@ -140,10 +167,29 @@ MAX_AGE: 150, | ||
var person = new Person("Rika", 20); | ||
Person.MIN_AGE === person.MIN_AGE; // true | ||
Person.MAX_AGE === person.MAX_AGE; // true | ||
Person.MIN_AGE === 1; // true | ||
Person.MAX_AGE === 150; // true | ||
Person.isValidAge(0); // false | ||
person.isValidAge(person.age); // true | ||
``` | ||
### Constants | ||
Constants work the same as static properties. The only different is they are immutable. | ||
``` javascript | ||
var Person = Class({ | ||
$const: { | ||
MIN_AGE: 1, | ||
MAX_AGE: 150 | ||
}, | ||
constructor: function(name, age) { | ||
this.name = name; | ||
this.age = age; | ||
} | ||
}); | ||
Person.MIN_AGE = -1; | ||
Person.MIN_AGE === 1; // true, MIN_AGE is immutable | ||
``` | ||
### Private properties | ||
@@ -272,3 +318,3 @@ | ||
``` html | ||
<script src="jsface.pointcut.js" type="text/javascript"></script> | ||
<script src="jsface.pointcut.js"></script> | ||
``` | ||
@@ -356,19 +402,4 @@ | ||
$ready plugin is designed to help parent classes to intercept their subclasses' creation. If a class uses $ready, | ||
it notifies itself. | ||
$ready plugin is designed to help a parent class to intercept their subclasses' creation. | ||
#### Setup | ||
Browser: | ||
``` html | ||
<script src="jsface.ready.js" type="text/javascript"></script> | ||
``` | ||
NodeJS: | ||
``` javascript | ||
var ready = require("jsface.ready"); | ||
``` | ||
#### Sample | ||
@@ -379,3 +410,3 @@ | ||
$ready: function(clazz, parent, api) { | ||
var type = (this !== clazz) && api.type; | ||
var type = (this !== clazz) && api.type; // (this !== clazz) means this comes from a sub-class | ||
@@ -408,3 +439,3 @@ switch (type) { | ||
Copyright (c) 2009-2013 Tan Nhu | ||
Copyright (c) Tan Nhu | ||
@@ -427,2 +458,2 @@ Permission is hereby granted, free of charge, to any person obtaining a copy | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
THE SOFTWARE. | ||
THE SOFTWARE. |
@@ -16,6 +16,14 @@ var Events = Class({ | ||
var Foo = Class({ | ||
$statics: { | ||
$static: { | ||
VERSION: "1.3" | ||
}, | ||
$const: { | ||
CONSTANT: "frozen value", | ||
Namespace: { | ||
FIRST_OPTION: "first option", | ||
SECOND_OPTION: "second option" | ||
} | ||
}, | ||
constructor: function(name) { | ||
@@ -22,0 +30,0 @@ this.name = name; |
@@ -145,3 +145,3 @@ var context = this, | ||
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)"); | ||
equal(classOrNil(Util), Util, "jsface.classOrNil works incorrectly a singleton class (singleton is a map, not a class)"); | ||
}); | ||
@@ -295,2 +295,110 @@ | ||
test("Constants", function() { | ||
var Bar = Class({ | ||
constructor: function(name) { | ||
this.name = name; | ||
}, | ||
$const: { | ||
CONSTANT: 1, | ||
OBJECT: { | ||
C1: "c1", | ||
C2: "c2", | ||
C3: { | ||
C31: "c31" | ||
} | ||
} | ||
} | ||
}); | ||
var bar = new Bar("Constants"); | ||
equal(Bar.CONSTANT, 1, "Error getting constant"); | ||
equal(Bar.OBJECT.C1, "c1", "Error getting object constant"); | ||
equal(bar.CONSTANT, undefined, "Constant should not be on instance level"); | ||
equal(bar.OBJECT, undefined, "Object constant should not be on instance level"); | ||
Bar.CONSTANT = 2; | ||
equal(Bar.CONSTANT, 1, "Error, constant value was changed"); | ||
Bar.OBJECT.C3.C31 = "foo"; | ||
equal(Bar.OBJECT.C3.C31, "c31", "Error, object constant value was changed"); | ||
}); | ||
test("Constants - inheritance", function() { | ||
var Foo = Class({ | ||
fooField: 100, | ||
constructor: function(name) { | ||
this.name = name; | ||
}, | ||
fooMethod: function() { | ||
return "fooMethod"; | ||
}, | ||
$const: { | ||
FOO_CONST: "fooConst", | ||
OVERRIDDEN_CONST: "originalValue", | ||
OBJECT_CONST: { | ||
FIRST: "first", | ||
SECOND: "second", | ||
THIRD: { | ||
THIRD_FIRST: "thirdFirst", | ||
THIRD_SECOND: "thirdSecond" | ||
} | ||
} | ||
} | ||
}); | ||
var Bar = Class(Foo, { | ||
barField: 200, | ||
constructor: function(name) { | ||
this.name = name; | ||
}, | ||
barMethod: function() { | ||
return "barMethod"; | ||
}, | ||
$const: { | ||
BAR_CONST: "barConst", | ||
OVERRIDDEN_CONST: "overriddenValue" | ||
} | ||
}); | ||
equal(Foo.FOO_CONST, "fooConst", "Constant is not created properly"); | ||
equal(Bar.FOO_CONST, "fooConst", "Constant is not inherited properly"); | ||
equal(Bar.BAR_CONST, "barConst", "Constant is not created properly"); | ||
equal(Foo.OVERRIDDEN_CONST, "originalValue", "Constant is not created properly"); | ||
equal(Bar.OVERRIDDEN_CONST, "overriddenValue", "Constant is not overridden properly"); | ||
equal(Bar.OBJECT_CONST.FIRST, "first", "Constant is not inherited properly"); | ||
equal(Bar.OBJECT_CONST.THIRD.THIRD_SECOND, "thirdSecond", "Constant is not inherited properly"); | ||
Bar.OBJECT_CONST.THIRD.THIRD_SECOND = "bar"; | ||
equal(Bar.OBJECT_CONST.THIRD.THIRD_SECOND, "thirdSecond", "Error, object constant value was changed"); | ||
var foo = new Foo("Tom"); | ||
var bar = new Bar("John Rambo"); | ||
equal(foo.name, "Tom", "Invalid class creation"); | ||
equal(foo.fooField, 100, "Invalid class creation"); | ||
equal(foo.fooMethod(), "fooMethod", "Invalid class creation"); | ||
equal(foo.FOO_CONST, undefined, "Constant should not be on instance"); | ||
equal(bar.name, "John Rambo", "Invalid class creation"); | ||
equal(bar.fooField, 100, "Invalid class creation"); | ||
equal(bar.barField, 200, "Invalid class creation"); | ||
equal(bar.fooMethod(), "fooMethod", "Invalid class creation"); | ||
equal(bar.barMethod(), "barMethod", "Invalid class creation"); | ||
equal(bar.FOO_CONST, undefined, "Constant should not be on instance"); | ||
equal(bar.BAR_CONST, undefined, "Constant should not be on instance"); | ||
equal(bar.OVERRIDDEN_CONST, undefined, "Constant should not be on instance"); | ||
equal(bar.OBJECT_CONST, undefined, "Constant should not be on instance"); | ||
}); | ||
test("Static methods", function() { | ||
@@ -302,3 +410,3 @@ var Bar = Class({ | ||
$statics: { | ||
$static: { | ||
sayBye: function() { | ||
@@ -313,6 +421,192 @@ return "Bye!"; | ||
equal(Bar.sayBye(), "Bye!", "Error invoking static method"); | ||
equal(bar.sayBye(), "Bye!", "Error invoking static method from class instance"); | ||
equal(bar.sayBye, Bar.sayBye, "Static method must be the same on both class and class instance"); | ||
equal(bar.sayBye, undefined, "Static method should not be on instance level"); | ||
}); | ||
test("Static methods should be inherited accordingly", function() { | ||
var Foo = Class({ | ||
fooField: 100, | ||
constructor: function(name) { | ||
this.name = name; | ||
}, | ||
fooMethod: function() { | ||
return "fooMethod"; | ||
}, | ||
$static: { | ||
fooStaticMethod: function() { | ||
return "fooStaticMethod"; | ||
} | ||
} | ||
}); | ||
var Bar = Class(Foo, { | ||
barField: 200, | ||
constructor: function(name) { | ||
this.name = name; | ||
}, | ||
barMethod: function() { | ||
return "barMethod"; | ||
}, | ||
$static: { | ||
barStaticMethod: function() { | ||
return "barStaticMethod"; | ||
} | ||
} | ||
}); | ||
equal(Foo.fooStaticMethod(), "fooStaticMethod", "Static method is not created properly"); | ||
equal(Bar.fooStaticMethod(), "fooStaticMethod", "Static method is not inherited properly"); | ||
equal(Bar.barStaticMethod(), "barStaticMethod", "Static method is not created properly"); | ||
var foo = new Foo("Tom"); | ||
bar = new Bar("John Rambo"); | ||
equal(foo.name, "Tom", "Invalid class creation"); | ||
equal(foo.fooField, 100, "Invalid class creation"); | ||
equal(foo.fooMethod(), "fooMethod", "Invalid class creation"); | ||
equal(foo.fooStaticMethod, undefined, "Static method should not be on instance"); | ||
equal(bar.name, "John Rambo", "Invalid class creation"); | ||
equal(bar.fooField, 100, "Invalid class creation"); | ||
equal(bar.barField, 200, "Invalid class creation"); | ||
equal(bar.fooMethod(), "fooMethod", "Invalid class creation"); | ||
equal(bar.barMethod(), "barMethod", "Invalid class creation"); | ||
equal(bar.fooStaticMethod, undefined, "Static method should not be on instance"); | ||
equal(bar.barStaticMethod, undefined, "Static method should not be on instance"); | ||
}); | ||
test("Properties - getters and setters", function() { | ||
var Person = Class({ | ||
constructor : function(name) { | ||
this._name = name; | ||
}, | ||
name : { | ||
get : function() { | ||
return this._name; | ||
}, | ||
set : function(value) { | ||
this._name = value; | ||
} | ||
} | ||
}); | ||
var person = new Person("Milos"); | ||
equal(person.name, "Milos", "Invalid property getter"); | ||
person.name = "Boki"; | ||
equal(person.name, "Boki", "Invalid property setter"); | ||
}); | ||
test("Properties - skip getters and setters", function() { | ||
var Person = Class({ | ||
name: undefined, | ||
setName: function(name) { | ||
this.name = name; | ||
} | ||
}); | ||
var person = new Person(); | ||
equal(person.name, undefined, "Invalid property getter"); | ||
person.setName("Boki"); | ||
equal(person.name, "Boki", "Invalid property setter"); | ||
}); | ||
test("Properties - getters and setters - inheritance", function() { | ||
var Person = Class({ | ||
constructor: function(name) { | ||
this._name = name; | ||
}, | ||
name: { | ||
get: function() { | ||
return this._name; | ||
}, | ||
set: function(value) { | ||
this._name = value; | ||
} | ||
} | ||
}); | ||
var Student = Class(Person, { | ||
constructor: function(name, age) { | ||
Student.$super.call(this, name); | ||
this._age = age; | ||
}, | ||
age: { | ||
get: function() { | ||
return this._age; | ||
}, | ||
set: function(value) { | ||
this._age = value; | ||
} | ||
} | ||
}); | ||
var student = new Student("Mia", 18); | ||
equal(student.name, "Mia", "Bad property inheritance"); | ||
student.name = "Persa"; | ||
equal(student.name, "Persa", "Bad property inheritance"); | ||
equal(student.age, 18, "Invalid property getter"); | ||
}); | ||
test("Properties - getters and setters - inheritance and mixins", function() { | ||
var Person = Class({ | ||
constructor: function(name) { | ||
this._name = name; | ||
}, | ||
name: { | ||
get: function() { | ||
return this._name; | ||
}, | ||
set: function(value) { | ||
this._name = value; | ||
} | ||
} | ||
}); | ||
var Options = Class({ | ||
option: { | ||
get: function() { | ||
return this._option; | ||
}, | ||
set: function(value) { | ||
this._option = value; | ||
} | ||
} | ||
}); | ||
var Student = Class([Person, Options], { | ||
constructor: function(name, age) { | ||
Student.$super.call(this, name); | ||
this._age = age; | ||
}, | ||
age: { | ||
get: function() { | ||
return this._age; | ||
}, | ||
set: function(value) { | ||
this._age = value; | ||
} | ||
} | ||
}); | ||
var student = new Student("Mia", 18); | ||
student.option = 'some option'; | ||
equal(student.option, 'some option', 'Getter/setter does not work properly with mixin'); | ||
}); | ||
test("Singleton class", function() { | ||
@@ -327,3 +621,3 @@ var Foo = Class({ | ||
equal(mapOrNil(Foo), Foo, "Singleton class must be a map object"); | ||
equal(classOrNil(Foo), Foo, "Singleton class must be a a class"); | ||
equal(Foo.sayHi(), "Hello World", "Error invoking method on singleton class"); | ||
@@ -349,3 +643,3 @@ }); | ||
equal(mapOrNil(Bar), Bar, "Singleton class must be a map object"); | ||
equal(classOrNil(Bar), Bar, "Singleton class must be a a class"); | ||
equal(Bar.sayHi(), "Hello World", "Error invoking method on singleton class"); | ||
@@ -373,10 +667,65 @@ equal(Bar.sayBye(), "Bye!", "Error invoking method on singleton class"); | ||
equal(classOrNil(Bar), Bar, "Class definition must be a class"); | ||
equal(bar.sayHi, Bar.sayHi, "Static method must be the same on both class and class instance"); | ||
equal(bar.sayHi, Foo.sayHi, "Static method must be the same on both class and class instance"); | ||
equal(bar.sayHi, undefined, "Static method from parent class should not be available on child instance"); | ||
equal(Foo.sayHi(), "Hello World", "Error invoking method on singleton class"); | ||
equal(Bar.sayHi(), "Hello World", "Static method should be available on child class"); | ||
equal(Foo.sayHi, Bar.sayHi, "Static method must be the same on both class"); | ||
equal(Bar.sayHi(), "Hello World", "Error invoking method on singleton class"); | ||
equal(bar.sayHi(), "Hello World", "Error invoking method on singleton class"); | ||
equal(bar.sayBye(), "Bye!", "Error invoking method on class"); | ||
}); | ||
test("Override singleton method", function() { | ||
var Foo = Class({ | ||
$singleton: true, | ||
hi: function() { | ||
return "hi"; | ||
}, | ||
bye: function() { | ||
return "bye"; | ||
} | ||
}); | ||
var Bar = Class(Foo, { | ||
$singleton: true, | ||
// override hi | ||
hi: function() { | ||
return "override-hi"; | ||
} | ||
}); | ||
equal(Foo.hi(), "hi", "Error invoking method on singleton class"); | ||
equal(Foo.bye(), "bye", "Static method should be available on child class"); | ||
equal(Bar.hi(), "override-hi", "Error invoking method on singleton class"); | ||
equal(Foo.bye(), "bye", "Static method should be available on child class"); | ||
}); | ||
test("Override singleton method and call parent method", function() { | ||
var Foo = Class({ | ||
$singleton: true, | ||
hi: function() { | ||
return "hi"; | ||
}, | ||
bye: function() { | ||
return "bye"; | ||
} | ||
}); | ||
var Bar = Class(Foo, { | ||
$singleton: true, | ||
// override hi | ||
hi: function() { | ||
return "override-" + Bar.$super.hi.call(this); | ||
} | ||
}); | ||
equal(Foo.hi(), "hi", "Error invoking method on singleton class"); | ||
equal(Foo.bye(), "bye", "Static method should be available on child class"); | ||
equal(Bar.hi(), "override-hi", "Error invoking method on singleton class"); | ||
equal(Foo.bye(), "bye", "Static method should be available on child class"); | ||
}); | ||
test("Mixin: class extends class", function() { | ||
@@ -593,5 +942,8 @@ var Foo = Class({ | ||
equal(bar.welcome(), "Welcome John Rambo", "Invalid extend() behavior, property must be overriden properly"); | ||
equal(bar.sayHi(), "Hello World John Rambo", "Invalid extend() behavior"); | ||
ok(functionOrNil(Bar.welcome), "Static method is not copied when extending"); | ||
ok(functionOrNil(Bar.sayHi), "Static method is not copied when extending"); | ||
equal(bar.welcome(), "invalid", "Invalid extend() behavior, property must be overriden properly"); | ||
equal(bar.sayBye(), "Bye!", "Invalid extend() behavior"); | ||
equal(bar.sayHi, undefined, "Invalid extend() behavior"); | ||
}); | ||
@@ -613,3 +965,3 @@ | ||
var Bar = Class({ | ||
$statics: { | ||
$static: { | ||
sample: 1, | ||
@@ -680,6 +1032,6 @@ fn: function() { return 2; } | ||
var Events = Class({ | ||
bind: function(event, fn) { | ||
_bind: function(event, fn) { | ||
return true; | ||
}, | ||
unbind: function(event, fn) { | ||
_unbind: function(event, fn) { | ||
return false; | ||
@@ -702,6 +1054,6 @@ } | ||
equal(foo.opts, "nothing", "Invalid extend() behavior, constructor must be bound correctly"); | ||
equal(Foo.bind(), true, "Invalid extend() behavior"); | ||
equal( !Foo.unbind(), true, "Invalid extend() behavior"); | ||
equal(foo.bind(), true, "Invalid extend() behavior"); | ||
equal( !foo.unbind(), true, "Invalid extend() behavior"); | ||
equal(Foo._bind(), true, "Invalid extend() behavior"); | ||
equal( !Foo._unbind(), true, "Invalid extend() behavior"); | ||
equal(foo._bind, undefined, "Static method can't be copied to instance"); | ||
equal(foo._unbind, undefined, "Static method can't be copied to instance"); | ||
}); | ||
@@ -711,3 +1063,3 @@ | ||
extend(String, { | ||
trim: function() { | ||
myTrim: function() { | ||
return this.replace(/^\s+|\s+$/g, ""); | ||
@@ -717,19 +1069,17 @@ } | ||
equal(" Hello World ".trim(), "Hello World", "Invalid extend() binding String.prototype"); | ||
ok(String.myTrim, "Extend does not work properly"); | ||
equal(String.prototype.myTrim, undefined, "Extend does not work properly"); | ||
equal(String.myTrim.apply(" Hello World "), "Hello World", "Invalid extend() binding String.prototype"); | ||
extend(Array, { | ||
sum: function() { | ||
var s = 0, len = this.length; | ||
while (len--) { | ||
s += this[len]; | ||
} | ||
return s; | ||
delete String.myTrim; | ||
extend(String.prototype, { | ||
myTrim: function() { | ||
return this.replace(/^\s+|\s+$/g, ""); | ||
} | ||
}); | ||
var a = [ 1, 2, 3, 4, 5 ]; | ||
ok(a.sum, "Invalid extend() binding native object"); | ||
ok(Array.sum, "Invalid extend() binding native object"); | ||
ok(Array.prototype.sum, "Invalid extend() binding native object"); | ||
equal(a.sum(), 15, "Invalid extend() binding native object"); | ||
ok(String.prototype.myTrim, "Extend does not work properly"); | ||
equal(String.myTrim, undefined, "Extend does not work properly"); | ||
equal(" Hello World ".myTrim(), "Hello World", "Invalid extend() binding String.prototype"); | ||
}); | ||
@@ -784,4 +1134,25 @@ | ||
// --------- PLUGINS --------- // | ||
test("Test instanceof", function() { | ||
var Person = Class({ | ||
constructor: function(name) { | ||
this.name = name; | ||
} | ||
}); | ||
var Student = Class(Person, { | ||
constructor: function(id, name) { | ||
this.id = id; | ||
Person.call(this, name); | ||
} | ||
}); | ||
var p = new Person("Tom"), | ||
s = new Student(1, "Mary"); | ||
ok(p instanceof Person, "class fails to test instanceof"); | ||
ok(s instanceof Person, "class fails to test instanceof"); | ||
ok(s instanceof Student, "class fails to test instanceof"); | ||
}); | ||
test("Develop a Class plugin", function() { | ||
@@ -805,1 +1176,66 @@ var Logger = Class({ | ||
}); | ||
test("$ready plugin: class notifies itself", function() { | ||
var notified = false; | ||
var Foo = Class({ | ||
$ready: function(clazz, parent, api) { | ||
notified = true; | ||
equal(this, clazz, "clazz must be equal to this"); | ||
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"); | ||
}, | ||
echo: function(o) { | ||
return o; | ||
} | ||
}); | ||
ok(notified, "$ready must be executed"); | ||
}); | ||
test("$ready plugin: class is notified when its subclasses are ready", function() { | ||
var notified = false; | ||
var Foo = Class({ | ||
$ready: function(clazz, parent, api) { | ||
notified = true; | ||
if (this !== clazz) { | ||
ok(api.echo2, "$ready works incorrectly"); | ||
ok( !api.$ready, "$ready works incorrectly"); | ||
ok(functionOrNil(clazz.prototype.echo2), "$ready works incorrectly"); | ||
} | ||
}, | ||
echo: function(o) { | ||
return o; | ||
} | ||
}); | ||
ok(notified, "$ready must be executed when class is created"); | ||
var Bar = Class(Foo, { | ||
echo2: function(o) { | ||
return o; | ||
} | ||
}); | ||
}); | ||
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"); | ||
}); |
@@ -37,3 +37,2 @@ var context = this; | ||
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"); | ||
@@ -40,0 +39,0 @@ delete context.module; |
@@ -340,6 +340,8 @@ var context = this, | ||
test("Pointcuts over static methods", function() { | ||
var num = 0; | ||
var Counter = Class({ | ||
constructor: function(num) { | ||
this.num = num; | ||
constructor: function(_num) { | ||
num = _num; | ||
}, | ||
$statics: { | ||
@@ -349,3 +351,3 @@ counter: 0, | ||
inc: function(n) { | ||
this.num += n; | ||
num += n; | ||
} | ||
@@ -358,7 +360,7 @@ } | ||
before: function(n) { | ||
this.num += n; | ||
num += n; | ||
Counter.counter += 1; | ||
}, | ||
after: function(n) { | ||
this.num += n; | ||
num += n; | ||
Counter.counter += 1; | ||
@@ -370,6 +372,6 @@ } | ||
var p = new Counter(100); | ||
p.inc(1); | ||
Counter.inc(0); | ||
equal(p.num, 103, "Pointcut over static methods works incorrectly"); | ||
equal(Counter.counter, 4, "Pointcut over static methods works incorrectly"); | ||
Counter.inc(1); | ||
equal(num, 103, "Pointcut over static methods works incorrectly"); | ||
equal(p.counter, undefined, "Pointcut over static methods works incorrectly"); | ||
}); | ||
@@ -558,3 +560,2 @@ | ||
equal(impl.$statics.foo, Person.prototype.foo, "Removing pointcut over prototype method unsuccessfully"); | ||
equal(impl.$statics.foo, Person.foo, "Removing pointcut over static method unsuccessfully"); | ||
@@ -582,4 +583,3 @@ }); | ||
equal(impl.$statics.foo, Person.prototype.foo, "Removing pointcut over prototype method unsuccessfully"); | ||
equal(impl.$statics.foo, Person.foo, "Removing pointcut over static method unsuccessfully"); | ||
}); |
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
5
1
448
156275
27
4234