sproutcore 0.9.2 → 0.9.3
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +33 -0
- data/Manifest.txt +3 -14
- data/clients/sc_docs/controllers/docs.js +1 -0
- data/clients/sc_docs/core.js +1 -1
- data/clients/sc_test_runner/controllers/runner.js +5 -0
- data/clients/sc_test_runner/core.js +1 -1
- data/frameworks/sproutcore/english.lproj/core.css +41 -0
- data/frameworks/sproutcore/english.lproj/theme.css +20 -0
- data/frameworks/sproutcore/foundation/animator.js +11 -2
- data/frameworks/sproutcore/foundation/date.js +2 -2
- data/frameworks/sproutcore/foundation/object.js +2 -2
- data/frameworks/sproutcore/foundation/server.js +4 -3
- data/frameworks/sproutcore/foundation/set.js +1 -1
- data/frameworks/sproutcore/foundation/unittest.js +12 -9
- data/frameworks/sproutcore/lib/collection_view.rb +1 -0
- data/frameworks/sproutcore/lib/core_views.rb +4 -0
- data/frameworks/sproutcore/mixins/editable.js +144 -0
- data/frameworks/sproutcore/mixins/inline_editor_delegate.js +72 -0
- data/frameworks/sproutcore/mixins/observable.js +45 -16
- data/frameworks/sproutcore/mixins/scrollable.js +0 -1
- data/frameworks/sproutcore/tests/controllers/controller.rhtml +12 -12
- data/frameworks/sproutcore/tests/controllers/object.rhtml +2 -2
- data/frameworks/sproutcore/views/collection/collection.js +122 -68
- data/frameworks/sproutcore/views/collection/source_list.js +5 -0
- data/frameworks/sproutcore/views/field/text_field.js +7 -1
- data/frameworks/sproutcore/views/inline_text_field.js +397 -0
- data/frameworks/sproutcore/views/label.js +78 -68
- data/frameworks/sproutcore/views/list_item.js +184 -31
- data/frameworks/sproutcore/views/view.js +41 -9
- data/generators/client/templates/core.js +1 -1
- data/generators/client/templates/english.lproj/body.css +74 -0
- data/generators/framework/templates/core.js +1 -1
- data/lib/sproutcore/version.rb +1 -1
- metadata +5 -16
- data/clients/view_builder/builders/builder.js +0 -339
- data/clients/view_builder/builders/button.js +0 -81
- data/clients/view_builder/controllers/document.js +0 -21
- data/clients/view_builder/core.js +0 -19
- data/clients/view_builder/english.lproj/body.css +0 -77
- data/clients/view_builder/english.lproj/body.rhtml +0 -41
- data/clients/view_builder/english.lproj/controls.css +0 -0
- data/clients/view_builder/english.lproj/strings.js +0 -14
- data/clients/view_builder/main.js +0 -38
- data/clients/view_builder/tests/controllers/document.rhtml +0 -20
- data/clients/view_builder/tests/views/builder.rhtml +0 -20
- data/clients/view_builder/views/builder.js +0 -23
- data/frameworks/sproutcore/english.lproj/inline_text_editor.css +0 -21
- data/frameworks/sproutcore/views/inline_text_editor.js +0 -96
@@ -26,6 +26,11 @@ SC.SourceListView = SC.CollectionView.extend(
|
|
26
26
|
*/
|
27
27
|
contentValueKey: null,
|
28
28
|
|
29
|
+
/**
|
30
|
+
Set to YES if you want the content value to be editable.
|
31
|
+
*/
|
32
|
+
contentValueIsEditable: NO,
|
33
|
+
|
29
34
|
/**
|
30
35
|
Set to YES if you want source list items to display an icon.
|
31
36
|
|
@@ -4,6 +4,7 @@
|
|
4
4
|
// ========================================================================
|
5
5
|
|
6
6
|
require('views/field/field') ;
|
7
|
+
require('mixins/editable') ;
|
7
8
|
|
8
9
|
/**
|
9
10
|
@class
|
@@ -12,9 +13,10 @@ require('views/field/field') ;
|
|
12
13
|
for hinted values, etc.
|
13
14
|
|
14
15
|
@extends SC.FieldView
|
16
|
+
@extends SC.Editable
|
15
17
|
@author Charles Jolley
|
16
18
|
*/
|
17
|
-
SC.TextFieldView = SC.FieldView.extend(
|
19
|
+
SC.TextFieldView = SC.FieldView.extend(SC.Editable,
|
18
20
|
/** @scope SC.TextFieldView.prototype */ {
|
19
21
|
|
20
22
|
emptyElement: '<input type="text" value="" />',
|
@@ -31,6 +33,10 @@ SC.TextFieldView = SC.FieldView.extend(
|
|
31
33
|
*/
|
32
34
|
isHintShowing: false,
|
33
35
|
|
36
|
+
/**
|
37
|
+
If YES then the text field is currently editing.
|
38
|
+
*/
|
39
|
+
isEditing: NO,
|
34
40
|
|
35
41
|
// PRIVATE SUPPORT METHODS
|
36
42
|
init: function() {
|
@@ -0,0 +1,397 @@
|
|
1
|
+
// ========================================================================
|
2
|
+
// SproutCore
|
3
|
+
// copyright 2006-2008 Sprout Systems, Inc.
|
4
|
+
// ========================================================================
|
5
|
+
|
6
|
+
require('views/view') ;
|
7
|
+
require('mixins/delegate_support') ;
|
8
|
+
require('views/field/text_field') ;
|
9
|
+
require('mixins/inline_editor_delegate');
|
10
|
+
|
11
|
+
/**
|
12
|
+
@class
|
13
|
+
|
14
|
+
The inline text editor is used to display an editable area for controls
|
15
|
+
that are not always editable such as label views and source list views.
|
16
|
+
|
17
|
+
You generally will not use the inline editor directly but instead will
|
18
|
+
invoke beginEditing() and endEditing() on the views yous are
|
19
|
+
editing. If you would like to use the inline editor for your own views,
|
20
|
+
you can do that also by using the editing API described here.
|
21
|
+
|
22
|
+
h2. Using the Inline Editor in Your Own Views
|
23
|
+
|
24
|
+
If you need to use the inline editor with custom views you have written,
|
25
|
+
you will need to work with the class methods to begin, commit, and discard
|
26
|
+
editing changes.
|
27
|
+
|
28
|
+
h3. Starting the Editor
|
29
|
+
|
30
|
+
The inline editor works by positioning itself over the top of your view
|
31
|
+
with the same offset, width, and font information. As the user types, the
|
32
|
+
field will automatically resize vertically to make room for the user's text.
|
33
|
+
|
34
|
+
To activate the inline editor you must call beginEdition() with at least
|
35
|
+
the target view you want the editor to position itself on top of:
|
36
|
+
|
37
|
+
{{{
|
38
|
+
SC.InlineTextFieldView.beginEditing({
|
39
|
+
target: view, validator: validator
|
40
|
+
}) ;
|
41
|
+
}}}
|
42
|
+
|
43
|
+
You can pass a variety of options to this method to configure the inline
|
44
|
+
editor behavior, including:
|
45
|
+
|
46
|
+
- *frame* The editors initial frame in viewport coordinates (REQ)
|
47
|
+
- *exampleElement* A DOM element to use when copying styles.
|
48
|
+
- *delegate* Optional delegate to receive update notices. If not passed, the target view will be treated as the delegate. (REQ)
|
49
|
+
- *value* The initial value of the edit field. If not passed, the value property of the target view will be used instead.
|
50
|
+
- *multiline* If YES then the hitting return will add to the value instead of exiting the inline editor.
|
51
|
+
- *selectedRange* The range of text that should be selected. If omitted, then the insertion point will be placed at the end of the value.
|
52
|
+
- *commitOnBlur* If YES then bluring will commit the value, otherwise it will discard the current value. Defaults to YES.
|
53
|
+
- *validator* Optional validator will be attached to the field.
|
54
|
+
|
55
|
+
If the inline editor is currently in use elsewhere, it will automatically
|
56
|
+
close itself over there and begin editing for your view instead. The
|
57
|
+
editor expects your source view to implement the InlineTextFieldViewDelegate
|
58
|
+
protocol.
|
59
|
+
|
60
|
+
h2. Commiting or Discarding Changes
|
61
|
+
|
62
|
+
Normally the editor will automatically commit or discard its changes
|
63
|
+
whenever the user exits the edit mode. If you need to force the editor to
|
64
|
+
end editing, you can do so by calling commitEditing() or discardEditing():
|
65
|
+
|
66
|
+
{{{
|
67
|
+
SC.InlineTextFieldView.commitEditing();
|
68
|
+
SC.InlineTextFieldView.discardEditing();
|
69
|
+
}}}
|
70
|
+
|
71
|
+
Both methods will try to end the editing context and will call the
|
72
|
+
relevent delegate methods on the delegate you passed to beginEditing().
|
73
|
+
|
74
|
+
Note that it is possible an editor may not be able to commit editing
|
75
|
+
changes because either the delegate disallowed it or because its validator
|
76
|
+
failed. In this case commitEditing() will return NO. If you want to
|
77
|
+
end editing anyway, you can discard the editing changes instead by calling
|
78
|
+
discardEditing(). This method will generally suceed unless your delegate
|
79
|
+
refuses it as well.
|
80
|
+
|
81
|
+
@extends SC.View
|
82
|
+
@extends SC.DelegateSupport
|
83
|
+
@extends SC.InlineEditorDelegate
|
84
|
+
@since SproutCore 1.0
|
85
|
+
*/
|
86
|
+
SC.InlineTextFieldView = SC.View.extend(SC.DelegateSupport, SC.InlineEditorDelegate,
|
87
|
+
/** @scope SC.InlineTextFieldView.prototype */ {
|
88
|
+
|
89
|
+
/**
|
90
|
+
Invoked by the class method to begin editing on an inline editor.
|
91
|
+
|
92
|
+
You generally should call the class method beginEditing() instead of
|
93
|
+
this one since it will make sure to create and use the shared editor
|
94
|
+
instance.
|
95
|
+
|
96
|
+
@params options {Hash} hash of options for editing
|
97
|
+
@returns {Boolean} YES if editor began editing, NO if it failed.
|
98
|
+
*/
|
99
|
+
beginEditing: function(options) {
|
100
|
+
|
101
|
+
// end existing editing if necessary
|
102
|
+
this.beginPropertyChanges();
|
103
|
+
if (this.get('isEditing') && !this.blurEditor()) {
|
104
|
+
this.endPropertyChanges(); return NO ;
|
105
|
+
}
|
106
|
+
|
107
|
+
this._frame = options.frame ;
|
108
|
+
this._exampleElement = options.exampleElement ;
|
109
|
+
this._delegate = options.delegate ;
|
110
|
+
|
111
|
+
if (!this._frame || !this._delegate) {
|
112
|
+
throw "At least frame and delegate options are required for inline editor";
|
113
|
+
}
|
114
|
+
|
115
|
+
this._originalValue = options.value || '' ;
|
116
|
+
this._multiline = (options.multiline !== undefined) ? options.multiline : NO ;
|
117
|
+
this._commitOnBlur = (options.commitOnBlur !== undefined) ? options.commitOnBlur : YES ;
|
118
|
+
|
119
|
+
// set field values
|
120
|
+
var field = this.outlet('field') ;
|
121
|
+
field.set('validator', options.validator) ;
|
122
|
+
field.set('value', this._originalValue) ;
|
123
|
+
field.set('selectedRange', options.selectedRange || { start: this._originalValue.length, length: 0 }) ;
|
124
|
+
|
125
|
+
this.set('isEditing', YES) ;
|
126
|
+
|
127
|
+
// add to window.
|
128
|
+
SC.window.appendChild(this) ;
|
129
|
+
|
130
|
+
// get style for view.
|
131
|
+
this.updateViewStyle() ;
|
132
|
+
|
133
|
+
var del = this._delegate ;
|
134
|
+
this.invokeDelegateMethod(del, 'inlineEditorWillBeginEditing', this) ;
|
135
|
+
this.resizeToFit(field.getFieldValue()) ;
|
136
|
+
|
137
|
+
// allow notifications to go
|
138
|
+
this.endPropertyChanges() ;
|
139
|
+
|
140
|
+
// and become first responder
|
141
|
+
this.field.becomeFirstResponder() ;
|
142
|
+
|
143
|
+
this.invokeDelegateMethod(del, 'inlineEditorDidBeginEditing', this) ;
|
144
|
+
},
|
145
|
+
|
146
|
+
|
147
|
+
/**
|
148
|
+
Tries to commit the current value of the field and end editing.
|
149
|
+
|
150
|
+
Do not use this method, use the class method instead.
|
151
|
+
*/
|
152
|
+
commitEditing: function() {
|
153
|
+
// try to validate field. If it fails, return false.
|
154
|
+
var field = this.outlet('field') ;
|
155
|
+
if (!$ok(field.validateSubmit())) return NO ;
|
156
|
+
return this._endEditing(field.get('value')) ;
|
157
|
+
},
|
158
|
+
|
159
|
+
/**
|
160
|
+
Tries to discard the current value of the field and end editing.
|
161
|
+
|
162
|
+
Do not use this method, use the class method instead.
|
163
|
+
*/
|
164
|
+
discardEditing: function() {
|
165
|
+
return this._endEditing(this._originalValue) ;
|
166
|
+
},
|
167
|
+
|
168
|
+
/**
|
169
|
+
Invoked whenever the editor loses (or should lose) first responder
|
170
|
+
status to commit or discard editing.
|
171
|
+
*/
|
172
|
+
blurEditor: function() {
|
173
|
+
if (!this.get('isEditing')) return YES ;
|
174
|
+
return (this._commitOnBlur) ? this.commitEditing() : this.discardEditing();
|
175
|
+
},
|
176
|
+
|
177
|
+
/** @private
|
178
|
+
Called by commitEditing and discardEditing to actually end editing.
|
179
|
+
|
180
|
+
@returns {Boolean} NO if editing did not exit
|
181
|
+
*/
|
182
|
+
_endEditing: function(finalValue) {
|
183
|
+
if (!this.get('isEditing')) return YES ;
|
184
|
+
|
185
|
+
// get permission from the delegate.
|
186
|
+
var del = this._delegate ;
|
187
|
+
if (!this.invokeDelegateMethod(del, 'inlineEditorShouldEndEditing', this, finalValue)) return NO ;
|
188
|
+
|
189
|
+
// OK, we are allowed to end editing. Notify delegate of final value
|
190
|
+
// and clean up.
|
191
|
+
console.log('applying finalValue: %@'.fmt(finalValue)) ;
|
192
|
+
this.invokeDelegateMethod(del, 'inlineEditorDidEndEditing', this, finalValue) ;
|
193
|
+
|
194
|
+
// cleanup cached values
|
195
|
+
this._originalValue = this._delegate = this._exampleElement = this._frame = null ;
|
196
|
+
this.set('isEditing', NO) ;
|
197
|
+
|
198
|
+
// resign first responder if not done already. This may call us in a
|
199
|
+
// loop but since isEditing is already NO, nothing will happen.
|
200
|
+
if (this.field.get('isFirstResponder')) this.field.resignFirstResponder();
|
201
|
+
if (this.get('parentNode')) this.removeFromParent() ;
|
202
|
+
|
203
|
+
return YES ;
|
204
|
+
},
|
205
|
+
|
206
|
+
/**
|
207
|
+
YES if the editor is currently visible and editing.
|
208
|
+
|
209
|
+
@type {Boolean}
|
210
|
+
*/
|
211
|
+
isEditing: NO,
|
212
|
+
|
213
|
+
/** @private */
|
214
|
+
emptyElement: [
|
215
|
+
'<div class="sc-inline-text-field-view">',
|
216
|
+
'<div class="sizer"></div>',
|
217
|
+
'<textarea class="inner-field" wrap="virtual"></textarea>',
|
218
|
+
'</div>'
|
219
|
+
].join(''),
|
220
|
+
|
221
|
+
/**
|
222
|
+
Collects the appropriate style information from the targetView to
|
223
|
+
make the inline editor appear similar.
|
224
|
+
*/
|
225
|
+
updateViewStyle: function() {
|
226
|
+
// collect font and frame from target.
|
227
|
+
var f= this._frame ;
|
228
|
+
var el = this._exampleElement ;
|
229
|
+
|
230
|
+
var styles = {
|
231
|
+
fontSize: Element.getStyle(el,'font-size'),
|
232
|
+
fontFamily: Element.getStyle(el,'font-family'),
|
233
|
+
fontWeight: Element.getStyle(el,'font-weight'),
|
234
|
+
paddingLeft: Element.getStyle(el,'padding-left'),
|
235
|
+
paddingRight: Element.getStyle(el,'padding-right'),
|
236
|
+
paddingTop: Element.getStyle(el,'padding-top'),
|
237
|
+
paddingBottom: Element.getStyle(el,'padding-bottom'),
|
238
|
+
lineHeight: Element.getStyle(el,'line-height'),
|
239
|
+
textAlign: Element.getStyle(el,'text-align')
|
240
|
+
} ;
|
241
|
+
|
242
|
+
var field = this.outlet('field') ;
|
243
|
+
var sizer = this.outlet('sizer') ;
|
244
|
+
|
245
|
+
field.setStyle(styles) ;
|
246
|
+
|
247
|
+
styles.opacity = 0 ;
|
248
|
+
sizer.setStyle(styles) ;
|
249
|
+
sizer.recacheFrames() ;
|
250
|
+
|
251
|
+
this.set('frame', f) ;
|
252
|
+
},
|
253
|
+
|
254
|
+
/**
|
255
|
+
Resizes the visible textarea to fix the actual text in the text area.
|
256
|
+
|
257
|
+
This method works by keeping a div positioned immediately beneath the
|
258
|
+
text area with an opacity of 0 that contains the same text as the
|
259
|
+
input text field itself. This is then used to calculate the required
|
260
|
+
size for the text area.
|
261
|
+
*/
|
262
|
+
resizeToFit: function(newValue)
|
263
|
+
{
|
264
|
+
var sizer = this.outlet('sizer');
|
265
|
+
var field = this.outlet('field');
|
266
|
+
|
267
|
+
// XSS attack waiting to happen... escape the form input;
|
268
|
+
var text = (newValue || '').escapeHTML();
|
269
|
+
|
270
|
+
// convert the textarea's newlines into something comparable for the sizer
|
271
|
+
// div appending a space to give a line with no text a visible height.
|
272
|
+
text = text.replace(/ /g, " ").replace(/\n/g, "<br /> ");
|
273
|
+
|
274
|
+
// get the text size
|
275
|
+
sizer.set('innerHTML', text || " ");
|
276
|
+
sizer.recacheFrames() ;
|
277
|
+
var h = sizer.get('frame').height;
|
278
|
+
this.set('frame', { height: h }) ;
|
279
|
+
},
|
280
|
+
|
281
|
+
/**
|
282
|
+
The actual text field view used for editing.
|
283
|
+
*/
|
284
|
+
field: SC.TextFieldView.extend({
|
285
|
+
|
286
|
+
// handle mouseDown event if we are currently editing so that events
|
287
|
+
// don't go any further?
|
288
|
+
mouseDown: function(e) {
|
289
|
+
arguments.callee.base.call(this, e) ;
|
290
|
+
return this.owner.get('isEditing');
|
291
|
+
},
|
292
|
+
|
293
|
+
// [Safari] if you don't take key focus away from an element before you
|
294
|
+
// remove it from the DOM key events are no longer sent to the browser.
|
295
|
+
willRemoveFromParent: function() {
|
296
|
+
this.get('rootElement').blur();
|
297
|
+
},
|
298
|
+
|
299
|
+
// ask owner to end editing.
|
300
|
+
willLoseFirstResponder: function()
|
301
|
+
{
|
302
|
+
// should have been covered by willRemoveFromParent, but this was needed
|
303
|
+
// too.
|
304
|
+
this.get('rootElement').blur();
|
305
|
+
return this.owner.blurEditor() ;
|
306
|
+
},
|
307
|
+
|
308
|
+
// invoked when the user presses escape. Returns true to ignore
|
309
|
+
// keystroke
|
310
|
+
cancel: function() {
|
311
|
+
this.owner.discardEditing();
|
312
|
+
return YES;
|
313
|
+
},
|
314
|
+
|
315
|
+
// do it here instead of waiting on the binding to make sure the UI
|
316
|
+
// updates immediately.
|
317
|
+
fieldValueDidChange: function(partialChange) {
|
318
|
+
arguments.callee.base.call(this, partialChange) ;
|
319
|
+
this.owner.resizeToFit(this.getFieldValue()) ;
|
320
|
+
},
|
321
|
+
|
322
|
+
// invoked when the user presses return. If this is a multi-line field,
|
323
|
+
// then allow the newine to proceed. Otherwise, try to commit the
|
324
|
+
// edit.
|
325
|
+
insertNewline: function(evt) {
|
326
|
+
if (this.owner_multiline) {
|
327
|
+
return arguments.callee.base.call(this, evt) ;
|
328
|
+
} else {
|
329
|
+
this.owner.commitEditing() ;
|
330
|
+
return YES ;
|
331
|
+
}
|
332
|
+
}
|
333
|
+
|
334
|
+
}).outletFor('.inner-field?'),
|
335
|
+
|
336
|
+
sizer: SC.View.outletFor('.sizer?')
|
337
|
+
|
338
|
+
});
|
339
|
+
|
340
|
+
|
341
|
+
SC.InlineTextFieldView.mixin(
|
342
|
+
/** @static SC.InlineTextFieldView */ {
|
343
|
+
/** Call this method to make the inline editor begin editing for your view.
|
344
|
+
|
345
|
+
If the inline editor is already being used for another value it will
|
346
|
+
try to dismiss itself from the other editor and attach itself to the
|
347
|
+
new view instead. If this process fails for some reason (for example
|
348
|
+
if the other view did not allow the view to end editing) then this
|
349
|
+
method will return false.
|
350
|
+
|
351
|
+
You should pass a set of options that at least includes the target
|
352
|
+
view. See class definition for options.
|
353
|
+
|
354
|
+
@params options {Hash} hash of options for editing
|
355
|
+
@returns {Boolean} YES if editor began editing, NO if it failed.
|
356
|
+
*/
|
357
|
+
beginEditing: function(options) {
|
358
|
+
if (!this.sharedEditor) this.sharedEditor = this.create() ;
|
359
|
+
return this.sharedEditor.beginEditing(options) ;
|
360
|
+
},
|
361
|
+
|
362
|
+
/** Save the current value of the inline editor and exit edit mode.
|
363
|
+
|
364
|
+
If the inline editor is being used it will try to end the editing and
|
365
|
+
close. If the inline editor could not end for some reason (for example
|
366
|
+
if the delegate did not allow the editing to end) then this method will
|
367
|
+
return NO.
|
368
|
+
|
369
|
+
@returns {Boolean} YES if the inline editor ended or no edit was in
|
370
|
+
progress.
|
371
|
+
*/
|
372
|
+
commitEditing: function() {
|
373
|
+
return (this.sharedEditor) ? this.sharedEditor.commitEditing() : YES ;
|
374
|
+
},
|
375
|
+
|
376
|
+
/** Discard the current value of the inline editor and exit edit mode.
|
377
|
+
|
378
|
+
If the inline editor is in use, this method will try to end the editing,
|
379
|
+
restoring the original value of the target view. If the inline editor
|
380
|
+
could not end for some reason (for example if the delegate did not
|
381
|
+
allow editing to end) then this method will return NO.
|
382
|
+
|
383
|
+
@returns {Boolean} YES if the inline editor ended or no edit was in progress.
|
384
|
+
*/
|
385
|
+
discardEditing: function() {
|
386
|
+
return (this.sharedEditor) ? this.sharedEditor.discardEditing() : YES ;
|
387
|
+
},
|
388
|
+
|
389
|
+
/**
|
390
|
+
The current shared inline editor. This property will often remain NULL
|
391
|
+
until you actually begin editing for the first time.
|
392
|
+
|
393
|
+
@type {SC.InlineTextFieldView}
|
394
|
+
*/
|
395
|
+
sharedEditor: null
|
396
|
+
|
397
|
+
}) ;
|
@@ -6,6 +6,8 @@
|
|
6
6
|
require('views/view') ;
|
7
7
|
require('mixins/control') ;
|
8
8
|
require('mixins/delegate_support');
|
9
|
+
require('views/inline_text_field');
|
10
|
+
require('mixins/inline_editor_delegate');
|
9
11
|
|
10
12
|
/**
|
11
13
|
@class
|
@@ -18,10 +20,11 @@ require('mixins/delegate_support');
|
|
18
20
|
@extends SC.View
|
19
21
|
@extends SC.Control
|
20
22
|
@extends SC.DelegateSupport
|
21
|
-
@
|
22
|
-
@
|
23
|
+
@extends SC.InlineEditorDelegate
|
24
|
+
@extends SC.Editable
|
25
|
+
@since SproutCore 1.0
|
23
26
|
*/
|
24
|
-
SC.LabelView = SC.View.extend(SC.DelegateSupport, SC.Control,
|
27
|
+
SC.LabelView = SC.View.extend(SC.DelegateSupport, SC.Control, SC.InlineEditorDelegate,
|
25
28
|
/** @scope SC.LabelView.prototype */ {
|
26
29
|
|
27
30
|
emptyElement: '<span class="sc-label-view"></span>',
|
@@ -120,6 +123,15 @@ SC.LabelView = SC.View.extend(SC.DelegateSupport, SC.Control,
|
|
120
123
|
*/
|
121
124
|
localize: false,
|
122
125
|
|
126
|
+
/**
|
127
|
+
Validator to use during inline editing.
|
128
|
+
|
129
|
+
If you have set isEditing to YES, then any validator you set on this
|
130
|
+
property will be used when the label view is put into edit mode.
|
131
|
+
|
132
|
+
@type {SC.Validator}
|
133
|
+
*/
|
134
|
+
validator: null,
|
123
135
|
|
124
136
|
/**
|
125
137
|
Event dispatcher callback.
|
@@ -128,85 +140,83 @@ SC.LabelView = SC.View.extend(SC.DelegateSupport, SC.Control,
|
|
128
140
|
@param {DOMMouseEvent} evt DOM event
|
129
141
|
|
130
142
|
*/
|
131
|
-
doubleClick: function( evt )
|
132
|
-
{
|
133
|
-
this.beginInlineEdit();
|
134
|
-
},
|
143
|
+
doubleClick: function( evt ) { return this.beginEditing(); },
|
135
144
|
|
136
145
|
|
137
146
|
/**
|
138
|
-
Opens the inline text editor (closing it if it was already open for
|
147
|
+
Opens the inline text editor (closing it if it was already open for
|
148
|
+
another view).
|
139
149
|
|
140
|
-
@return
|
150
|
+
@return {Boolean} YES if did begin editing
|
141
151
|
*/
|
142
|
-
|
152
|
+
beginEditing: function()
|
143
153
|
{
|
144
|
-
if (
|
145
|
-
if (
|
146
|
-
|
147
|
-
this.
|
148
|
-
this.
|
149
|
-
this.
|
150
|
-
SC.
|
151
|
-
|
154
|
+
if (this.get('isEditing')) return YES ;
|
155
|
+
if (!this.get('isEditable')) return NO ;
|
156
|
+
|
157
|
+
var value = this.get('value') || '' ;
|
158
|
+
var f = this.convertFrameToView(this.get('frame'), null) ;
|
159
|
+
var el = this.rootElement;
|
160
|
+
SC.InlineTextFieldView.beginEditing({
|
161
|
+
frame: f,
|
162
|
+
delegate: this,
|
163
|
+
exampleElement: el,
|
164
|
+
value: value,
|
165
|
+
multiline: NO,
|
166
|
+
validator: this.get('validator')
|
167
|
+
});
|
152
168
|
},
|
169
|
+
|
153
170
|
/**
|
154
|
-
|
171
|
+
Cancels the current inline editor and then exits editor.
|
155
172
|
|
156
|
-
@return
|
173
|
+
@return {Boolean} NO if the editor could not exit.
|
157
174
|
*/
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
// if there were changes, then commit them...
|
163
|
-
if ( SC.inlineTextEditor.field.get('value') != this.get('value') )
|
164
|
-
{
|
165
|
-
this._inlineEditValue = SC.inlineTextEditor.field.get('value') ;
|
166
|
-
this._closeInlineEditor(false) ;
|
167
|
-
}
|
168
|
-
else
|
169
|
-
{
|
170
|
-
this.cancelInlineEdit() ;
|
171
|
-
}
|
175
|
+
discardEditing: function() {
|
176
|
+
if (!this.get('isEditing')) return YES ;
|
177
|
+
return SC.InlineTextFieldView.discardEditing() ;
|
172
178
|
},
|
173
179
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
+
/**
|
181
|
+
Commits current inline editor and then exits editor.
|
182
|
+
|
183
|
+
@return {Boolean} NO if the editor could not exit
|
184
|
+
*/
|
185
|
+
commitEditing: function() {
|
186
|
+
if (!this.get('isEditing')) return YES ;
|
187
|
+
return SC.InlineTextFieldView.commitEditing() ;
|
180
188
|
},
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
{
|
188
|
-
this.set('value',this._inlineEditValue) ;
|
189
|
-
this.commitInlineEdit();
|
190
|
-
}
|
191
|
-
else
|
192
|
-
{
|
193
|
-
this._valueDidChange() ; // restore value.
|
194
|
-
}
|
189
|
+
|
190
|
+
/** @private
|
191
|
+
Set editing to true so edits will no longer be allowed.
|
192
|
+
*/
|
193
|
+
inlineEditorWillBeginEditing: function(inlineEditor) {
|
194
|
+
this.set('isEditing', YES);
|
195
195
|
},
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
196
|
+
|
197
|
+
/** @private
|
198
|
+
Hide the label view while the inline editor covers it.
|
199
|
+
*/
|
200
|
+
inlineEditorDidBeginEditing: function(inlineEditor) {
|
201
|
+
this._oldOpacity = this.getStyle('opacity') ;
|
202
|
+
this.setStyle({ opacity: 0.0 }) ;
|
203
|
+
},
|
204
|
+
|
205
|
+
/** @private
|
206
|
+
Could check with a validator someday...
|
207
|
+
*/
|
208
|
+
inlineEditorShouldEndEditing: function(inlineEditor, finalValue) {
|
209
|
+
return YES ;
|
210
|
+
},
|
211
|
+
|
212
|
+
/** @private
|
213
|
+
Update the field value and make it visible again.
|
214
|
+
*/
|
215
|
+
inlineEditorDidEndEditing: function(inlineEditor, finalValue) {
|
216
|
+
this.setIfChanged('value', finalValue) ;
|
217
|
+
this.setStyle({ opacity: this._oldOpacity }) ;
|
218
|
+
this._oldOpacity = null ;
|
219
|
+
this.set('isEditing', NO) ;
|
210
220
|
},
|
211
221
|
|
212
222
|
/**
|