1 | define([ |
---|
2 | "dojo/_base/kernel", |
---|
3 | "dojo/_base/array", |
---|
4 | "dojo/_base/connect", |
---|
5 | "dojo/_base/declare", |
---|
6 | "dojo/_base/lang", |
---|
7 | "dojo/_base/window", |
---|
8 | "dojo/_base/Deferred", |
---|
9 | "dojo/dom", |
---|
10 | "dojo/dom-class", |
---|
11 | "dojo/dom-construct", |
---|
12 | "dojo/on", |
---|
13 | "dojo/ready", |
---|
14 | "dijit/registry", |
---|
15 | "./ProgressIndicator", |
---|
16 | "./TransitionEvent", |
---|
17 | "./viewRegistry" |
---|
18 | ], function(dojo, array, connect, declare, lang, win, Deferred, dom, domClass, domConstruct, on, ready, registry, ProgressIndicator, TransitionEvent, viewRegistry){ |
---|
19 | |
---|
20 | // module: |
---|
21 | // dojox/mobile/ViewController |
---|
22 | |
---|
23 | var Controller = declare("dojox.mobile.ViewController", null, { |
---|
24 | // summary: |
---|
25 | // A singleton class that controls view transition. |
---|
26 | // description: |
---|
27 | // This class listens to the "startTransition" events and performs |
---|
28 | // view transitions. If the transition destination is an external |
---|
29 | // view specified with the url parameter, the view content is |
---|
30 | // retrieved and parsed to create a new target view. |
---|
31 | |
---|
32 | // dataHandlerClass: Object |
---|
33 | // The data handler class used to load external views, |
---|
34 | // by default "dojox/mobile/dh/DataHandler" |
---|
35 | // (see the Data Handlers page in the reference documentation). |
---|
36 | dataHandlerClass: "dojox/mobile/dh/DataHandler", |
---|
37 | // dataSourceClass: Object |
---|
38 | // The data source class used to load external views, |
---|
39 | // by default "dojox/mobile/dh/UrlDataSource" |
---|
40 | // (see the Data Handlers page in the reference documentation). |
---|
41 | dataSourceClass: "dojox/mobile/dh/UrlDataSource", |
---|
42 | // fileTypeMapClass: Object |
---|
43 | // The file type map class used to load external views, |
---|
44 | // by default "dojox/mobile/dh/SuffixFileTypeMap" |
---|
45 | // (see the Data Handlers page in the reference documentation). |
---|
46 | fileTypeMapClass: "dojox/mobile/dh/SuffixFileTypeMap", |
---|
47 | |
---|
48 | constructor: function(){ |
---|
49 | // summary: |
---|
50 | // Creates a new instance of the class. |
---|
51 | // tags: |
---|
52 | // private |
---|
53 | this.viewMap = {}; |
---|
54 | ready(lang.hitch(this, function(){ |
---|
55 | on(win.body(), "startTransition", lang.hitch(this, "onStartTransition")); |
---|
56 | })); |
---|
57 | }, |
---|
58 | |
---|
59 | findTransitionViews: function(/*String*/moveTo){ |
---|
60 | // summary: |
---|
61 | // Parses the moveTo argument and determines a starting view and a destination view. |
---|
62 | // returns: Array |
---|
63 | // An array containing the currently showing view, the destination view |
---|
64 | // and the transition parameters, or an empty array if the moveTo argument |
---|
65 | // could not be parsed. |
---|
66 | if(!moveTo){ return []; } |
---|
67 | // removes a leading hash mark (#) and params if exists |
---|
68 | // ex. "#bar&myParam=0003" -> "bar" |
---|
69 | moveTo.match(/^#?([^&?]+)(.*)/); |
---|
70 | var params = RegExp.$2; |
---|
71 | var view = registry.byId(RegExp.$1); |
---|
72 | if(!view){ return []; } |
---|
73 | for(var v = view.getParent(); v; v = v.getParent()){ // search for the topmost invisible parent node |
---|
74 | if(v.isVisible && !v.isVisible()){ |
---|
75 | var sv = view.getShowingView(); |
---|
76 | if(sv && sv.id !== view.id){ |
---|
77 | view.show(); |
---|
78 | } |
---|
79 | view = v; |
---|
80 | } |
---|
81 | } |
---|
82 | return [view.getShowingView(), view, params]; // fromView, toView, params |
---|
83 | }, |
---|
84 | |
---|
85 | openExternalView: function(/*Object*/ transOpts, /*DomNode*/ target){ |
---|
86 | // summary: |
---|
87 | // Loads an external view and performs a transition to it. |
---|
88 | // returns: dojo/_base/Deferred |
---|
89 | // Deferred object that resolves when the external view is |
---|
90 | // ready and a transition starts. Note that it resolves before |
---|
91 | // the transition is complete. |
---|
92 | // description: |
---|
93 | // This method loads external view content through the |
---|
94 | // dojox/mobile data handlers, creates a new View instance with |
---|
95 | // the loaded content, and performs a view transition to the |
---|
96 | // new view. The external view content can be specified with |
---|
97 | // the url property of transOpts. The new view is created under |
---|
98 | // a DOM node specified by target. |
---|
99 | // |
---|
100 | // example: |
---|
101 | // This example loads view1.html, creates a new view under |
---|
102 | // `<body>`, and performs a transition to the new view with the |
---|
103 | // slide animation. |
---|
104 | // |
---|
105 | // | var vc = ViewController.getInstance(); |
---|
106 | // | vc.openExternalView({ |
---|
107 | // | url: "view1.html", |
---|
108 | // | transition: "slide" |
---|
109 | // | }, win.body()); |
---|
110 | // |
---|
111 | // |
---|
112 | // example: |
---|
113 | // If you want to perform a view transition without animation, |
---|
114 | // you can give transition:"none" to transOpts. |
---|
115 | // |
---|
116 | // | var vc = ViewController.getInstance(); |
---|
117 | // | vc.openExternalView({ |
---|
118 | // | url: "view1.html", |
---|
119 | // | transition: "none" |
---|
120 | // | }, win.body()); |
---|
121 | // |
---|
122 | // example: |
---|
123 | // If you want to dynamically create an external view, but do |
---|
124 | // not want to perform a view transition to it, you can give noTransition:true to transOpts. |
---|
125 | // This may be useful when you want to preload external views before the user starts using them. |
---|
126 | // |
---|
127 | // | var vc = ViewController.getInstance(); |
---|
128 | // | vc.openExternalView({ |
---|
129 | // | url: "view1.html", |
---|
130 | // | noTransition: true |
---|
131 | // | }, win.body()); |
---|
132 | // |
---|
133 | // example: |
---|
134 | // To do something when the external view is ready: |
---|
135 | // |
---|
136 | // | var vc = ViewController.getInstance(); |
---|
137 | // | Deferred.when(vc.openExternalView({...}, win.body()), function(){ |
---|
138 | // | doSomething(); |
---|
139 | // | }); |
---|
140 | |
---|
141 | var d = new Deferred(); |
---|
142 | var id = this.viewMap[transOpts.url]; |
---|
143 | if(id){ |
---|
144 | transOpts.moveTo = id; |
---|
145 | if(transOpts.noTransition){ |
---|
146 | registry.byId(id).hide(); |
---|
147 | }else{ |
---|
148 | new TransitionEvent(win.body(), transOpts).dispatch(); |
---|
149 | } |
---|
150 | d.resolve(true); |
---|
151 | return d; |
---|
152 | } |
---|
153 | |
---|
154 | // if a fixed bottom bar exists, a new view should be placed before it. |
---|
155 | var refNode = null; |
---|
156 | for(var i = target.childNodes.length - 1; i >= 0; i--){ |
---|
157 | var c = target.childNodes[i]; |
---|
158 | if(c.nodeType === 1){ |
---|
159 | var fixed = c.getAttribute("fixed") // TODO: Remove the non-HTML5-compliant attribute in 2.0 |
---|
160 | || c.getAttribute("data-mobile-fixed") |
---|
161 | || (registry.byNode(c) && registry.byNode(c).fixed); |
---|
162 | if(fixed === "bottom"){ |
---|
163 | refNode = c; |
---|
164 | break; |
---|
165 | } |
---|
166 | } |
---|
167 | } |
---|
168 | |
---|
169 | var dh = transOpts.dataHandlerClass || this.dataHandlerClass; |
---|
170 | var ds = transOpts.dataSourceClass || this.dataSourceClass; |
---|
171 | var ft = transOpts.fileTypeMapClass || this.fileTypeMapClass; |
---|
172 | require([dh, ds, ft], lang.hitch(this, function(DataHandler, DataSource, FileTypeMap){ |
---|
173 | var handler = new DataHandler(new DataSource(transOpts.data || transOpts.url), target, refNode); |
---|
174 | var contentType = transOpts.contentType || FileTypeMap.getContentType(transOpts.url) || "html"; |
---|
175 | handler.processData(contentType, lang.hitch(this, function(id){ |
---|
176 | if(id){ |
---|
177 | this.viewMap[transOpts.url] = transOpts.moveTo = id; |
---|
178 | if(transOpts.noTransition){ |
---|
179 | registry.byId(id).hide(); |
---|
180 | }else{ |
---|
181 | new TransitionEvent(win.body(), transOpts).dispatch(); |
---|
182 | } |
---|
183 | d.resolve(true); |
---|
184 | }else{ |
---|
185 | d.reject("Failed to load "+transOpts.url); |
---|
186 | } |
---|
187 | })); |
---|
188 | })); |
---|
189 | return d; |
---|
190 | }, |
---|
191 | |
---|
192 | onStartTransition: function(evt){ |
---|
193 | // summary: |
---|
194 | // A handler that performs view transition. |
---|
195 | evt.preventDefault(); |
---|
196 | if(!evt.detail){ return; } |
---|
197 | var detail = evt.detail; |
---|
198 | if(!detail.moveTo && !detail.href && !detail.url && !detail.scene){ return; } |
---|
199 | |
---|
200 | if(detail.url && !detail.moveTo){ |
---|
201 | var urlTarget = detail.urlTarget; |
---|
202 | var w = registry.byId(urlTarget); |
---|
203 | var target = w && w.containerNode || dom.byId(urlTarget); |
---|
204 | if(!target){ |
---|
205 | w = viewRegistry.getEnclosingView(evt.target); |
---|
206 | target = w && w.domNode.parentNode || win.body(); |
---|
207 | } |
---|
208 | this.openExternalView(detail, target); |
---|
209 | return; |
---|
210 | }else if(detail.href){ |
---|
211 | if(detail.hrefTarget && detail.hrefTarget != "_self"){ |
---|
212 | win.global.open(detail.href, detail.hrefTarget); |
---|
213 | }else{ |
---|
214 | var view; // find top level visible view |
---|
215 | for(var v = viewRegistry.getEnclosingView(evt.target); v; v = viewRegistry.getParentView(v)){ |
---|
216 | view = v; |
---|
217 | } |
---|
218 | if(view){ |
---|
219 | view.performTransition(null, detail.transitionDir, detail.transition, evt.target, function(){location.href = detail.href;}); |
---|
220 | } |
---|
221 | } |
---|
222 | return; |
---|
223 | }else if(detail.scene){ |
---|
224 | connect.publish("/dojox/mobile/app/pushScene", [detail.scene]); |
---|
225 | return; |
---|
226 | } |
---|
227 | |
---|
228 | var arr = this.findTransitionViews(detail.moveTo), |
---|
229 | fromView = arr[0], |
---|
230 | toView = arr[1], |
---|
231 | params = arr[2]; |
---|
232 | if(!location.hash && !detail.hashchange){ |
---|
233 | viewRegistry.initialView = fromView; |
---|
234 | } |
---|
235 | if(detail.moveTo && toView){ |
---|
236 | detail.moveTo = (detail.moveTo.charAt(0) === '#' ? '#' + toView.id : toView.id) + params; |
---|
237 | } |
---|
238 | if(!fromView || (detail.moveTo && fromView === registry.byId(detail.moveTo.replace(/^#?([^&?]+).*/, "$1")))){ return; } |
---|
239 | var src = registry.getEnclosingWidget(evt.target); |
---|
240 | if(src && src.callback){ |
---|
241 | detail.context = src; |
---|
242 | detail.method = src.callback; |
---|
243 | } |
---|
244 | fromView.performTransition(detail); |
---|
245 | } |
---|
246 | }); |
---|
247 | Controller._instance = new Controller(); // singleton |
---|
248 | Controller.getInstance = function(){ |
---|
249 | return Controller._instance; |
---|
250 | }; |
---|
251 | return Controller; |
---|
252 | }); |
---|
253 | |
---|