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 // ``, 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; });