265 lines
8.6 KiB
JavaScript
265 lines
8.6 KiB
JavaScript
//>>built
|
|
define("dojox/mobile/ViewController", [
|
|
"dojo/_base/kernel",
|
|
"dojo/_base/array",
|
|
"dojo/_base/connect",
|
|
"dojo/_base/declare",
|
|
"dojo/_base/lang",
|
|
"dojo/_base/window",
|
|
"dojo/dom",
|
|
"dojo/dom-class",
|
|
"dojo/dom-construct",
|
|
// "dojo/hash", // optionally prereq'ed
|
|
"dojo/on",
|
|
"dojo/ready",
|
|
"dijit/registry", // registry.byId
|
|
"./ProgressIndicator",
|
|
"./TransitionEvent"
|
|
], function(dojo, array, connect, declare, lang, win, dom, domClass, domConstruct, on, ready, registry, ProgressIndicator, TransitionEvent){
|
|
|
|
// module:
|
|
// dojox/mobile/ViewController
|
|
// summary:
|
|
// A singleton class that controlls view transition.
|
|
|
|
var dm = lang.getObject("dojox.mobile", true);
|
|
|
|
var Controller = declare("dojox.mobile.ViewController", null, {
|
|
// summary:
|
|
// A singleton class that controlls view transition.
|
|
// description:
|
|
// This class listens to the "startTransition" events and performs
|
|
// view transitions. If the transition destination is an external
|
|
// view specified with the url parameter, retrieves the view
|
|
// content and parses it to create a new target view.
|
|
|
|
constructor: function(){
|
|
this.viewMap={};
|
|
this.currentView=null;
|
|
this.defaultView=null;
|
|
ready(lang.hitch(this, function(){
|
|
on(win.body(), "startTransition", lang.hitch(this, "onStartTransition"));
|
|
}));
|
|
},
|
|
|
|
findCurrentView: function(moveTo,src){
|
|
// summary:
|
|
// Searches for the currently showing view.
|
|
if(moveTo){
|
|
var w = registry.byId(moveTo);
|
|
if(w && w.getShowingView){ return w.getShowingView(); }
|
|
}
|
|
if(dm.currentView){
|
|
return dm.currentView; //TODO:1.8 may not return an expected result especially when views are nested
|
|
}
|
|
//TODO:1.8 probably never reaches here
|
|
w = src;
|
|
while(true){
|
|
w = w.getParent();
|
|
if(!w){ return null; }
|
|
if(domClass.contains(w.domNode, "mblView")){ break; }
|
|
}
|
|
return w;
|
|
},
|
|
|
|
onStartTransition: function(evt){
|
|
// summary:
|
|
// A handler that performs view transition.
|
|
|
|
evt.preventDefault();
|
|
if(!evt.detail || (evt.detail && !evt.detail.moveTo && !evt.detail.href && !evt.detail.url && !evt.detail.scene)){ return; }
|
|
var w = this.findCurrentView(evt.detail.moveTo, (evt.target && evt.target.id)?registry.byId(evt.target.id):registry.byId(evt.target)); // the current view widget
|
|
if(!w || (evt.detail && evt.detail.moveTo && w === registry.byId(evt.detail.moveTo))){ return; }
|
|
if(evt.detail.href){
|
|
var t = registry.byId(evt.target.id).hrefTarget;
|
|
if(t){
|
|
dm.openWindow(evt.detail.href, t);
|
|
}else{
|
|
w.performTransition(null, evt.detail.transitionDir, evt.detail.transition, evt.target, function(){location.href = evt.detail.href;});
|
|
}
|
|
return;
|
|
} else if(evt.detail.scene){
|
|
connect.publish("/dojox/mobile/app/pushScene", [evt.detail.scene]);
|
|
return;
|
|
}
|
|
var moveTo = evt.detail.moveTo;
|
|
if(evt.detail.url){
|
|
var id;
|
|
if(dm._viewMap && dm._viewMap[evt.detail.url]){
|
|
// external view has already been loaded
|
|
id = dm._viewMap[evt.detail.url];
|
|
}else{
|
|
// get the specified external view and append it to the <body>
|
|
var text = this._text;
|
|
if(!text){
|
|
if(registry.byId(evt.target.id).sync){
|
|
// We do not add explicit dependency on dojo/_base/xhr to this module
|
|
// to be able to create a build that does not contain dojo/_base/xhr.
|
|
// User applications that do sync loading here need to explicitly
|
|
// require dojo/_base/xhr up front.
|
|
dojo.xhrGet({url:evt.detail.url, sync:true, load:function(result){
|
|
text = lang.trim(result);
|
|
}});
|
|
}else{
|
|
var s = "dojo/_base/xhr"; // assign to a variable so as not to be picked up by the build tool
|
|
require([s], lang.hitch(this, function(xhr){
|
|
var prog = ProgressIndicator.getInstance();
|
|
win.body().appendChild(prog.domNode);
|
|
prog.start();
|
|
var obj = xhr.get({
|
|
url: evt.detail.url,
|
|
handleAs: "text"
|
|
});
|
|
obj.addCallback(lang.hitch(this, function(response, ioArgs){
|
|
prog.stop();
|
|
if(response){
|
|
this._text = response;
|
|
new TransitionEvent(evt.target, {
|
|
transition: evt.detail.transition,
|
|
transitionDir: evt.detail.transitionDir,
|
|
moveTo: moveTo,
|
|
href: evt.detail.href,
|
|
url: evt.detail.url,
|
|
scene: evt.detail.scene},
|
|
evt.detail)
|
|
.dispatch();
|
|
}
|
|
}));
|
|
obj.addErrback(function(error){
|
|
prog.stop();
|
|
console.log("Failed to load "+evt.detail.url+"\n"+(error.description||error));
|
|
});
|
|
}));
|
|
return;
|
|
}
|
|
}
|
|
this._text = null;
|
|
id = this._parse(text, registry.byId(evt.target.id).urlTarget);
|
|
if(!dm._viewMap){
|
|
dm._viewMap = [];
|
|
}
|
|
dm._viewMap[evt.detail.url] = id;
|
|
}
|
|
moveTo = id;
|
|
w = this.findCurrentView(moveTo,registry.byId(evt.target.id)) || w; // the current view widget
|
|
}
|
|
w.performTransition(moveTo, evt.detail.transitionDir, evt.detail.transition, null, null);
|
|
},
|
|
|
|
_parse: function(text, id){
|
|
// summary:
|
|
// Parses the given view content.
|
|
// description:
|
|
// If the content is html fragment, constructs dom tree with it
|
|
// and runs the parser. If the content is json data, passes it
|
|
// to _instantiate().
|
|
var container, view, i, j, len;
|
|
var currentView = this.findCurrentView();
|
|
var target = registry.byId(id) && registry.byId(id).containerNode
|
|
|| dom.byId(id)
|
|
|| currentView && currentView.domNode.parentNode
|
|
|| win.body();
|
|
// if a fixed bottom bar exists, a new view should be placed before it.
|
|
var refNode = null;
|
|
for(j = target.childNodes.length - 1; j >= 0; j--){
|
|
var c = target.childNodes[j];
|
|
if(c.nodeType === 1){
|
|
if(c.getAttribute("fixed") === "bottom"){
|
|
refNode = c;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if(text.charAt(0) === "<"){ // html markup
|
|
container = domConstruct.create("DIV", {innerHTML: text});
|
|
for(i = 0; i < container.childNodes.length; i++){
|
|
var n = container.childNodes[i];
|
|
if(n.nodeType === 1){
|
|
view = n; // expecting <div dojoType="dojox.mobile.View">
|
|
break;
|
|
}
|
|
}
|
|
if(!view){
|
|
console.log("dojox.mobile.ViewController#_parse: invalid view content");
|
|
return;
|
|
}
|
|
view.style.visibility = "hidden";
|
|
target.insertBefore(container, refNode);
|
|
var ws = dojo.parser.parse(container);
|
|
array.forEach(ws, function(w){
|
|
if(w && !w._started && w.startup){
|
|
w.startup();
|
|
}
|
|
});
|
|
|
|
// allows multiple root nodes in the fragment,
|
|
// but transition will be performed to the 1st view.
|
|
for(i = 0, len = container.childNodes.length; i < len; i++){
|
|
target.insertBefore(container.firstChild, refNode); // reparent
|
|
}
|
|
target.removeChild(container);
|
|
|
|
registry.byNode(view)._visible = true;
|
|
}else if(text.charAt(0) === "{"){ // json
|
|
container = domConstruct.create("DIV");
|
|
target.insertBefore(container, refNode);
|
|
this._ws = [];
|
|
view = this._instantiate(eval('('+text+')'), container);
|
|
for(i = 0; i < this._ws.length; i++){
|
|
var w = this._ws[i];
|
|
w.startup && !w._started && (!w.getParent || !w.getParent()) && w.startup();
|
|
}
|
|
this._ws = null;
|
|
}
|
|
view.style.display = "none";
|
|
view.style.visibility = "visible";
|
|
return dojo.hash ? "#" + view.id : view.id;
|
|
},
|
|
|
|
_instantiate: function(/*Object*/obj, /*DomNode*/node, /*Widget*/parent){
|
|
// summary:
|
|
// Given the evaluated json data, does the same thing as what
|
|
// the parser does.
|
|
var widget;
|
|
for(var key in obj){
|
|
if(key.charAt(0) == "@"){ continue; }
|
|
var cls = lang.getObject(key);
|
|
if(!cls){ continue; }
|
|
var params = {};
|
|
var proto = cls.prototype;
|
|
var objs = lang.isArray(obj[key]) ? obj[key] : [obj[key]];
|
|
for(var i = 0; i < objs.length; i++){
|
|
for(var prop in objs[i]){
|
|
if(prop.charAt(0) == "@"){
|
|
var val = objs[i][prop];
|
|
prop = prop.substring(1);
|
|
if(typeof proto[prop] == "string"){
|
|
params[prop] = val;
|
|
}else if(typeof proto[prop] == "number"){
|
|
params[prop] = val - 0;
|
|
}else if(typeof proto[prop] == "boolean"){
|
|
params[prop] = (val != "false");
|
|
}else if(typeof proto[prop] == "object"){
|
|
params[prop] = eval("(" + val + ")");
|
|
}
|
|
}
|
|
}
|
|
widget = new cls(params, node);
|
|
if(node){ // to call View's startup()
|
|
widget._visible = true;
|
|
this._ws.push(widget);
|
|
}
|
|
if(parent && parent.addChild){
|
|
parent.addChild(widget);
|
|
}
|
|
this._instantiate(objs[i], null, widget);
|
|
}
|
|
}
|
|
return widget && widget.domNode;
|
|
}
|
|
});
|
|
new Controller(); // singleton
|
|
return Controller;
|
|
});
|
|
|