<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -1,4 +1,12 @@
 
+- Added support for reduced properties.  These properties begin with &quot;@&quot; and
+  will automatically calculate some kind of summary information about the 
+  receiving array.  They are also bindable so you can bind to myArray.@average
+  and it will update whenever the average changes. [CAJ]
+  
+- Cleaned up SC.Array support to incorporate new SC.Enumerable functions.  The
+  Enumerable methods from Prototype are no longer supported. [CAJ]
+  
 - Removed Array.asArray().  This helper has been deprecated for 6mo now.  Use
   SC.$A() or Array.from() instead.  [CAJ]
   </diff>
      <filename>HISTORY</filename>
    </modified>
    <modified>
      <diff>@@ -185,7 +185,7 @@ SC.ArrayController = SC.Controller.extend(SC.Array, SC.SelectionSupport,
 
       this.beginPropertyChanges();
       this.contentCloneReset();
-      this.arrayContentDidChange() ;
+      this.enumerableContentDidChange() ;
       this.notifyPropertyChange('length') ;
       this.updateSelectionAfterContentChange();
       this.endPropertyChanges() ;
@@ -261,7 +261,7 @@ SC.ArrayController = SC.Controller.extend(SC.Array, SC.SelectionSupport,
     contentClone.replace(idx, amt, sourceObjects);
     
     this.editorDidChange() ;
-    this.arrayContentDidChange();
+    this.enumerableContentDidChange();
     this.updateSelectionAfterContentChange();
     
     return this;</diff>
      <filename>controllers/array.js</filename>
    </modified>
    <modified>
      <diff>@@ -21,6 +21,7 @@
 SC.Enumerator = function(enumerableObject) {
   this.enumerable = enumerableObject ;
   this.reset() ;
+  return this ;
 } ;
 
 SC.Enumerator.prototype = {</diff>
      <filename>foundation/enumerator.js</filename>
    </modified>
    <modified>
      <diff>@@ -4,6 +4,7 @@
 // ==========================================================================
 
 require('mixins/enumerable') ;
+require('mixins/observable') ;
 
 /**
   @class 
@@ -83,6 +84,7 @@ SC.Set = function(items) {
       while(--idx &gt;= 0) this.add(items[idx]) ;
     }
   }
+  return this ;
 } ;
 
 SC.Set.prototype = {
@@ -207,8 +209,8 @@ SC.Set.prototype = {
   
 } ;
 
-// Make this enumerable
-SC.mixin(SC.Set.prototype, SC.Enumerable) ;
+// Make this enumerable and observable
+SC.mixin(SC.Set.prototype, SC.Enumerable, SC.Observable) ;
 
 SC.Set.prototype.push = SC.Set.prototype.unshift = SC.Set.prototype.add ;
 SC.Set.prototype.shift = SC.Set.prototype.pop ;</diff>
      <filename>foundation/set.js</filename>
    </modified>
    <modified>
      <diff>@@ -3,6 +3,8 @@
 // copyright 2006-2008, Sprout Systems, Inc. and contributors.
 // ==========================================================================
 
+require('mixins/enumerable') ;
+
 SC.OUT_OF_RANGE_EXCEPTION = &quot;Index out of range&quot; ;
 
 /**
@@ -12,6 +14,17 @@ SC.OUT_OF_RANGE_EXCEPTION = &quot;Index out of range&quot; ;
   picked up by the Array class as well as other controllers, etc. that want to  
   appear to be arrays.
   
+  Unlike SC.Enumerable, this mixin defines methods specifically for 
+  collections that provide index-ordered access to their contents.  When you
+  are designing code that needs to accept any kind of Array-like object, you
+  should use these methods instead of Array primitives because these will 
+  properly notify observers of changes to the array. 
+  
+  Although these methods are efficient, they do add a layer of indirection to
+  your application so it is a good idea to use them only when you need the 
+  flexibility of using both true JavaScript arrays and &quot;virtual&quot; arrays such
+  as controllers and collections.
+  
   You can use the methods defined in this module to access and modify array 
   contents in a KVO-friendly way.  You can also be notified whenever the 
   membership if an array changes by observing the &quot;[]&quot; property.
@@ -19,6 +32,11 @@ SC.OUT_OF_RANGE_EXCEPTION = &quot;Index out of range&quot; ;
   To support SC.Array in your own class, you must override two
   primitives to use it: replace() and objectAt().  
 
+  Note that the SC.Array mixin also incorporates the SC.Enumerable mixin.  All
+  SC.Array-like objects are also enumerable.
+
+  @extends SC.Enumerable
+  @since SproutCore 0.9.0
 */
 SC.Array = {
 
@@ -33,7 +51,7 @@ SC.Array = {
 /**
   This is one of the primitves you must implement to support SC.Array.  You 
   should replace amt objects started at idx with the objects in the passed 
-  array.  You should also call this.arrayContentDidChange() ;
+  array.  You should also call this.enumerableContentDidChange() ;
   
   @param {Number} idx 
     Starting index in the array to replace.  If idx &gt;= length, then append to 
@@ -67,27 +85,6 @@ SC.Array = {
     if (idx &gt;= this.get('length')) return undefined;
     return this.get(idx);
   },
-
-  // this is required to support the enumerable options.  Override with your
-  // own method if you prefer.
-  _each: function(iterator) {
-    var len ;
-    for (var i = 0, len = this.get('length'); i &lt; len; i++)
-      iterator(this.objectAt(i));
-  },
-  
-  /**  
-    When you implement replace(), be sure to call this method whenever the 
-    membership of your array changes.  This will make sure users are properly 
-    notified.
-  */
-  arrayContentDidChange: function() {
-    var kvo = (this._kvo) ? this._kvo().changes : '(null)';
-    this.notifyPropertyChange('[]') ;
-    if (this.ownerRecord &amp;&amp; this.ownerRecord.recordDidChange) {
-      this.ownerRecord.recordDidChange(this) ;
-    }
-  },
   
   /**
     @field []
@@ -95,6 +92,8 @@ SC.Array = {
     This is the handler for the special array content property.  If you get
     this property, it will return this.  If you set this property it a new 
     array, it will replace the current content.
+    
+    This property overrides the default property defined in SC.Enumerable.
   */
   '[]': function(key, value) {
     if (value !== undefined) {
@@ -198,82 +197,67 @@ SC.Array = {
     }
     return true ;
   }
-        
+    
 } ;
 
-// All arrays have the SC.Array mixin.  Do this before we add the 
-// enumerable methods since Arrays are already enumerable.
-Object.extend(Array.prototype, SC.Array) ; 
+// Add SC.Array to the built-in array before we add SC.Enumerable to SC.Array
+// since built-in Array's are already enumerable.
+SC.mixin(Array.prototype, SC.Array) ; 
+SC.Array = SC.mixin({}, SC.Enumerable, SC.Array) ;
 
-// Now make SC.Array enumerable and add other array method we did not want to
-// override in Array itself.
-Object.extend(SC.Array, Enumerable) ;
-Object.extend(SC.Array, {
-  /**
-    Returns a new array that is a slice of the receiver.  This implementation
-    uses the observable array methods to retrieve the objects for the new slice.
-    
-    @param beginIndex {Integer} (Optional) index to begin slicing from. Default: 0
-    @param endIndex {Integer} (Optional) index to end the slice at. Default: 0
-  */
-  slice: function(beginIndex, endIndex) {
-    var ret = []; 
-    var length = this.get('length') ;
-    if (beginIndex == null) beginIndex = 0 ;
-    if ((endIndex == null) || (endIndex &gt; length)) endIndex = length ;
-    while(beginIndex &lt; endIndex) ret[ret.length] = this.objectAt(beginIndex++) ;
-    return ret ;
-  }
+// Add any extra methods to SC.Array that are native to the built-in Array.
+/**
+  Returns a new array that is a slice of the receiver.  This implementation
+  uses the observable array methods to retrieve the objects for the new 
+  slice.
   
-}) ;
+  @param beginIndex {Integer} (Optional) index to begin slicing from.     
+  @param endIndex {Integer} (Optional) index to end the slice at.
+  @returns {Array} New array with specified slice
+*/
+SC.Array.slice = function(beginIndex, endIndex) {
+  var ret = []; 
+  var length = this.get('length') ;
+  if (beginIndex == null) beginIndex = 0 ;
+  if ((endIndex == null) || (endIndex &gt; length)) endIndex = length ;
+  while(beginIndex &lt; endIndex) ret[ret.length] = this.objectAt(beginIndex++) ;
+  return ret ;
+}  ;
 
-// ........................................................
-// A few basic enhancements to the Array class.
-// These methods add support for the SproutCore replace() method as well as 
-// optimizing certain enumerable methods.
+
+// ......................................................
+// ARRAY SUPPORT
 //
-Object.extend(Array.prototype, {
+// Implement the same enhancements on Array.  We use specialized methods
+// because working with arrays are so common.
+(function() {
+  SC.mixin(Array.prototype, {
 
-  // primitive for array support.
-  replace: function(idx, amt, objects) {
-    if (!objects || objects.length == 0) {
-      this.splice(idx, amt) ;
-    } else {
-      var args = [idx, amt].concat(objects) ;
-      this.splice.apply(this,args) ;
-    }
-    this.arrayContentDidChange() ;
-    return this ;
-  },
+    // primitive for array support.
+    replace: function(idx, amt, objects) {
+      if (!objects || objects.length == 0) {
+        this.splice(idx, amt) ;
+      } else {
+        var args = [idx, amt].concat(objects) ;
+        this.splice.apply(this,args) ;
+      }
+      this.enumerableContentDidChange() ;
+      return this ;
+    },
   
-  // These are faster implementations of the iterations defined by prototype.
-  // The iterators there are cool but they consume large numbers of stack
-  // frames.  These are API compatible, but much faster because they duplicate
-  // code instead of calling a bunch of common methods.
-
-  each: function(iterator) {
-    try {
-      for(var index=0;index&lt;this.length;index++) {
-        var item = this[index] ;
-        iterator.call(item,item,index) ;
+    // If you ask for an unknown property, then try to collect the value
+    // from member items.
+    unknownProperty: function(key, value) {
+      var ret = this.reducedProperty(key, value) ;
+      if (ret === undefined) {
+        ret = (value === undefined) ? this.invoke('get', key) : null ;
       }
-    } catch (e) {
-      if (e != $break) throw e ;
+      return ret ;
     }
-    return this ;
-  },
-  
-  // If you ask for an unknown property, then try to collect the value
-  // from member items.
-  unknownProperty: function(key, value) {
-    if (value !== undefined) return null ;
-    return this.invoke('get', key) ;
-  }
     
-}) ;
-
-Array.prototype.collect = Array.prototype.map ;
-
+  }) ;
+  
+})() ;
 
 // Returns the passed item as an array.  If the item is already an array,
 // it is returned as is.  If it is not an array, it is placed into one.  If</diff>
      <filename>mixins/array.js</filename>
    </modified>
    <modified>
      <diff>@@ -575,10 +575,136 @@ SC.Enumerable = {
     last = null ;
     context = SC.Enumerator._pushContext(context);
     return ret ;
-  }
+  }        
+} ;
+
+// Build in a separate function to avoid unintential leaks through closures...
+SC._buildReducerFor = function(reducerKey) {
+  return function(key, value) {
+    var reducer = this[reducerKey] ;
+    if (SC.typeOf(reducer) !== T_FUNCTION) {
+      return (this.unknownProperty) ? this.unknownProperty(key, value) : null;
+    } else {
+      return this.reduce(reducer, null) ;
+    }
+  }.property('[]') ;
+};
+
+SC.Reducers = {
+  /**
+    This property will trigger anytime the enumerable's content changes.
+    You can observe this property to be notified of changes to the enumerables
+    content.
+    
+    For plain enumerables, this property is read only.  SC.Array overrides
+    this method.
+  */
+  '[]': function(key, value) { return this ; }.property(),
+
+  /**
+    Invoke this method when the contents of your enumerable has changed.
+    This will notify any observers watching for content changes.
+  */
+  enumerableContentDidChange: function() {
+    this.notifyPropertyChange('[]') ;
+    if (this.ownerRecord &amp;&amp; this.ownerRecord.recordDidChange) {
+      this.ownerRecord.recordDidChange(this) ;
+    }
+  },
+  
+  /**
+    Call this method from your unknownProperty() handler to implement 
+    automatic reduced properties.  A reduced property is a property that 
+    collects its contents dynamically from your array contents.  Reduced 
+    properties always begin with &quot;@&quot;.  Getting this property will call 
+    reduce() on your array with the function matching the key name as the
+    processor.
+    
+    The return value of this will be either the return value from the 
+    reduced property or undefined, which means this key is not a reduced 
+    property.  You can call this at the top of your unknownProperty handler
+    like so:
     
+    {{{
+      unknownProperty: function(key, value) {
+        var ret = this.handleReduceProperty(key, value) ;
+        if (ret === undefined) {
+          // process like normal
+        }
+      }
+    }}}
+    
+    @param key {String} the reduce property key
+    @param value {Object} a value or undefined.
+    @param generateProperty {Boolean} only set to false if you do not want an
+      optimized computed property handler generated for this.  Not common.
+    @returns {Object} the reduced property or undefined
+  */
+  reducedProperty: function(key, value, generateProperty) {
+    
+    if (key[0] !== '@') return undefined ; // not a reduced property
+    
+    // get the reducer key and the reducer
+    var reducerKey = &quot;reduce%@&quot;.fmt(key.slice(1,key.length).capitalize()) ; 
+    var reducer = this[reducerKey] ;
+
+    // if there is no reduce function defined for this key, then we can't 
+    // build a reducer for it.
+    if (SC.typeOf(reducer) !== T_FUNCTION) return undefined;
+    
+    // if we can't generate the property, just run reduce
+    if (generateProperty === NO) return this.reduce(reducer, null) ;
+
+    // ok, found the reducer.  Let's build the computed property and install
+    var func = SC._buildReducerFor(reducerKey);
+    var p = this.constructor.prototype ;
+    if (p) {
+      p[key] = func ;
+      this.registerDependentKey(key, '[]') ;
+    }
+    
+    // and reduce anyway...
+    return this.reduce(reducer, null) ;
+  },
+  
+  /** 
+    Reducer for @max reduced property.
+  */
+  reduceMax: function(previousValue, item, index, enumerable) {
+    if (previousValue == null) return item ;
+    return (item &gt; previousValue) ? item : previousValue ;
+  },
+
+  /** 
+    Reducer for @min reduced property.
+  */
+  reduceMin: function(previousValue, item, index, enumerable) {
+    if (previousValue == null) return item ;
+    return (item &lt; previousValue) ? item : previousValue ;
+  },
+
+  /** 
+    Reducer for @average reduced property.
+  */
+  reduceAverage: function(previousValue, item, index, e) {
+    var ret = (previousValue || 0) + item ;
+    var len = (e.get) ? e.get('length') : e.length;
+    if (index &gt;= len-1) ret = ret / len; //avg after last item.
+    return ret ; 
+  },
+
+  /** 
+    Reducer for @sum reduced property.
+  */
+  reduceSum: function(previousValue, item, index, e) {
+    return (previousValue == null) ? item : previousValue + item ;
+  }
 } ;
 
+// Apply reducers...
+SC.mixin(SC.Enumerable, SC.Reducers) ;
+SC.mixin(Array.prototype, SC.Reducers) ;
+
 // ......................................................
 // ARRAY SUPPORT
 //
@@ -802,7 +928,7 @@ SC.Enumerable = {
   // and don't break the browsers.
   for(var key in mixinIfMissing) {
     if (!mixinIfMissing.hasOwnProperty(key)) continue ;
-    if (!Array.prototype[key] || (Enumerable &amp;&amp; Array.prototype[key] === Enumerable[key])) {
+    if (!Array.prototype[key] || (Prototype &amp;&amp; Prototype.Version.match(/^1\.6/))) {
       Array.prototype[key] = mixinIfMissing[key] ;
     }
   }</diff>
      <filename>mixins/enumerable.js</filename>
    </modified>
    <modified>
      <diff>@@ -202,7 +202,7 @@ DummyArray = SC.Object.extend(SC.Array, {
     this.content.replace(idx,amt,objects) ;
     
     this.set('length', this.content.length) ;
-    this.arrayContentDidChange() ;
+    this.enumerableContentDidChange() ;
     this.endPropertyChanges() ;
   },
   </diff>
      <filename>tests/foundation/array.rhtml</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>acb85841974c46c12153de2ed2189a9576660b6b</id>
    </parent>
  </parents>
  <author>
    <name>Charles Jolley</name>
    <email>charles@sproutit.com</email>
  </author>
  <url>http://github.com/mauritslamers/sproutcore/commit/468db4d1a163f1372f77e40bd80e80b1bbafabe3</url>
  <id>468db4d1a163f1372f77e40bd80e80b1bbafabe3</id>
  <committed-date>2008-08-07T16:08:20-07:00</committed-date>
  <authored-date>2008-08-07T16:08:20-07:00</authored-date>
  <message>Cleanup SC.Array and add support for reduced properties.

- Added support for reduced properties.  These properties begin with &quot;@&quot; and
  will automatically calculate some kind of summary information about the
  receiving array.  They are also bindable so you can bind to myArray.@average
  and it will update whenever the average changes. [CAJ]

- Cleaned up SC.Array support to incorporate new SC.Enumerable functions.  The
  Enumerable methods from Prototype are no longer supported. [CAJ]</message>
  <tree>fbcef7b1c07e33459c4f1c8f7b43313c45aaa75e</tree>
  <committer>
    <name>Charles Jolley</name>
    <email>charles@sproutit.com</email>
  </committer>
</commit>
