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/inline_editable');
  9 sc_require('mixins/inline_editor_delegate');
 10 sc_require('delegates/inline_text_field');
 11 
 12 
 13 /**
 14   @class
 15 
 16   Displays a static string of text.
 17 
 18   You use a label view anytime you need to display a static string of text
 19   or to display text that may need to be edited using only an inline control.
 20 
 21   @extends SC.View
 22   @extends SC.Control
 23   @extends SC.InlineEditable
 24   @extends SC.InlineEditorDelegate
 25   @since SproutCore 1.0
 26 */
 27 SC.LabelView = SC.View.extend(SC.Control, SC.InlineEditable,
 28 /** @scope SC.LabelView.prototype */ {
 29 
 30   classNames: ['sc-label-view'],
 31 
 32   displayProperties: ['displayTitle', 'displayHint', 'displayToolTip', 'icon'],
 33 
 34   /**
 35     The delegate that gets notified of events related to the editing process. Set
 36     this to the object you want to handles the lifecycle of the inline editor.
 37 
 38     Defaults to itself.
 39     @type SC.Object
 40   */
 41   inlineEditorDelegate: SC.InlineTextFieldDelegate,
 42 
 43   isEditable: NO,
 44 
 45   /**
 46     The exampleInlineTextFieldView property is by default a
 47     SC.InlineTextFieldView but it can be set to a customized inline text field
 48     view.
 49 
 50     @property
 51     @type {SC.View}
 52     @default {SC.InlineTextFieldView}
 53   */
 54   exampleEditor: SC.InlineTextFieldView,
 55 
 56   /**
 57     Whether the value, hint and toolTip will be escaped to avoid HTML injection
 58     attacks or not.
 59 
 60     You should only disable this option if you are sure you are displaying
 61     non-user generated text.
 62 
 63     Note: this is not an observed display property.  If you change it after
 64     rendering, you should call `displayDidChange` on the view to update the layer.
 65 
 66     @type Boolean
 67     @default true
 68   */
 69   escapeHTML: true,
 70 
 71   /**
 72     If true, then the value will be localized.
 73     This is a default that can be overidden by the settings in the owner view.
 74   */
 75   localize: NO,
 76   localizeBindingDefault: SC.Binding.oneWay().bool(),
 77 
 78   /**
 79     If set to true, the label element will include the 'ellipsis' class, which
 80     by default sets the 'white-space' style to 'nowrap' and the 'text-overflow'
 81     style to 'ellipsis'.
 82 
 83     Note: that this does NOT work with multi-line text.
 84 
 85     Note: this is not an observed display property.  If you change it after
 86     rendering, you should call `displayDidChange` on the view to update the layer.
 87 
 88     @type Boolean
 89     @default false
 90    */
 91   needsEllipsis: false,
 92 
 93   /**
 94     Set this to a validator or to a function and the value
 95     will be passed through it before being set.
 96 
 97     This is a default default that can be overidden by the
 98     settings in the owner view.
 99   */
100   formatter: null,
101 
102   /**
103     The value of the label.
104 
105     You may also set the value using a content object and a contentValueKey.
106 
107     @field {String}
108   */
109   value: '',
110 
111   /**
112     The hint to display if no value is set.  Should be used only if isEditable
113     is set to YES.
114   */
115   hint: null,
116 
117   /** @deprecated */
118   hintEnabled: function() {
119     //@if(debug)
120     SC.warn("Developer Warning: The hintEnabled property of SC.LabelView is deprecated.  Please simply get the isEditable property to determine if the hint will be displayed instead.");
121     //@endif
122     return this.get('isEditable');
123   }.property('isEditable').cacheable(),
124 
125   /**
126     An optional icon to display to the left of the label.  Set this value
127     to either a CSS class name (for spriting) or an image URL.
128   */
129   icon: null,
130 
131   /**
132     Set the alignment of the label view.
133 
134     Note: this is not an observed display property.  If you change it after
135     rendering, you should call `displayDidChange` on the view to update the layer.
136 
137     @type String SC.ALIGN_LEFT|SC.ALIGN_CENTER|SC.ALIGN_RIGHT
138     @default null
139     @deprecated Use CSS instead.
140   */
141   textAlign: null,
142 
143   //
144   // SUPPORT FOR AUTOMATIC RESIZING
145   //
146   supportsAutoResize: YES,
147   autoResizeLayer: function() { return this.get('layer'); }
148   .property('layer').cacheable(),
149 
150   autoResizeText: function() { return this.get('displayTitle'); }
151   .property('displayTitle').cacheable(),
152 
153   autoResizePadding: SC.propertyFromRenderDelegate('autoResizePadding', { height: 0, width: 10 }),
154 
155   /**
156     The name of the theme's SC.LabelView render delegate.
157 
158     @type String
159   */
160   renderDelegateName: 'labelRenderDelegate',
161 
162   /**
163     The value that will actually be displayed.
164 
165     This property is dynamically computed by applying localization,
166     string conversion and other normalization utilities.
167 
168     @type String
169   */
170   displayTitle: function() {
171     var value, formatter;
172 
173     value = this.get('value') ;
174 
175     // 1. apply the formatter
176     formatter = this.getDelegateProperty('formatter', this.displayDelegate) ;
177     if (formatter) {
178       var formattedValue = (SC.typeOf(formatter) === SC.T_FUNCTION) ?
179           formatter(value, this) : formatter.fieldValueForObject(value, this) ;
180       if (!SC.none(formattedValue)) value = formattedValue ;
181     }
182 
183     // 2. If the returned value is an array, convert items to strings and
184     // join with commas.
185     if (SC.typeOf(value) === SC.T_ARRAY) {
186       var ary = [];
187       for(var idx=0, idxLen = value.get('length'); idx< idxLen;idx++) {
188         var x = value.objectAt(idx) ;
189         if (!SC.none(x) && x.toString) x = x.toString() ;
190         ary.push(x) ;
191       }
192       value = ary.join(',') ;
193     }
194 
195     // 3. If value is not a string, convert to string. (handles 0)
196     if (!SC.none(value) && value.toString) value = value.toString() ;
197 
198     // 4. Localize
199     if (value && this.getDelegateProperty('localize', this.displayDelegate)) value = SC.String.loc(value) ;
200 
201     return value;
202   }.property('value', 'localize', 'formatter').cacheable(),
203 
204   /**
205     The hint that will actually be displayed depending on localization and
206     sanitizing (or not).
207 
208     @type String
209   */
210   displayHint: function () {
211     var hint = this.get('hint'),
212       isEditable = this.get('isEditable');
213 
214     if (isEditable) {
215       if (hint && this.getDelegateProperty('localize', this.displayDelegate)) {
216         hint = SC.String.loc(hint);
217       }
218     } else {
219       hint = null;
220     }
221 
222     return hint;
223   }.property('hint', 'localize', 'isEditable').cacheable(),
224 
225   /** @deprecated */
226   hintValue: function() {
227     //@if(debug)
228     SC.warn("Developer Warning: The hintValue property of SC.LabelView is deprecated.  Please simply get the hint or displayHint (localized) property instead.");
229     //@endif
230     var hintVal = this.get('hint');
231     return hintVal;
232   }.property('hint').cacheable(),
233 
234   /** @private */
235   mouseDown: function(evt) {
236     // Capture the event if it's a double click and we are editable.
237     return this.get('isEditable') && evt.clickCount === 2;
238   },
239 
240   /** @private If isEditable is set to true, opens the inline text editor view. */
241   doubleClick: function (evt) { return this.beginEditing(); },
242 
243   /*
244   * @method
245   *
246   * Hide the label view while the inline editor covers it.
247   */
248   inlineEditorDidBeginEditing: function(original, editor, value, editable) {
249     this._oldOpacity = this.get('layout').opacity || 1;
250     this.adjust('opacity', 0);
251 
252     original(editor, value, editable);
253   }.enhance(),
254 
255   /*
256   * @method
257   *
258   * Restore the label view when the inline editor finishes.
259   */
260   inlineEditorDidEndEditing: function() {
261     this.adjust('opacity', this._oldOpacity);
262     this._oldOpacity = null ;
263   }
264 });
265