-
Notifications
You must be signed in to change notification settings - Fork 291
/
text_field_support.js
172 lines (137 loc) · 4.45 KB
/
text_field_support.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// ==========================================================================
// Project: SproutCore - JavaScript Application Framework
// Copyright: ©2006-2011 Strobe Inc. and contributors.
// ©2008-2011 Apple Inc. All rights reserved.
// License: Licensed under MIT license (see license.js)
// ==========================================================================
sc_require('views/template');
/** @class */
SC.TextField = SC.TemplateView.extend(
/** @scope SC.TextField.prototype */ {
classNames: ['sc-text-field'],
// we can't use bindAttr because of a race condition:
//
// when `value` is set, the bindAttr observer immediately calls
// `get` in order to persist it to the DOM, but because we made
// the `value` property idempotent, when it gets called by
// bindAttr, it fetches the not-yet-updated value from the DOM
// and returns it.
//
// In short, because we need to be able to catch changes to the
// DOM made directly, we cannot also rely on bindAttr to update
// the property: a chicken-and-egg problem.
template: SC.Handlebars.compile('<input type="text">'),
didCreateLayer: function() {
var self = this;
var input = this.$('input');
input.val(this._value);
SC.Event.add(input, 'focus', this, this.focusIn);
SC.Event.add(input, 'blur', this, this.focusOut);
this.$('input').bind('change', function() {
self.domValueDidChange(SC.$(this));
});
},
/**
The problem this property is trying to solve is twofold:
1. Make it possible to set the value of a text field that has
not yet been inserted into the DOM
2. Make sure that `value` properly reflects changes made directly
to the element's `value` property.
In order to achieve (2), we need to make the property volatile,
so that SproutCore will call the getter no matter what if get()
is called.
In order to achieve (1), we need to store a local cache of the
value, so that SproutCore can set the proper value as soon as
the underlying DOM element is created.
*/
value: function(key, value) {
var input = this.$('input');
if (value !== undefined) {
this._value = value;
input.val(value);
} else if (input.length) {
this._value = value = input.val();
} else {
value = this._value;
}
return value;
}.property().idempotent(),
domValueDidChange: function(jquery) {
this.set('value', jquery.val());
},
focusIn: function(event) {
this.becomeFirstResponder();
this.tryToPerform('focus', event);
},
focusOut: function(event) {
this.resignFirstResponder();
this.tryToPerform('blur', event);
},
willLoseFirstResponder: function() {
this.notifyPropertyChange('value');
},
keyUp: function(evt) {
this.domValueDidChange(this.$('input'));
if (evt.keyCode === 13) {
return this.insertNewline(evt);
} else if (evt.keyCode === 27) {
return this.cancel(evt);
}
return true;
}
});
SC.TextFieldSupport = /** @scope SC.TextFieldSupport */{
/** @private
Used internally to store value because the layer may not exist
*/
_value: null,
/**
@type String
@default null
*/
value: function(key, value) {
var input = this.$('input');
if (value !== undefined) {
this._value = value;
input.val(value);
} else {
if (input.length > 0) {
value = this._value = input.val();
} else {
value = this._value;
}
}
return value;
}.property().idempotent(),
didCreateLayer: function() {
var input = this.$('input');
input.val(this._value);
SC.Event.add(input, 'focus', this, this.focusIn);
SC.Event.add(input, 'blur', this, this.focusOut);
},
focusIn: function(event) {
this.becomeFirstResponder();
this.tryToPerform('focus', event);
},
focusOut: function(event) {
this.resignFirstResponder();
this.tryToPerform('blur', event);
},
/** @private
Make sure our input value is synced with any bindings.
In some cases, such as auto-filling, a value can get
changed without an event firing. We could do this
on focusOut, but blur can potentially get called
after other events.
*/
willLoseFirstResponder: function() {
this.notifyPropertyChange('value');
},
keyUp: function(event) {
if (event.keyCode === 13) {
return this.tryToPerform('insertNewline', event);
} else if (event.keyCode === 27) {
return this.tryToPerform('cancel', event);
}
}
};