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

Side by Side Diff: chrome/browser/resources/options/inline_editable_list.js

Issue 7003007: Apply content-security-policy to the HTML options page. This is a (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years, 7 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) 2010 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 const DeletableItem = options.DeletableItem;
7 const DeletableItemList = options.DeletableItemList;
8
9 /**
10 * Creates a new list item with support for inline editing.
11 * @constructor
12 * @extends {options.DeletableListItem}
13 */
14 function InlineEditableItem() {
15 var el = cr.doc.createElement('div');
16 InlineEditableItem.decorate(el);
17 return el;
18 }
19
20 /**
21 * Decorates an element as a inline-editable list item. Note that this is
22 * a subclass of DeletableItem.
23 * @param {!HTMLElement} el The element to decorate.
24 */
25 InlineEditableItem.decorate = function(el) {
26 el.__proto__ = InlineEditableItem.prototype;
27 el.decorate();
28 };
29
30 InlineEditableItem.prototype = {
31 __proto__: DeletableItem.prototype,
32
33 /**
34 * Whether or not this item can be edited.
35 * @type {boolean}
36 * @private
37 */
38 editable_: true,
39
40 /**
41 * Whether or not this is a placeholder for adding a new item.
42 * @type {boolean}
43 * @private
44 */
45 isPlaceholder_: false,
46
47 /**
48 * Fields associated with edit mode.
49 * @type {array}
50 * @private
51 */
52 editFields_: null,
53
54 /**
55 * Whether or not the current edit should be considered cancelled, rather
56 * than committed, when editing ends.
57 * @type {boolean}
58 * @private
59 */
60 editCancelled_: true,
61
62 /**
63 * The editable item corresponding to the last click, if any. Used to decide
64 * initial focus when entering edit mode.
65 * @type {HTMLElement}
66 * @private
67 */
68 editClickTarget_: null,
69
70 /** @inheritDoc */
71 decorate: function() {
72 DeletableItem.prototype.decorate.call(this);
73
74 this.editFields_ = [];
75 this.addEventListener('mousedown', this.handleMouseDown_.bind(this));
76 this.addEventListener('keydown', this.handleKeyDown_.bind(this));
77 this.addEventListener('leadChange', this.handleLeadChange_);
78 },
79
80 /** @inheritDoc */
81 selectionChanged: function() {
82 this.updateEditState();
83 },
84
85 /**
86 * Called when this element gains or loses 'lead' status. Updates editing
87 * mode accordingly.
88 * @private
89 */
90 handleLeadChange_: function() {
91 this.updateEditState();
92 },
93
94 /**
95 * Updates the edit state based on the current selected and lead states.
96 */
97 updateEditState: function() {
98 if (this.editable)
99 this.editing = this.selected && this.lead;
100 },
101
102 /**
103 * Whether the user is currently editing the list item.
104 * @type {boolean}
105 */
106 get editing() {
107 return this.hasAttribute('editing');
108 },
109 set editing(editing) {
110 if (this.editing == editing)
111 return;
112
113 if (editing)
114 this.setAttribute('editing', '');
115 else
116 this.removeAttribute('editing');
117
118 if (editing) {
119 this.editCancelled_ = false;
120
121 cr.dispatchSimpleEvent(this, 'edit', true);
122
123 var focusElement = this.editClickTarget_ || this.initialFocusElement;
124 this.editClickTarget_ = null;
125
126 // When this is called in response to the selectedChange event,
127 // the list grabs focus immediately afterwards. Thus we must delay
128 // our focus grab.
129 var self = this;
130 if (focusElement) {
131 window.setTimeout(function() {
132 // Make sure we are still in edit mode by the time we execute.
133 if (self.editing) {
134 focusElement.focus();
135 focusElement.select();
136 }
137 }, 50);
138 }
139 } else {
140 if (!this.editCancelled_ && this.hasBeenEdited &&
141 this.currentInputIsValid) {
142 this.updateStaticValues_();
143 cr.dispatchSimpleEvent(this, 'commitedit', true);
144 } else {
145 this.resetEditableValues_();
146 cr.dispatchSimpleEvent(this, 'canceledit', true);
147 }
148 }
149 },
150
151 /**
152 * Whether the item is editable.
153 * @type {boolean}
154 */
155 get editable() {
156 return this.editable_;
157 },
158 set editable(editable) {
159 this.editable_ = editable;
160 if (!editable)
161 this.editing = false;
162 },
163
164 /**
165 * Whether the item is a new item placeholder.
166 * @type {boolean}
167 */
168 get isPlaceholder() {
169 return this.isPlaceholder_;
170 },
171 set isPlaceholder(isPlaceholder) {
172 this.isPlaceholder_ = isPlaceholder;
173 if (isPlaceholder)
174 this.deletable = false;
175 },
176
177 /**
178 * The HTML element that should have focus initially when editing starts,
179 * if a specific element wasn't clicked.
180 * Defaults to the first <input> element; can be overriden by subclasses if
181 * a different element should be focused.
182 * @type {HTMLElement}
183 */
184 get initialFocusElement() {
185 return this.contentElement.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 overrided 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 overrided 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 * Returns a div containing an <input>, as well as static text if
211 * isPlaceholder is not true.
212 * @param {string} text The text of the cell.
213 * @return {HTMLElement} The HTML element for the cell.
214 * @private
215 */
216 createEditableTextCell: function(text) {
217 var container = this.ownerDocument.createElement('div');
218
219 if (!this.isPlaceholder) {
220 var textEl = this.ownerDocument.createElement('div');
221 textEl.className = 'static-text';
222 textEl.textContent = text;
223 textEl.setAttribute('displaymode', 'static');
224 container.appendChild(textEl);
225 }
226
227 var inputEl = this.ownerDocument.createElement('input');
228 inputEl.type = 'text';
229 inputEl.value = text;
230 if (!this.isPlaceholder) {
231 inputEl.setAttribute('displaymode', 'edit');
232 inputEl.staticVersion = textEl;
233 }
234 container.appendChild(inputEl);
235 this.editFields_.push(inputEl);
236
237 return container;
238 },
239
240 /**
241 * Resets the editable version of any controls created by createEditable*
242 * to match the static text.
243 * @private
244 */
245 resetEditableValues_: function() {
246 var editFields = this.editFields_;
247 for (var i = 0; i < editFields.length; i++) {
248 var staticLabel = editFields[i].staticVersion;
249 if (!staticLabel && !this.isPlaceholder)
250 continue;
251 if (editFields[i].tagName == 'INPUT') {
252 editFields[i].value =
253 this.isPlaceholder ? '' : staticLabel.textContent;
254 }
255 // Add more tag types here as new createEditable* methods are added.
256
257 editFields[i].setCustomValidity('');
258 }
259 },
260
261 /**
262 * Sets the static version of any controls created by createEditable*
263 * to match the current value of the editable version. Called on commit so
264 * that there's no flicker of the old value before the model updates.
265 * @private
266 */
267 updateStaticValues_: function() {
268 var editFields = this.editFields_;
269 for (var i = 0; i < editFields.length; i++) {
270 var staticLabel = editFields[i].staticVersion;
271 if (!staticLabel)
272 continue;
273 if (editFields[i].tagName == 'INPUT')
274 staticLabel.textContent = editFields[i].value;
275 // Add more tag types here as new createEditable* methods are added.
276 }
277 },
278
279 /**
280 * Called a key is pressed. Handles committing and cancelling edits.
281 * @param {Event} e The key down event.
282 * @private
283 */
284 handleKeyDown_: function(e) {
285 if (!this.editing)
286 return;
287
288 var endEdit = false;
289 switch (e.keyIdentifier) {
290 case 'U+001B': // Esc
291 this.editCancelled_ = true;
292 endEdit = true;
293 break;
294 case 'Enter':
295 if (this.currentInputIsValid)
296 endEdit = true;
297 break;
298 }
299
300 if (endEdit) {
301 // Blurring will trigger the edit to end; see InlineEditableItemList.
302 this.ownerDocument.activeElement.blur();
303 // Make sure that handled keys aren't passed on and double-handled.
304 // (e.g., esc shouldn't both cancel an edit and close a subpage)
305 e.stopPropagation();
306 }
307 },
308
309 /**
310 * Called when the list item is clicked. If the click target corresponds to
311 * an editable item, stores that item to focus when edit mode is started.
312 * @param {Event} e The mouse down event.
313 * @private
314 */
315 handleMouseDown_: function(e) {
316 if (!this.editable || this.editing)
317 return;
318
319 var clickTarget = e.target;
320 var editFields = this.editFields_;
321 for (var i = 0; i < editFields.length; i++) {
322 if (editFields[i] == clickTarget ||
323 editFields[i].staticVersion == clickTarget) {
324 this.editClickTarget_ = editFields[i];
325 return;
326 }
327 }
328 },
329 };
330
331 var InlineEditableItemList = cr.ui.define('list');
332
333 InlineEditableItemList.prototype = {
334 __proto__: DeletableItemList.prototype,
335
336 /** @inheritDoc */
337 decorate: function() {
338 DeletableItemList.prototype.decorate.call(this);
339 this.setAttribute('inlineeditable', '');
340 this.addEventListener('hasElementFocusChange',
341 this.handleListFocusChange_);
342 },
343
344 /**
345 * Called when the list hierarchy as a whole loses or gains focus; starts
346 * or ends editing for the lead item if necessary.
347 * @param {Event} e The change event.
348 * @private
349 */
350 handleListFocusChange_: function(e) {
351 var leadItem = this.getListItemByIndex(this.selectionModel.leadIndex);
352 if (leadItem) {
353 if (e.newValue)
354 leadItem.updateEditState();
355 else
356 leadItem.editing = false;
357 }
358 },
359 };
360
361 // Export
362 return {
363 InlineEditableItem: InlineEditableItem,
364 InlineEditableItemList: InlineEditableItemList,
365 };
366 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698