1 // ==========================================================================
  2 // Project:   SproutCore - JavaScript Application Framework
  3 // Copyright: ©2006-2011 Strobe Inc. and contributors.
  4 //            Portions ©2008-2011 Apple Inc. All rights reserved.
  5 // License:   Licensed under MIT license (see license.js)
  6 // ==========================================================================
  7 
  8 sc_require("views/toolbar");
  9 
 10 /** @class
 11   NavigationBars do Great Things. They transition themselves (fade in/out) and
 12   all children (swoosh left/right). They accept isSwipeLeft and isSwipeRight views
 13   that handle, well, swiping. In short, they are neat.
 14   
 15   @extends SC.ToolbarView
 16   @extends SC.Gesturable
 17   @since SproutCore 1.0
 18 */
 19 SC.NavigationBarView = SC.ToolbarView.extend(SC.Gesturable,
 20 /** @scope SC.NavigationBarView.prototype */{
 21 
 22   /** @private */
 23   init: function() {
 24     sc_super();
 25     
 26     if (!SC.Animatable) {
 27       SC.Logger.error(
 28         "NavigationBarView requires SC.Animatable. " +
 29         "Please make your app or framework require the animation framework. CRASH."
 30       );
 31     }
 32   },
 33 
 34   /** @private */
 35   mixinAnimatable: function() {
 36     this.mixin(SC.Animatable);
 37     this.transitions = this.navigationTransitions;
 38   },
 39   
 40   /**
 41     The default navigation transitions.
 42   */
 43   navigationTransitions: { 
 44     opacity: {
 45       duration: 0.25, action: "didFinishTransition"
 46     } 
 47   },
 48   
 49   /**
 50     The default style (opacity is 1)
 51   */
 52   style: {
 53     opacity: 1
 54   },
 55   
 56 
 57   // ..........................................................
 58   // Gesture Support
 59   // 
 60   
 61   /** @private */
 62   gestures: ["swipeGesture"],
 63   
 64   /** @private */
 65   swipeGesture: SC.SwipeGesture,
 66 
 67   /** @private */
 68   swipe: function(gesture, touch, direction) {
 69     var lookingFor = (direction === SC.SWIPE_LEFT) ? "isSwipeLeft" : "isSwipeRight",
 70         cv = this.get("childViews"), 
 71         child, idx, len = cv.get("length");
 72     
 73     // loop through the children
 74     for (idx = 0; idx < len; idx++) {
 75       child = cv[idx];
 76       
 77       // see if this is the view we are looking for
 78       if (child.get(lookingFor)) {
 79         // just give it touch responder and end right away, just like ScrollView. Good times, eh?
 80         touch.makeTouchResponder(child);
 81         touch.end();
 82         return;
 83       }
 84     }
 85     
 86   },
 87   
 88   
 89   // ..........................................................
 90   // View Build Support
 91   // 
 92   
 93   /** @private */
 94   resetBuild: function() {
 95     if (!this.isAnimatable) this.mixinAnimatable();
 96   },
 97   
 98   /** @private */
 99   didFinishTransition: function() {
100     if (this.isBuildingIn) {
101       // and please continue
102       this.buildInDidFinish();
103     } else if (this.isBuildingOut) this.buildOutDidFinish();
104   },
105   
106   /** @private */
107   preBuildIn: function() {
108     // first, fade this view out
109     this.disableAnimation();
110     this.adjust("opacity", 0).updateLayout();
111     this.enableAnimation();
112     
113     // now, loop over child views
114     var cv = this.get("childViews"), child, idx, len = cv.get("length");
115     for (idx = 0; idx < len; idx++) {
116       child = cv[idx];
117       
118       // if the child disables navigation transitions, skip
119       if (child.disableNavigationTransition) continue;
120       
121       // make sure the navigation stuff is mixed in as needed
122       if (!child._nv_mixedIn) this.mixinNavigationChild(child);
123       
124       // now, set the initial state, which is either to the left or to the right 100px.
125       child.disableAnimation();
126       child.transform(this.buildDirection === SC.TO_LEFT ? 100  : -100);
127       child.enableAnimation();
128     }
129   },
130   
131   /** @private */
132   buildIn: function() {
133     // first, we do the precursor
134     this.preBuildIn();
135     
136     // then, we queue the actual animation
137     this.invokeLater("startBuildIn", 10);
138   },
139   
140   /** @private */
141   startBuildIn: function() {
142     this.adjust("opacity", 1);
143 
144     // get our frame, because we use it when computing child frames.
145     var cv = this.get("childViews"), child, idx, len = cv.get("length");
146     for (idx = 0; idx < len; idx++) {
147       child = cv[idx];
148       if (child.disableNavigationTransition) continue;
149       child.transform(0);
150     }
151   },
152 
153   /** @private */
154   buildOut: function() {
155     this.adjust("opacity", 0);
156     
157     var cv = this.get("childViews"), child, idx, len = cv.get("length");
158     for (idx = 0; idx < len; idx++) {
159       child = cv[idx];
160       if (child.disableNavigationTransition) continue;
161       if (!child._nv_mixedIn) this.mixinNavigationChild(child);
162       child.transform(this.buildDirection === SC.TO_LEFT ? -100  : 100);
163     }
164   },
165   
166   /** @private */
167   mixinNavigationChild: function(child) {
168     if (child.isAnimatable) return;
169     
170     // mix in animatable
171     child.mixin(SC.Animatable);
172     
173     // mix in the transitions (and the "natural" layout)
174     child.mixin({
175       transitions: {
176         transform: {timing: SC.Animatable.TRANSITION_EASE_IN_OUT, duration: 0.25}
177       },
178       naturalLayout: child.get("layout"),
179       transform: function(pos) {
180         if (SC.platform.supportsCSS3DTransforms) {
181           this.adjust("transform", "translate3d(" + pos + "px,0px,0px)");
182         } else {
183           this.adjust("transform", "translate(" + pos + "px,0px)");          
184         }
185       }
186     });
187     
188     // and mark as having mixed in.
189     child._nv_mixedIn = YES;
190   }
191 });