You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
258 lines
8.7 KiB
258 lines
8.7 KiB
define([ |
|
"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") // TODO: Remove the non-HTML5-compliant attribute in 2.0 |
|
|| c.getAttribute("data-mobile-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(); |
|
} |
|
var src = registry.getEnclosingWidget(evt.target); |
|
if(src && src.callback){ |
|
detail.context = src; |
|
detail.method = src.callback; |
|
} |
|
this.openExternalView(detail, target); |
|
return; |
|
}else if(detail.href){ |
|
if(detail.hrefTarget && detail.hrefTarget != "_self"){ |
|
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; } |
|
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; |
|
}); |
|
|
|
|