1 // ==========================================================================
  2 // Project:   SproutCore - JavaScript Application Framework
  3 // Copyright: ©2009 Alex Iskander and TPSi
  4 //            Portions ©2008-2011 Apple Inc. All rights reserved.
  5 // License:   Licensed under MIT license (see license.js)
  6 // ==========================================================================
  7 
  8 sc_require("mixins/emptiness");
  9 sc_require("mixins/edit_mode");
 10 
 11 /*globals Forms */
 12 
 13 /** @class
 14   Represents a single row in a form. Rows have label and any number of other child views.
 15 
 16 
 17   @extends SC.View
 18   @author Alex Iskander
 19 */
 20 SC.FormRowView = SC.View.extend(SC.FlowedLayout, SC.CalculatesEmptiness, SC.FormsEditMode,
 21 /** @scope Forms.FormRowView.prototype */ {
 22   classNames: ["sc-form-row-view"],
 23   renderDelegateName: 'formRowRenderDelegate',
 24 
 25   flowPadding: SC.propertyFromRenderDelegate('flowPadding'),
 26   defaultFlowSpacing: SC.propertyFromRenderDelegate('flowSpacing'),
 27 
 28   fillWidth: YES,
 29   layoutDirection: SC.LAYOUT_HORIZONTAL,
 30 
 31   layout: {left: 0, width: 0, height: 0},
 32 
 33   /**
 34     Walks like a duck.
 35   */
 36   isFormRow: YES,
 37 
 38   /**
 39     A value set so that FormView knows to tell us about the row label size change.
 40   */
 41   hasRowLabel: YES,
 42 
 43 
 44   /**
 45     The text to display next to the row. If undefined, SproutCore will try
 46     to set it automatically to the key corresponding to this row in the FormView.
 47   */
 48   label: undefined,
 49 
 50   /**
 51     The actual size for the label, as assigned by the parent FormView.
 52   */
 53   rowLabelSize: 0,
 54 
 55   /**
 56     The measured size of the label. The parent FormView may use this to
 57     determine the proper rowLabelSize.
 58   */
 59   rowLabelMeasuredSize: 0,
 60 
 61   /**
 62     If NO, the label will not automatically measure itself. The parent
 63     FormView normally manages this property for FormRowView.
 64 
 65     Note that FormRowView never changes its own rowLabelSize: it only
 66     measures it. The measurement is placed into rowLabelMeasuredSize.
 67 
 68     The FormView then sets the rowLabelSize, which is used to set the
 69     width of the LabelView.
 70   */
 71   shouldMeasureLabel: YES,
 72 
 73   /**
 74     The label view. The default is an SC.FormRowView.LabelView, which is
 75     configured to handle resizing.
 76   */
 77   labelView: null, // NOTE: gets set at end of file.
 78 
 79   /**
 80     Updates keys, content, etc. on fields. Also, handles our "special" field (only-one case)
 81   */
 82   createChildViews: function() {
 83     // keep array of keys so we can pass on key to child.
 84     var cv = SC.clone(this.get('childViews'));
 85 
 86     // add label
 87     if (this.labelView.isClass) {
 88       this.labelView = this.createChildView(this.labelView, {
 89         value: this.get('label')
 90       });
 91 
 92       this.labelView.addObserver('measuredSize', this, 'labelSizeDidChange');
 93       this.labelView.bind('shouldMeasureSize', this, 'shouldMeasureLabel');
 94       this.get('childViews').unshift(this.labelView);
 95     }
 96 
 97     var content = this.get('content');
 98 
 99     sc_super();
100 
101 
102     // now, do the actual passing it
103     var idx, len = cv.length, key, v;
104     for (idx = 0; idx < len; idx++) {
105       key = cv[idx];
106 
107       // if the view was originally declared as a string, then we have something to give it
108       if (SC.typeOf(key) === SC.T_STRING) {
109         // try to get the actual view
110         v = this.get(key);
111 
112         // see if it does indeed exist, and if it doesn't have a value already
113         if (v && !v.isClass) {
114           if (!v.get('contentValueKey')) {
115             //
116             // NOTE: WE HAVE A SPECIAL CASE
117             //       If this is the single field, pass through our formKey
118             //       Single-field rows are created by the SC.FormView.row helper.
119             if (key === "_singleField")  {
120               v.set('contentValueKey', this.get('formKey'));
121             } else {
122               v.set('contentValueKey', key);
123             }
124           }
125 
126           if (!v.get('content')) {
127             v.bind('content', this, 'content') ;
128           }
129         }
130 
131       }
132     }
133 
134     this.rowLabelSizeDidChange();
135   },
136 
137   labelDidChange: function() {
138     this.get("labelView").set("value", this.get("label"));
139   }.observes("label"),
140 
141   labelSizeDidChange: function() {
142     var size = this.get("labelView").get("measuredSize");
143     this.set("rowLabelMeasuredSize", size.width);
144 
145     // alert parent view if it is a row delegate
146     var pv = this.get("parentView");
147     if (pv && pv.get("isRowDelegate")) pv.rowLabelMeasuredSizeDidChange(this, size);
148   },
149 
150   rowLabelSizeDidChange: function() {
151     this.get("labelView").adjust({
152       "width": this.get("rowLabelSize")
153     });
154   }.observes("rowLabelSize")
155 
156 });
157 
158 SC.FormRowView.mixin({
159   row: function(label, fieldType, ext) {
160     if (label.isClass) {
161       ext = fieldType;
162       fieldType = label;
163       label = null;
164     }
165     // now, create a hash (will be used by the parent form's exampleRow)
166     if (!ext) {
167       ext = {};
168     } else {
169       ext = SC.clone(ext);
170     }
171     ext.label = label;
172     ext.childViews = ["_singleField"];
173     ext._singleField = fieldType;
174     return ext;
175   },
176 
177   LabelView: SC.LabelView.extend(SC.AutoResize, SC.CalculatesEmptiness, {
178     shouldAutoResize: NO, // only change the measuredSize so we can update.
179     layout: { left:0, top:0, width: 0, height: 18 },
180     fillHeight: YES,
181     classNames: ["sc-form-label"],
182     isValue: NO
183   })
184 });
185 
186 SC.FormRowView.prototype.labelView = SC.FormRowView.LabelView.design();
187