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/segmented');
  9 
 10 /**
 11   @static
 12   @type String
 13   @constant
 14 */
 15 SC.TOP_LOCATION = 'top';
 16 
 17 /**
 18   @static
 19   @type String
 20   @constant
 21 */
 22 SC.TOP_TOOLBAR_LOCATION = 'top-toolbar';
 23 
 24 /**
 25   @static
 26   @type String
 27   @constant
 28 */
 29 SC.BOTTOM_LOCATION = 'bottom';
 30 
 31 /**
 32   @class
 33 
 34   Incorporates a segmented view and a container view to display the selected
 35   tab.  Provide an array of items, which will be passed onto the segmented
 36   view.
 37 
 38   @extends SC.View
 39   @since SproutCore 1.0
 40 */
 41 SC.TabView = SC.View.extend(
 42 /** @scope SC.TabView.prototype */ {
 43 
 44   /** @private
 45     @type Array
 46     @default ['sc-tab-view']
 47     @see SC.View#classNames
 48   */
 49   classNames: ['sc-tab-view'],
 50 
 51   /** @private
 52     @type Array
 53     @default ['nowShowing']
 54     @see SC.View#displayProperties
 55   */
 56   displayProperties: ['nowShowing'],
 57 
 58   // ..........................................................
 59   // PROPERTIES
 60   //
 61 
 62  /**
 63     Set nowShowing with the view you want to display. (You may specify globally-accessible views
 64     like `MyApp.tabsPage.myTabView`, local views defined on the TabView itself like `myLocalTabView`,
 65     or deep local views like `.myLocalPage.myTabView`.)
 66 
 67     @type String
 68     @default null
 69   */
 70   nowShowing: null,
 71 
 72   /**
 73     The list of items for the SegmentedView, and specifying the associated view to display. For example:
 74 
 75         items: [
 76           { title: "Tab 1", value: "MyApp.tabsPage.view1" },
 77           { title: "Tab 2", value: "MyApp.tabsPage.view2" }
 78         ]
 79 
 80     (Note that if needed you can specify the item keys by specifying `itemTitleKey`, `itemValueKey`, et
 81     cetera, on your TabView.)
 82 
 83     @type Array
 84     @default []
 85   */
 86   items: [],
 87 
 88   /**
 89     @type String
 90     @default null
 91   */
 92   itemTitleKey: null,
 93 
 94   /**
 95     @type String
 96     @default null
 97   */
 98   itemValueKey: null,
 99 
100   /**
101     @type String
102     @default null
103   */
104   itemIsEnabledKey: null,
105 
106   /**
107     @type String
108     @default null
109   */
110   itemIconKey: null,
111 
112   /**
113     @type String
114     @default null
115   */
116   itemWidthKey: null,
117 
118   /**
119     @type String
120     @default null
121   */
122   itemToolTipKey: null,
123 
124   /**
125     @type Number
126     @default SC.REGULAR_BUTTON_HEIGHT
127   */
128   tabHeight: SC.REGULAR_BUTTON_HEIGHT,
129 
130   /**
131     Possible values:
132 
133       - SC.TOP_LOCATION
134       - SC.TOP_TOOLBAR_LOCATION
135       - SC.BOTTOM_LOCATION
136 
137     @type String
138     @default SC.TOP_LOCATION
139   */
140   tabLocation: SC.TOP_LOCATION,
141 
142   /**
143     If set, then the tab location will be automatically saved in the user
144     defaults.  Browsers that support localStorage will automatically store
145     this information locally.
146 
147     @type String
148     @default null
149   */
150   userDefaultKey: null,
151 
152 
153   // ..........................................................
154   // FORWARDING PROPERTIES
155   //
156 
157   /** @private Sync important changes with the child views. */
158   _tab_nowShowingDidChange: function() {
159     var content = this.get('nowShowing');
160 
161     // Sync the segmented view.
162     this.get('segmentedView').set('value', content);
163 
164     // If the user default is set, save it.
165     var defaultKey = this.get('userDefaultKey');
166     if (defaultKey) {
167       SC.userDefaults.set([defaultKey,'nowShowing'].join(':'), content);
168     }
169 
170     // If it's a string, try to turn it into the object it references...
171     if (SC.typeOf(content) === SC.T_STRING && content.length > 0) {
172       var dotspot = content.indexOf('.');
173       // No dot means a local property, either to this view or this view's page.
174       if (dotspot === -1) {
175         var tempContent = this.get(content);
176         content = SC.kindOf(tempContent, SC.CoreView) ? tempContent : SC.objectForPropertyPath(content, this.get('page'));
177       }
178       // Dot at beginning means local property path.
179       else if (dotspot === 0) {
180         content = this.getPath(content.slice(1));
181       }
182       // Dot after the beginning
183       else {
184         content = SC.objectForPropertyPath(content);
185       }
186     }
187 
188     // Sync the container view.
189     this.get('containerView').set('nowShowing', content);
190 
191     return this;
192   }.observes('nowShowing'),
193 
194   /** @private */
195   _tab_itemsDidChange: function() {
196     this.get('segmentedView').set('items', this.get('items'));
197     return this ;
198   }.observes('items'),
199 
200   /** @private
201     Restore userDefault key if set.
202   */
203   init: function() {
204     sc_super();
205     this._tab_nowShowingDidChange()._tab_itemsDidChange();
206     // Wake up the userDefaults support, if in use.
207     var defaultKey = this.get('userDefaultKey');
208     if (defaultKey) {
209       defaultKey = [defaultKey,'nowShowing'].join(':');
210       var nowShowing = SC.userDefaults.get(defaultKey);
211       if (!SC.none(nowShowing)) this.set('nowShowing', nowShowing);
212     }
213   },
214 
215   /** @private */
216   createChildViews: function() {
217     var childViews  = [], containerView, layout,
218         tabLocation = this.get('tabLocation'),
219         tabHeight   = this.get('tabHeight'),
220         controlSize = this.get('controlSize');
221 
222     if (tabLocation === SC.TOP_LOCATION) {
223       layout = { top: tabHeight/2+1, left: 0, right: 0, bottom: 0, border: 1 };
224     } else if (tabLocation === SC.TOP_TOOLBAR_LOCATION) {
225       layout = { top: tabHeight+1, left: 0, right: 0, bottom: 0, border: 1 };
226     } else {
227       layout = { top: 0, left: 0, right: 0, bottom: (tabHeight/2) - 1, border: 1 };
228     }
229 
230     containerView = this.containerView.extend({
231       layout: layout,
232       //adding the role
233       ariaRole: 'tabpanel'
234     });
235 
236     this.containerView = this.createChildView(containerView) ;
237 
238     //  The segmentedView managed by this tab view.  Note that this TabView uses
239     //  a custom segmented view.  You can access this view but you cannot change
240     // it.
241     layout = (tabLocation === SC.TOP_LOCATION ||
242               tabLocation === SC.TOP_TOOLBAR_LOCATION) ?
243              { height: tabHeight, left: 0, right: 0, top: 0 } :
244              { height: tabHeight, left: 0, right: 0, bottom: 0 } ;
245 
246     this.segmentedView = this.get('segmentedView').extend({
247       layout: layout,
248 
249       controlSize: controlSize,
250 
251       /** @private
252         When the value changes, update the parentView's value as well.
253       */
254       _sc_tab_segmented_valueDidChange: function() {
255         var pv = this.get('parentView');
256         if (pv) pv.set('nowShowing', this.get('value'));
257       }.observes('value'),
258 
259       /** @private */
260       init: function() {
261         // before we setup the rest of the view, copy key config properties
262         // from the owner view...
263         var pv = this.get('parentView');
264         if (pv) {
265           SC._TAB_ITEM_KEYS.forEach(function(k) { this[k] = pv.get(k); }, this);
266         }
267         return sc_super();
268       }
269     });
270 
271     this.segmentedView = this.createChildView(this.segmentedView);
272 
273     childViews.push(this.containerView);
274     childViews.push(this.segmentedView);
275 
276     this.set('childViews', childViews);
277     return this;
278   },
279 
280   // ..........................................................
281   // COMPONENT VIEWS
282   //
283 
284   /**
285     The containerView managed by this tab view.  Note that TabView uses a
286     custom container view.  You can access this view but you cannot change
287     it.
288 
289     @type SC.View
290     @default SC.ContainerView
291     @readOnly
292   */
293   containerView: SC.ContainerView.extend({ renderDelegateName: 'wellRenderDelegate' }),
294 
295   /**
296     @type SC.View
297     @default SC.SegmentedView
298   */
299   segmentedView: SC.SegmentedView
300 
301 }) ;
302 
303 SC._TAB_ITEM_KEYS = ['itemTitleKey', 'itemValueKey', 'itemIsEnabledKey', 'itemIconKey', 'itemWidthKey', 'itemToolTipKey', 'itemActionKey', 'itemTargetKey'];
304