Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(389)

Side by Side Diff: chrome/browser/resources/options2/editable_text_field.js

Issue 10827283: This updates the StaticIP configuration UI to match new mocks. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Cleanup for review Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 cr.define('options', function() {
6 var EditableTextField = cr.ui.define('div');
7
8 /**
9 * Decorates an element as an editable text field.
10 * @param {!HTMLElement} el The element to decorate.
11 */
12 EditableTextField.decorate = function(el) {
13 el.__proto__ = EditableTextField.prototype;
14 el.decorate();
15 };
16
17 EditableTextField.prototype = {
18 __proto__: HTMLDivElement.prototype,
19
20 /**
21 * The actual input element in this field.
22 * @type {HTMLElement}
23 * @private
24 */
25 editField_: null,
26
27 /**
28 * The static text displayed when this field isn't editable.
29 * @type {HTMLElement}
30 * @private
31 */
32 staticText_: null,
33
34 /**
35 * The data model for this field.
36 * @type {Object}
37 * @private
38 */
39 model_: null,
40
41 /**
42 * Whether or not the current edit should be considered canceled, rather
43 * than committed, when editing ends.
44 * @type {boolean}
45 * @private
46 */
47 editCanceled_: true,
48
49 /** @inheritDoc */
50 decorate: function() {
51 this.classList.add('editable-text-field');
52
53 if (!this.editField_)
54 this.createEditableTextCell();
55
56 if (this.hasAttribute('i18n-placeholder-text')) {
57 var identifier = this.getAttribute('i18n-placeholder-text');
58 var localizedText = loadTimeData.getString(identifier);
Dan Beam 2012/08/11 01:38:02 do you also need to check if loadTimeData.valueExi
Greg Spencer (Chromium) 2012/08/13 19:20:04 No, you're right, I should probably check that too
Dan Beam 2012/08/13 19:48:55 If you assume it's there, just use the value. loa
59 if (localizedText)
60 this.setAttribute('placeholder-text', localizedText);
61 }
62
63 var self = this;
Dan Beam 2012/08/11 01:38:02 I think .bind() is preferred, i.e. this.editFie
Greg Spencer (Chromium) 2012/08/13 19:20:04 OK, thanks. Wasn't sure about bind here. Indent i
64 this.addEventListener('keydown', this.handleKeyDown_);
65 this.editField_.addEventListener('focus',
66 function(e) { self.handleFocus_(e); });
67 this.editField_.addEventListener('blur',
68 function(e) { self.handleBlur_(e); });
69 this.checkForEmpty_();
70 },
71
72 /**
73 * Indicates that this field has no value in the model, and the placeholder
74 * text (if any) should be shown.
75 * @type {boolean}
76 */
77 get empty() {
78 return this.hasAttribute('empty');
79 },
80
81 /**
82 * The placeholder text to be used when the model or its value is empty.
83 * @type {string}
84 */
85 get placeholderText() {
86 return this.getAttribute('placeholder-text');
87 },
88 set placeholderText(text) {
89 if (text) {
Dan Beam 2012/08/11 01:38:02 nit: no curlies since all the bodys and conditiona
Greg Spencer (Chromium) 2012/08/13 19:20:04 Done.
90 this.setAttribute('placeholder-text', text);
91 } else {
92 this.removeAttribute('placeholder-text');
93 }
94 this.checkForEmpty_();
95 },
96
97 /**
98 * Returns the input element in this text field.
99 * @type {HTMLElement} The element that is the actual input field.
100 */
101 get editField() {
102 return this.editField_;
103 },
104
105 /**
106 * Whether the user is currently editing the list item.
107 * @type {boolean}
108 */
109 get editing() {
110 return this.hasAttribute('editing');
111 },
112 set editing(editing) {
113 if (this.editing == editing)
114 return;
115
116 if (editing)
117 this.setAttribute('editing', '');
118 else
119 this.removeAttribute('editing');
120
121 if (editing) {
122 this.editCanceled_ = false;
123
124 if (this.empty) {
125 this.removeAttribute('empty');
126 if (this.editField)
127 this.editField.value = '';
128 }
129 if (this.editField) {
130 this.editField.focus();
131 this.editField.select();
132 }
133 } else {
134 if (!this.editCanceled_ && this.hasBeenEdited &&
135 this.currentInputIsValid) {
136 this.updateStaticValues_();
137 cr.dispatchSimpleEvent(this, 'commitedit', true);
138 } else {
139 this.resetEditableValues_();
140 cr.dispatchSimpleEvent(this, 'canceledit', true);
141 }
142 this.checkForEmpty_();
143 }
144 },
145
146 /**
147 * Whether the item is editable.
148 * @type {boolean}
149 */
150 get editable() {
151 return this.hasAttribute('editable');
152 },
153 set editable(editable) {
154 if (this.editable == editable)
155 return;
156
157 if (editable)
158 this.setAttribute('editable', '');
159 else
160 this.removeAttribute('editable');
161 this.editable_ = editable;
162 },
163
164 /**
165 * The data model for this field.
166 * @type {Object}
167 */
168 get model() {
169 return this.model_;
170 },
171 set model(model) {
172 this.model_ = model;
173 this.checkForEmpty_(); // Also updates the editField value
Dan Beam 2012/08/11 01:38:02 nit: . at end (even if it's a sentence fragment, I
Greg Spencer (Chromium) 2012/08/13 19:20:04 Sorry, that was sloppy: I usually like that too.
174 this.updateStaticValues_();
175 },
176
177 /**
178 * The HTML element that should have focus initially when editing starts,
179 * if a specific element wasn't clicked.
Dan Beam 2012/08/11 01:38:02 nit: you can leave this \n here between sentences
Greg Spencer (Chromium) 2012/08/13 19:20:04 Done, here and below.
180 * Defaults to the first <input> element; can be overridden by subclasses if
181 * a different element should be focused.
182 * @type {HTMLElement}
Dan Beam 2012/08/11 01:38:02 @type {?HTMLElement} is probably more appropriate
Greg Spencer (Chromium) 2012/08/13 19:20:04 Done.
183 */
184 get initialFocusElement() {
185 return this.querySelector('input');
186 },
187
188 /**
189 * Whether the input in currently valid to submit. If this returns false
190 * when editing would be submitted, either editing will not be ended,
191 * or it will be cancelled, depending on the context.
192 * Can be overridden by subclasses to perform input validation.
193 * @type {boolean}
194 */
195 get currentInputIsValid() {
196 return true;
197 },
198
199 /**
200 * Returns true if the item has been changed by an edit.
201 * Can be overridden by subclasses to return false when nothing has changed
202 * to avoid unnecessary commits.
203 * @type {boolean}
204 */
205 get hasBeenEdited() {
206 return true;
207 },
208
209 /**
210 * Mutates the input during a successful commit. Can be overridden to
Dan Beam 2012/08/11 01:38:02 nit: 1 \s between sentences
Greg Spencer (Chromium) 2012/08/13 19:20:04 Done.
211 * provide a way to "clean up" valid input so that it conforms to a
212 * desired format. Will only be called when commit succeeds for valid
213 * input, or when the model is set.
214 * @param {Object} value Input to be mutated.
215 * @return {Object} mutated value.
216 */
217 mutateInput: function(value) {
218 return value;
219 },
220
221 /**
222 * Returns a div containing an <input>, as well as static text.
223 * @param {string} text The text of the cell.
224 * @return {HTMLElement} The HTML element for the cell.
225 * @private
226 */
227 createEditableTextCell: function(text) {
228 var container = this.ownerDocument.createElement('div');
229
230 var textEl = this.ownerDocument.createElement('div');
231 textEl.className = 'static-text';
232 textEl.textContent = text;
233 textEl.setAttribute('displaymode', 'static');
234 this.appendChild(textEl);
Dan Beam 2012/08/11 01:38:02 do you ever only call this method once? might be
Greg Spencer (Chromium) 2012/08/13 19:20:04 Yes, it's only called once (well, it's called only
235 this.staticText_ = textEl;
236
237 var inputEl = this.ownerDocument.createElement('input');
238 inputEl.className = 'editable-text';
239 inputEl.type = 'text';
240 inputEl.value = text;
241 inputEl.setAttribute('displaymode', 'edit');
242 inputEl.staticVersion = textEl;
243 this.appendChild(inputEl);
244 this.editField_ = inputEl;
245
246 return container;
247 },
248
249 /**
250 * Resets the editable version of any controls created by
251 * createEditableTextCell to match the static text.
252 * @private
253 */
254 resetEditableValues_: function() {
255 var editField = this.editField_;
256 var staticLabel = editField.staticVersion;
257 if (!staticLabel)
258 return;
259
260 if (editField.tagName == 'INPUT')
Dan Beam 2012/08/11 01:38:02 nit: if (editField instanceof HTMLInputElement) IM
Greg Spencer (Chromium) 2012/08/13 19:20:04 Done.
261 editField.value = staticLabel.textContent;
262
263 editField.setCustomValidity('');
264 },
265
266 /**
267 * Sets the static version of any controls created by createEditableTextCell
268 * to match the current value of the editable version. Called on commit so
269 * that there's no flicker of the old value before the model updates. Also
270 * updates the model's value with the mutated value of the edit field.
271 * @private
272 */
273 updateStaticValues_: function() {
274 var editField = this.editField_;
275 var staticLabel = editField.staticVersion;
276 if (!staticLabel)
277 return;
278
279 if (editField.tagName == 'INPUT') {
280 staticLabel.textContent = editField.value;
281 this.model_.value = this.mutateInput(editField.value);
282 }
283 },
284
285 /**
286 * Checks to see if the model or its value are empty. If they are, then set
287 * the edit field to the placeholder text, if any, and if not, set it to the
288 * model's value.
289 * @private
290 */
291 checkForEmpty_: function() {
292 var editField = this.editField_;
293 if (!editField)
294 return;
295
296 if (!this.model_ || !this.model_.value) {
297 this.setAttribute('empty', '');
298 editField.value = this.placeholderText ? this.placeholderText : '';
299 } else {
300 this.removeAttribute('empty');
301 editField.value = this.model_.value;
302 }
303 },
304
305 /**
306 * Called when this widget receives focus.
307 * @param {Event} e the focus event.
308 * @private
309 */
310 handleFocus_: function(e) {
311 if (this.editing)
312 return;
313 this.editing = true;
314 if (this.editField_)
315 this.editField_.focus();
316 },
317
318 /**
319 * Called when this widget loses focus.
320 * @param {Event} e the blur event.
321 * @private
322 */
323 handleBlur_: function(e) {
324 if (!this.editing)
325 return;
326 this.editing = false;
327 },
328
329 /**
330 * Called when a key is pressed. Handles committing and canceling edits.
331 * @param {Event} e The key down event.
332 * @private
333 */
334 handleKeyDown_: function(e) {
335 if (!this.editing)
336 return;
337
338 var endEdit = false;
339 switch (e.keyIdentifier) {
340 case 'U+001B': // Esc
341 this.editCanceled_ = true;
342 endEdit = true;
343 break;
344 case 'Enter':
345 if (this.currentInputIsValid)
346 endEdit = true;
347 break;
348 }
349
350 if (endEdit) {
351 // Blurring will trigger the edit to end.
352 this.ownerDocument.activeElement.blur();
353 // Make sure that handled keys aren't passed on and double-handled.
354 // (e.g., esc shouldn't both cancel an edit and close a subpage)
355 e.stopPropagation();
356 }
357 },
358 };
359
360 /**
361 * Takes care of committing changes to EditableTextField items when the
362 * window loses focus.
363 */
364 function handleWindowBlurs() {
365 window.addEventListener('blur', function(e) {
366 var itemAncestor = findAncestor(document.activeElement, function(node) {
367 return node instanceof EditableTextField;
368 });
369 if (itemAncestor)
370 document.activeElement.blur();
371 });
372 }
373 handleWindowBlurs();
Dan Beam 2012/08/11 01:38:02 why are you making this a function then calling it
Greg Spencer (Chromium) 2012/08/13 19:20:04 Yeah, good point. It was a pattern I saw in some
374
375 return {
376 EditableTextField: EditableTextField,
377 };
378 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698