UNPKG

dojox

Version:

Dojo eXtensions, a rollup of many useful sub-projects and varying states of maturity – from very stable and robust, to alpha and experimental. See individual projects contain README files for details.

320 lines (295 loc) 10.5 kB
define([ "dojo/_base/array", "dojo/_base/connect", "dojo/_base/declare", "dojo/dom", "dojo/dom-class", "dijit/registry", "./View", "./_ScrollableMixin", "./sniff", "./_css3", "dojo/has!dojo-bidi?dojox/mobile/bidi/SwapView" ], function(array, connect, declare, dom, domClass, registry, View, ScrollableMixin, has, css3, BidiSwapView){ // module: // dojox/mobile/SwapView var SwapView = declare(has("dojo-bidi") ? "dojox.mobile.NonBidiSwapView" : "dojox.mobile.SwapView", [View, ScrollableMixin], { // summary: // A container that can be swiped horizontally. // description: // SwapView is a container widget which can be swiped horizontally. // SwapView is a subclass of dojox/mobile/View. It allows the user to // swipe the screen left or right to move between the views. When // SwapView is swiped, it finds an adjacent SwapView to open. When // the transition is done, a topic "/dojox/mobile/viewChanged" is // published. Note that, to behave properly, the SwapView needs to // occupy the entire width of the screen. /* internal properties */ // scrollDir: [private] String // Scroll direction, used by dojox/mobile/scrollable (always "f" for this class). scrollDir: "f", // weight: [private] Number // Frictional weight used to compute scrolling speed. weight: 1.2, // _endOfTransitionTimeoutHandle: [private] Object // The handle (returned by _WidgetBase.defer) for the timeout set on touchEnd in case // the end of transition event is not fired by the browser. _endOfTransitionTimeoutHandle: null, buildRendering: function(){ this.inherited(arguments); domClass.add(this.domNode, "mblSwapView"); this.setSelectable(this.domNode, false); this.containerNode = this.domNode; this.subscribe("/dojox/mobile/nextPage", "handleNextPage"); this.subscribe("/dojox/mobile/prevPage", "handlePrevPage"); this.noResize = true; // not to call resize() from scrollable#init }, startup: function(){ if(this._started){ return; } this.inherited(arguments); }, resize: function(){ // summary: // Calls resize() of each child widget. this.inherited(arguments); // scrollable#resize() will be called array.forEach(this.getChildren(), function(child){ if(child.resize){ child.resize(); } }); }, onTouchStart: function(/*Event*/e){ // summary: // Internal function to handle touchStart events. if(this._siblingViewsInMotion()){ // Ignore touchstart if the views are already in motion this.propagatable ? e.preventDefault() : event.stop(e); return; } var fromTop = this.domNode.offsetTop; var nextView = this.nextView(this.domNode); if(nextView){ nextView.stopAnimation(); domClass.add(nextView.domNode, "mblIn"); // Temporarily add padding to align with the fromNode while transition nextView.containerNode.style.paddingTop = fromTop + "px"; } var prevView = this.previousView(this.domNode); if(prevView){ prevView.stopAnimation(); domClass.add(prevView.domNode, "mblIn"); // Temporarily add padding to align with the fromNode while transition prevView.containerNode.style.paddingTop = fromTop + "px"; } this._setSiblingViewsInMotion(true); this.inherited(arguments); }, onTouchEnd: function(/*Event*/e){ if(e){ if(!this._moved){ // No transition / animation following touchend in this case this._setSiblingViewsInMotion(false); }else{ // There might be a transition / animation following touchend // As the webkitTransitionEndEvent is not always fired, make sure we call this._setSiblingViewsInMotion(false) even // if the event is not fired (and onFlickAnimationEnd is not called as a result) this._endOfTransitionTimeoutHandle = this.defer(function(){ this._setSiblingViewsInMotion(false); }, 1000); } } this.inherited(arguments); }, handleNextPage: function(/*Widget*/w){ // summary: // Called when the "/dojox/mobile/nextPage" topic is published. var refNode = w.refId && dom.byId(w.refId) || w.domNode; if(this.domNode.parentNode !== refNode.parentNode){ return; } if(this.getShowingView() !== this){ return; } this.goTo(1); }, handlePrevPage: function(/*Widget*/w){ // summary: // Called when the "/dojox/mobile/prevPage" topic is published. var refNode = w.refId && dom.byId(w.refId) || w.domNode; if(this.domNode.parentNode !== refNode.parentNode){ return; } if(this.getShowingView() !== this){ return; } this.goTo(-1); }, goTo: function(/*Number*/dir, /*String?*/moveTo){ // summary: // Moves to the next or previous view. var view = moveTo ? registry.byId(moveTo) : ((dir == 1) ? this.nextView(this.domNode) : this.previousView(this.domNode)); if(view && view !== this){ this.stopAnimation(); // clean-up animation states view.stopAnimation(); this.domNode._isShowing = false; // update isShowing flag view.domNode._isShowing = true; this.performTransition(view.id, dir, "slide", null, function(){ connect.publish("/dojox/mobile/viewChanged", [view]); }); } }, isSwapView: function(/*DomNode*/node){ // summary: // Returns true if the given node is a SwapView widget. return (node && node.nodeType === 1 && domClass.contains(node, "mblSwapView")); }, nextView: function(/*DomNode*/node){ // summary: // Returns the next view. for(var n = node.nextSibling; n; n = n.nextSibling){ if(this.isSwapView(n)){ return registry.byNode(n); } } return null; }, previousView: function(/*DomNode*/node){ // summary: // Returns the previous view. for(var n = node.previousSibling; n; n = n.previousSibling){ if(this.isSwapView(n)){ return registry.byNode(n); } } return null; }, scrollTo: function(/*Object*/to){ // summary: // Overrides dojox/mobile/scrollable.scrollTo(). if(!this._beingFlipped){ var newView, x; if(to.x){ if(to.x < 0){ newView = this.nextView(this.domNode); x = to.x + this.domNode.offsetWidth; }else{ newView = this.previousView(this.domNode); x = to.x - this.domNode.offsetWidth; } } if(newView){ if(newView.domNode.style.display === "none"){ newView.domNode.style.display = ""; newView.resize(); } newView._beingFlipped = true; newView.scrollTo({x:x}); newView._beingFlipped = false; } } this.inherited(arguments); }, findDisp: function(/*DomNode*/node){ // summary: // Overrides dojox/mobile/scrollable.findDisp(). // description: // When this function is called from scrollable.js, there are // two visible views, one is the current view, the other is the // next view. This function returns the current view, not the // next view, which has the mblIn class. if(!domClass.contains(node, "mblSwapView")){ return this.inherited(arguments); } if(!node.parentNode){ return null; } var nodes = node.parentNode.childNodes; for(var i = 0; i < nodes.length; i++){ var n = nodes[i]; if(n.nodeType === 1 && domClass.contains(n, "mblSwapView") && !domClass.contains(n, "mblIn") && n.style.display !== "none"){ return n; } } return node; }, slideTo: function(/*Object*/to, /*Number*/duration, /*String*/easing, /*Object?*/fake_pos){ // summary: // Overrides dojox/mobile/scrollable.slideTo(). if(!this._beingFlipped){ var w = this.domNode.offsetWidth; var pos = fake_pos || this.getPos(); var newView, newX; if(pos.x < 0){ // moving to left newView = this.nextView(this.domNode); if(pos.x < -w/4){ // slide to next if(newView){ to.x = -w; newX = 0; } }else{ // go back if(newView){ newX = w; } } }else{ // moving to right newView = this.previousView(this.domNode); if(pos.x > w/4){ // slide to previous if(newView){ to.x = w; newX = 0; } }else{ // go back if(newView){ newX = -w; } } } if(newView){ newView._beingFlipped = true; newView.slideTo({x:newX}, duration, easing); newView._beingFlipped = false; newView.domNode._isShowing = (newView && newX === 0); } this.domNode._isShowing = !(newView && newX === 0); } this.inherited(arguments); }, onAnimationEnd: function(/*Event*/e){ // summary: // Overrides dojox/mobile/View.onAnimationEnd(). if(e && e.target && domClass.contains(e.target, "mblScrollableScrollTo2")){ return; } this.inherited(arguments); }, onFlickAnimationEnd: function(/*Event*/e){ if(this._endOfTransitionTimeoutHandle){ this._endOfTransitionTimeoutHandle = this._endOfTransitionTimeoutHandle.remove(); } // summary: // Overrides dojox/mobile/scrollable.onFlickAnimationEnd(). if(e && e.target && !domClass.contains(e.target, "mblScrollableScrollTo2")){ return; } this.inherited(arguments); if(this.domNode._isShowing){ // Hide all the views other than the currently showing one. // Otherwise, when the orientation is changed, other views // may appear unexpectedly. array.forEach(this.domNode.parentNode.childNodes, function(c){ if(this.isSwapView(c)){ domClass.remove(c, "mblIn"); if(!c._isShowing){ c.style.display = "none"; c.style[css3.name("transform")] = ""; c.style.left = "0px"; // top/left mode needs this // reset the temporaty padding on the container node c.style.paddingTop = ""; } } }, this); connect.publish("/dojox/mobile/viewChanged", [this]); // Reset the temporary padding this.containerNode.style.paddingTop = ""; }else if(!has("css3-animations")){ this.containerNode.style.left = "0px"; // compat mode needs this } this._setSiblingViewsInMotion(false); }, _setSiblingViewsInMotion: function(/*Boolean*/inMotion){ var inMotionAttributeValue = inMotion ? "true" : false; var parent = this.domNode.parentNode; if(parent){ parent.setAttribute("data-dojox-mobile-swapview-inmotion", inMotionAttributeValue); } }, _siblingViewsInMotion: function(){ var parent = this.domNode.parentNode; if(parent){ return parent.getAttribute("data-dojox-mobile-swapview-inmotion") == "true"; }else{ return false; } } }); return has("dojo-bidi") ? declare("dojox.mobile.SwapView", [SwapView, BidiSwapView]) : SwapView; });