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/dom", |
---|
9 | "dojo/dom-class", |
---|
10 | "dojo/dom-construct", |
---|
11 | // "dojo/hash", // optionally prereq'ed |
---|
12 | "dojo/on", |
---|
13 | "dojo/ready", |
---|
14 | "dijit/registry", // registry.byId |
---|
15 | "./ProgressIndicator", |
---|
16 | "./TransitionEvent" |
---|
17 | ], function(dojo, array, connect, declare, lang, win, dom, domClass, domConstruct, on, ready, registry, ProgressIndicator, TransitionEvent){ |
---|
18 | |
---|
19 | // module: |
---|
20 | // dojox/mobile/ViewController |
---|
21 | // summary: |
---|
22 | // A singleton class that controlls view transition. |
---|
23 | |
---|
24 | var dm = lang.getObject("dojox.mobile", true); |
---|
25 | |
---|
26 | var Controller = declare("dojox.mobile.ViewController", null, { |
---|
27 | // summary: |
---|
28 | // A singleton class that controlls view transition. |
---|
29 | // description: |
---|
30 | // This class listens to the "startTransition" events and performs |
---|
31 | // view transitions. If the transition destination is an external |
---|
32 | // view specified with the url parameter, retrieves the view |
---|
33 | // content and parses it to create a new target view. |
---|
34 | |
---|
35 | constructor: function(){ |
---|
36 | this.viewMap={}; |
---|
37 | this.currentView=null; |
---|
38 | this.defaultView=null; |
---|
39 | ready(lang.hitch(this, function(){ |
---|
40 | on(win.body(), "startTransition", lang.hitch(this, "onStartTransition")); |
---|
41 | })); |
---|
42 | }, |
---|
43 | |
---|
44 | findCurrentView: function(moveTo,src){ |
---|
45 | // summary: |
---|
46 | // Searches for the currently showing view. |
---|
47 | if(moveTo){ |
---|
48 | var w = registry.byId(moveTo); |
---|
49 | if(w && w.getShowingView){ return w.getShowingView(); } |
---|
50 | } |
---|
51 | if(dm.currentView){ |
---|
52 | return dm.currentView; //TODO:1.8 may not return an expected result especially when views are nested |
---|
53 | } |
---|
54 | //TODO:1.8 probably never reaches here |
---|
55 | w = src; |
---|
56 | while(true){ |
---|
57 | w = w.getParent(); |
---|
58 | if(!w){ return null; } |
---|
59 | if(domClass.contains(w.domNode, "mblView")){ break; } |
---|
60 | } |
---|
61 | return w; |
---|
62 | }, |
---|
63 | |
---|
64 | onStartTransition: function(evt){ |
---|
65 | // summary: |
---|
66 | // A handler that performs view transition. |
---|
67 | |
---|
68 | evt.preventDefault(); |
---|
69 | if(!evt.detail || (evt.detail && !evt.detail.moveTo && !evt.detail.href && !evt.detail.url && !evt.detail.scene)){ return; } |
---|
70 | 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 |
---|
71 | if(!w || (evt.detail && evt.detail.moveTo && w === registry.byId(evt.detail.moveTo))){ return; } |
---|
72 | if(evt.detail.href){ |
---|
73 | var t = registry.byId(evt.target.id).hrefTarget; |
---|
74 | if(t){ |
---|
75 | dm.openWindow(evt.detail.href, t); |
---|
76 | }else{ |
---|
77 | w.performTransition(null, evt.detail.transitionDir, evt.detail.transition, evt.target, function(){location.href = evt.detail.href;}); |
---|
78 | } |
---|
79 | return; |
---|
80 | } else if(evt.detail.scene){ |
---|
81 | connect.publish("/dojox/mobile/app/pushScene", [evt.detail.scene]); |
---|
82 | return; |
---|
83 | } |
---|
84 | var moveTo = evt.detail.moveTo; |
---|
85 | if(evt.detail.url){ |
---|
86 | var id; |
---|
87 | if(dm._viewMap && dm._viewMap[evt.detail.url]){ |
---|
88 | // external view has already been loaded |
---|
89 | id = dm._viewMap[evt.detail.url]; |
---|
90 | }else{ |
---|
91 | // get the specified external view and append it to the <body> |
---|
92 | var text = this._text; |
---|
93 | if(!text){ |
---|
94 | if(registry.byId(evt.target.id).sync){ |
---|
95 | // We do not add explicit dependency on dojo/_base/xhr to this module |
---|
96 | // to be able to create a build that does not contain dojo/_base/xhr. |
---|
97 | // User applications that do sync loading here need to explicitly |
---|
98 | // require dojo/_base/xhr up front. |
---|
99 | dojo.xhrGet({url:evt.detail.url, sync:true, load:function(result){ |
---|
100 | text = lang.trim(result); |
---|
101 | }}); |
---|
102 | }else{ |
---|
103 | var s = "dojo/_base/xhr"; // assign to a variable so as not to be picked up by the build tool |
---|
104 | require([s], lang.hitch(this, function(xhr){ |
---|
105 | var prog = ProgressIndicator.getInstance(); |
---|
106 | win.body().appendChild(prog.domNode); |
---|
107 | prog.start(); |
---|
108 | var obj = xhr.get({ |
---|
109 | url: evt.detail.url, |
---|
110 | handleAs: "text" |
---|
111 | }); |
---|
112 | obj.addCallback(lang.hitch(this, function(response, ioArgs){ |
---|
113 | prog.stop(); |
---|
114 | if(response){ |
---|
115 | this._text = response; |
---|
116 | new TransitionEvent(evt.target, { |
---|
117 | transition: evt.detail.transition, |
---|
118 | transitionDir: evt.detail.transitionDir, |
---|
119 | moveTo: moveTo, |
---|
120 | href: evt.detail.href, |
---|
121 | url: evt.detail.url, |
---|
122 | scene: evt.detail.scene}, |
---|
123 | evt.detail) |
---|
124 | .dispatch(); |
---|
125 | } |
---|
126 | })); |
---|
127 | obj.addErrback(function(error){ |
---|
128 | prog.stop(); |
---|
129 | console.log("Failed to load "+evt.detail.url+"\n"+(error.description||error)); |
---|
130 | }); |
---|
131 | })); |
---|
132 | return; |
---|
133 | } |
---|
134 | } |
---|
135 | this._text = null; |
---|
136 | id = this._parse(text, registry.byId(evt.target.id).urlTarget); |
---|
137 | if(!dm._viewMap){ |
---|
138 | dm._viewMap = []; |
---|
139 | } |
---|
140 | dm._viewMap[evt.detail.url] = id; |
---|
141 | } |
---|
142 | moveTo = id; |
---|
143 | w = this.findCurrentView(moveTo,registry.byId(evt.target.id)) || w; // the current view widget |
---|
144 | } |
---|
145 | w.performTransition(moveTo, evt.detail.transitionDir, evt.detail.transition, null, null); |
---|
146 | }, |
---|
147 | |
---|
148 | _parse: function(text, id){ |
---|
149 | // summary: |
---|
150 | // Parses the given view content. |
---|
151 | // description: |
---|
152 | // If the content is html fragment, constructs dom tree with it |
---|
153 | // and runs the parser. If the content is json data, passes it |
---|
154 | // to _instantiate(). |
---|
155 | var container, view, i, j, len; |
---|
156 | var currentView = this.findCurrentView(); |
---|
157 | var target = registry.byId(id) && registry.byId(id).containerNode |
---|
158 | || dom.byId(id) |
---|
159 | || currentView && currentView.domNode.parentNode |
---|
160 | || win.body(); |
---|
161 | // if a fixed bottom bar exists, a new view should be placed before it. |
---|
162 | var refNode = null; |
---|
163 | for(j = target.childNodes.length - 1; j >= 0; j--){ |
---|
164 | var c = target.childNodes[j]; |
---|
165 | if(c.nodeType === 1){ |
---|
166 | if(c.getAttribute("fixed") === "bottom"){ |
---|
167 | refNode = c; |
---|
168 | } |
---|
169 | break; |
---|
170 | } |
---|
171 | } |
---|
172 | if(text.charAt(0) === "<"){ // html markup |
---|
173 | container = domConstruct.create("DIV", {innerHTML: text}); |
---|
174 | for(i = 0; i < container.childNodes.length; i++){ |
---|
175 | var n = container.childNodes[i]; |
---|
176 | if(n.nodeType === 1){ |
---|
177 | view = n; // expecting <div dojoType="dojox.mobile.View"> |
---|
178 | break; |
---|
179 | } |
---|
180 | } |
---|
181 | if(!view){ |
---|
182 | console.log("dojox.mobile.ViewController#_parse: invalid view content"); |
---|
183 | return; |
---|
184 | } |
---|
185 | view.style.visibility = "hidden"; |
---|
186 | target.insertBefore(container, refNode); |
---|
187 | var ws = dojo.parser.parse(container); |
---|
188 | array.forEach(ws, function(w){ |
---|
189 | if(w && !w._started && w.startup){ |
---|
190 | w.startup(); |
---|
191 | } |
---|
192 | }); |
---|
193 | |
---|
194 | // allows multiple root nodes in the fragment, |
---|
195 | // but transition will be performed to the 1st view. |
---|
196 | for(i = 0, len = container.childNodes.length; i < len; i++){ |
---|
197 | target.insertBefore(container.firstChild, refNode); // reparent |
---|
198 | } |
---|
199 | target.removeChild(container); |
---|
200 | |
---|
201 | registry.byNode(view)._visible = true; |
---|
202 | }else if(text.charAt(0) === "{"){ // json |
---|
203 | container = domConstruct.create("DIV"); |
---|
204 | target.insertBefore(container, refNode); |
---|
205 | this._ws = []; |
---|
206 | view = this._instantiate(eval('('+text+')'), container); |
---|
207 | for(i = 0; i < this._ws.length; i++){ |
---|
208 | var w = this._ws[i]; |
---|
209 | w.startup && !w._started && (!w.getParent || !w.getParent()) && w.startup(); |
---|
210 | } |
---|
211 | this._ws = null; |
---|
212 | } |
---|
213 | view.style.display = "none"; |
---|
214 | view.style.visibility = "visible"; |
---|
215 | return dojo.hash ? "#" + view.id : view.id; |
---|
216 | }, |
---|
217 | |
---|
218 | _instantiate: function(/*Object*/obj, /*DomNode*/node, /*Widget*/parent){ |
---|
219 | // summary: |
---|
220 | // Given the evaluated json data, does the same thing as what |
---|
221 | // the parser does. |
---|
222 | var widget; |
---|
223 | for(var key in obj){ |
---|
224 | if(key.charAt(0) == "@"){ continue; } |
---|
225 | var cls = lang.getObject(key); |
---|
226 | if(!cls){ continue; } |
---|
227 | var params = {}; |
---|
228 | var proto = cls.prototype; |
---|
229 | var objs = lang.isArray(obj[key]) ? obj[key] : [obj[key]]; |
---|
230 | for(var i = 0; i < objs.length; i++){ |
---|
231 | for(var prop in objs[i]){ |
---|
232 | if(prop.charAt(0) == "@"){ |
---|
233 | var val = objs[i][prop]; |
---|
234 | prop = prop.substring(1); |
---|
235 | if(typeof proto[prop] == "string"){ |
---|
236 | params[prop] = val; |
---|
237 | }else if(typeof proto[prop] == "number"){ |
---|
238 | params[prop] = val - 0; |
---|
239 | }else if(typeof proto[prop] == "boolean"){ |
---|
240 | params[prop] = (val != "false"); |
---|
241 | }else if(typeof proto[prop] == "object"){ |
---|
242 | params[prop] = eval("(" + val + ")"); |
---|
243 | } |
---|
244 | } |
---|
245 | } |
---|
246 | widget = new cls(params, node); |
---|
247 | if(node){ // to call View's startup() |
---|
248 | widget._visible = true; |
---|
249 | this._ws.push(widget); |
---|
250 | } |
---|
251 | if(parent && parent.addChild){ |
---|
252 | parent.addChild(widget); |
---|
253 | } |
---|
254 | this._instantiate(objs[i], null, widget); |
---|
255 | } |
---|
256 | } |
---|
257 | return widget && widget.domNode; |
---|
258 | } |
---|
259 | }); |
---|
260 | new Controller(); // singleton |
---|
261 | return Controller; |
---|
262 | }); |
---|
263 | |
---|