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/workspace"); 9 10 /** 11 @static 12 @type String 13 @constant 14 */ 15 SC.TO_LEFT = "TOLEFT"; 16 17 /** 18 @static 19 @type String 20 @constant 21 */ 22 SC.TO_RIGHT = "TORIGHT"; 23 24 25 /** @class 26 27 NavigationView is very loosely based on UINavigationController: 28 that is, it implements a push/pop based API. 29 30 NavigationView checks if the view is NavigationBuildable--that is, if it has 31 32 Views may specify a topToolbar or bottomToolbar property. These will become the 33 top or bottom toolbars of the NavigationView (which is, incidentally, a WorkspaceView). 34 35 Of course, this process is animated... 36 37 @author Alex Iskander 38 @extends SC.WorkspaceView 39 @since SproutCore 1.4 40 */ 41 SC.NavigationView = SC.WorkspaceView.extend( 42 /** @scope SC.NavigationView.prototype */ { 43 44 /** @private */ 45 _views: null, 46 47 /** @private */ 48 _current: null, 49 50 /** 51 @type SC.View 52 @default SC.View 53 */ 54 navigationContentView: SC.View, 55 56 /** @private */ 57 init: function() { 58 sc_super(); 59 this._views = []; 60 }, 61 62 /** @private */ 63 createChildViews: function() { 64 sc_super(); 65 66 // get the content 67 var content = this.get("navigationContentView"); 68 69 // instantiate if needed 70 if (content.isClass) content = this.createChildView(content); 71 72 // set internal values 73 this._defaultContent = this.navigationContentView = content; 74 75 // append to the content view 76 this.contentView.appendChild(content); 77 }, 78 79 /** @private */ 80 changeNavigationContent: function(view) { 81 var top = null, bottom = null; 82 83 // find top and bottom toolbars if we are setting it to a view 84 if (view) { 85 top = view.get("topToolbar"); 86 bottom = view.get("bottomToolbar"); 87 } 88 89 // instantiate top if needed 90 if (top && top.isClass) { 91 view.set("topToolbar", top = top.create()); 92 } 93 94 // and now bottom 95 if (bottom && bottom.isClass) { 96 view.set("bottomToolbar", bottom = bottom.create()); 97 } 98 99 100 // batch property changes for efficiency 101 this.beginPropertyChanges(); 102 103 // update current, etc. etc. 104 this._current = view; 105 this.set("navigationContentView", view ? view : this._defaultContent); 106 107 // set the top/bottom appropriately 108 this.set("topToolbar", top); 109 this.set("bottomToolbar", bottom); 110 111 // and we are done 112 this.endPropertyChanges(); 113 }, 114 115 /** 116 Pushes a view into the navigation view stack. The view may have topToolbar and bottomToolbar properties. 117 118 @param {SC.View} view The view to display 119 */ 120 push: function(view) { 121 this._currentDirection = this._current ? SC.TO_LEFT : null; 122 123 // add current view to the stack (if needed) 124 if (this._current) this._views.push(this._current); 125 126 // update content now... 127 this.changeNavigationContent(view); 128 }, 129 130 /** 131 Pops the current view off the navigation view stack. 132 */ 133 pop: function() { 134 this._currentDirection = SC.TO_RIGHT; 135 136 // pop the view 137 var view = this._views.pop(); 138 139 // set new (old) content view 140 this.changeNavigationContent(view); 141 }, 142 143 /** 144 Pops to the specified view on the navigation view stack; the view you pass will become the current view. 145 146 @param {SC.View} toView The view to display 147 */ 148 popToView: function(toView) { 149 this._currentDirection = SC.TO_RIGHT; 150 var views = this._views, 151 idx = views.length - 1, 152 view = views[idx]; 153 154 // loop back from end 155 while (view && view !== toView) { 156 this._views.pop(); 157 idx--; 158 view = views[idx]; 159 } 160 161 // and change the content 162 this.changeNavigationContent(view); 163 }, 164 165 /** @private */ 166 topToolbarDidChange: function() { 167 var active = this.activeTopToolbar, replacement = this.get("topToolbar"); 168 169 // if we have an active toolbar, set the build direction and build out 170 if (active) { 171 if (this._currentDirection !== null) { 172 active.set("buildDirection", this._currentDirection); 173 this.buildOutChild(active); 174 } else { 175 this.removeChild(active); 176 } 177 } 178 179 // if we have a new toolbar, set the build direction and build in 180 if (replacement) { 181 if (this._currentDirection !== null) { 182 replacement.set("buildDirection", this._currentDirection); 183 this.buildInChild(replacement); 184 } else { 185 this.appendChild(replacement); 186 } 187 } 188 189 // update, and queue retiling 190 this.activeTopToolbar = replacement; 191 this.invokeOnce("childDidChange"); 192 }.observes("topToolbar"), 193 194 /** @private */ 195 bottomToolbarDidChange: function() { 196 var active = this.activeBottomToolbar, replacement = this.get("bottomToolbar"); 197 198 if (active) { 199 if (this._currentDirection !== null) { 200 active.set("buildDirection", this._currentDirection); 201 this.buildOutChild(active); 202 } else { 203 this.removeChild(active); 204 } 205 } 206 if (replacement) { 207 if (this._currentDirection !== null) { 208 replacement.set("buildDirection", this._currentDirection); 209 this.buildInChild(replacement); 210 } else { 211 this.appendChild(replacement); 212 } 213 } 214 215 this.activeBottomToolbar = replacement; 216 this.invokeOnce("childDidChange"); 217 }.observes("topToolbar"), 218 219 /** @private */ 220 contentViewDidChange: function() { 221 var active = this.activeNavigationContentView, replacement = this.get("navigationContentView"); 222 223 // mix in navigationbuilder if needed 224 if (!replacement.isNavigationBuilder) { 225 replacement.mixin(SC.NavigationBuilder); 226 } 227 228 // tiling really needs to happen _before_ animation 229 // so, we set "pending" and queue tiling. 230 this._pendingBuildOut = active; 231 this._pendingBuildIn = replacement; 232 233 this.activeNavigationContentView = replacement; 234 this.invokeOnce("childDidChange"); 235 }.observes("navigationContentView"), 236 237 /** @private */ 238 childDidChange: function() { 239 var replacement = this._pendingBuildIn, active = this._pendingBuildOut; 240 if (active) { 241 if (this._currentDirection !== null) { 242 active.set("buildDirection", this._currentDirection); 243 this.contentView.buildOutChild(active); 244 } else { 245 this.contentView.removeChild(active); 246 } 247 } 248 249 this._scws_tile(); 250 251 if (replacement) { 252 if (this._currentDirection !== null) { 253 replacement.set("buildDirection", this._currentDirection); 254 this.contentView.buildInChild(replacement); 255 } else { 256 this.contentView.appendChild(replacement); 257 } 258 } 259 } 260 261 }); 262