// ========================================================================== // Project: SproutCore - JavaScript Application Framework // Copyright: ©2006-2010 Sprout Systems, Inc. and contributors. // Portions ©2008-2010 Apple Inc. All rights reserved. // License: Licensed under MIT license (see license.js) // ========================================================================== sc_require('views/view') ; sc_require('mixins/control') ; SC.ALIGN_LEFT = 'left'; SC.ALIGN_RIGHT = 'right'; SC.ALIGN_CENTER = 'center'; SC.REGULAR_WEIGHT = 'normal'; SC.BOLD_WEIGHT = 'bold'; /** @class Displays a static string of text. You use a label view anytime you need to display a static string of text or to display text that may need to be edited using only an inline control. @extends SC.View @extends SC.Control @extends SC.InlineEditorDelegate @since SproutCore 1.0 */ SC.LabelView = SC.View.extend(SC.Control, /** @scope SC.LabelView.prototype */ { classNames: ['sc-label-view'], /** Specify the font weight for this. You may pass SC.REGULAR_WEIGHT, or SC.BOLD_WEIGHT. */ fontWeight: SC.REGULAR_WEIGHT, /** If true, value will be escaped to avoid scripting attacks. This is a default value that can be overridden by the settings on the owner view. */ escapeHTML: true, escapeHTMLBindingDefault: SC.Binding.oneWay().bool(), /** If true, then the value will be localized. This is a default default that can be overidden by the settings in the owner view. */ localize: false, localizeBindingDefault: SC.Binding.oneWay().bool(), /** Set this to a validator or to a function and the value will be passed through it before being set. This is a default default that can be overidden by the settings in the owner view. */ formatter: null, /** The value of the label. You may also set the value using a content object and a contentValueKey. @field {String} */ value: '', /** The hint to display if no value is set. Should be used only if isEditable is set to YES. */ hint: null, /** The exampleInlineTextFieldView property is by default a SC.InlineTextFieldView but it can be set to a customized inline text field view. @property @type {SC.View} @default {SC.InlineTextFieldView} */ exampleInlineTextFieldView: SC.InlineTextFieldView, /** An optional icon to display to the left of the label. Set this value to either a CSS class name (for spriting) or an image URL. */ icon: null, /** Set the alignment of the label view. */ textAlign: SC.ALIGN_LEFT, /** If you want the inline editor to be multiline set this property to YES. */ isInlineEditorMultiline: NO, /** [RO] The value that will actually be displayed. This property is dynamically computed by applying localization, string conversion and other normalization utilities. @field */ displayValue: function() { var value, formatter; value = this.get('value') ; // 1. apply the formatter formatter = this.getDelegateProperty('formatter', this.displayDelegate) ; if (formatter) { var formattedValue = (SC.typeOf(formatter) === SC.T_FUNCTION) ? formatter(value, this) : formatter.fieldValueForObject(value, this) ; if (!SC.none(formattedValue)) value = formattedValue ; } // 2. If the returned value is an array, convert items to strings and // join with commas. if (SC.typeOf(value) === SC.T_ARRAY) { var ary = []; for(var idx=0, idxLen = value.get('length'); idx< idxLen;idx++) { var x = value.objectAt(idx) ; if (!SC.none(x) && x.toString) x = x.toString() ; ary.push(x) ; } value = ary.join(',') ; } // 3. If value is not a string, convert to string. (handles 0) if (!SC.none(value) && value.toString) value = value.toString() ; // 4. Localize if (value && this.getDelegateProperty('localize', this.displayDelegate)) value = value.loc() ; // 5. escapeHTML if needed if (this.get('escapeHTML')) value = SC.RenderContext.escapeHTML(value); return value ; }.property('value', 'localize', 'formatter', 'escapeHTML').cacheable(), /** [RO] The hint value that will actually be displayed. This property is dynamically computed by applying localization and other normalization utilities. */ hintValue: function() { var hintVal = this.get('hint'); if (this.get('escapeHTML')) hintVal = SC.RenderContext.escapeHTML(hintVal); return hintVal ; }.property('hint', 'escapeHTML').cacheable(), /** Enables editing using the inline editor. */ isEditable: NO, isEditableBindingDefault: SC.Binding.bool(), /** YES if currently editing label view. */ isEditing: NO, /** Validator to use during inline editing. If you have set isEditing to YES, then any validator you set on this property will be used when the label view is put into edit mode. @type {SC.Validator} */ validator: null, /** Event dispatcher callback. If isEditable is set to true, opens the inline text editor view. @param {DOMMouseEvent} evt DOM event */ doubleClick: function( evt ) { return this.beginEditing(); }, /** Opens the inline text editor (closing it if it was already open for another view). @return {Boolean} YES if did begin editing */ beginEditing: function() { if (this.get('isEditing')) return YES ; if (!this.get('isEditable')) return NO ; var el = this.$(), value = this.get('value') || '', f = SC.viewportOffset(el[0]), frameTemp = this.convertFrameFromView(this.get('frame'), null) ; f.width=frameTemp.width; f.height=frameTemp.height; SC.InlineTextFieldView.beginEditing({ frame: f, delegate: this, exampleElement: el, value: value, multiline: this.get('isInlineEditorMultiline'), isCollection: NO, validator: this.get('validator'), exampleInlineTextFieldView: this.get('exampleInlineTextFieldView') }); }, /** Cancels the current inline editor and then exits editor. @return {Boolean} NO if the editor could not exit. */ discardEditing: function() { if (!this.get('isEditing')) return YES ; return SC.InlineTextFieldView.discardEditing() ; }, /** Commits current inline editor and then exits editor. @return {Boolean} NO if the editor could not exit */ commitEditing: function() { if (!this.get('isEditing')) return YES ; return SC.InlineTextFieldView.commitEditing() ; }, /** @private Set editing to true so edits will no longer be allowed. */ inlineEditorWillBeginEditing: function(inlineEditor) { this.set('isEditing', YES); }, /** @private Hide the label view while the inline editor covers it. */ inlineEditorDidBeginEditing: function(inlineEditor) { var layer = this.$(); this._oldOpacity = layer.css('opacity') ; layer.css('opacity', 0.0); }, /** @private Delegate method defaults to the isEditable property */ inlineEditorShouldBeginEditing: function(){ return this.get('isEditable'); }, /** @private Could check with a validator someday... */ inlineEditorShouldEndEditing: function(inlineEditor, finalValue) { return YES ; }, /** @private Update the field value and make it visible again. */ inlineEditorDidEndEditing: function(inlineEditor, finalValue) { this.setIfChanged('value', finalValue) ; this.$().css('opacity', this._oldOpacity); this._oldOpacity = null ; this.set('isEditing', NO) ; }, displayProperties: 'displayValue textAlign fontWeight icon'.w(), _TEMPORARY_CLASS_HASH: {}, render: function(context, firstTime) { var value = this.get('displayValue'), icon = this.get('icon'), hint = this.get('hintValue'), classes, stylesHash, text, iconChanged = false, textChanged = false; if (icon) { var url = (icon.indexOf('/')>=0) ? icon : SC.BLANK_IMAGE_URL, className = (url === icon) ? '' : icon ; icon = ''; if(icon!==this._iconCache) { this._iconCache=icon; iconChanged = true; } } if (hint && (!value || value === '')) { text = ''+hint+''; }else{ text = value; } if(text!==this._textCache) { this._textCache=text; textChanged = true; } if(firstTime || textChanged || iconChanged){ context.push(icon, text); } // and setup alignment and font-weight on styles stylesHash = { 'text-align': this.get('textAlign'), 'font-weight': this.get('fontWeight') }; // if we are editing, set the opacity to 0 if (this.get('isEditing')) stylesHash['opacity']=0; context.addStyle(stylesHash); classes = this._TEMPORARY_CLASS_HASH; classes.icon = !!this.get('icon'); context.setClass(classes); } });