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   The SC.ContentDisplay mixin makes it easy to automatically update your view
 12   display whenever relevant properties on a content object change.  To use
 13   this mixin, include it in your view and then add the names of the
 14   properties on the content object you want to trigger a displayDidChange()
 15   method on your view. Your updateDisplay() method will then be called at the
 16   end of the run loop.
 17 
 18   ## Example
 19 
 20       MyApp.MyViewClass = SC.View.extend(SC.ContentDisplay, {
 21 
 22         contentDisplayProperties: ['title', 'isEnabled', 'hasChildren'],
 23 
 24         ...
 25       });
 26 
 27   @since SproutCore 1.0
 28 */
 29 SC.ContentDisplay = {
 30 
 31   /** @private */
 32   concatenatedProperties: 'contentDisplayProperties',
 33 
 34   /**
 35     Add an array with the names of any property on the content object that
 36     should trigger an update of the display for your view.  Changes to the
 37     content object will only invoke your display method once per runloop.
 38 
 39     @type Array
 40     @default []
 41   */
 42   contentDisplayProperties: [],
 43 
 44   /** @private
 45     Setup observers on the content object when initializing the mixin.
 46   */
 47   initMixin: function() {
 48     this._display_contentDidChange();
 49   },
 50 
 51   /**
 52    * Remove observer on existing content object, if present
 53    * @private
 54    */
 55   destroyMixin: function () {
 56     if (!this._display_content) return;
 57     this._display_stopObservingContent(this._display_content);
 58     this._display_content = null;
 59   },
 60 
 61   /** @private */
 62   _display_beginObservingContent: function(content) {
 63     var f = this._display_contentPropertyDidChange;
 64 
 65     if (SC.isArray(content)) {
 66       content.invoke('addObserver', '*', this, f);
 67     }
 68     else if (content.addObserver) {
 69       content.addObserver('*', this, f);
 70     }
 71   },
 72 
 73   /** @private */
 74   _display_stopObservingContent: function(content) {
 75     var f = this._display_contentPropertyDidChange;
 76 
 77     if (SC.isArray(content)) {
 78       content.invoke('removeObserver', '*', this, f);
 79     }
 80     else if (content.removeObserver) {
 81       content.removeObserver('*', this, f);
 82     }
 83   },
 84 
 85   /** @private */
 86   _display_contentDidChange: function(target, key, value) {
 87     // handle changes to the content...
 88     if ((value = this.get('content')) === this._display_content) return;
 89 
 90     // stop listening to old content.
 91     var content = this._display_content;
 92     if (content) this._display_stopObservingContent(content);
 93 
 94     // start listening for changes on the new content object.
 95     content = this._display_content = value;
 96     if (content) this._display_beginObservingContent(content);
 97 
 98     this.displayDidChange();
 99   }.observes('content'),
100 
101   /** @private Invoked when properties on the content object change. */
102   _display_contentPropertyDidChange: function(target, key, value, propertyRevision) {
103     if (key === '*') {
104       this.displayDidChange() ;
105     } else {
106       // only update if a displayProperty actually changed...s
107       var ary = this.get('contentDisplayProperties') ;
108       if (ary && ary.indexOf(key)>=0) this.displayDidChange();
109     }
110   }
111 
112 } ;
113