refinejs
Version:
Powerful and flexible generator objects with the possibility of inheritance
636 lines (572 loc) • 23.2 kB
JavaScript
/**
* RefineJS v1.2.3 - Powerful and flexible generator objects with the possibility of inheritance for JavaScript
* Copyright 2011-2014 Dmitrii Pakhtinov (spb.piksel@gmail.com)
* Released under the MIT license
* Update: 11/23/2014
*/
(function(factory) {
if (typeof define === 'function' && define['amd']) {
define(typeof document !== "object" || document.readyState !== "loading" ? [] : "refinejs", factory);
} else if (typeof exports === "object" && typeof module !== "undefined") {
module['exports'] = factory();
} else {
factory();
}
})(function() {
"use strict";
var // for advanced mode compilation in GCC
True = true,
False = false,
Null = null,
undefined = void 0,
globalScope = (typeof window === 'object' ? window : (typeof global !== 'undefined' ? global : this)) || {},
JSON = globalScope['JSON'],
Array = globalScope['Array'],
Object = globalScope['Object'],
Boolean = globalScope['Boolean'],
ObjectPrototype = Object.prototype,
defineProperty = Object.defineProperty,
hasOwnProperty = ObjectPrototype.hasOwnProperty,
isNeedProto = !(Object.getPrototypeOf || "__proto__" in ObjectPrototype),
emptyFunction = (function() {
return function() {
}
})(),
proxyFunction = function(e) {
return function() {
return e
}
},
errorFunction = function(prop, type) {
return function() {
throw new Error("'" + prop + "' property is " + (type ? "read" : "write") + "-only");
}
};
var libID = (new Date()).getTime(), // Identifier for library, it is will be necessary for the objects in VBScript
hasDontEnumBug = !({toString: 1}).propertyIsEnumerable('toString'), // is enumerable properties?
dontEnums = [
// not enumerated properties in IE <9
'toString',
'toLocaleString',
'valueOf',
'hasOwnProperty',
'isPrototypeOf',
'propertyIsEnumerable',
'constructor'
];
/**
* Object definition
*
* @param {*} [context] The context in which created a new constructor
* @param {*} [cName] Name of the constructor can have a namespace dividing point
* @param {*} [extend] One or a list of names/constructors from which inherit property
* @param {*} [options] Object contains static properties and other options
* @param {*} [structure] The structure of the object
* @return {Function|Object} Returns constructor or an instance
*/
function refine(context, cName, extend, options, structure) {
var p1, p2, p3,
VB = VBInc,
firstPass = 1,
argv = arguments,
argn = argv.length - 1,
constructorName = 'constructor',
staticClass = Null,
staticClassParts = [],
staticClassNames = [],
accessorsActive = 0,
accessors = VBInc === Null ? 0 : {},
element, parts, statics, compact, extendCount,
returnInstance = this instanceof refine;
// get a reference to the object structure
structure = argv[argn--] || {};
if (argv[argn] && typeof argv[argn] === 'object' && !(argv[argn] instanceof Array) &&
argv[argn].constructor && argv[argn].constructor !== ObjectPrototype.constructor) {
if (VBInc && defineProperty) {
try {
defineProperty(argv[argn], 't' + libID, {
configurable: 1, set: function(a) {
VB = a
}
});
argv[argn]['t' + libID] = 0;
delete argv[argn]['t' + libID];
} catch(_e_) {
}
}
element = VB ? Null : (compact = True, argv[argn--]);
}
// option can only be stored in the object or boolean(compact mode)
options = !argv[argn] || argv[argn] instanceof Array || typeof argv[argn] !== 'object' ?
typeof argv[argn] === 'boolean' || typeof argv[argn] === 'number' ?
{"compact": argv[argn--]} : {} : argv[argn--];
// inclusion of compact mode
compact = compact || options['compact'];
// Save references on the properties
p1 = options['context'];
p2 = options['extend'];
p3 = options['mixins'];
// static properties
statics = options['statics'] || (p1 || p2 || p3 || "compact" in options ? {} : options);
// construct an array of inherited objects
extend = argv[argn] instanceof Array ? argv[argn--] : typeof argv[argn] === 'function' ? [argv[argn--]] :
typeof argv[argn - 1] === 'string' && ('' + argv[argn--]).replace(/(^|\s)(extends|implements)(\s|$)/g, ',')
.replace(/^[\s,]+|\s(?=\s)|[\s,]+$/g, '').replace(/\s*,\s*/g, ',').split(',') || [];
// additional array of inherited objects
parts = typeof argv[argn] === 'string' && argv[argn--].split(/extends|implements|,/g) || [];
// get the context in which to add the constructor
context = argv[argn--] || p1 || refine['refineScope'] || (refine['refineScope'] = globalScope);
// name of the new constructor
cName = (parts.shift() || '').replace(/^\s+|\s+$/g, '');
// lists of constructors from which inherit properties
p1 = (parts = parts.join(',').replace(/^[\s,]+|[\s,]+$/g, '').replace(/\s*,\s*/g, ',')) ? parts.split(',') : [];
p2 = (p2 = (p2 || [])) instanceof Array ? p2 : [p2];
p3 = (p3 = (p3 || [])) instanceof Array ? p3 : [p3];
// obtain a complete list of parent objects
extend = p1.concat.call(p1, extend.concat.call(extend, p2.concat.call(p2, p3)));
// parent objects numbers
if (!((extendCount = extend.length) || cName || compact !== undefined)) {
compact = True;
}
if (typeof structure !== 'function') {
// if the structure is not a function
var originalStructure = structure;
structure = function() {
// make a copy of an object
emptyFunction.prototype = originalStructure.constructor.prototype;
parts = new emptyFunction;
each(originalStructure, function(prop, val) {
parts[prop] = val
});
return parts;
}
}
// this will be the constructor for the generated object
var refineConstructor = function() {
var self = this,
args = arguments,
isParent = self instanceof Boolean,
hasNew = isParent ? args[4] : self instanceof refineConstructor,
compactMode = !isParent || args[2] === undefined || compact !== undefined ? compact : args[2];
if (!compactMode && !isParent && !hasNew && args.length && '__refine__' in args[0]) {
// if calling YourConstructor(instance)
return getInstanceOf.call(refineConstructor, args[0]);
}
var index = extendCount,
oParent = Null,
obj = structure.apply({"isParent": isParent, "isInstance": hasNew, "options": options}, isParent ? args[0] : args),
proto = isParent && args[3] || Null,
copy = element || proto || obj,
owner = isParent ? args[1] : {o: obj};
// copy the static properties
each(statics, function(prop, val) {
if (!hasOwnProperty.call(obj, prop)) {
obj[prop] = val;
}
});
for(; index--;) {
if (typeof extend[index] !== 'function') {
if (typeof extend[index] === 'string') {
// get the constructor by its name
extend[index] = getRefineByName(extend[index], context);
} else {
extend[index] = proxyFunction(extend[index]);
compactMode = compactMode === undefined ? True : compactMode;
}
}
emptyFunction.prototype = oParent = extend[index].call(new Boolean, args, owner, compactMode, proto, hasNew);
if (index > 0) {
// cannot auto execute constructor in implements
owner['i'] = Null;
}
copy = proto = new emptyFunction;
if (!compactMode) {
copy['parent'] = oParent;
if (isNeedProto) {
copy['__proto__'] = oParent;
}
}
}
if (extendCount || isParent || element) {
// proxy method to wrap functions
var bindMethod = function(value) {
return compactMode ? value : typeof value === 'function' ? function() {
var object = owner.o, _parent = object['parent'];
!compactMode && (object['parent'] = oParent);
var result = value.apply(this === copy || this == globalScope ? object : this, arguments);
!compactMode && (object['parent'] = _parent);
return result;
} : value;
};
// wrap all the functions of a object in the proxy
each(obj, function(prop, value) {
if (value && typeof value === 'object') {
if ('set' in value) {
value.set = bindMethod(value.set);
}
if ('get' in value) {
value.get = bindMethod(value.get);
}
}
copy[prop] = bindMethod(value);
});
owner.o = isParent ? owner.o : copy;
}
if (!compactMode) {
// adds special statements
copy['__refine__'] = refineConstructor;
}
// keep a reference to the constructor
if (hasOwnProperty.call(copy, constructorName)) {
owner['i'] = copy[constructorName];
} else if (hasOwnProperty.call(copy, 'constructor')) {
owner['i'] = copy['constructor'];
}
// if supported accessors
if (!isParent && accessors !== 0) {
// first initialization of the object or if the browser supports accessors in ordinary objects
if (!VB || firstPass) {
each(firstPass ? copy : accessors, function(prop) {
// search accessors
var value = copy[prop],
subName = prop,
type = 0;
if (firstPass) {
if (value && typeof value === 'object' && prop !== 'parent' && prop !== '__proto__') {
type = (hasOwnProperty.call(value, 'set') ? 1 : 0) + (hasOwnProperty.call(value, 'get') ? 2 : 0);
} else {
type = prop.indexOf('get ') === 0 ? 4 : prop.indexOf('set ') === 0
? 8 : VB && prop === 'toString' ? 16 : 0;
}
} else {
type = accessors[prop];
}
if (type) {
// if has found an accessors and not toString property
if (firstPass && (accessors[prop] = type) !== 16) {
accessorsActive++;
}
if (type & 3) {
emptyFunction.prototype = value;
value = new emptyFunction;
} else {
// trimmed prefix set/get
subName = prop.split(' ').pop();
if (type !== 8 && typeof value !== "function") {
value = (function(value) {
return function() {
return value
};
})(value);
}
}
if (!VB) {
// for browsers supported accessors to ordinary objects
var descriptorSet = compactMode ? undefined : errorFunction(subName, 1),
descriptorGet = compactMode ? undefined : errorFunction(subName, 0);
if (type & 1 ? value.set : type === 8 && value) {
descriptorSet = function(val) {
// proxy for setter
(type & 1 ? value.set : value).call(this, val, value);
}
}
if (type & 2 ? value.get : type === 4 && value) {
descriptorGet = function(val) {
// proxy for getter
return (type & 2 ? value.get : value).call(this, value);
}
}
if (hasOwnProperty.call(copy, prop)) {
// remove original property
delete copy[prop];
}
if (defineProperty) {
// w3c standard
var descr = Object.getOwnPropertyDescriptor(copy, subName);
defineProperty(copy, subName, {
enumerable: VBInc ? 0 : 1,
configurable: 1,
set: type === 4 && descr && descr.set || descriptorSet,
get: type === 8 && descr && descr.get || descriptorGet
});
} else {
// Mozilla standard
descriptorSet = type === 4 && copy.__lookupSetter__(subName) || descriptorSet;
descriptorSet && copy.__defineSetter__(subName, descriptorSet);
descriptorGet = type === 8 && copy.__lookupGetter__(subName) || descriptorGet;
descriptorGet && copy.__defineGetter__(subName, descriptorGet);
}
} else {
// for Internet Explorer VisualBasic Script accessors
if (type & 22) {
// create getter in VB Class
staticClassParts.push(
'Public ' + (type & 16 ? 'Default ' : '' ) + 'Property Get [' + subName + ']',
'Call VBCorrectVal(' + ( value ?
'[(accessors)].[' + prop + ']' + (type & 2 ? '.get' : '') +
'.call(me,[(accessors)].[' + prop + '])' : 'window.undefined' ) +
',[' + subName + '])', 'End Property'
);
}
if (type & 25) {
// create setter in VB Class
staticClassParts.push(
'Public Property Let [' + subName + '](val)',
type = (type & 16 ? 'Set [(accessors)].[' + prop + ']=val' : value ?
'Call [(accessors)].[' + prop + ']' + (type & 1 ? '.set' : '') +
'.call(me,val,[(accessors)].[' + prop + '])' : '') +
'\nEnd Property', 'Public Property Set [' + subName + '](val)', type
);
}
}
} else if (VB) {
// VBScript up to 60 multiple dimensions may be declared.
if (staticClassNames.length === 50) { // flush 50 items
staticClassParts.push('Public [' + staticClassNames.join('],[') + ']');
staticClassNames.length = 0;
}
staticClassNames[staticClassNames.length] = prop;
}
}, firstPass);
}
if (firstPass && !(firstPass = 0) && accessorsActive === 0) {
staticClassNames = staticClassParts = accessors = 0;
} else if (VB) {
if (accessorsActive) {
// once initialize VB Class for later use
staticClass = 'StaticClass' + libID + VBInc++;
staticClassParts.unshift('Class ' + staticClass);
staticClassParts.push(
(staticClassNames.length
? 'Public [' + staticClassNames.join('],[') + ']\n' : '') + 'Private [(accessors)]',
'Private Sub Class_Initialize()',
'Set [(accessors)]=' + staticClass + 'FactoryJS()',
'End Sub',
'End Class',
'Function ' + staticClass + 'Factory()',
'Set ' + staticClass + 'Factory=New ' + staticClass,
'End Function'
);
globalScope[staticClass + 'FactoryJS'] = function() {
return staticClassParts;
};
globalScope['execVBScript'](staticClassParts.join('\n'));
accessorsActive = staticClassNames = staticClassParts = Null;
}
staticClassParts = {};
owner.o = globalScope[staticClass + "Factory"]();
// copy all values into new VB Class object
each(copy, function(prop, val) {
if (!accessors.hasOwnProperty(prop)) {
if ((!extendCount || compactMode) && typeof val === 'function' && prop !== '__refine__') {
owner.o[prop] = function() {
return val.apply(this === copy || this == globalScope ? owner.o : this, arguments);
}
} else {
owner.o[prop] = val;
}
} else {
if (accessors[prop] & 3) {
emptyFunction.prototype = copy[prop];
staticClassParts[prop] = new emptyFunction;
} else if (accessors[prop] !== 8 && typeof val !== "function") {
staticClassParts[prop] = function() {
return val
};
} else {
staticClassParts[prop] = copy[prop];
}
}
}, 1);
// new link to VB Class object
copy = owner.o;
}
}
function finalConstructor() {
var result;
if (!isParent && !element && typeof owner['i'] === 'function') {
result = owner['i'].apply(hasNew ? copy : self, args);
}
return result === undefined ? copy : result;
}
// create object
return hasNew ? new finalConstructor : finalConstructor();
};
refineConstructor.toString = function() {
return "[object " + (cName || "Function") + "]";
};
refineConstructor['getInstanceOf'] = getInstanceOf;
refineConstructor['baseContext'] = context;
// copy the static properties
each(statics, function(prop, val) {
refineConstructor[prop] = val;
});
// if the name of the constructor is defined, put it into context
if (refineConstructor.cName = cName) {
var _context = context;
// The name can be a namespace
argv = (parts = cName.split('.')).shift();
do {
if (parts.length === 0) {
_context[constructorName = argv] = refineConstructor;
} else {
if (!(argv in _context)) {
_context[argv] = {};
}
_context = _context[argv];
}
} while(argv = parts.shift());
}
// return an instance of Object if summoned by the operator new, otherwise return the constructor
return returnInstance ? new refineConstructor : refineConstructor;
}
/**
* Returns the function by name
*
* @param {String} name Name of the constructor that will search
* @param {Object} context Context where it is necessary to find a constructor
* @return {Function|null} Returns constructor
*/
function findFunctionByName(name, context) {
var result = Null, baseContext = context, subName, parts;
if (typeof name === 'string') {
parts = name.split('.');
while((subName = parts.shift()) && (context = context[subName])) {
}
if (typeof context === 'function' && baseContext === context['baseContext']) {
result = context;
}
}
return result;
}
/**
* Returns the constructor by name
*
* @param {String} name Name of the constructor that will search
* @param {Object} context Context where it is necessary to find a constructor
* @return {Function} Returns constructor
*/
function getRefineByName(name, context) {
var construct = findFunctionByName(name, context) || findFunctionByName(name, globalScope) ||
(typeof refine['autoload'] === 'function' && refine['autoload'](name, context)) || Null;
if (!construct) {
throw new Error("Parent constructor '" + name + "' not Initialized or Undefined");
}
return construct;
}
/**
* Iterates through each property of the object by calling the callback
* with the parameters of the property name and value
*
* @param {Object|Array} object The object or array to iterate properties
* @param {Function} callback Function that will be called for each property
* @param {Boolean|Number} [all] If true/1 will list all the properties of an object, including parent
*/
function each(object, callback, all) {
var index, length = dontEnums.length, value;
for(index in object) {
value = object[index];
if (((all && value !== ObjectPrototype[index]) || hasOwnProperty.call(object, index)) &&
callback.call(value, index, value) === False) {
length = False;
break;
}
}
if (length && hasDontEnumBug) {
for(index = 0; index < length; index++) {
value = object[dontEnums[index]];
if ((hasOwnProperty.call(object, dontEnums[index]) ||
(all && dontEnums[index] in object && value !== ObjectPrototype[dontEnums[index]])) &&
callback.call(value, dontEnums[index], value) === False) {
break;
}
}
}
}
/**
* Finds and returns an instance of the object
*
* @param {Object} object
* @return {Object}
*/
function getInstanceOf(object) {
while(object && object['__refine__'] != Null) {
if (object['__refine__'] === this) {
return object;
}
object = Object.getPrototypeOf ? Object.getPrototypeOf(object) : object.__proto__;
}
return Null;
}
/**
* Whether an object is an instance of constructor
*
* @param {Object} object
* @param {Function} constructor
* @return {Boolean}
*/
refine['instanceOf'] = function(object, constructor) {
return !!getInstanceOf.call(constructor, object);
};
/**
* The function determines the capabilities of the browser. If your browser supports
* accessors to ordinary objects, VBInc will be zero, otherwise returns 1
*/
var VBInc = (function(object, stringify) {
var setter = function(value) {
object = value;
};
try {
// test w3c standard
defineProperty(object, 't', {set: setter});
} catch(error) {
try {
// test Mozilla standard
object.__defineSetter__('t', setter);
} catch(error) {
}
}
// test setter
object['t'] = 0;
// if setter not supports
if (object) {
object = 1; // Maybe VBScript classes supports???
if (!('execVBscript' in globalScope)) {
// for IE only, if VisualBasic script compiler supports
if ('execScript' in globalScope) {
globalScope['execScript'](
'Function execVBscript(code) '
+ 'ExecuteGlobal(code) '
+ 'End Function\n'
+ 'Function VBCorrectVal(o,r) '
+ 'If IsObject(o) Then '
+ 'Set r=o Else r=o '
+ 'End If '
+ 'End Function',
'VBScript'
);
} else {
// if not supported any accessors :(
object = Null;
}
}
// Fix for VBScript objects
if (object && typeof stringify === 'function') {
JSON.stringify = function(object) {
if (object && !(object instanceof Object)) {
var copy = {};
each(object, function(key, value) {
if (key !== '__proto__') copy[key] = value;
});
arguments[0] = copy;
}
return stringify.apply(JSON, arguments);
}
}
}
return object;
})({}, JSON && JSON.stringify);
// default namespace for the Constructors
refine['refineScope'] = refine['refineScope'] || globalScope;
return globalScope['refine'] = globalScope['refinejs'] = refine;
});