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('mixins/collection_group');
  9 sc_require('views/disclosure');
 10 
 11 /**
 12   @class
 13   
 14   Displays a group view in a source list.  Handles displaying a disclosure
 15   triangle which can be used to show/hide children.
 16   
 17   @extends SC.View
 18   @extends SC.Control
 19   @extends SC.CollectionGroup
 20   @author Charles Jolley
 21   @author Erich Ocean
 22   @version 1.0
 23   @since 0.9
 24 */
 25 SC.SourceListGroupView = SC.View.extend(SC.Control, SC.CollectionGroup,
 26 /** @scope SC.SourceListGroupView.prototype */ {
 27   
 28   /**
 29     @type Array
 30     @default ['sc-source-list-group']
 31     @see SC.View#classNames
 32   */
 33   classNames: ['sc-source-list-group'],
 34   
 35   // ..........................................................
 36   // KEY PROPERTIES
 37   // 
 38   
 39   /**
 40     The content object the source list group will display.
 41     
 42     @type SC.Object
 43     @default null
 44   */
 45   content: null,
 46   
 47   /**
 48     The current group visibility.  Used by the source list to determine the 
 49     layout size of the group.
 50     
 51     @type Boolean
 52     @default YES
 53   */
 54   isGroupVisible: YES,
 55   
 56   /** 
 57     YES if group is showing its titlebar.
 58     
 59     Group views will typically hide their header if the content is set to 
 60     null.  You can also override this method to always hide the header if 
 61     you want and the SourceListView will not leave room for it.
 62     
 63     @type Boolean
 64     @default YES
 65   */
 66   hasGroupTitle: YES,
 67   
 68   /**
 69     The content property key to use as the group view's title.
 70     
 71     @type String
 72     @default null
 73   */
 74   groupTitleKey: null,
 75   
 76   /**
 77     The content property key to use to determine if the group's children are 
 78     visible or not.
 79     
 80     @type String
 81     @default null
 82   */
 83   groupVisibleKey: null,
 84   
 85   /** @private */
 86   render: function(context, firstTime) {
 87     context.push('<div role="button" class="sc-source-list-label sc-disclosure-view sc-button-view button disclosure no-disclosure">',
 88               '<img src="'+SC.BLANK_IMAGE_URL+'" class="button" />',
 89               '<span class="label"></span></div>') ;
 90   },
 91   
 92   /** @private */
 93   createChildViews: function() {
 94     
 95   },
 96   
 97   /** @private */
 98   contentPropertyDidChange: function(target, key) {
 99     var content = this.get('content') ;
100     var labelView = this.outlet('labelView') ;
101     
102     // hide labelView if content is null.
103     if (content === null) {
104       labelView.setIfChanged('isVisible', NO) ;
105       this.setIfChanged('hasGroupTitle', NO) ;
106       return ;
107     } else {
108       labelView.setIfChanged('isVisible', YES) ;
109       this.setIfChanged('hasGroupTitle', YES) ;
110     }
111     
112    // set the title if that changed.
113     var groupTitleKey = this.getDelegateProperty('groupTitleKey', this.displayDelegate) ;
114     if ((key == '*') || (groupTitleKey && (key == groupTitleKey))) {
115       var title = (content && content.get && groupTitleKey) ? content.get(groupTitleKey) : content;
116       if (title != this._title) {
117         this._title = title ;
118         if (title) title = title.capitalize() ;
119         labelView.set('title', title) ;
120       }
121     }
122     
123     // set the group visibility if changed
124     var groupVisibleKey = this.getDelegateProperty('groupVisibleKey', this.displayDelegate) ;
125     if ((key == '*') || (groupVisibleKey && (key == groupVisibleKey))) {
126       if (groupVisibleKey) {
127         labelView.removeClassName('no-disclosure') ;
128         
129         var isVisible = (content && content.get) ?
130           !!content.get(groupVisibleKey) :
131           YES ;
132         if (isVisible != this.get('isGroupVisible')) {
133           this.set('isGroupVisible', isVisible) ;
134           labelView.set('value', isVisible) ;
135         }
136       } else labelView.addClassName('no-disclosure') ;
137     }
138   },
139   
140   /** @private
141     Called when the user clicks on the disclosure triangle
142   */
143   disclosureValueDidChange: function(newValue) {
144     if (newValue == this.get('isGroupVisible')) return; // nothing to do
145     
146     // update group if necessary
147     var group = this.get('content') ;
148     var groupVisibleKey = this.getDelegateProperty('groupVisibleKey', this.displayDelegate) ;
149     if (group && group.set && groupVisibleKey) {
150       group.set(groupVisibleKey, newValue) ;
151     }
152     
153     // update my own value and then update my collection view.
154     this.set('isGroupVisible', newValue) ;
155     if (this.owner && this.owner.updateChildren) this.owner.updateChildren(true) ;
156     
157   },
158   
159   /** @private */
160   labelView: SC.DisclosureView.extend({
161     
162     /** @private */
163     value: YES,
164     
165     /** @private
166       If the disclosure value changes, call the owner's method.  Note
167       normally you would do this with a binding, but since this is a semi-
168       private class anyway, there is no reason to go to all that trouble.
169     */
170     _valueObserver: function() {
171       if (this.owner) this.owner.disclosureValueDidChange(this.get('value')) ;
172     }.observes('value')
173     
174   })
175   
176 });
177