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 /**
  9   @namespace
 10 
 11   A tree item is a model object that acts as a node in a tree-like data
 12   structure such as a hierarchy of folders or outline of items.  This mixin
 13   can be applied to tree item model objects to customize the way the tree
 14   information is extracted from the object.
 15 
 16   ## Basic Implementation
 17 
 18   If you add this mixin, you must implement the treeItemChildren property so
 19   that it returns the current array of child tree items for the receiver.  If
 20   you do not implement this property the tree item will not function.
 21 
 22   ## Optimizing Branches
 23 
 24   The most common use of this mixin is to override the treeItemBranchIndexes
 25   property to return an index set of child items that are themselves branches
 26   in the tree.  Normally the TreeController will need to walk every item in
 27   your list to determine these branch items.  However by implementing this
 28   method yourself, you can provide a result faster.
 29 
 30   If none of your child items are branches, override this property to return
 31   null or an empty index set.
 32 
 33   @since SproutCore 1.0
 34 */
 35 SC.TreeItemContent = {
 36 
 37   /**
 38     Walk like a duck.
 39 
 40     @type Boolean
 41     @default YES
 42   */
 43   isTreeItemContent: YES,
 44 
 45   /**
 46     Property returns the children for this tree item.  The default simply
 47     returns null.  If you implement this mixin, you MUST implement this
 48     property to return the actual tree item children for the item.
 49 
 50     @type SC.Array
 51     @default null
 52   */
 53   treeItemChildren: null,
 54 
 55   /**
 56     The default property used to determine if the tree item is expanded.  You
 57     can implement you model object to update this property or you can override
 58     treeItemDisclosureState() to compute the disclosure state however you
 59     want.
 60 
 61     @type Boolean
 62     @default YES
 63   */
 64   treeItemIsExpanded: YES,
 65 
 66   /**
 67     Indicates whether the tree item should be rendered as a group or not.
 68     This property is only useful on the root item in your tree.  Setting it to
 69     YES on any other item will be ignored.
 70 
 71     @type Boolean
 72     @default NO
 73   */
 74   treeItemIsGrouped: NO,
 75 
 76   /**
 77     Returns the disclosure state for the tree item, which appears at the
 78     index of the parent's treeItemChildren array.  The response must be one of
 79     SC.BRANCH_OPEN, SC.BRANCH_CLOSED or SC.LEAF_NODE.
 80 
 81     If the parent parameter is null, then this item is part of the root
 82     children array.
 83 
 84     This method will only be called for tree items that have children.  Tree
 85     items with no children are assumed to be leaf nodes.
 86 
 87     The default implementation uses the treeItemIsExpanded property to
 88     determine if the item should be open or closed.
 89 
 90     @param {Object} parent the parent item containing this item
 91     @param {Number} idx the index of the item in the parent
 92     @returns {Number} branch state
 93   */
 94   treeItemDisclosureState: function(parent, idx) {
 95     return this.get('treeItemIsExpanded') ? SC.BRANCH_OPEN : SC.BRANCH_CLOSED;
 96   },
 97 
 98   /**
 99     Collapse the tree item.  The default implementation will change the
100     treeItemIsExpanded property, but you can override this method to handle
101     collapsing anyway you like.
102 
103     @param {Object} parent the parent item containing this item
104     @param {Number} idx the index of the item in the parent
105     @returns {void}
106   */
107   treeItemCollapse: function(parent, idx) {
108     this.setIfChanged('treeItemIsExpanded', NO);
109   },
110 
111   /**
112     Expand the tree item.  The default implementation will change the
113     treeItemIsExpanded property, but you can override this method to handle
114     collapsing anyway you like.
115 
116     @param {Object} parent the parent item containing this item
117     @param {Number} idx the index of the item in the parent
118     @returns {void}
119   */
120   treeItemExpand: function(parent, idx) {
121     this.setIfChanged('treeItemIsExpanded', YES);
122   },
123 
124   /**
125     Returns an index set containing the child indexes of the item that are
126     themselves branches.  This will only be called on tree items with a branch
127     disclosure state.
128 
129     If the passed parent and index are both null, then the receiver is the
130     root node in the tree.
131 
132     The default implementation iterates over the item's children to get the
133     disclosure state of each one.  Child items with a branch disclosure state
134     will have their index added to the return index set.
135 
136     You may want to override this method to provide a more efficient
137     implementation if you are working with large data sets and can infer which
138     children are branches without iterating over each one.
139 
140     If you know for sure that all of the child items for this item are leaf
141     nodes and not branches, simply override this method to return null.
142 
143     @param {Object} parent the parent item containing this item
144     @param {Number} index the index of the item in the parent
145     @returns {SC.IndexSet} branch indexes
146   */
147   treeItemBranchIndexes: function(parent, index) {
148     var children = this.get('treeItemChildren'),
149         ret, lim, idx, item;
150 
151     if (!children) return null ; // nothing to do
152 
153     ret = SC.IndexSet.create();
154     lim = children.get('length');
155     for(idx=0;idx<lim;idx++) {
156       if (!(item = children.objectAt(idx))) continue;
157       if (!item.get('treeItemChildren')) continue;
158       if (item.treeItemDisclosureState(this,idx)!==SC.LEAF_NODE) ret.add(idx);
159     }
160 
161     return ret.get('length')>0 ? ret : null;
162   }
163 
164 };
165