sumeru
Version:
A Realtime Javascript RIA Framework For Mobile WebApp
333 lines (280 loc) • 11.1 kB
JavaScript
//Debug开关, 发布时删除
var SUMERU_APP_FW_DEBUG = true;
(function(global,rootName) {
/**
* 包结构的自管理方法.提供添加包空间的addSubPackage及添加取得方法的getter,setter方法 所有自管理方法
* 均会在页面载入完成后被自动清理掉.
*/
var NS = 'NAMESPACE';
var interName = 'sumeru_AppFW';
/**
* 记录所有包结构的全名及对应的包对像
*/
var tables = {};
var old = false;
/*
* internal function
*/
var require = function(path,callback){
Library.net.get({
url : path,
callback:function(data){
var exports = {};
(new Function('exports',data)).call(null,exports);
callback && callback(exports);
}
});
};
/**
* 根据给定的全名创建一个名称空间.
* 方法利用创建出空间对像的prototype做为所创建名称空间的公共资源池,
* 直接除加于空间对像的资源被认为是private,
* 最终seal时候,替换空间对像为该对像的prototype达到隐藏的private资源的目的.
*
* @param fullName {string} 当前命名空间的全名.
* @returns {Object}
*/
var createSpace = function(fullName){
var space , _prot = {};
if(Object.create){
space = Object.create(_prot);
}else{
/*
* 如果存在Object.create 则js实现版本为1.85以上.
* 如果不存在,则同时不存在getPrototypeOf等方法. 对于个别低版本浏览器同时不存在__proto__属性,如Opera 10.5等
* 所以在这里需要补充一个可以获取到得prototype的方法,
*/
function fn(){};
fn.prototype = _prot;
space = new fn();
space.__getPrototypeof = function(){
return _prot;
};
}
/*
* 添加私有成员、方法
*/
space.__namespace = NS; // 是否是一个命名空间
space.__isSeal = false; // 是否已经执行seal处理
space.__require = function(path,callback,name){
require(path,function(exports){
callback && callback(exports);
if(name){
space.__reg(name,exports);
}
});
};
/**
* 触发当前空间下对某个资源所设的所有陷阱
* @param name {string} 资源名称
*/
space.__runTraps = function(name){
var list = tables[fullName].traps[name];
if(list && list.length > 0){
list.forEach(function(item){
item(space[name]);
});
}
};
/**
* 同步注册一个资源
* @param namej {string} 资源名称
* @param resource {Any} 将注册到该名称空间上的资源
* @param isPrivate {Boolean} 是否注册为私有
* @returns {any} 返回注册的resource
*/
space.__reg = function(name, resource, isPrivate){
if(isPrivate){
space[name] = resource;
}else{
_prot[name] = resource;
}
// 尝试触发trap队列
space.__runTraps(name);
return resource;
};
/**
* 异步注册一个资源
* @param namej {string} 资源名称
* @param isPrivate {Boolean} 是否注册为私有
* @returns {function callback(resource){}} 回调方法,resource将被注册到该名称空间下.
* 如果重复调用这个callback方法,则可反复覆盖修改资源.
*/
space.__regAsync = function(name, isPrivate){
// return callback fun...
return function(resource){
if(isPrivate){
space[name] = resource;
}else{
_prot[name] = resource;
}
// 尝试触发trap队列
space.__runTraps(name);
};
};
/**
* 获取一个已注册的资源.
* @param name {string} 目标获取的资源名称.
* @returns {any} 目标资源,如果没有返回undefined.
*/
space.__load = function(name){
return space[name];
};
/**
* 异步获取一个资源.当这个资源被注册、修改时,将自动触发callback所指定的方法.
* @param name {string} 目标获取的资源名称.;
* @param callback {function} 回调方法,
* 当调用之个方法时,如果目标资源已存在,则自动执行一次callback.
* 如果获到一个未被注册的资源,创建一个trap,直到目标资源被注册时,自动调用一次;
*/
space.__loadAsync = function(name,callback){
var traps;
// 是否已有陷阱队列,没有则创建
if(!(tables[fullName].traps[name])){
// 此处使用array ,便于对一个资源,使用多个trap.
traps = tables[fullName].traps[name] = [];
}else{
traps = tables[fullName].traps[name];
}
/*
* FIXME:
* 此处不检测是否callback被重复注册,
* 目前情况下,如果重复注册callback会导到traps队列中存在多个相同callback,
* 在资源改变时将导至callback可能被执行多次,是否FIX视最终使用情况反馈决定.
*/
traps.push(callback);
// 如果资源存在,则自动执行一次
if(space[name] !== undefined){
// 资源被注册时,自动执行一次当前注册的trap
callback(space[name]);
}
};
/**
* 在当前空间下添加子空间
* @param name {string} 子空间名称
* @returns {Object} 子空间对像
*/
space.addSubPackage = function(name){
var sfn = fullName + "." + name; //package full name
if(tables[fullName].isSeal){
throw 'package ["' + fullName + '"] has sealed.';
}else if(!tables[sfn]){
// 创建包空间
_prot[name] = createSpace(sfn);
/*
* 记录包结构
* {
* package:包结构对像,
* isSeal : 是否执行过清理方法
* traps: 资源陷阱,用于存放资源获的回调
* }
*/
tables[sfn] = {'spaceObj':space[name],'isSeal':false,traps:{}};
return _prot[name];
}else{
throw 'package ["' + sfn + '"] already exists';
}
};
/**
* 清理当前命名空间下的子空间
*/
space.__clear = function(){
var ns;
// 清理过的内容,不再执行清
if(tables[fullName].isSeal){
return;
}
// 清理子空间
for(var key in _prot){
ns = _prot[key];
if(ns.__namespace === NS){
ns.__clear();
if(Object.getPrototypeOf){
_prot[key] = Object.getPrototypeOf(ns);
}else{
_prot[key] = ns.__getPrototypeof();
}
/*
* 利用prototype的求值检索顺序,在完整的空间对像间建立引用,
* 保证在清理方法调用之前,已经对空间对像所做的引用仍然可以访问private资源。
*/
space[key] = ns;
}
}
// 标记清理完成
tables[fullName].isSeal = true;
};
return space;
};
/**
* 如果目标空间存在,则将当前包空间上的对像,copy到新的包对像上用作public
*/
old = global[rootName];
global[rootName] = createSpace(interName);
tables[interName] = {'spaceObj':global[rootName],'isSeal':false,traps:{}};
if(old){
for(var key in old){
global[rootName].__reg([key],old[key]);
}
}
/**
* 隐藏根命名空间下的私有成员.
*/
global[rootName].clear = function(){
global[rootName].__clear();
global[rootName].seal = undefined;
delete global[rootName].seal;
global[rootName] = Object.getPrototypeOf(global[rootName]);
};
if(typeof module !='undefined' && module.exports){
GLOBAL[rootName] = global[rootName]; // 在node端仍然绑定到全局空间一个名为sumeru的对像
module.exports = function(){
return global[rootName];
};
}
if(SUMERU_APP_FW_DEBUG){
// DEBUG, 暴露当前所有包结构的映射
global.DEBUG_PKG_MAPPING = tables;
// 同时绑定debug开关到根结点上,此操作用于在server端区分debug状态.
global[rootName].__reg("SUMERU_APP_FW_DEBUG", true);
}
})(this,'sumeru');
var Run_UnitTest_PKG = function(){
var root = sumeru; // 引用最初的FW对像,
// simple unit testing....
a = fw.addSubPackage('a');
b = a.addSubPackage('b');
// async resource trap...
a.__loadAsync('name',function(name){
console.log('fire trap...fw.a.name : ' + name);
});
b.__loadAsync('name',function(name){
console.log('fire trap...fw.b.name : ' + name);
});
// sync __reg , async __load
fw.a.b.__reg('age',100,true);
b.__loadAsync('age',function(age){
console.log('fire trap...fw.b.age : ' + age);
});
var f0 = a.__regAsync('name');
var f1 = fw.a.b.__regAsync('name');
// async reg delay.
setTimeout(function(){
f0('package fw.a');
f1('package fw.a.b');
for(var i = 100;i<110;i++){
f1('name _ ' + i); // fire trap. 10 times
}
console.log(a.b.age); // 100;
console.log(fw.a.b.age); // undefined;
console.log('async done...');
}, 1000);
fw.clear();
console.log(fw.a.b.age === undefined); // true
console.log(root.a.b.age === 100); // true
console.log(fw.addSubPackage === undefined); // true
console.log(fw.a.addSubPackage === undefined); // true
console.log(fw.a.b.addSubPackage === undefined); //true
console.log(a.addSubPackage === undefined); // false
console.log(b.addSubPackage === undefined); // false
};