253 lines
8.5 KiB
JavaScript
253 lines
8.5 KiB
JavaScript
define("dojox/mobile/ViewController", [
|
|
"dojo/_base/kernel",
|
|
"dojo/_base/array",
|
|
"dojo/_base/connect",
|
|
"dojo/_base/declare",
|
|
"dojo/_base/lang",
|
|
"dojo/_base/window",
|
|
"dojo/_base/Deferred",
|
|
"dojo/dom",
|
|
"dojo/dom-class",
|
|
"dojo/dom-construct",
|
|
"dojo/on",
|
|
"dojo/ready",
|
|
"dijit/registry",
|
|
"./ProgressIndicator",
|
|
"./TransitionEvent",
|
|
"./viewRegistry"
|
|
], function(dojo, array, connect, declare, lang, win, Deferred, dom, domClass, domConstruct, on, ready, registry, ProgressIndicator, TransitionEvent, viewRegistry){
|
|
|
|
// module:
|
|
// dojox/mobile/ViewController
|
|
|
|
var Controller = declare("dojox.mobile.ViewController", null, {
|
|
// summary:
|
|
// A singleton class that controls 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, the view content is
|
|
// retrieved and parsed to create a new target view.
|
|
|
|
// dataHandlerClass: Object
|
|
// The data handler class used to load external views,
|
|
// by default "dojox/mobile/dh/DataHandler"
|
|
// (see the Data Handlers page in the reference documentation).
|
|
dataHandlerClass: "dojox/mobile/dh/DataHandler",
|
|
// dataSourceClass: Object
|
|
// The data source class used to load external views,
|
|
// by default "dojox/mobile/dh/UrlDataSource"
|
|
// (see the Data Handlers page in the reference documentation).
|
|
dataSourceClass: "dojox/mobile/dh/UrlDataSource",
|
|
// fileTypeMapClass: Object
|
|
// The file type map class used to load external views,
|
|
// by default "dojox/mobile/dh/SuffixFileTypeMap"
|
|
// (see the Data Handlers page in the reference documentation).
|
|
fileTypeMapClass: "dojox/mobile/dh/SuffixFileTypeMap",
|
|
|
|
constructor: function(){
|
|
// summary:
|
|
// Creates a new instance of the class.
|
|
// tags:
|
|
// private
|
|
this.viewMap = {};
|
|
ready(lang.hitch(this, function(){
|
|
on(win.body(), "startTransition", lang.hitch(this, "onStartTransition"));
|
|
}));
|
|
},
|
|
|
|
findTransitionViews: function(/*String*/moveTo){
|
|
// summary:
|
|
// Parses the moveTo argument and determines a starting view and a destination view.
|
|
// returns: Array
|
|
// An array containing the currently showing view, the destination view
|
|
// and the transition parameters, or an empty array if the moveTo argument
|
|
// could not be parsed.
|
|
if(!moveTo){ return []; }
|
|
// removes a leading hash mark (#) and params if exists
|
|
// ex. "#bar&myParam=0003" -> "bar"
|
|
moveTo.match(/^#?([^&?]+)(.*)/);
|
|
var params = RegExp.$2;
|
|
var view = registry.byId(RegExp.$1);
|
|
if(!view){ return []; }
|
|
for(var v = view.getParent(); v; v = v.getParent()){ // search for the topmost invisible parent node
|
|
if(v.isVisible && !v.isVisible()){
|
|
var sv = view.getShowingView();
|
|
if(sv && sv.id !== view.id){
|
|
view.show();
|
|
}
|
|
view = v;
|
|
}
|
|
}
|
|
return [view.getShowingView(), view, params]; // fromView, toView, params
|
|
},
|
|
|
|
openExternalView: function(/*Object*/ transOpts, /*DomNode*/ target){
|
|
// summary:
|
|
// Loads an external view and performs a transition to it.
|
|
// returns: dojo/_base/Deferred
|
|
// Deferred object that resolves when the external view is
|
|
// ready and a transition starts. Note that it resolves before
|
|
// the transition is complete.
|
|
// description:
|
|
// This method loads external view content through the
|
|
// dojox/mobile data handlers, creates a new View instance with
|
|
// the loaded content, and performs a view transition to the
|
|
// new view. The external view content can be specified with
|
|
// the url property of transOpts. The new view is created under
|
|
// a DOM node specified by target.
|
|
//
|
|
// example:
|
|
// This example loads view1.html, creates a new view under
|
|
// `<body>`, and performs a transition to the new view with the
|
|
// slide animation.
|
|
//
|
|
// | var vc = ViewController.getInstance();
|
|
// | vc.openExternalView({
|
|
// | url: "view1.html",
|
|
// | transition: "slide"
|
|
// | }, win.body());
|
|
//
|
|
//
|
|
// example:
|
|
// If you want to perform a view transition without animation,
|
|
// you can give transition:"none" to transOpts.
|
|
//
|
|
// | var vc = ViewController.getInstance();
|
|
// | vc.openExternalView({
|
|
// | url: "view1.html",
|
|
// | transition: "none"
|
|
// | }, win.body());
|
|
//
|
|
// example:
|
|
// If you want to dynamically create an external view, but do
|
|
// not want to perform a view transition to it, you can give noTransition:true to transOpts.
|
|
// This may be useful when you want to preload external views before the user starts using them.
|
|
//
|
|
// | var vc = ViewController.getInstance();
|
|
// | vc.openExternalView({
|
|
// | url: "view1.html",
|
|
// | noTransition: true
|
|
// | }, win.body());
|
|
//
|
|
// example:
|
|
// To do something when the external view is ready:
|
|
//
|
|
// | var vc = ViewController.getInstance();
|
|
// | Deferred.when(vc.openExternalView({...}, win.body()), function(){
|
|
// | doSomething();
|
|
// | });
|
|
|
|
var d = new Deferred();
|
|
var id = this.viewMap[transOpts.url];
|
|
if(id){
|
|
transOpts.moveTo = id;
|
|
if(transOpts.noTransition){
|
|
registry.byId(id).hide();
|
|
}else{
|
|
new TransitionEvent(win.body(), transOpts).dispatch();
|
|
}
|
|
d.resolve(true);
|
|
return d;
|
|
}
|
|
|
|
// if a fixed bottom bar exists, a new view should be placed before it.
|
|
var refNode = null;
|
|
for(var i = target.childNodes.length - 1; i >= 0; i--){
|
|
var c = target.childNodes[i];
|
|
if(c.nodeType === 1){
|
|
var fixed = c.getAttribute("fixed")
|
|
|| (registry.byNode(c) && registry.byNode(c).fixed);
|
|
if(fixed === "bottom"){
|
|
refNode = c;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
var dh = transOpts.dataHandlerClass || this.dataHandlerClass;
|
|
var ds = transOpts.dataSourceClass || this.dataSourceClass;
|
|
var ft = transOpts.fileTypeMapClass || this.fileTypeMapClass;
|
|
require([dh, ds, ft], lang.hitch(this, function(DataHandler, DataSource, FileTypeMap){
|
|
var handler = new DataHandler(new DataSource(transOpts.data || transOpts.url), target, refNode);
|
|
var contentType = transOpts.contentType || FileTypeMap.getContentType(transOpts.url) || "html";
|
|
handler.processData(contentType, lang.hitch(this, function(id){
|
|
if(id){
|
|
this.viewMap[transOpts.url] = transOpts.moveTo = id;
|
|
if(transOpts.noTransition){
|
|
registry.byId(id).hide();
|
|
}else{
|
|
new TransitionEvent(win.body(), transOpts).dispatch();
|
|
}
|
|
d.resolve(true);
|
|
}else{
|
|
d.reject("Failed to load "+transOpts.url);
|
|
}
|
|
}));
|
|
}));
|
|
return d;
|
|
},
|
|
|
|
onStartTransition: function(evt){
|
|
// summary:
|
|
// A handler that performs view transition.
|
|
evt.preventDefault();
|
|
if(!evt.detail){ return; }
|
|
var detail = evt.detail;
|
|
if(!detail.moveTo && !detail.href && !detail.url && !detail.scene){ return; }
|
|
|
|
if(detail.url && !detail.moveTo){
|
|
var urlTarget = detail.urlTarget;
|
|
var w = registry.byId(urlTarget);
|
|
var target = w && w.containerNode || dom.byId(urlTarget);
|
|
if(!target){
|
|
w = viewRegistry.getEnclosingView(evt.target);
|
|
target = w && w.domNode.parentNode || win.body();
|
|
}
|
|
this.openExternalView(detail, target);
|
|
return;
|
|
}else if(detail.href){
|
|
if(detail.hrefTarget){
|
|
win.global.open(detail.href, detail.hrefTarget);
|
|
}else{
|
|
var view; // find top level visible view
|
|
for(var v = viewRegistry.getEnclosingView(evt.target); v; v = viewRegistry.getParentView(v)){
|
|
view = v;
|
|
}
|
|
if(view){
|
|
view.performTransition(null, detail.transitionDir, detail.transition, evt.target, function(){location.href = detail.href;});
|
|
}
|
|
}
|
|
return;
|
|
}else if(detail.scene){
|
|
connect.publish("/dojox/mobile/app/pushScene", [detail.scene]);
|
|
return;
|
|
}
|
|
|
|
var arr = this.findTransitionViews(detail.moveTo),
|
|
fromView = arr[0],
|
|
toView = arr[1],
|
|
params = arr[2];
|
|
if(!location.hash && !detail.hashchange){
|
|
viewRegistry.initialView = fromView;
|
|
}
|
|
if(detail.moveTo && toView){
|
|
detail.moveTo = (detail.moveTo.charAt(0) === '#' ? '#' + toView.id : toView.id) + params;
|
|
}
|
|
if(!fromView || (detail.moveTo && fromView === registry.byId(detail.moveTo.replace(/^#?([^&?]+).*/, "$1")))){ return; }
|
|
var src = registry.getEnclosingWidget(evt.target);
|
|
if(src && src.callback){
|
|
detail.context = src;
|
|
detail.method = src.callback;
|
|
}
|
|
fromView.performTransition(detail);
|
|
}
|
|
});
|
|
Controller._instance = new Controller(); // singleton
|
|
Controller.getInstance = function(){
|
|
return Controller._instance;
|
|
};
|
|
return Controller;
|
|
});
|
|
|