1 | define([ |
---|
2 | "dojo/_base/array", |
---|
3 | "dojo/_base/config", |
---|
4 | "dojo/_base/connect", |
---|
5 | "dojo/_base/declare", |
---|
6 | "dojo/_base/lang", |
---|
7 | "dojo/sniff", |
---|
8 | "dojo/_base/window", |
---|
9 | "dojo/_base/Deferred", |
---|
10 | "dojo/dom", |
---|
11 | "dojo/dom-class", |
---|
12 | "dojo/dom-construct", |
---|
13 | "dojo/dom-geometry", |
---|
14 | "dojo/dom-style", |
---|
15 | "dijit/registry", |
---|
16 | "dijit/_Contained", |
---|
17 | "dijit/_Container", |
---|
18 | "dijit/_WidgetBase", |
---|
19 | "./ViewController", // to load ViewController for you (no direct references) |
---|
20 | "./common", |
---|
21 | "./transition", |
---|
22 | "./viewRegistry", |
---|
23 | "./_css3" |
---|
24 | ], function(array, config, connect, declare, lang, has, win, Deferred, dom, domClass, domConstruct, domGeometry, domStyle, registry, Contained, Container, WidgetBase, ViewController, common, transitDeferred, viewRegistry, css3){ |
---|
25 | |
---|
26 | // module: |
---|
27 | // dojox/mobile/View |
---|
28 | |
---|
29 | var dm = lang.getObject("dojox.mobile", true); |
---|
30 | |
---|
31 | return declare("dojox.mobile.View", [WidgetBase, Container, Contained], { |
---|
32 | // summary: |
---|
33 | // A widget that represents a view that occupies the full screen |
---|
34 | // description: |
---|
35 | // View is a container widget for any HTML element and/or Dojo widgets. |
---|
36 | // As a Dojo widget container it can itself contain View widgets |
---|
37 | // forming a set of nested views. A Dojo Mobile application is usually |
---|
38 | // made of multiple View widgets and the user can navigate through |
---|
39 | // the views back and forth with animated transition effects. |
---|
40 | // |
---|
41 | // When using several sibling views (direct children of the same |
---|
42 | // element), you can use the 'selected' attribute to define whether |
---|
43 | // the view should be displayed when the application is launched. |
---|
44 | // If no view has selected=true, the first sibling view is displayed |
---|
45 | // at startup time. |
---|
46 | |
---|
47 | // selected: Boolean |
---|
48 | // If true, the view is displayed at startup time. |
---|
49 | selected: false, |
---|
50 | |
---|
51 | // keepScrollPos: Boolean |
---|
52 | // If true, the scroll position is kept when transition occurs between views. |
---|
53 | keepScrollPos: true, |
---|
54 | |
---|
55 | // tag: String |
---|
56 | // The name of the HTML tag to create as domNode. The default value is "div". |
---|
57 | tag: "div", |
---|
58 | |
---|
59 | /* internal properties */ |
---|
60 | baseClass: "mblView", |
---|
61 | |
---|
62 | constructor: function(/*Object*/params, /*DomNode?*/node){ |
---|
63 | // summary: |
---|
64 | // Creates a new instance of the class. |
---|
65 | // params: |
---|
66 | // Contains the parameters. |
---|
67 | // node: |
---|
68 | // The DOM node. If none is specified, it is automatically created. |
---|
69 | if(node){ |
---|
70 | dom.byId(node).style.visibility = "hidden"; |
---|
71 | } |
---|
72 | }, |
---|
73 | |
---|
74 | destroy: function(){ |
---|
75 | viewRegistry.remove(this.id); |
---|
76 | this.inherited(arguments); |
---|
77 | }, |
---|
78 | |
---|
79 | buildRendering: function(){ |
---|
80 | if(!this.templateString){ |
---|
81 | // Create root node if it wasn't created by _TemplatedMixin |
---|
82 | this.domNode = this.containerNode = this.srcNodeRef || domConstruct.create(this.tag); |
---|
83 | } |
---|
84 | |
---|
85 | this._animEndHandle = this.connect(this.domNode, css3.name("animationEnd"), "onAnimationEnd"); |
---|
86 | this._animStartHandle = this.connect(this.domNode, css3.name("animationStart"), "onAnimationStart"); |
---|
87 | if(!config['mblCSS3Transition']){ |
---|
88 | this._transEndHandle = this.connect(this.domNode, css3.name("transitionEnd"), "onAnimationEnd"); |
---|
89 | } |
---|
90 | if(has('mblAndroid3Workaround')){ |
---|
91 | // workaround for the screen flicker issue on Android 3.x/4.0 |
---|
92 | // applying "-webkit-transform-style:preserve-3d" to domNode can avoid |
---|
93 | // transition animation flicker |
---|
94 | domStyle.set(this.domNode, css3.name("transformStyle"), "preserve-3d"); |
---|
95 | } |
---|
96 | |
---|
97 | viewRegistry.add(this); |
---|
98 | this.inherited(arguments); |
---|
99 | }, |
---|
100 | |
---|
101 | startup: function(){ |
---|
102 | if(this._started){ return; } |
---|
103 | |
---|
104 | // Determine which view among the siblings should be visible. |
---|
105 | // Priority: |
---|
106 | // 1. fragment id in the url (ex. #view1,view2) |
---|
107 | // 2. this.selected |
---|
108 | // 3. the first view |
---|
109 | if(this._visible === undefined){ |
---|
110 | var views = this.getSiblingViews(); |
---|
111 | var ids = location.hash && location.hash.substring(1).split(/,/); |
---|
112 | var fragView, selectedView, firstView; |
---|
113 | array.forEach(views, function(v, i){ |
---|
114 | if(array.indexOf(ids, v.id) !== -1){ fragView = v; } |
---|
115 | if(i == 0){ firstView = v; } |
---|
116 | if(v.selected){ selectedView = v; } |
---|
117 | v._visible = false; |
---|
118 | }, this); |
---|
119 | (fragView || selectedView || firstView)._visible = true; |
---|
120 | } |
---|
121 | if(this._visible){ |
---|
122 | // The 2nd arg is not to hide its sibling views so that they can be |
---|
123 | // correctly initialized. |
---|
124 | this.show(true, true); |
---|
125 | |
---|
126 | // Defer firing events to let user connect to events just after creation |
---|
127 | // TODO: revisit this for 2.0 |
---|
128 | this.defer(function(){ |
---|
129 | this.onStartView(); |
---|
130 | connect.publish("/dojox/mobile/startView", [this]); |
---|
131 | }); |
---|
132 | } |
---|
133 | |
---|
134 | if(this.domNode.style.visibility === "hidden"){ // this check is to avoid screen flickers |
---|
135 | this.domNode.style.visibility = "inherit"; |
---|
136 | } |
---|
137 | |
---|
138 | // Need to call inherited first - so that child widgets get started |
---|
139 | // up correctly |
---|
140 | this.inherited(arguments); |
---|
141 | |
---|
142 | var parent = this.getParent(); |
---|
143 | if(!parent || !parent.resize){ // top level widget |
---|
144 | this.resize(); |
---|
145 | } |
---|
146 | |
---|
147 | if(!this._visible){ |
---|
148 | // hide() should be called last so that child widgets can be |
---|
149 | // initialized while they are visible. |
---|
150 | this.hide(); |
---|
151 | } |
---|
152 | }, |
---|
153 | |
---|
154 | resize: function(){ |
---|
155 | // summary: |
---|
156 | // Calls resize() of each child widget. |
---|
157 | array.forEach(this.getChildren(), function(child){ |
---|
158 | if(child.resize){ child.resize(); } |
---|
159 | }); |
---|
160 | }, |
---|
161 | |
---|
162 | onStartView: function(){ |
---|
163 | // summary: |
---|
164 | // Stub function to connect to from your application. |
---|
165 | // description: |
---|
166 | // Called only when this view is shown at startup time. |
---|
167 | }, |
---|
168 | |
---|
169 | onBeforeTransitionIn: function(moveTo, dir, transition, context, method){ |
---|
170 | // summary: |
---|
171 | // Stub function to connect to from your application. |
---|
172 | // description: |
---|
173 | // Called before the arriving transition occurs. |
---|
174 | }, |
---|
175 | |
---|
176 | onAfterTransitionIn: function(moveTo, dir, transition, context, method){ |
---|
177 | // summary: |
---|
178 | // Stub function to connect to from your application. |
---|
179 | // description: |
---|
180 | // Called after the arriving transition occurs. |
---|
181 | }, |
---|
182 | |
---|
183 | onBeforeTransitionOut: function(moveTo, dir, transition, context, method){ |
---|
184 | // summary: |
---|
185 | // Stub function to connect to from your application. |
---|
186 | // description: |
---|
187 | // Called before the leaving transition occurs. |
---|
188 | }, |
---|
189 | |
---|
190 | onAfterTransitionOut: function(moveTo, dir, transition, context, method){ |
---|
191 | // summary: |
---|
192 | // Stub function to connect to from your application. |
---|
193 | // description: |
---|
194 | // Called after the leaving transition occurs. |
---|
195 | }, |
---|
196 | |
---|
197 | _clearClasses: function(/*DomNode*/node){ |
---|
198 | // summary: |
---|
199 | // Clean up the domNode classes that were added while making a transition. |
---|
200 | // description: |
---|
201 | // Remove all the "mbl" prefixed classes except mbl*View. |
---|
202 | if(!node){ return; } |
---|
203 | var classes = []; |
---|
204 | array.forEach(lang.trim(node.className||"").split(/\s+/), function(c){ |
---|
205 | if(c.match(/^mbl\w*View$/) || c.indexOf("mbl") === -1){ |
---|
206 | classes.push(c); |
---|
207 | } |
---|
208 | }, this); |
---|
209 | node.className = classes.join(' '); |
---|
210 | }, |
---|
211 | |
---|
212 | _fixViewState: function(/*DomNode*/toNode){ |
---|
213 | // summary: |
---|
214 | // Sanity check for view transition states. |
---|
215 | // description: |
---|
216 | // Sometimes uninitialization of Views fails after making view transition, |
---|
217 | // and that results in failure of subsequent view transitions. |
---|
218 | // This function does the uninitialization for all the sibling views. |
---|
219 | var nodes = this.domNode.parentNode.childNodes; |
---|
220 | for(var i = 0; i < nodes.length; i++){ |
---|
221 | var n = nodes[i]; |
---|
222 | if(n.nodeType === 1 && domClass.contains(n, "mblView")){ |
---|
223 | this._clearClasses(n); |
---|
224 | } |
---|
225 | } |
---|
226 | this._clearClasses(toNode); // just in case toNode is a sibling of an ancestor. |
---|
227 | |
---|
228 | // #16337 |
---|
229 | // Uninitialization may fail to clear _inProgress when multiple |
---|
230 | // performTransition calls occur in a short duration of time. |
---|
231 | var toWidget = registry.byNode(toNode); |
---|
232 | if(toWidget){ |
---|
233 | toWidget._inProgress = false; |
---|
234 | } |
---|
235 | }, |
---|
236 | |
---|
237 | convertToId: function(moveTo){ |
---|
238 | if(typeof(moveTo) == "string"){ |
---|
239 | // removes a leading hash mark (#) and params if exists |
---|
240 | // ex. "#bar&myParam=0003" -> "bar" |
---|
241 | return moveTo.replace(/^#?([^&?]+).*/, "$1"); |
---|
242 | } |
---|
243 | return moveTo; |
---|
244 | }, |
---|
245 | |
---|
246 | _isBookmarkable: function(detail){ |
---|
247 | return detail.moveTo && (config['mblForceBookmarkable'] || detail.moveTo.charAt(0) === '#') && !detail.hashchange; |
---|
248 | }, |
---|
249 | |
---|
250 | performTransition: function(/*String*/moveTo, /*Number*/transitionDir, /*String*/transition, |
---|
251 | /*Object|null*/context, /*String|Function*/method /*...*/){ |
---|
252 | // summary: |
---|
253 | // Function to perform the various types of view transitions, such as fade, slide, and flip. |
---|
254 | // moveTo: String |
---|
255 | // The id of the transition destination view which resides in |
---|
256 | // the current page. |
---|
257 | // If the value has a hash sign ('#') before the id |
---|
258 | // (e.g. #view1) and the dojo/hash module is loaded by the user |
---|
259 | // application, the view transition updates the hash in the |
---|
260 | // browser URL so that the user can bookmark the destination |
---|
261 | // view. In this case, the user can also use the browser's |
---|
262 | // back/forward button to navigate through the views in the |
---|
263 | // browser history. |
---|
264 | // If null, transitions to a blank view. |
---|
265 | // If '#', returns immediately without transition. |
---|
266 | // transitionDir: Number |
---|
267 | // The transition direction. If 1, transition forward. If -1, transition backward. |
---|
268 | // For example, the slide transition slides the view from right to left when transitionDir == 1, |
---|
269 | // and from left to right when transitionDir == -1. |
---|
270 | // transition: String |
---|
271 | // A type of animated transition effect. You can choose from |
---|
272 | // the standard transition types, "slide", "fade", "flip", or |
---|
273 | // from the extended transition types, "cover", "coverv", |
---|
274 | // "dissolve", "reveal", "revealv", "scaleIn", "scaleOut", |
---|
275 | // "slidev", "swirl", "zoomIn", "zoomOut", "cube", and |
---|
276 | // "swap". If "none" is specified, transition occurs |
---|
277 | // immediately without animation. |
---|
278 | // context: Object |
---|
279 | // The object that the callback function will receive as "this". |
---|
280 | // method: String|Function |
---|
281 | // A callback function that is called when the transition has finished. |
---|
282 | // A function reference, or name of a function in context. |
---|
283 | // tags: |
---|
284 | // public |
---|
285 | // |
---|
286 | // example: |
---|
287 | // Transition backward to a view whose id is "foo" with the slide animation. |
---|
288 | // | performTransition("foo", -1, "slide"); |
---|
289 | // |
---|
290 | // example: |
---|
291 | // Transition forward to a blank view, and then open another page. |
---|
292 | // | performTransition(null, 1, "slide", null, function(){location.href = href;}); |
---|
293 | |
---|
294 | if(this._inProgress){ return; } // transition is in progress |
---|
295 | this._inProgress = true; |
---|
296 | |
---|
297 | // normalize the arg |
---|
298 | var detail, optArgs; |
---|
299 | if(moveTo && typeof(moveTo) === "object"){ |
---|
300 | detail = moveTo; |
---|
301 | optArgs = transitionDir; // array |
---|
302 | }else{ |
---|
303 | detail = { |
---|
304 | moveTo: moveTo, |
---|
305 | transitionDir: transitionDir, |
---|
306 | transition: transition, |
---|
307 | context: context, |
---|
308 | method: method |
---|
309 | }; |
---|
310 | optArgs = []; |
---|
311 | for(var i = 5; i < arguments.length; i++){ |
---|
312 | optArgs.push(arguments[i]); |
---|
313 | } |
---|
314 | } |
---|
315 | |
---|
316 | // save the parameters |
---|
317 | this._detail = detail; |
---|
318 | this._optArgs = optArgs; |
---|
319 | this._arguments = [ |
---|
320 | detail.moveTo, |
---|
321 | detail.transitionDir, |
---|
322 | detail.transition, |
---|
323 | detail.context, |
---|
324 | detail.method |
---|
325 | ]; |
---|
326 | |
---|
327 | if(detail.moveTo === "#"){ return; } |
---|
328 | var toNode; |
---|
329 | if(detail.moveTo){ |
---|
330 | toNode = this.convertToId(detail.moveTo); |
---|
331 | }else{ |
---|
332 | if(!this._dummyNode){ |
---|
333 | this._dummyNode = win.doc.createElement("div"); |
---|
334 | win.body().appendChild(this._dummyNode); |
---|
335 | } |
---|
336 | toNode = this._dummyNode; |
---|
337 | } |
---|
338 | |
---|
339 | if(this.addTransitionInfo && typeof(detail.moveTo) == "string" && this._isBookmarkable(detail)){ |
---|
340 | this.addTransitionInfo(this.id, detail.moveTo, {transitionDir:detail.transitionDir, transition:detail.transition}); |
---|
341 | } |
---|
342 | |
---|
343 | var fromNode = this.domNode; |
---|
344 | var fromTop = fromNode.offsetTop; |
---|
345 | toNode = this.toNode = dom.byId(toNode); |
---|
346 | if(!toNode){ console.log("dojox/mobile/View.performTransition: destination view not found: "+detail.moveTo); return; } |
---|
347 | toNode.style.visibility = "hidden"; |
---|
348 | toNode.style.display = ""; |
---|
349 | this._fixViewState(toNode); |
---|
350 | var toWidget = registry.byNode(toNode); |
---|
351 | if(toWidget){ |
---|
352 | // Now that the target view became visible, it's time to run resize() |
---|
353 | if(config["mblAlwaysResizeOnTransition"] || !toWidget._resized){ |
---|
354 | common.resizeAll(null, toWidget); |
---|
355 | toWidget._resized = true; |
---|
356 | } |
---|
357 | |
---|
358 | if(detail.transition && detail.transition != "none"){ |
---|
359 | // Temporarily add padding to align with the fromNode while transition |
---|
360 | toWidget._addTransitionPaddingTop(fromTop); |
---|
361 | } |
---|
362 | |
---|
363 | toWidget.load && toWidget.load(); // for ContentView |
---|
364 | |
---|
365 | toWidget.movedFrom = fromNode.id; |
---|
366 | } |
---|
367 | if(has('mblAndroidWorkaround') && !config['mblCSS3Transition'] |
---|
368 | && detail.transition && detail.transition != "none"){ |
---|
369 | // workaround for the screen flicker issue on Android 2.2/2.3 |
---|
370 | // apply "-webkit-transform-style:preserve-3d" to both toNode and fromNode |
---|
371 | // to make them 3d-transition-ready state just before transition animation |
---|
372 | domStyle.set(toNode, css3.name("transformStyle"), "preserve-3d"); |
---|
373 | domStyle.set(fromNode, css3.name("transformStyle"), "preserve-3d"); |
---|
374 | // show toNode offscreen to avoid flicker when switching "display" and "visibility" styles |
---|
375 | domClass.add(toNode, "mblAndroidWorkaround"); |
---|
376 | } |
---|
377 | |
---|
378 | this.onBeforeTransitionOut.apply(this, this._arguments); |
---|
379 | connect.publish("/dojox/mobile/beforeTransitionOut", [this].concat(lang._toArray(this._arguments))); |
---|
380 | if(toWidget){ |
---|
381 | // perform view transition keeping the scroll position |
---|
382 | if(this.keepScrollPos && !this.getParent()){ |
---|
383 | var scrollTop = win.body().scrollTop || win.doc.documentElement.scrollTop || win.global.pageYOffset || 0; |
---|
384 | fromNode._scrollTop = scrollTop; |
---|
385 | var toTop = (detail.transitionDir == 1) ? 0 : (toNode._scrollTop || 0); |
---|
386 | toNode.style.top = "0px"; |
---|
387 | if(scrollTop > 1 || toTop !== 0){ |
---|
388 | fromNode.style.top = toTop - scrollTop + "px"; |
---|
389 | // address bar hiding does not work on iOS 7+. |
---|
390 | if(!(has("ios") >= 7) && config["mblHideAddressBar"] !== false){ |
---|
391 | this.defer(function(){ // iPhone needs setTimeout (via defer) |
---|
392 | win.global.scrollTo(0, (toTop || 1)); |
---|
393 | }); |
---|
394 | } |
---|
395 | } |
---|
396 | }else{ |
---|
397 | toNode.style.top = "0px"; |
---|
398 | } |
---|
399 | toWidget.onBeforeTransitionIn.apply(toWidget, this._arguments); |
---|
400 | connect.publish("/dojox/mobile/beforeTransitionIn", [toWidget].concat(lang._toArray(this._arguments))); |
---|
401 | } |
---|
402 | toNode.style.display = "none"; |
---|
403 | toNode.style.visibility = "inherit"; |
---|
404 | |
---|
405 | common.fromView = this; |
---|
406 | common.toView = toWidget; |
---|
407 | |
---|
408 | this._doTransition(fromNode, toNode, detail.transition, detail.transitionDir); |
---|
409 | }, |
---|
410 | |
---|
411 | _addTransitionPaddingTop: function(/*String|Integer*/ value){ |
---|
412 | // add padding top to the view in order to get alignment during the transition |
---|
413 | this.containerNode.style.paddingTop = value + "px"; |
---|
414 | }, |
---|
415 | |
---|
416 | _removeTransitionPaddingTop: function(){ |
---|
417 | // remove padding top from the view after the transition |
---|
418 | this.containerNode.style.paddingTop = ""; |
---|
419 | }, |
---|
420 | |
---|
421 | _toCls: function(s){ |
---|
422 | // convert from transition name to corresponding class name |
---|
423 | // ex. "slide" -> "mblSlide" |
---|
424 | return "mbl"+s.charAt(0).toUpperCase() + s.substring(1); |
---|
425 | }, |
---|
426 | |
---|
427 | _doTransition: function(fromNode, toNode, transition, transitionDir){ |
---|
428 | var rev = (transitionDir == -1) ? " mblReverse" : ""; |
---|
429 | toNode.style.display = ""; |
---|
430 | if(!transition || transition == "none"){ |
---|
431 | this.domNode.style.display = "none"; |
---|
432 | this.invokeCallback(); |
---|
433 | }else if(config['mblCSS3Transition']){ |
---|
434 | //get dojox/css3/transit first |
---|
435 | Deferred.when(transitDeferred, lang.hitch(this, function(transit){ |
---|
436 | //follow the style of .mblView.mblIn in View.css |
---|
437 | //need to set the toNode to absolute position |
---|
438 | var toPosition = domStyle.get(toNode, "position"); |
---|
439 | domStyle.set(toNode, "position", "absolute"); |
---|
440 | Deferred.when(transit(fromNode, toNode, {transition: transition, reverse: (transitionDir===-1)?true:false}),lang.hitch(this,function(){ |
---|
441 | domStyle.set(toNode, "position", toPosition); |
---|
442 | // Reset the temporary padding on toNode |
---|
443 | toNode.style.paddingTop = ""; |
---|
444 | this.invokeCallback(); |
---|
445 | })); |
---|
446 | })); |
---|
447 | }else{ |
---|
448 | if(transition.indexOf("cube") != -1){ |
---|
449 | if(has('ipad')){ |
---|
450 | domStyle.set(toNode.parentNode, {webkitPerspective:1600}); |
---|
451 | }else if(has("ios")){ |
---|
452 | domStyle.set(toNode.parentNode, {webkitPerspective:800}); |
---|
453 | } |
---|
454 | } |
---|
455 | var s = this._toCls(transition); |
---|
456 | if(has('mblAndroidWorkaround')){ |
---|
457 | // workaround for the screen flicker issue on Android 2.2 |
---|
458 | // applying transition css classes just after setting toNode.style.display = "" |
---|
459 | // causes flicker, so wait for a while using setTimeout (via defer) |
---|
460 | var _this = this; |
---|
461 | _this.defer(function(){ |
---|
462 | domClass.add(fromNode, s + " mblOut" + rev); |
---|
463 | domClass.add(toNode, s + " mblIn" + rev); |
---|
464 | domClass.remove(toNode, "mblAndroidWorkaround"); // remove offscreen style |
---|
465 | _this.defer(function(){ |
---|
466 | domClass.add(fromNode, "mblTransition"); |
---|
467 | domClass.add(toNode, "mblTransition"); |
---|
468 | }, 30); // 30 = 100 - 70, to make total delay equal to 100ms |
---|
469 | }, 70); // 70ms is experiential value |
---|
470 | }else{ |
---|
471 | domClass.add(fromNode, s + " mblOut" + rev); |
---|
472 | domClass.add(toNode, s + " mblIn" + rev); |
---|
473 | this.defer(function(){ |
---|
474 | domClass.add(fromNode, "mblTransition"); |
---|
475 | domClass.add(toNode, "mblTransition"); |
---|
476 | }, 100); |
---|
477 | } |
---|
478 | // set transform origin |
---|
479 | var fromOrigin = "50% 50%"; |
---|
480 | var toOrigin = "50% 50%"; |
---|
481 | var scrollTop, posX, posY; |
---|
482 | if(transition.indexOf("swirl") != -1 || transition.indexOf("zoom") != -1){ |
---|
483 | if(this.keepScrollPos && !this.getParent()){ |
---|
484 | scrollTop = win.body().scrollTop || win.doc.documentElement.scrollTop || win.global.pageYOffset || 0; |
---|
485 | }else{ |
---|
486 | scrollTop = -domGeometry.position(fromNode, true).y; |
---|
487 | } |
---|
488 | posY = win.global.innerHeight / 2 + scrollTop; |
---|
489 | fromOrigin = "50% " + posY + "px"; |
---|
490 | toOrigin = "50% " + posY + "px"; |
---|
491 | }else if(transition.indexOf("scale") != -1){ |
---|
492 | var viewPos = domGeometry.position(fromNode, true); |
---|
493 | posX = ((this.clickedPosX !== undefined) ? this.clickedPosX : win.global.innerWidth / 2) - viewPos.x; |
---|
494 | if(this.keepScrollPos && !this.getParent()){ |
---|
495 | scrollTop = win.body().scrollTop || win.doc.documentElement.scrollTop || win.global.pageYOffset || 0; |
---|
496 | }else{ |
---|
497 | scrollTop = -viewPos.y; |
---|
498 | } |
---|
499 | posY = ((this.clickedPosY !== undefined) ? this.clickedPosY : win.global.innerHeight / 2) + scrollTop; |
---|
500 | fromOrigin = posX + "px " + posY + "px"; |
---|
501 | toOrigin = posX + "px " + posY + "px"; |
---|
502 | } |
---|
503 | domStyle.set(fromNode, css3.add({}, {transformOrigin:fromOrigin})); |
---|
504 | domStyle.set(toNode, css3.add({}, {transformOrigin:toOrigin})); |
---|
505 | } |
---|
506 | }, |
---|
507 | |
---|
508 | onAnimationStart: function(e){ |
---|
509 | // summary: |
---|
510 | // A handler that is called when transition animation starts. |
---|
511 | }, |
---|
512 | |
---|
513 | onAnimationEnd: function(e){ |
---|
514 | // summary: |
---|
515 | // A handler that is called after transition animation ends. |
---|
516 | var name = e.animationName || e.target.className; |
---|
517 | if(name.indexOf("Out") === -1 && |
---|
518 | name.indexOf("In") === -1 && |
---|
519 | name.indexOf("Shrink") === -1){ return; } |
---|
520 | var isOut = false; |
---|
521 | if(domClass.contains(this.domNode, "mblOut")){ |
---|
522 | isOut = true; |
---|
523 | this.domNode.style.display = "none"; |
---|
524 | domClass.remove(this.domNode, [this._toCls(this._detail.transition), "mblIn", "mblOut", "mblReverse"]); |
---|
525 | }else{ |
---|
526 | // Reset the temporary padding |
---|
527 | this._removeTransitionPaddingTop(); |
---|
528 | } |
---|
529 | domStyle.set(this.domNode, css3.add({}, {transformOrigin:""})); |
---|
530 | if(name.indexOf("Shrink") !== -1){ |
---|
531 | var li = e.target; |
---|
532 | li.style.display = "none"; |
---|
533 | domClass.remove(li, "mblCloseContent"); |
---|
534 | |
---|
535 | // If target is placed inside scrollable, need to call onTouchEnd |
---|
536 | // to adjust scroll position |
---|
537 | var p = viewRegistry.getEnclosingScrollable(this.domNode); |
---|
538 | p && p.onTouchEnd(); |
---|
539 | } |
---|
540 | if(isOut){ |
---|
541 | this.invokeCallback(); |
---|
542 | } |
---|
543 | this._clearClasses(this.domNode); |
---|
544 | |
---|
545 | // clear the clicked position |
---|
546 | this.clickedPosX = this.clickedPosY = undefined; |
---|
547 | |
---|
548 | if(name.indexOf("Cube") !== -1 && |
---|
549 | name.indexOf("In") !== -1 && has("ios")){ |
---|
550 | this.domNode.parentNode.style[css3.name("perspective")] = ""; |
---|
551 | } |
---|
552 | }, |
---|
553 | |
---|
554 | invokeCallback: function(){ |
---|
555 | // summary: |
---|
556 | // A function to be called after performing a transition to |
---|
557 | // call a specified callback. |
---|
558 | this.onAfterTransitionOut.apply(this, this._arguments); |
---|
559 | connect.publish("/dojox/mobile/afterTransitionOut", [this].concat(this._arguments)); |
---|
560 | var toWidget = registry.byNode(this.toNode); |
---|
561 | if(toWidget){ |
---|
562 | toWidget.onAfterTransitionIn.apply(toWidget, this._arguments); |
---|
563 | connect.publish("/dojox/mobile/afterTransitionIn", [toWidget].concat(this._arguments)); |
---|
564 | toWidget.movedFrom = undefined; |
---|
565 | if(this.setFragIds && this._isBookmarkable(this._detail)){ |
---|
566 | this.setFragIds(toWidget); // setFragIds is defined in bookmarkable.js |
---|
567 | } |
---|
568 | } |
---|
569 | if(has('mblAndroidWorkaround')){ |
---|
570 | // workaround for the screen flicker issue on Android 2.2/2.3 |
---|
571 | // remove "-webkit-transform-style" style after transition finished |
---|
572 | // to avoid side effects such as input field auto-scrolling issue |
---|
573 | // use setTimeout (via defer) to avoid flicker in case of ScrollableView |
---|
574 | this.defer(function(){ |
---|
575 | if(toWidget){ domStyle.set(this.toNode, css3.name("transformStyle"), ""); } |
---|
576 | domStyle.set(this.domNode, css3.name("transformStyle"), ""); |
---|
577 | }); |
---|
578 | } |
---|
579 | |
---|
580 | var c = this._detail.context, m = this._detail.method; |
---|
581 | if(c || m){ |
---|
582 | if(!m){ |
---|
583 | m = c; |
---|
584 | c = null; |
---|
585 | } |
---|
586 | c = c || win.global; |
---|
587 | if(typeof(m) == "string"){ |
---|
588 | c[m].apply(c, this._optArgs); |
---|
589 | }else if(typeof(m) == "function"){ |
---|
590 | m.apply(c, this._optArgs); |
---|
591 | } |
---|
592 | } |
---|
593 | this._detail = this._optArgs = this._arguments = undefined; |
---|
594 | this._inProgress = false; |
---|
595 | }, |
---|
596 | |
---|
597 | isVisible: function(/*Boolean?*/checkAncestors){ |
---|
598 | // summary: |
---|
599 | // Return true if this view is visible |
---|
600 | // checkAncestors: |
---|
601 | // If true, in addition to its own visibility, also checks the |
---|
602 | // ancestors visibility to see if the view is actually being |
---|
603 | // shown or not. |
---|
604 | var visible = function(node){ |
---|
605 | return domStyle.get(node, "display") !== "none"; |
---|
606 | }; |
---|
607 | if(checkAncestors){ |
---|
608 | for(var n = this.domNode; n.tagName !== "BODY"; n = n.parentNode){ |
---|
609 | if(!visible(n)){ return false; } |
---|
610 | } |
---|
611 | return true; |
---|
612 | }else{ |
---|
613 | return visible(this.domNode); |
---|
614 | } |
---|
615 | }, |
---|
616 | |
---|
617 | getShowingView: function(){ |
---|
618 | // summary: |
---|
619 | // Find the currently showing view from my sibling views. |
---|
620 | // description: |
---|
621 | // Note that depending on the ancestor views' visibility, |
---|
622 | // the found view may not be actually shown. |
---|
623 | var nodes = this.domNode.parentNode.childNodes; |
---|
624 | for(var i = 0; i < nodes.length; i++){ |
---|
625 | var n = nodes[i]; |
---|
626 | if(n.nodeType === 1 && domClass.contains(n, "mblView") && n.style.display !== "none"){ |
---|
627 | return registry.byNode(n); |
---|
628 | } |
---|
629 | } |
---|
630 | return null; |
---|
631 | }, |
---|
632 | |
---|
633 | getSiblingViews: function(){ |
---|
634 | // summary: |
---|
635 | // Returns an array of the sibling views. |
---|
636 | if(!this.domNode.parentNode){ return [this]; } |
---|
637 | return array.map(array.filter(this.domNode.parentNode.childNodes, |
---|
638 | function(n){ return n.nodeType === 1 && domClass.contains(n, "mblView"); }), |
---|
639 | function(n){ return registry.byNode(n); }); |
---|
640 | }, |
---|
641 | |
---|
642 | show: function(/*Boolean?*/noEvent, /*Boolean?*/doNotHideOthers){ |
---|
643 | // summary: |
---|
644 | // Shows this view without a transition animation. |
---|
645 | var out = this.getShowingView(); |
---|
646 | if(!noEvent){ |
---|
647 | if(out){ |
---|
648 | out.onBeforeTransitionOut(out.id); |
---|
649 | connect.publish("/dojox/mobile/beforeTransitionOut", [out, out.id]); |
---|
650 | } |
---|
651 | this.onBeforeTransitionIn(this.id); |
---|
652 | connect.publish("/dojox/mobile/beforeTransitionIn", [this, this.id]); |
---|
653 | } |
---|
654 | |
---|
655 | if(doNotHideOthers){ |
---|
656 | this.domNode.style.display = ""; |
---|
657 | }else{ |
---|
658 | array.forEach(this.getSiblingViews(), function(v){ |
---|
659 | v.domNode.style.display = (v === this) ? "" : "none"; |
---|
660 | }, this); |
---|
661 | } |
---|
662 | this.load && this.load(); // for ContentView |
---|
663 | |
---|
664 | if(!noEvent){ |
---|
665 | if(out){ |
---|
666 | out.onAfterTransitionOut(out.id); |
---|
667 | connect.publish("/dojox/mobile/afterTransitionOut", [out, out.id]); |
---|
668 | } |
---|
669 | this.onAfterTransitionIn(this.id); |
---|
670 | connect.publish("/dojox/mobile/afterTransitionIn", [this, this.id]); |
---|
671 | } |
---|
672 | }, |
---|
673 | |
---|
674 | hide: function(){ |
---|
675 | // summary: |
---|
676 | // Hides this view without a transition animation. |
---|
677 | this.domNode.style.display = "none"; |
---|
678 | } |
---|
679 | }); |
---|
680 | }); |
---|