sumeru
Version:
A Realtime Javascript RIA Framework For Mobile WebApp
1,299 lines (1,060 loc) • 67.4 kB
JavaScript
var runnable = function(fw){
var _controller = fw.addSubPackage('controller');
//所有当前活跃的控制器的容器
//[{controller : controllerClass, renderFunc : renderFunc}, ...]
//其中renderFunc主要是当服务器推送数据时,给MsgDispatcher使用的
var activeController = [];
var activeControllerId;
var cloneDom = null;
var transitionOut = false;//如果为true,则表明此controller是反转出场
var globalIsforce = null;
function updateBlockHtml(element, html, opts) {
if(element.innerHTML == html)
{
return;
}
var options = Library.objUtils.extend({}, {
domdiff : false
}, opts);
Library.touch.trigger(element, 'block_before_render');
if (options.domdiff) {
element.innerHTML = element.innerHTML;
fw.domdiff.convert(html, element);
} else {
element.innerHTML = html;
}
Library.touch.trigger(element, 'block_after_render');
}
/**
* 全局的模板ID => 数据的Map
*/
var _bindingMap = {};
/**
* 绑定数据和事件的方法
*/
//FIXME 现在所有的widgetId都默认全App之内唯一
// _bindingData中的widgetId, 是应该表示一个数据集的名称,由于数据集原本就是在messageDispatcher.js中向全局任意位置派发,所以此处全局唯一不应存在问题. wangsu
var _bindingData = function(key, dataMap){
var widgetId = this.__UK + "_" + key;
if(!_bindingMap[widgetId]){
_bindingMap[widgetId] = {
view_data : {},
eventMap : {}
};
}
//如果存在tapped_blocks,则是在messageDispatcher处定义的
var tapped_blocks = _controller.__load('_tapped_blocks');
if(typeof tapped_blocks != 'undefined'){
tapped_blocks.push({
widgetId : widgetId
});
}
for(var i in dataMap){
if(dataMap.hasOwnProperty(i)){
_bindingMap[widgetId].view_data[i] = dataMap[i];
}
}
};
//_bindingDataByPage和_bindingData的唯一区别是viewdata中细分了按页码存放的子数组
var _bindingDataByPage = function(key, dataMap){
var widgetId = this.__UK + "_" + key;
if(!_bindingMap[widgetId]){
_bindingMap[widgetId] = {
view_data : {},
eventMap : {}
};
}
if (typeof dataMap.page == 'undefined') {
throw "subscribeByPage always need a page parameter";
};
_bindingMap[widgetId].isByPage = true;
var container = _bindingMap[widgetId]['view_data'][dataMap.page] = {};
//如果存在tapped_blocks,则是在messageDispatcher处定义的
var tapped_blocks = _controller.__load('_tapped_blocks');
if(typeof tapped_blocks != 'undefined'){
tapped_blocks.push({
widgetId : widgetId,
page : dataMap.page,
options : dataMap.options || {}
});
}
for(var i in dataMap){
if(dataMap.hasOwnProperty(i)){
container[i] = dataMap[i];
}
}
};
var _cloneDom = function(instance) {
cloneDom = instance.getDom().cloneNode(true);
cloneDom.style.zIndex = "0";
cloneDom.removeAttribute("id");
var tpls = cloneDom.querySelectorAll("[tpl-id]");//去掉tpl-id,防止多加载
for (var i = 0,len = tpls.length ; i < len ; i++ ) {
tpls[i].removeAttribute("tpl-id");
}
}
var _bindingEvent = function(key, eventFunc){
var widgetId = this.__UK + "_" + key;
if(!_bindingMap[widgetId]){
_bindingMap[widgetId] = {
view_data : {},
eventMap : {}
};
}
_bindingMap[widgetId].eventMap['__default_key__'] = eventFunc;
//
// var self = this;
//
// _bindingMap[widgetId].eventMap['__default_key__'] = function(){
// eventFunc.call(self);
// //event之后,检查a标签,通过history.js实现非刷新页面
// if (typeof document != 'undefined'){
// var anchors = document.querySelectorAll('a');
// for(var i=0,len=anchors.length;i<len;i++){
// if (anchors[i].href.indexOf(location.origin) != -1 && !anchors[i].getAttribute("link-ajax")){//已?或者/开头的链接将阻止跳转
// (function(item){
// item.addEventListener("click", function(e){
// var _this = this;
// sumeru.router.redirect(_this.href.substr(location.origin.length));
// e.preventDefault();
// });
// item.setAttribute("link-ajax",true)
// })(anchors[i]);
//
// }
// }
// }
//
// };
//立即执行一次
_bindingMap[widgetId].eventMap['__default_key__']();
};
var queryElementsByTplId = function(id,rootElement){
return (rootElement || document).querySelectorAll('block[tpl-id="' + id + '"]') || [];
};
//根据模板关系,从父模板到子模板,对每个block进行渲染
var doRenderByRelation = function(relationMap, func){
for (var i in relationMap){
func(i, relationMap[i]);
doRenderByRelation(relationMap[i], func);
}
};
/**
* 查找重用一个dom的controller,并无条件将其销毁...
*
* FIXME 真狠..此处可能有销毁性能上的潜藏问题
* @param id dom元素的id, 一般为模板根block的名称
* @param butMe 需要跳过的销毁的controller id
*/
var findAndDestroy = _controller.__reg('findAndDestroy',function(idArr,butMe){
var destroyItems = activeController.filter(function(item){
var id = item.__getID();
if(id === butMe){
}else if(idArr.indexOf(id)!== -1){
return true;
}
return false;
});
destroyItems.forEach(function(item){
item.destroy();
});
},true);
/**
* Controller's Env base
*/
var _Environment = function(){
this.isWaiting = 0;
this.isWaitingChecker = null;
this.callbackFunc = null;
this.isDestroy = false;
this.withEnyo = false;//third plugin enyojs
};
var _pubSubObject = new fw.pubsub._pubsubObject();
_Environment.prototype = {
__destroy:function(){
fw.utils.cleanObj(this);
this.isDestroy = true;
fw.pubsub.clear();
},
setClockChecker : function(){
var after = 8 * 1000;
this.isWaitingChecker = setTimeout(function(){
//这个checker可能会重复,所以添加hack
if (this.isWaiting > 0)//hack
throw 'NOT call env.start after ' + after / 1000 + ' seconds, did you forget it?';
}, after);
},
clearClockChecker : function(){
clearTimeout(this.isWaitingChecker);
},
setCallback : function(func){
this.callbackFunc = func;
},
fireCallback : function(){
if(this.isWaiting <= 0 && this.callbackFunc){
this.callbackFunc();
}
},
redirect:function(queryPath,paramMap,isforce){
var urlHash = queryPath;
if(paramMap){
urlHash += "?" + fw.utils.mapToUriParam(paramMap);
}
fw.router.redirect(urlHash,isforce);
},
refresh:function(){
return this.__getControllerInstance().__refresh();
},
/**
* 调用一个子controller
* @param conNam {string} controller的Name
*/
callSub:function(conNam){
this.__getControllerInstance().__callSubController(conNam);
},
wait : function () {//移到了pubsub.js中
this.isWaiting++;
this.setClockChecker();
},
start : function () {//移到了pubsub.js中
this.clearClockChecker();
if (--this.isWaiting <= 0) {
this.isWaiting = 0;
this.fireCallback();
};
},
onload : function(){
return [];
},
onrender : function(){
throw "Didn't specify a onrender event handler";//这句不能被注释掉,当未指定onrender时候,server渲染会一直等待,只有抛出异常才会执行客户端渲染
},
onready : function(){
},
/*
* 以下情况执行该方法:
* 当前controller移出屏幕并且dom对像将被复用时。
* 性能调优时,长时间不活动的controller需要被暂停时。
*/
onsleep : function(){
},
/*
* 当controller从sleep中恢复活动时,执行该方法
*/
onresume : function(){
},
/**
* 通知controller将被销毁
*/
ondestroy : function(){
},
/**
* 当前controller收到错误消息时被触发
*/
onerror : function(msg){
sumeru.log(msg);
},
subscribe : _pubSubObject.subscribe,
subscribeByPage : _pubSubObject.subscribeByPage,
prioritySubscribe : _pubSubObject.prioritySubscribe
};
var controllerBase = {
__init:function(){
var env = null;
var tapped_block = [];
if(this.__isInited === false){
env = this.getEnvironment();
session = this.getSession();
var that = this;
activeController.push(this);
// 构建空的tapped_blocks用于触发不使用subscribe时的block的渲染
fw.controller.__reg('_tapped_blocks', tapped_block, true);
var toLoad = env.onload();
var doLoad = function(i) {
if (i >= toLoad.length) {
env.setCallback(null);
return;
}
env.setCallback(function() {
doLoad(++i);
});
fw.sense.runSenseContext((function(customClosure) {
return function() {
// 构建空的tapped_blocks用于触发不使用subscribe时的block的渲染
fw.controller.__reg('_tapped_blocks', [], true);
customClosure.call({});
// 触发同步使用session.bind所应产生的演染
that.__render(fw.controller._tapped_blocks);
};
})(toLoad[i]));
// toLoad[i]();
// 如果这次没调用wait函数,则fire生效。否则此fire进入后就立即推出了,等待resume方法的fire进行实际触发。
env.fireCallback();
};
// 开始初始化记载数据
doLoad(0);
// 构建空的tapped_blocks用于触发不使用subscribe时的block的渲染
// 触发第一次渲染
this.__render();
// 标记初始化结束
this.__isInited = true;
}
},
__render:function(tapped_block,increment){
var me = this;
var item = tapped_block.widgetId;
var env = this.getEnvironment();
//FIXME 添加了enyo的hack
if ( env.withEnyo ) {
this.__renderEnyo(tapped_block);
return true;
} else if(typeof _bindingMap[item] == 'undefined' || me.__templateBlocks[item] == undefined){
return false;
}
var record = _bindingMap[item];
if (record.isByPage) {
//如果是分页类的数据绑定
var targets = queryElementsByTplId(item),
page = tapped_block.page,
data = _bindingMap[item]['view_data'][page],
onePageContainer;
for(var i = 0, l = targets.length; i < l; i++){
var target = targets[i];
if (tapped_block.options.cleanExist) { //如果指明要清除现有内容
var matched = target.querySelectorAll('[__page-unit-rendered-page]'),
existNode;
for (var x = 0, y = matched.length; x < y; x++){
existNode = matched[x];
existNode.parentNode.removeChild(existNode);
existNode.innerHTML = '';
};
};
//检查是否已被渲染的分页区域 page-unit-rendered-page
if (target.querySelectorAll('[__page-unit-rendered-page]').length == 0) {
//初次渲染
var html = me.__templateBlocks[item](data);
//将标记的tpl-role=page_unit换为page-unit-rendered-page
html = html.replace(/tpl-role[\s]*=[\s]*['"]page_unit['"]/, '__page-unit-rendered-page="' + page + '"');
//target.innerHTML = html;
updateBlockHtml(target, html);
} else {
var fakeNode = document.createElement('div');
fakeNode.innerHTML = me.__templateBlocks[item](data);
var onePageDom;
var onePageContentMatchElement = fakeNode.querySelector('[tpl-role="page_unit"]');
if (onePageContentMatchElement == null) {
//如果没有找到tpl-role的语法标记,则默认使用整个模板
onePageDom = fakeNode;
} else {
onePageDom = onePageContentMatchElement;
}
//将标记的tpl-role=page_unit换为page-unit-rendered-page
onePageDom.removeAttribute('tpl-role');
onePageDom.setAttribute('__page-unit-rendered-page', page);
//判断是否需要创建容器
var container;
if (container = target.querySelector('[__page-unit-rendered-page="' + page + '"]')) {
//如果已经存在容器
container.innerHTML = onePageDom.innerHTML;
} else {
onePageDom.innerHTML = onePageDom.innerHTML.replace(/tpl-role[\s]*=[\s]*['"]page_unit['"]/, '__page-unit-rendered-page="' + page + '"');
//取当前最后一个单页面容器,插入在其后面
var tmp = target.querySelectorAll('[__page-unit-rendered-page]');
container = tmp[tmp.length - 1];
//insertAfeter
container.parentNode.insertBefore(onePageDom, container.nextSibling);
//it will still work because when the second parameter of insertBefore is null then the onePageDom is appended to the end of the parentNode
}
//重写HTML抹除事件绑定
target.innerHTML = target.innerHTML + ' ';
}
}
var blockEvents = _bindingMap[item]['eventMap'];
for(var i in blockEvents){
blockEvents[i]();
}
} else {
var data = _bindingMap[item]['view_data'];
var targets = queryElementsByTplId(item);
for(var i = 0, l = targets.length; i < l; i++){
var target = targets[i];
if(increment&&fw.config.get('domdiff')){
//target.innerHTML = target.innerHTML;
//fw.domdiff.convert(me.__templateBlocks[item](data),target);
updateBlockHtml(target, me.__templateBlocks[item](data), { domdiff:true });
}else{
//target.innerHTML = me.__templateBlocks[item](data);
updateBlockHtml(target, me.__templateBlocks[item](data));
}
}
var blockEvents = _bindingMap[item]['eventMap'];
for(var i in blockEvents){
blockEvents[i]();
}
//根据模板关系,从触发的当前模块块,对其下每个block重新进行渲染
var found = null;
doRenderByRelation(me.__templateRelation, function(i, currentItem){
if (i == item) {
found = currentItem;
};
});
doRenderByRelation(found, function(i){
if(_bindingMap[i]){ //如果都没绑定数据和事件,就不用处理重新绘制了
var data = _bindingMap[i]['view_data'];
var targets = queryElementsByTplId(i);
for (var x = 0, y = targets.length; x < y; x++){
var target = targets[x];
if(increment&&fw.config.get('domdiff')){
//target.innerHTML = target.innerHTML;
//fw.domdiff.convert(me.__templateBlocks[i](data),target);
updateBlockHtml(target, me.__templateBlocks[item](data), { domdiff:true });
}else{
//target.innerHTML = me.__templateBlocks[i](data);
updateBlockHtml(target, me.__templateBlocks[item](data));
}
}
var blockEvents = _bindingMap[i]['eventMap'] || {};
for(var key in blockEvents){
blockEvents[key]();
}
}
});
}
return true;
},
__renderEnyo: function( tapped_block ) {
var enyoapp = session.get('enyoapp');
if ( !enyoapp ) {
sumeru.log("__renderEnyo -> no enyoapp detacted!");
return false;
}
if ( !tapped_block ) {//第一次要render到body上
var doc = document.getElementById('view/blank@@content');
enyoapp.renderInto(doc);
return;
}
var item = tapped_block.widgetId;
var tmp = item.split(".").pop();
var blockEvents = _bindingMap[item]['eventMap'];
//执行步骤,是enyo更新dom的步骤
//1.销毁,2.更新数据,3.render
enyoapp.$[tmp].destroyClientControls();
enyoapp.$[tmp].fwdata =_bindingMap[item].view_data.fwdata;
enyoapp.$[tmp].fwdataChanged();
enyoapp.$[tmp].render();
for( var i in blockEvents ) {//这里是执行enyo.
blockEvents[i]();
}
},
__refresh:function(){
var identifier = this.__getID(), constructor , instance;
var arr = identifier.split("!");
if(arr.length === 1){
arr.push('');
}
constructor = findController(arr[0]);
if (constructor === false) {
//可能由第三方程序接管,framework停止响应
return;
};
this.destroy(true,true); // 销毁自身,但保留session
//创建一个新的controller,将自动使用未被销毁的旧session
instance = new MainController(identifier, fw.utils.uriParamToMap(arr[1]), constructor);
instance.__init();
},
rewriteUri:function(dom){
if (typeof dom != 'undefined'){
var checkRedirect = function(e){
if (e.target.nodeName.toLowerCase() =='a' && e.target.href.indexOf(location.origin) != -1) {//站内的链接,阻止跳转 已?或者/开头的链接将阻止跳转
if (!e.defaultPrevented) {
sumeru.router.redirect(e.target.href.substr(location.origin.length));
e.preventDefault();
}
}
};
if (!dom.getAttribute("link-ajax") && !dom.getAttribute("data-rel")) {
dom.addEventListener("click", checkRedirect);
dom.addEventListener("touchstart", checkRedirect);
dom.setAttribute("link-ajax",true);
}
}
},
destroy:function(isKeepSession,isKeepDomForTrans){
var id = this.__getID();
var uk = this.__UK;
//try {
var session = this.getSession();
var env = this.getEnvironment();
//销毁之前,我要 clonedom 一份用于转场
//自己转自己,没有 clonedom 会出错,所以添加容错
if(!cloneDom && typeof this.getDom != 'undefined' && isKeepDomForTrans) _cloneDom(this);
try {
env.ondestroy(); // 通知用户controller将被销毁.
} catch (e) {}
// 清理当前占用的dom
fw.render.clearTplContent(session);
// 销毁过程..
if(isKeepSession !== true){
fw.session.destroy(session);
}else{
session.cleanObserver();
}
env.__destroy();
//销毁dom
//added view delete
fw.render.delTpl(this.tplName);
// 销毁子controller
if(this.__subControllerList){
for(var key in this.__subControllerList){
this.__subControllerList[key].destroy();
}
}
// 清理_bindingMap
for(var key in _bindingMap){
if(key.indexOf(uk) === 0){
fw.utils.cleanObj(_bindingMap[key].view_data);
fw.utils.cleanObj(_bindingMap[key].eventMap);
fw.utils.cleanObj(_bindingMap[key]);
delete _bindingMap[key];
}
}
// 外引对像 & 闭包方法
this.getSession = null;
this.getEnvironment = null;
this.__getIdentifier = null;
delete this.getSession;
delete this.getEnvironment;
delete this.__getIdentifier;
/*} catch (e) {
// TODO: handle exception
console.error(e);
}finally{*/
var old = activeController;
// 无论如何,清除activeController中的引用
activeController = old.filter(function(item){
return item.__getID() !== id;
});
// 清空旧数组
old.length = 0;
fw.dev('destory ["' + id + '"]');
//}
}
};
/**
* 子controller的基类
* 子controller,访问父controller的session与传入的collection对像用于触发父对像的随动反馈.
* @param id {string}
* @param params {any} 传入参数
*/
var SubControler = function(id, params, _parent , constructor){
var me = this;
var env , session;
env= new _Environment();
session = fw.session.create(id); // 相同的id会返回相同的session
session.bind = _bindingData;
session.event = _bindingEvent;
//FIXME 为了与给HP的代码一致,临时给出一个简单的eventMap实现。后续要与事件库一起考虑
session.eventMap = fw.event.mapEvent;
session.__isSubController = true;
// 取得父controller的session
session.getMain = function(){
return _parent.session;
};
env.isReady = function(){
return false;
};
env.show = function(){
throw 'this controller not ready';
};
env.hide = function(){
throw 'this controller not ready';
};
env.destroy = function(){
me.destroy();
};
// 确保session bind时数据项唯一的随机字符串key
env.__UK = session.__UK = this.__UK = "T" + fw.utils.randomStr(10);
// 添加取得当前对像的get方法,
// 保证值在controller的生命周期中是唯一,并且不可变更.
this.getSession = function(){
return session;
};
this.getEnvironment = function(){
return env;
};
this.__getID = function(){
return id;
};
this.__isFirstRender = true;
this.__isInited = false;
this.__templateBlocks = false;
this.__templateRelation = false;
constructor( env, session , params , _parent);
env.__getControllerInstance = function(){
return me;
};
fw.dev("create new : " + id);
};
SubControler.prototype = fw.utils.extendFrom(controllerBase,{
__render:function(tapped_blocks){
var me = this;
var env = me.getEnvironment();
var session = me.getSession();
var _transitionType = null;//push left,right,down,up
//此处env是在构造方法中创建,并在用户controller的构造方法执行时被替换了真正的生命周期方法.
env.onrender(function(tplName, position , transitionType){
session.__currentTplName = tplName; // 记录到session
var block , blockStyle = {
position : 'absolute',
top:0,
left:0,
width:'100%',
height:70,
display:'none'
};
//这里添加render时候的记录
_transitionType = (typeof transitionType !== 'undefined') ? ( transitionType ) : null;
var doRender = function(){
//pub sub触发的局部渲染
if (typeof tapped_blocks != 'undefined'){ //如果定义了tapped_blocks,那么就一定是来自msgDispater的局部渲染调用
//优先抛弃length == 0的情况
if(!tapped_blocks.length){
return;
}
//如果isFirstRender还为true,则说明数据push在首次渲染之前就到达了。需要等待首次渲染完成。
if (me.__isFirstRender) {
setTimeout(function(){
me.__render(tapped_blocks);
}, 50);
return;
}
for (var i = 0, l = tapped_blocks.length; i < l; i++){
//=====================
controllerBase.__render.call(me,tapped_blocks[i]);
//=====================
};
return;
}
// 只记录模版的更新方法,模版骨架的创建方法,会在getTpl的时候,首次返回时解析.如果实在必须重新渲染骨架,使用 render.renderBone
var renderObject = fw.render.buildRender(session);
me.__templateBlocks = renderObject.renderBlock;
me.__templateRelation = renderObject.blocksRelation;
block = fw.render.getTplContent(session);
fw.utils.setStyles(block,fw.utils.cpp(blockStyle,position));
//对每个block进行渲染
//根据模板关系,从父模板到子模板,对每个block进行渲染
doRenderByRelation(me.__templateRelation, function(i){
var targets = queryElementsByTplId(i);
for(var x = 0, y = targets.length; x < y; x++){
var target = targets[x];
//target.innerHTML = me.__templateBlocks[i]({});
updateBlockHtml(target, me.__templateBlocks[i]({}));
}
});
// 创建模版容器后,创真正的显示和隐藏方法
//这里添加转场动画,暂时只在onrender时候设置了默认动画,如果以后这里要加也可以
//其实这里很容易,因为只需要进场出场效果而已
env.show = function( transitionType ){
fw.transition._subrun({
"dom" : block,
"anim" : transitionType || _transitionType // 如果没指明,则使用上一次的
},false);
if (!_transitionType && transitionType ) {
_transitionType = transitionType;
}
me.rewriteUri(block);
};
env.hide = function( transitionType ){
fw.transition._subrun({
"dom" : block,
"anim" : transitionType || _transitionType // 如果没指明,则使用上一次的
},true);
};
env.setPosition = function(css){
fw.utils.setStyles(block,css);
};
env.isReady = function(){
return true;
};
session.toBeCommited.length = 0;
//FIXME 不能直接onready,还要判断是否FirstRender完成
env.onready(block);
me.__isFirstRender = false;
}; //end dorender
//这里修复了一个可能出现死锁的BUG,当tpl已经加载,对象却destroy的时候会出现,永远都是__isFirstRender == true
//可能引起其他问题,另外不开控制台断点没问题,代码逻辑没问题,暂不修复 FIXME
// if ( fw.render.getTplStatus(tplName) === 'loaded' ) {
// me.__isFirstRender = false;
// }
fw.render.getTpl(tplName,session,function(render){
me.tplName = tplName;//加入tplName入口
doRender();
});
});
}
});
/**
* controller
*/
var MainController = function(id, contr_argu , constructor){
var me = this;
var env , session;
env= new _Environment();
var isFirstPage = (activeController.length==0);
session = fw.session.create(id); // 相同的id会返回相同的session
session.bind = _bindingData;
session.bindByPage = _bindingDataByPage;
session.event = _bindingEvent;
//FIXME 为了与给HP的代码一致,临时给出一个简单的eventMap实现。后续要与事件库一起考虑
session.eventMap = fw.event.mapEvent;
env.callSubController = function(name,param,forceFresh){
return me.__callSubController(name,param,forceFresh);
};
// 确保session bind时数据项唯一的随机字符串key
env.__UK = session.__UK = this.__UK = "T" + fw.utils.randomStr(10);
session.__isSubController = false;
// 添加取得当前对像的get方法,
// 保证值在controller的生命周期中是唯一,并且不可变更.
this.getSession = function(){
return session;
};
this.getEnvironment = function(){
return env;
};
this.__getID = function(){
return id;
};
this.__isFirstRender = true;
this.__isInited = false;
this.__templateBlocks = false;
this.__subControllerList = {};
// constructor( env, session , params);
constructor( env, session , session.getContainer());//兼容老式写法,仅限maincontroller和servercontroller
env.__getControllerInstance = function(){
return me;
};
fw.dev("create new : " + id);
};
MainController.prototype = fw.utils.extendFrom(controllerBase,{
__render:function(tapped_blocks){
var me = this;
var env = me.getEnvironment();
var session = me.getSession();
//此处env是在构造方法中创建,并在用户controller的构造方法执行时被替换了真正的生命周期方法.
env.onrender(function( tplName, transitionType ){
session.__currentTplName = tplName; // 记录到session
var tplContentDom;
var doRender = function(){
//pub sub触发的局部渲染
if (typeof tapped_blocks != 'undefined'){ //如果定义了tapped_blocks,那么就一定是来自msgDispater的局部渲染调用
//优先抛弃length == 0的情况
if(!tapped_blocks.length){
return;
}
//如果isFirstRender还为true,则说明数据push在首次渲染之前就到达了。需要等待首次渲染完成。
if (me.__isFirstRender) {
setTimeout(function(){
me.__render(tapped_blocks);
}, 50);
return;
}
for (var i = 0, l = tapped_blocks.length; i < l; i++){
//=====================
controllerBase.__render.call(me,tapped_blocks[i],true);
//=====================
};
return;
}
// 只记录模版的更新方法,模版骨架的创建方法,会在getTpl的时候,首次返回时解析.如果实在必须重新渲染骨架,使用 render.renderBone
var renderObject = fw.render.buildRender(session);
me.__templateBlocks = renderObject.renderBlock;
me.__templateRelation = renderObject.blocksRelation;
tplContentDom = fw.render.getTplContent(session);
//根据模板关系,从父模板到子模板,对每个block进行渲染
doRenderByRelation(me.__templateRelation, function(i){
var targets = queryElementsByTplId(i);
for(var x = 0, y = targets.length; x < y; x++){
var target = targets[x];
if (!target.innerHTML){//如果里面有内容,其实我并不需要清空它
//target.innerHTML = me.__templateBlocks[i]({});
updateBlockHtml(target, me.__templateBlocks[i]({}));
}
}
});
// if (me._isFirstPage && (fw.config.get("runServerRender")!==false) ) {//开启server渲染,且是首页,则不进行转场(转场会销毁dom)
// me.__transition(tplName,["none","none"],function() {
// session.toBeCommited.length = 0;
// env.onready(tplContentDom);
// });
// me._isFirstPage = false;
// }else{
me.__transition(tplName,transitionType,function() {
session.toBeCommited.length = 0;
env.onready(tplContentDom);
});
// }
me.rewriteUri(tplContentDom);
if ( env.withEnyo && me.__isFirstRender ) {//初始化enyo render,确保只执行一次!
controllerBase.__renderEnyo();
}
me.__isFirstRender = false;
}; //end dorender
fw.render.getTpl(tplName,session,function(render){
doRender();
});
});
},
/**
* 调用一个子controller
* @param conNam {string} controller的名称
*/
__callSubController:function(conNam,params,forceFresh){
var constructor = findController(conNam), rootElement = this.__lastTransitionIn;
if (constructor === false) {
//可能由第三方程序接管,framework停止响应
return;
};
var env = this.getEnvironment();
var subEnv , subId = "__subFrom/" + this.__getID() + "/" + conNam ,instance;
var parent = {
session:this.getSession(),
env:fw.utils.getProxy(env,['redirect','refresh']),
tplContent:rootElement
};
if(!constructor){
throw 'can not find a sub controller';
}
//这个判断添加一个feature,用于更新subcontroller add by sundong
instance = this.__subControllerList[subId];
if( instance && typeof forceFresh !== 'undefined' && forceFresh){
instance.destroy();
}
if( instance = this.__subControllerList[subId] ){
/**
* 重用子controller
*/
}else{
instance = new SubControler(subId , params , parent , constructor);
this.__subControllerList[subId] = instance;
subEnv = instance.getEnvironment();
// 代理env的一些事件方法,用于通知创建者子controller的生命周期
subEnv.onready = (function(before,after){
return function(){
before.apply(this,arguments);
after && after.apply({},arguments);
};
})(subEnv.onready,params.oncreated);
subEnv.ondestroy = (function(before,after,superController,subId){
return function(){
try {
before.apply(this,arguments);
after && after();
} catch (e) {
// TODO: handle exception
console.error(e);
}finally{
// 子controller被destroy时,自动清理父controller记录的__subControllerList,用于防止再次创建时引发错误.
superController.__subControllerList[subId] = null;
delete superController.__subControllerList[subId];
}
};
})(subEnv.ondestroy,params.ondestroy,this,subId);
subEnv.onerror = (function(before,after){
return function(){
before.apply(this,arguments);
after && after.apply({},arguments);
};
})(subEnv.onerror);
}
instance.__init();
return fw.utils.getProxy(instance.getEnvironment(),['show','hide','setPosition','isReady','destroy']);
},
__transition:function(tplName,transitionType,oncomplete){
var lastTransitionIn , rv,tmp;
var me = this;
/*
* 偷偷记录,当前url,session与使用dom的历史关系,用于返回,
* 由于controller的id,由controllerPath + params组成,
* 所以此处不再从url中取值.直接取得session中序列化部份的的值对session的key进行排序.
* 然后对形成新对像进行JSON序列化记录.拼到id后面,生成索引键.
*/
this.urlToTplHistory = this.urlToTplHistory || {};
var index = this.__getID();
var session = this.getSession();
var hashKey = session.__hashKey.sort();
var jsonObj = {};
hashKey.forEach(function(key){
jsonObj[key] = session.get(key);
},hashKey);
index += JSON.stringify(jsonObj);
// 如果提供tplName进行history记录处理,否则进行检索处理
if(tplName || tplName === ""){
// 此处不考虑一个完全不变的url对应多个tplName的问题,只记录最后一次的使用.所以,此处使用map结构,便于检索使用
this.urlToTplHistory[index] = tplName;
lastTransitionIn = fw.render.getTplContent(session);
}else{
// 查找历史记录
tplName = this.urlToTplHistory[index];
// 找到对应的tplName,并且的确被当前实例引用则使用,未找到,则使用最后一次的
if(tplName || tplName == ""){
lastTransitionIn = fw.render.getTplContent(session);
}else{
lastTransitionIn = this.__lastTransitionIn;
}
if(!lastTransitionIn){
// 如果此处再无法决定转场进入那一个controller,则认为转场失败.
return false;
}
}
var anim = transitionType || this.__lastTransitionType;// 如果没指明,则使用上一次的
if (me._isFirstPage && (fw.config.get("runServerRender")!==false) ) {//开启server渲染,且是首页,则不进行转场(转场会销毁dom)
me._isFirstPage = false;
anim=["none","none"]
}
//before转场,let's 对历史记录进行一下操作,
//由于现在的逻辑是,无论是否为activecontroller,都必须走这里
//这里很悲剧,我得不到 render 的 transiton 方法。
if ( tmp = fw.historyCache.hitUrl([index ,(transitionType || this.__lastTransitionType)], globalIsforce) ) {
// transitionOut = tmp[1];
}
rv = fw.transition._run({
"dom" : lastTransitionIn,
"cloneDom" : cloneDom,
"transitionOut" : transitionOut,
"anim" : anim,
"callback" : {
"load" : oncomplete || function(){} // 如果不指明,则什么都不做
}
});
transitionOut = false;//赋值完成,自己回归
cloneDom = null;//赋值完成,自己回归
// 如果转场未移动任务内容,则手动触发 oncomplete
rv || (oncomplete || function(){})();
// 记录最后当前controller移入的dom和转场效果类型
//位置提前,因为js是引用传递,下面的run会修改这个类型
//可能会长期不传入transitiontype,原来不加——lasttype会出现bug
this.__lastTransitionType = (transitionType || this.__lastTransitionType);
this.__lastTransitionIn = lastTransitionIn;
return true; //
},
getDom : function() {
return fw.render.getTplContent(this.getSession());
},
setFirstPage : function(isFirstPage){
this._isFirstPage = isFirstPage;
}
});
//server begin
var _bindingServerEvent = function(key,eventFunc){
var widgetId = this.__UK + "_" + key;
if(!_bindingMap[widgetId]){
_bindingMap[widgetId] = {
view_data : {},
eventMap : {}
};
}
_bindingMap[widgetId].eventMap['__default_key__'] = function(){};
};
var serverController = function(id, clientId , constructor){
var me = this;
var env , session;
env= new _Environment();
env.clientId = clientId;
var uk = "T" + fw.utils.randomStr(10);
session = fw.session.create(uk); // 相同的id会返回相同的session
session.bind = _bindingData;
session.bindByPage = _bindingDataByPage;
session.event = _bindingServerEvent;
//FIXME 为了与给HP的代码一致,临时给出一个简单的eventMap实现。后续要与事件库一起考虑
session.eventMap = fw.event.mapEvent;
env.callSubController = function(name,param,forceFresh){
return me.__callSubController(name,param,forceFresh);
};
// 确保session bind时数据项唯一的随机字符串key
env.__UK = session.__UK = this.__UK = uk;//"T" + fw.utils.randomStr(10);
session.__isSubController = false;
// 添加取得当前对像的get方法,
// 保证值在controller的生命周期中是唯一,并且不可变更.
this.getSession = function(){
return session;
};
this.getEnvironment = function(){
return env;
};
this.__getID = function(){
return id;
};
this.__isFirstRender = true;
this.__isInited = false;
this.__templateBlocks = false;
this.__subControllerList = {};
constructor( env, session , session.getContainer());
env.__getControllerInstance = function(){
return me;
};
// activeController.push(me);
fw.dev("create new : " + id);
};
serverController.prototype = fw.utils.extendFrom(controllerBase,{
__render:function(tapped_blocks){
var me = this;
if (!me.getEnvironment){
console.warn("render... error... @controller on line 1179");
return ;
}
var env = me.getEnvironment();
var session = me.getSession();
//此处env是在构造方法中创建,并在用户controller的构造方法执行时被替换了真正的生命周期方法.
env.onrender(function( tplName, transitionType ){
session.__currentTplName = tplName; // 记录到session
// var tplContentDom;
var doRender = function(render){
//pub sub触发的局部渲染
var uk = me.__UK;
try{
if ( me.__isFirstRender ) {//first render 之前应该先初始化模板
// 只记录模版的更新方法,模版骨架的创建方法,会在getTpl的时候,首次返回时解析.如果实在必须重新渲染骨架,使用 render.renderBone
var renderObject = fw.render.buildRender(session,render);
me.tplContent = renderObject.tplContent;
me.domId = renderObject.domId;
me.__templateBlocks = renderObject.renderBlock;
me.__templateRelation = renderObject.blocksRelation;
me.__isFirstRender = false;
}
if (typeof tapped_blocks != 'undefined'){ //如果定义了tapped_blocks,那么就一定是来自msgDispater的局部渲染调用
//优先抛弃length == 0的情况
if(!tapped_blocks.length){
return;
}
for (var i = 0, l = tapped_blocks.length; i < l; i++){
//=====================
me.__renderData.call(me,tapped_blocks[i]);
//=====================
};
return;
}
//销毁前渲染
var finishhtml = '<div class="_smr_runtime_wrapper" id="_smr_runtime_wrapper"><section id="'+me.domId+'" class="__viewBlock__">'+me.tplContent+'</section></div>';
env.__onFinish && env.__onFinish(finishhtml);
me.server_destroy();
}catch(e){
console.warn("controller do render error on line 1226 "+uk);
env.__onFinish && env.__onFinish();
me && me.server_destroy();
}
}; //end dorender
fw.render.getTpl(tplName,session,function(render){
doRender(render);
});
//onready
});
},
server_destroy:function(){
if (!this || !this.__UK) {
console.warn("server destroy already... on line 1242 controller.js");
return;
}
var id = this.__getID();
var uk = this.__UK;
var session = this.getSession();
var env = this.getEnvironment();