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 });