forked from Botanical/BotanJS
431 lines
8.4 KiB
JavaScript
431 lines
8.4 KiB
JavaScript
var BOTANJS_VERSION = "1.0.0b";
|
|
/*{{{ Shorthand Functions */
|
|
var __extends = function( obj, target ) {
|
|
obj.prototype = Object.create( target.prototype );
|
|
obj.prototype.constructor = obj;
|
|
};
|
|
var __readOnly = function( prototype, name, callback )
|
|
{
|
|
Object.defineProperty( prototype, name, {
|
|
get: callback
|
|
, set: function( v ) {
|
|
throw new Error( "Setting a read-only property: " + this.p );
|
|
}.bind( { p: name } )
|
|
} );
|
|
};
|
|
var __static_method = function( obj, name, callback )
|
|
{
|
|
Object.defineProperty( obj, name, {
|
|
get: function(){ return callback; }
|
|
, set: function( v ) {
|
|
throw new Error( "Setting a read-only property: " + this.p );
|
|
}.bind( { p: name } )
|
|
} );
|
|
};
|
|
var __const = __static_method;
|
|
/* End Shorthand Functions }}}*/
|
|
|
|
/*{{{ BotanEvent & EventDispatcher */
|
|
|
|
/** @type {typeof _BotanEvent} */
|
|
var BotanEvent;
|
|
|
|
BotanEvent = function( name, data )
|
|
{
|
|
var __propagating = false;
|
|
var __propagated = false;
|
|
|
|
__static_method(
|
|
this, "propagate"
|
|
, function()
|
|
{
|
|
if( !__propagated )
|
|
{
|
|
__propagating = true;
|
|
}
|
|
__propagated = true;
|
|
}
|
|
);
|
|
|
|
__static_method(
|
|
this, "stopPropagating"
|
|
, function() { __propagating = false; }
|
|
);
|
|
|
|
__const( this, "type", name );
|
|
|
|
__readOnly(
|
|
this, "propagating"
|
|
, function() { return __propagating; }
|
|
);
|
|
|
|
__const( this, "data", data );
|
|
|
|
this.setTarget = function( target )
|
|
{
|
|
__const( this, "target", target );
|
|
this.setTarget = undefined;
|
|
}.bind( this );
|
|
};
|
|
|
|
/**
|
|
* @constructor
|
|
*/
|
|
var EventDispatcher = function() {
|
|
var events = {};
|
|
var _self = this;
|
|
|
|
var getStack = function(name)
|
|
{
|
|
if (!events[name]) events[name] = [];
|
|
return events[name];
|
|
};
|
|
|
|
var _dispatch = function()
|
|
{
|
|
this.evt.propagate();
|
|
|
|
for (var i in this.stack)
|
|
{
|
|
if (this.evt.propagating)
|
|
{
|
|
this.stack[i](this.evt);
|
|
}
|
|
}
|
|
|
|
this.evt.stopPropagating();
|
|
};
|
|
|
|
/**
|
|
* @param {string} type
|
|
* @param {function(!BotanEvent): void} handler
|
|
* @return {void}
|
|
*/
|
|
this.addEventListener = function(type, handler)
|
|
{
|
|
var stack = getStack(type);
|
|
stack[stack.length] = handler;
|
|
};
|
|
|
|
/**
|
|
* @param {string} type
|
|
* @param {function(!BotanEvent): void} handler
|
|
* @return {void}
|
|
*/
|
|
this.removeEventListener = function(type, handler)
|
|
{
|
|
var stack = getStack(type);
|
|
var i = stack.indexOf(handler);
|
|
if (i < 0) return;
|
|
delete stack[i];
|
|
};
|
|
|
|
/**
|
|
* @param {!BotanEvent} _evt
|
|
* @return {boolean}
|
|
*/
|
|
this.dispatchEvent = function(_evt)
|
|
{
|
|
var _stack = getStack(_evt.type);
|
|
if (_evt.setTarget) _evt.setTarget(_self);
|
|
|
|
setTimeout(_dispatch.bind({ evt: _evt, stack: _stack }), 0);
|
|
return true;
|
|
};
|
|
};
|
|
|
|
/* End BotanEvent & EventDispatcher }}}*/
|
|
|
|
/**
|
|
* @constructor
|
|
* @extends {EventDispatcher}
|
|
* @param {!Object<string, *>} target
|
|
* @param {string} name
|
|
*/
|
|
var NamespaceObj = function(target, name)
|
|
{
|
|
EventDispatcher.call(this);
|
|
|
|
/** @private {!Object<string, *>} */
|
|
this.t_ = target;
|
|
|
|
/** @private {string} */
|
|
this.n_ = name;
|
|
};
|
|
|
|
NamespaceObj.prototype = Object.create(EventDispatcher.prototype);
|
|
NamespaceObj.prototype.constructor = NamespaceObj;
|
|
|
|
/**
|
|
* @param {string} type
|
|
* @param {string} name
|
|
* @param {*} obj
|
|
* @return {void}
|
|
*/
|
|
NamespaceObj.prototype.exportSymbol = function(type, name, obj)
|
|
{
|
|
if (this.t_[name]) return;
|
|
|
|
this.t_[name] = [type, obj];
|
|
|
|
var evt = new BotanEvent("NS_EXPORT", {
|
|
"name": this.n_ + "." + name,
|
|
"type": type
|
|
});
|
|
|
|
BotanJS.dispatchEvent(evt);
|
|
};
|
|
|
|
/**
|
|
* @param {string} target
|
|
* @return {*}
|
|
*/
|
|
NamespaceObj.prototype.invoke = function(target)
|
|
{
|
|
if (!this.t_[target])
|
|
{
|
|
throw new Error(
|
|
"[" + this.n_ + "] "
|
|
+ "Invoke failed: " + target + " does not exists"
|
|
);
|
|
}
|
|
|
|
return this.t_[target][1];
|
|
};
|
|
|
|
/**
|
|
* @param {string} code
|
|
* @param {function(...?): *} func
|
|
* @return {void}
|
|
*/
|
|
NamespaceObj.prototype.trigger = function(code, func)
|
|
{
|
|
this.t_.__TRIGGERS[code] = func;
|
|
};
|
|
|
|
/**
|
|
* @param {string} message
|
|
* @param {string=} subclass
|
|
* @return {void}
|
|
*/
|
|
NamespaceObj.prototype.throwError = function(message, subclass)
|
|
{
|
|
subclass = subclass ? ("." + subclass) : "";
|
|
|
|
throw new Error(
|
|
"[" + this.n_ + subclass + "] " + message
|
|
);
|
|
};
|
|
|
|
var packages = {};
|
|
var _global = {};
|
|
var _NSs = {};
|
|
var _cacheIMP = {};
|
|
|
|
/*{{{ Constants */
|
|
var EX_CONST = 0;
|
|
var EX_VAR = 1;
|
|
var EX_CLASS = 2;
|
|
var EX_FUNC = 3;
|
|
|
|
var EX_READONLY_GETTER = 10;
|
|
|
|
var NS_INVOKE = 0;
|
|
var NS_EXPORT = 1;
|
|
var NS_TRIGGER = 10;
|
|
var NS_THROW = 11;
|
|
|
|
var TGR_IMPORT = 0;
|
|
/* End Constants }}}*/
|
|
|
|
/*{{{ Multi-instance handle */
|
|
// Check if we are being imported 2nd time
|
|
// If yes, we get the instance and overload
|
|
// some core methods
|
|
var sGarden = window["BotanJS"];
|
|
|
|
var BotanJS = null;
|
|
var __namespace = null;
|
|
var __import = null;
|
|
|
|
if( sGarden )
|
|
{
|
|
BotanJS = sGarden["sg"][0];
|
|
__namespace = sGarden["sg"][1];
|
|
__import = sGarden["sg"][2];
|
|
}
|
|
/* End Multi-instance handle }}}*/
|
|
|
|
/*{{{ Root level bug-free handlers */
|
|
BotanJS = BotanJS || (function()
|
|
{
|
|
var i = 0;
|
|
var mesg = [];
|
|
var structures = [];
|
|
var edp = new EventDispatcher();
|
|
|
|
var log = {};
|
|
log.write = function( m ) { mesg[ mesg.length ] = m; };
|
|
__static_method( log, "read", function() { return mesg[ i ++ ]; } );
|
|
__static_method( log, "end", function() { return ( mesg.length <= i ); } );
|
|
|
|
__const( edp, "log", log );
|
|
|
|
__static_method( edp, "define", function( f ) { structures[ structures.length ] = f; } );
|
|
__static_method( edp, "getDef", function() { return structures.slice(); } );
|
|
|
|
return edp;
|
|
})();
|
|
/* End Root level bug-free handlers }}}*/
|
|
|
|
/*{{{ Namespace declarator */
|
|
__namespace = __namespace || function(ns)
|
|
{
|
|
if (_NSs[ns]) return _NSs[ns];
|
|
|
|
var p = ns.split(".");
|
|
var l = p.length;
|
|
|
|
var target = packages;
|
|
for (var i = 0; i < l; i++)
|
|
{
|
|
target[p[i]] = target[p[i]] || {};
|
|
target = target[p[i]];
|
|
}
|
|
|
|
/** @type {!Object<string, function(...?): *>} */
|
|
target.__TRIGGERS = {};
|
|
|
|
var nsObj = new NamespaceObj(target, ns);
|
|
|
|
BotanJS.dispatchEvent(new BotanEvent("NS_INIT", ns));
|
|
|
|
nsObj[NS_EXPORT] = nsObj.exportSymbol.bind(nsObj);
|
|
nsObj[NS_INVOKE] = nsObj.invoke.bind(nsObj);
|
|
nsObj[NS_TRIGGER] = nsObj.trigger.bind(nsObj);
|
|
nsObj[NS_THROW] = nsObj.throwError.bind(nsObj);
|
|
|
|
return (_NSs[ns] = nsObj);
|
|
};
|
|
/* End Namespace declarator }}}*/
|
|
|
|
/*{{{ Import operator */
|
|
__import = __import || function( ns, noCache )
|
|
{
|
|
var nss = ns.replace( ".*", "" );
|
|
if( _NSs[ nss ] )
|
|
{
|
|
_NSs[ nss ].dispatchEvent( new BotanEvent( "NS_IMPORT", nss ) );
|
|
}
|
|
|
|
// Read The Cache First
|
|
if( !noCache && _cacheIMP[ ns ] ) return _cacheIMP[ ns ];
|
|
|
|
var p = ns.split(".");
|
|
var l = p.length;
|
|
|
|
var target = packages;
|
|
var wildcard = false;
|
|
for( var i = 0; i < l; i ++ )
|
|
{
|
|
if( p[i] == "*" )
|
|
{
|
|
wildcard = true;
|
|
break;
|
|
}
|
|
|
|
target = target[ p[i] ];
|
|
|
|
if( !target )
|
|
throw new Error( "No such class: " + ns );
|
|
}
|
|
|
|
if( target instanceof Array && p[i] != "*" )
|
|
{
|
|
var rtarget = null;
|
|
if( target[0] == EX_READONLY_GETTER )
|
|
{
|
|
rtarget = target[1]();
|
|
}
|
|
else
|
|
{
|
|
rtarget = target[1];
|
|
}
|
|
|
|
_cacheIMP[ ns ] = rtarget;
|
|
return rtarget;
|
|
}
|
|
|
|
var nsObj = {};
|
|
|
|
for( var i in target )
|
|
{
|
|
var j = target[i];
|
|
if( j instanceof Array )
|
|
{
|
|
if( wildcard && j[0] == EX_CLASS )
|
|
{
|
|
__const( nsObj, i, j[1] );
|
|
}
|
|
else if( j[0] == EX_FUNC )
|
|
{
|
|
__const( nsObj, i, j[1] );
|
|
}
|
|
else if( j[0] == EX_CONST )
|
|
{
|
|
Object.defineProperty( nsObj, i, {
|
|
get: function() {
|
|
return this.t[ this.p ][1];
|
|
}.bind( { p: i, t: target } )
|
|
, set: function( v ) {
|
|
throw new Error( "Setting a read-only property: " + this.p );
|
|
}.bind( { p: i } )
|
|
});
|
|
}
|
|
else if( j[0] == EX_VAR )
|
|
{
|
|
Object.defineProperty( nsObj, i, {
|
|
get: function() {
|
|
return this.t[ this.p ][1]();
|
|
}.bind( { p: i, t: target } )
|
|
, set: function( v ) {
|
|
this.t[ this.p ][1]( v );
|
|
}.bind( { p: i, t: target } )
|
|
});
|
|
}
|
|
else if( j[0] == EX_READONLY_GETTER )
|
|
{
|
|
Object.defineProperty( nsObj, i, {
|
|
get: j[1]
|
|
, set: function( v ) {
|
|
throw new Error( "Setting a read-only property: " + this.p );
|
|
}.bind( { p: i } )
|
|
});
|
|
}
|
|
}
|
|
// import the namespace
|
|
else if( wildcard && j.__TRIGGERS )
|
|
{
|
|
__const( nsObj, i, __import( nss + "." + i ) );
|
|
}
|
|
}
|
|
|
|
_cacheIMP[ ns ] = nsObj;
|
|
return nsObj;
|
|
};
|
|
/* End Import operator }}}*/
|
|
|
|
window["BotanJS"] = {};
|
|
window["BotanJS"]["version"] = BOTANJS_VERSION;
|
|
window["BotanJS"]["codename"] = "Botanical framework.js";
|
|
window["BotanJS"]["import"] = function( p )
|
|
{
|
|
try { return __import( p ); }
|
|
catch( e )
|
|
{
|
|
if( sGarden ) return sGarden["import"]( p );
|
|
throw e;
|
|
}
|
|
};
|
|
window["BotanJS"]["sg"] = [ BotanJS, __namespace, __import ];
|