Website/node_modules/inherit/lib/inherit.js
2015-12-02 18:21:44 -05:00

189 lines
5.3 KiB
JavaScript

/**
* @module inherit
* @version 2.2.2
* @author Filatov Dmitry <dfilatov@yandex-team.ru>
* @description This module provides some syntax sugar for "class" declarations, constructors, mixins, "super" calls and static members.
*/
(function(global) {
var hasIntrospection = (function(){'_';}).toString().indexOf('_') > -1,
emptyBase = function() {},
hasOwnProperty = Object.prototype.hasOwnProperty,
objCreate = Object.create || function(ptp) {
var inheritance = function() {};
inheritance.prototype = ptp;
return new inheritance();
},
objKeys = Object.keys || function(obj) {
var res = [];
for(var i in obj) {
hasOwnProperty.call(obj, i) && res.push(i);
}
return res;
},
extend = function(o1, o2) {
for(var i in o2) {
hasOwnProperty.call(o2, i) && (o1[i] = o2[i]);
}
return o1;
},
toStr = Object.prototype.toString,
isArray = Array.isArray || function(obj) {
return toStr.call(obj) === '[object Array]';
},
isFunction = function(obj) {
return toStr.call(obj) === '[object Function]';
},
noOp = function() {},
needCheckProps = true,
testPropObj = { toString : '' };
for(var i in testPropObj) { // fucking ie hasn't toString, valueOf in for
testPropObj.hasOwnProperty(i) && (needCheckProps = false);
}
var specProps = needCheckProps? ['toString', 'valueOf'] : null;
function getPropList(obj) {
var res = objKeys(obj);
if(needCheckProps) {
var specProp, i = 0;
while(specProp = specProps[i++]) {
obj.hasOwnProperty(specProp) && res.push(specProp);
}
}
return res;
}
function override(base, res, add) {
var addList = getPropList(add),
j = 0, len = addList.length,
name, prop;
while(j < len) {
if((name = addList[j++]) === '__self') {
continue;
}
prop = add[name];
if(isFunction(prop) &&
(!hasIntrospection || prop.toString().indexOf('.__base') > -1)) {
res[name] = (function(name, prop) {
var baseMethod = base[name]?
base[name] :
name === '__constructor'? // case of inheritance from plane function
res.__self.__parent :
noOp;
return function() {
var baseSaved = this.__base;
this.__base = baseMethod;
var res = prop.apply(this, arguments);
this.__base = baseSaved;
return res;
};
})(name, prop);
} else {
res[name] = prop;
}
}
}
function applyMixins(mixins, res) {
var i = 1, mixin;
while(mixin = mixins[i++]) {
res?
isFunction(mixin)?
inherit.self(res, mixin.prototype, mixin) :
inherit.self(res, mixin) :
res = isFunction(mixin)?
inherit(mixins[0], mixin.prototype, mixin) :
inherit(mixins[0], mixin);
}
return res || mixins[0];
}
/**
* Creates class
* @exports
* @param {Function|Array} [baseClass|baseClassAndMixins] class (or class and mixins) to inherit from
* @param {Object} prototypeFields
* @param {Object} [staticFields]
* @returns {Function} class
*/
function inherit() {
var args = arguments,
withMixins = isArray(args[0]),
hasBase = withMixins || isFunction(args[0]),
base = hasBase? withMixins? applyMixins(args[0]) : args[0] : emptyBase,
props = args[hasBase? 1 : 0] || {},
staticProps = args[hasBase? 2 : 1],
res = props.__constructor || (hasBase && base.prototype.__constructor)?
function() {
return this.__constructor.apply(this, arguments);
} :
hasBase?
function() {
return base.apply(this, arguments);
} :
function() {};
if(!hasBase) {
res.prototype = props;
res.prototype.__self = res.prototype.constructor = res;
return extend(res, staticProps);
}
extend(res, base);
res.__parent = base;
var basePtp = base.prototype,
resPtp = res.prototype = objCreate(basePtp);
resPtp.__self = resPtp.constructor = res;
props && override(basePtp, resPtp, props);
staticProps && override(base, res, staticProps);
return res;
}
inherit.self = function() {
var args = arguments,
withMixins = isArray(args[0]),
base = withMixins? applyMixins(args[0], args[0][0]) : args[0],
props = args[1],
staticProps = args[2],
basePtp = base.prototype;
props && override(basePtp, basePtp, props);
staticProps && override(base, base, staticProps);
return base;
};
var defineAsGlobal = true;
if(typeof exports === 'object') {
module.exports = inherit;
defineAsGlobal = false;
}
if(typeof modules === 'object') {
modules.define('inherit', function(provide) {
provide(inherit);
});
defineAsGlobal = false;
}
if(typeof define === 'function') {
define(function(require, exports, module) {
module.exports = inherit;
});
defineAsGlobal = false;
}
defineAsGlobal && (global.inherit = inherit);
})(this);