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

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

Issue 5699004: [tabbed options] more work on content settings exceptions lists (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fix maybeSetPatternValid Created 10 years 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
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 cr.define('options.contentSettings', function() { 5 cr.define('options.contentSettings', function() {
6 const List = cr.ui.List; 6 const List = cr.ui.List;
7 const ListItem = cr.ui.ListItem; 7 const ListItem = cr.ui.ListItem;
8 const ArrayDataModel = cr.ui.ArrayDataModel; 8 const ArrayDataModel = cr.ui.ArrayDataModel;
9 9
10 /** 10 /**
(...skipping 21 matching lines...) Expand all
32 32
33 ExceptionsListItem.prototype = { 33 ExceptionsListItem.prototype = {
34 __proto__: ListItem.prototype, 34 __proto__: ListItem.prototype,
35 35
36 /** 36 /**
37 * Called when an element is decorated as a list item. 37 * Called when an element is decorated as a list item.
38 */ 38 */
39 decorate: function() { 39 decorate: function() {
40 ListItem.prototype.decorate.call(this); 40 ListItem.prototype.decorate.call(this);
41 41
42 // Labels for display mode. 42 // Labels for display mode. |pattern| will be null for the 'add new
43 var patternLabel = cr.doc.createElement('span'); 43 // exception' row.
44 patternLabel.textContent = this.pattern; 44 if (this.pattern) {
45 this.appendChild(patternLabel); 45 this.isAddNewRow = false;
46 46
47 var settingLabel = cr.doc.createElement('span'); 47 var patternLabel = cr.doc.createElement('span');
48 settingLabel.textContent = this.settingForDisplay(); 48 patternLabel.textContent = this.pattern;
49 settingLabel.className = 'exceptionSetting'; 49 patternLabel.className = 'exceptionPattern';
50 this.appendChild(settingLabel); 50 this.appendChild(patternLabel);
51 this.patternLabel = patternLabel;
52
53 var settingLabel = cr.doc.createElement('span');
54 settingLabel.textContent = this.settingForDisplay();
55 settingLabel.className = 'exceptionSetting';
56 this.appendChild(settingLabel);
57 this.settingLabel = settingLabel;
58 } else {
59 this.isAddNewRow = true;
60 }
51 61
52 // Elements for edit mode. 62 // Elements for edit mode.
53 var input = cr.doc.createElement('input'); 63 var input = cr.doc.createElement('input');
54 input.type = 'text'; 64 input.type = 'text';
55 this.appendChild(input); 65 this.appendChild(input);
56 input.className = 'exceptionInput hidden'; 66 input.className = 'exceptionPattern';
67 if (this.isAddNewRow)
68 input.placeholder = templateData.addNewExceptionInstructions;
69 else
70 input.classList.add('hidden');
57 71
58 var select = cr.doc.createElement('select'); 72 var select = cr.doc.createElement('select');
59 var optionAllow = cr.doc.createElement('option'); 73 var optionAllow = cr.doc.createElement('option');
60 optionAllow.textContent = templateData.allowException; 74 optionAllow.textContent = templateData.allowException;
61 select.appendChild(optionAllow); 75 select.appendChild(optionAllow);
62 76
63 if (this.enableAskOption) { 77 if (this.enableAskOption) {
64 var optionAsk = cr.doc.createElement('option'); 78 var optionAsk = cr.doc.createElement('option');
65 optionAsk.textContent = templateData.askException; 79 optionAsk.textContent = templateData.askException;
66 select.appendChild(optionAsk); 80 select.appendChild(optionAsk);
67 this.optionAsk = optionAsk; 81 this.optionAsk = optionAsk;
68 } 82 }
69 83
70 if (this.contentType == 'cookies') { 84 if (this.contentType == 'cookies') {
71 var optionSession = cr.doc.createElement('option'); 85 var optionSession = cr.doc.createElement('option');
72 optionSession.textContent = templateData.sessionException; 86 optionSession.textContent = templateData.sessionException;
73 select.appendChild(optionSession); 87 select.appendChild(optionSession);
74 this.optionSession = optionSession; 88 this.optionSession = optionSession;
75 } 89 }
76 90
77 var optionBlock = cr.doc.createElement('option'); 91 var optionBlock = cr.doc.createElement('option');
78 optionBlock.textContent = templateData.blockException; 92 optionBlock.textContent = templateData.blockException;
79 select.appendChild(optionBlock); 93 select.appendChild(optionBlock);
80 94
81 this.appendChild(select); 95 this.appendChild(select);
82 select.className = 'exceptionSetting hidden'; 96 select.className = 'exceptionSetting';
97 if (!this.isAddNewRow)
98 select.classList.add('hidden');
83 99
84 // Used to track whether the URL pattern in the input is valid. 100 // Used to track whether the URL pattern in the input is valid.
85 // This will be true if the browser process has informed us that the 101 // This will be true if the browser process has informed us that the
86 // current text in the input is valid. Changing the text resets this to 102 // current text in the input is valid. Changing the text resets this to
87 // false, and getting a response from the browser sets it back to true. 103 // false, and getting a response from the browser sets it back to true.
88 // It starts off as false for empty string (new exceptions) or true for 104 // It starts off as false for empty string (new exceptions) or true for
89 // already-existing exceptions (which we assume are valid). 105 // already-existing exceptions (which we assume are valid).
90 this.inputValidityKnown = this.pattern; 106 this.inputValidityKnown = this.pattern;
91 // This one tracks the actual validity of the pattern in the input. This 107 // This one tracks the actual validity of the pattern in the input. This
92 // starts off as true so as not to annoy the user when he adds a new and 108 // starts off as true so as not to annoy the user when he adds a new and
93 // empty input. 109 // empty input.
94 this.inputIsValid = true; 110 this.inputIsValid = true;
95 111
96 this.patternLabel = patternLabel;
97 this.settingLabel = settingLabel;
98 this.input = input; 112 this.input = input;
99 this.select = select; 113 this.select = select;
100 this.optionAllow = optionAllow; 114 this.optionAllow = optionAllow;
101 this.optionBlock = optionBlock; 115 this.optionBlock = optionBlock;
102 116
103 this.updateEditables(); 117 this.updateEditables();
104 if (!this.pattern)
105 input.value = templateData.examplePattern;
106 118
107 var listItem = this; 119 var listItem = this;
108 this.ondblclick = function(event) { 120
121 this.addEventListener('selectedChange', function(event) {
109 // Editing notifications and geolocation is disabled for now. 122 // Editing notifications and geolocation is disabled for now.
110 if (listItem.contentType == 'notifications' || 123 if (listItem.contentType == 'notifications' ||
111 listItem.contentType == 'location') 124 listItem.contentType == 'location')
112 return; 125 return;
113 126
114 listItem.editing = true; 127 listItem.editing = listItem.selected;
115 }; 128 });
116 129
117 // Handle events on the editable nodes. 130 // Handle events on the editable nodes.
118 input.oninput = function(event) { 131 input.oninput = function(event) {
119 listItem.inputValidityKnown = false; 132 listItem.inputValidityKnown = false;
120 chrome.send('checkExceptionPatternValidity', 133 chrome.send('checkExceptionPatternValidity',
121 [listItem.contentType, listItem.mode, input.value]); 134 [listItem.contentType, listItem.mode, input.value]);
122 }; 135 };
123 136
124 // Handles enter and escape which trigger reset and commit respectively. 137 // Handles enter and escape which trigger reset and commit respectively.
125 function handleKeydown(e) { 138 function handleKeydown(e) {
126 // Make sure that the tree does not handle the key. 139 // Make sure that the tree does not handle the key.
127 e.stopPropagation(); 140 e.stopPropagation();
128 141
129 // Calling list.focus blurs the input which will stop editing the list 142 // Calling list.focus blurs the input which will stop editing the list
130 // item. 143 // item.
131 switch (e.keyIdentifier) { 144 switch (e.keyIdentifier) {
132 case 'U+001B': // Esc 145 case 'U+001B': // Esc
133 // Reset the inputs. 146 // Reset the inputs.
134 listItem.updateEditables(); 147 listItem.updateEditables();
135 if (listItem.pattern) 148 listItem.setPatternValid(true);
136 listItem.maybeSetPatternValid(listItem.pattern, true);
137 case 'Enter': 149 case 'Enter':
138 if (listItem.parentNode) 150 listItem.ownerDocument.activeElement.blur();
139 listItem.parentNode.focus();
140 } 151 }
141 } 152 }
142 153
143 function handleBlur(e) {
144 // When the blur event happens we do not know who is getting focus so we
145 // delay this a bit since we want to know if the other input got focus
146 // before deciding if we should exit edit mode.
147 var doc = e.target.ownerDocument;
148 window.setTimeout(function() {
149 var activeElement = doc.activeElement;
150 if (!listItem.contains(activeElement)) {
151 listItem.editing = false;
152 }
153 }, 50);
154 }
155
156 input.addEventListener('keydown', handleKeydown); 154 input.addEventListener('keydown', handleKeydown);
157 input.addEventListener('blur', handleBlur);
158
159 select.addEventListener('keydown', handleKeydown); 155 select.addEventListener('keydown', handleKeydown);
160 select.addEventListener('blur', handleBlur);
161 }, 156 },
162 157
163 /** 158 /**
164 * The pattern (e.g., a URL) for the exception. 159 * The pattern (e.g., a URL) for the exception.
165 * @type {string} 160 * @type {string}
166 */ 161 */
167 get pattern() { 162 get pattern() {
168 return this.dataItem['displayPattern']; 163 return this.dataItem['displayPattern'];
169 }, 164 },
170 set pattern(pattern) { 165 set pattern(pattern) {
(...skipping 21 matching lines...) Expand all
192 return templateData.allowException; 187 return templateData.allowException;
193 else if (setting == 'block') 188 else if (setting == 'block')
194 return templateData.blockException; 189 return templateData.blockException;
195 else if (setting == 'ask') 190 else if (setting == 'ask')
196 return templateData.askException; 191 return templateData.askException;
197 else if (setting == 'session') 192 else if (setting == 'session')
198 return templateData.sessionException; 193 return templateData.sessionException;
199 }, 194 },
200 195
201 /** 196 /**
202 * Update this list item to reflect whether the input is a valid pattern 197 * Update this list item to reflect whether the input is a valid pattern.
203 * if |pattern| matches the text currently in the input.
204 * @param {string} pattern The pattern.
205 * @param {boolean} valid Whether said pattern is valid in the context of 198 * @param {boolean} valid Whether said pattern is valid in the context of
206 * a content exception setting. 199 * a content exception setting.
207 */ 200 */
208 maybeSetPatternValid: function(pattern, valid) { 201 setPatternValid: function(valid) {
209 // Don't do anything for messages where we are not the intended recipient,
210 // or if the response is stale (i.e. the input value has changed since we
211 // sent the request to analyze it).
212 if (pattern != this.input.value)
213 return;
214
215 if (valid) 202 if (valid)
216 this.input.setCustomValidity(''); 203 this.input.setCustomValidity('');
217 else 204 else
218 this.input.setCustomValidity(' '); 205 this.input.setCustomValidity(' ');
219 this.inputIsValid = valid; 206 this.inputIsValid = valid;
220 this.inputValidityKnown = true; 207 this.inputValidityKnown = true;
221 }, 208 },
222 209
223 /** 210 /**
224 * Copy the data model values to the editable nodes. 211 * Copy the data model values to the editable nodes.
225 */ 212 */
226 updateEditables: function() { 213 updateEditables: function() {
227 this.input.value = this.pattern; 214 if (this.isAddNewRow)
stuartmorgan 2010/12/13 17:49:51 It seems like there's a bunch of this kind of logi
Evan Stade 2010/12/13 20:00:19 ok, i'll do this
215 this.input.value = '';
216 else
217 this.input.value = this.pattern;
228 218
229 if (this.setting == 'allow') 219 if (this.setting == 'allow')
230 this.optionAllow.selected = true; 220 this.optionAllow.selected = true;
231 else if (this.setting == 'block') 221 else if (this.setting == 'block')
232 this.optionBlock.selected = true; 222 this.optionBlock.selected = true;
233 else if (this.setting == 'session' && this.optionSession) 223 else if (this.setting == 'session' && this.optionSession)
234 this.optionSession.selected = true; 224 this.optionSession.selected = true;
235 else if (this.setting == 'ask' && this.optionAsk) 225 else if (this.setting == 'ask' && this.optionAsk)
236 this.optionAsk.selected = true; 226 this.optionAsk.selected = true;
237 }, 227 },
238 228
239 /** 229 /**
240 * Whether the user is currently able to edit the list item. 230 * Whether the user is currently able to edit the list item.
241 * @type {boolean} 231 * @type {boolean}
242 */ 232 */
243 get editing() { 233 get editing() {
244 return this.hasAttribute('editing'); 234 return this.isAddNewRow || this.hasAttribute('editing');
245 }, 235 },
246 set editing(editing) { 236 set editing(editing) {
247 var oldEditing = this.editing; 237 var oldEditing = this.editing;
248 if (oldEditing == editing) 238 if (!this.isAddNewRow && (oldEditing == editing))
249 return; 239 return;
250 240
251 var listItem = this; 241 var listItem = this;
252 var pattern = this.pattern; 242 var pattern = this.pattern;
253 var setting = this.setting; 243 var setting = this.setting;
254 var patternLabel = this.patternLabel; 244 var patternLabel = this.patternLabel;
255 var settingLabel = this.settingLabel; 245 var settingLabel = this.settingLabel;
256 var input = this.input; 246 var input = this.input;
257 var select = this.select; 247 var select = this.select;
258 var optionAllow = this.optionAllow; 248 var optionAllow = this.optionAllow;
259 var optionBlock = this.optionBlock; 249 var optionBlock = this.optionBlock;
260 var optionSession = this.optionSession; 250 var optionSession = this.optionSession;
261 var optionAsk = this.optionAsk; 251 var optionAsk = this.optionAsk;
262 252
263 // Just delete this row if it was added via the Add button. 253 if (!this.isAddNewRow) {
264 if (!editing && !pattern && !input.value) { 254 patternLabel.classList.toggle('hidden');
265 var model = listItem.parentNode.dataModel; 255 settingLabel.classList.toggle('hidden');
266 model.splice(model.indexOf(listItem.dataItem), 1); 256 input.classList.toggle('hidden');
267 return; 257 select.classList.toggle('hidden');
268 } 258 }
269 259
270 // Check that we have a valid pattern and if not we do not change the
271 // editing mode.
272 if (!editing && (!this.inputValidityKnown || !this.inputIsValid)) {
273 input.focus();
274 input.select();
275 return;
276 }
277
278 patternLabel.classList.toggle('hidden');
279 settingLabel.classList.toggle('hidden');
280 input.classList.toggle('hidden');
281 select.classList.toggle('hidden');
282
283 var doc = this.ownerDocument;
284 var area = doc.querySelector('div[contentType=' +
285 listItem.contentType + '][mode=' + listItem.mode + ']');
286 area.enableAddAndEditButtons(!editing);
287
288 if (editing) { 260 if (editing) {
289 this.setAttribute('editing', ''); 261 this.setAttribute('editing', '');
290 cr.ui.limitInputWidth(input, this, 20); 262 cr.ui.limitInputWidth(input, this, 20);
291 input.focus(); 263 // When this is called in response to the selectedChange event,
292 input.select(); 264 // the list grabs focus immediately afterwards. Thus we must delay
265 // our focus grab.
266 window.setTimeout(function() {
267 input.focus();
268 input.select();
269 }, 50);
270
271 // TODO(estade): should we insert example text here for the AddNewRow
272 // input?
293 } else { 273 } else {
294 this.removeAttribute('editing'); 274 this.removeAttribute('editing');
295 275
276 // Check that we have a valid pattern and if not we do not, abort
277 // changes to the exception.
278 if (!this.inputValidityKnown || !this.inputIsValid) {
279 this.updateEditables();
280 this.setPatternValid(true);
281 return;
282 }
283
296 var newPattern = input.value; 284 var newPattern = input.value;
297 285
298 var newSetting; 286 var newSetting;
299 if (optionAllow.selected) 287 if (optionAllow.selected)
300 newSetting = 'allow'; 288 newSetting = 'allow';
301 else if (optionBlock.selected) 289 else if (optionBlock.selected)
302 newSetting = 'block'; 290 newSetting = 'block';
303 else if (optionSession && optionSession.selected) 291 else if (optionSession && optionSession.selected)
304 newSetting = 'session'; 292 newSetting = 'session';
305 else if (optionAsk && optionAsk.selected) 293 else if (optionAsk && optionAsk.selected)
306 newSetting = 'ask'; 294 newSetting = 'ask';
307 295
308 // Empty edit - do nothing. 296 // Empty edit - do nothing.
309 if (pattern == newPattern && newSetting == this.setting) 297 if ((pattern == newPattern && newSetting == this.setting) ||
298 (this.isAddNewRow && newPattern == '')) {
310 return; 299 return;
300 }
311 301
312 this.pattern = patternLabel.textContent = newPattern; 302 if (!this.isAddNewRow) {
313 this.setting = newSetting; 303 patternLabel.textContent = newPattern;
314 settingLabel.textContent = this.settingForDisplay(); 304 settingLabel.textContent = this.settingForDisplay();
305 this.pattern = newPattern;
306 this.setting = newSetting;
307 }
315 308
316 if (pattern != this.pattern) { 309 if (pattern != newPattern) {
317 chrome.send('removeExceptions', 310 chrome.send('removeExceptions',
318 [this.contentType, this.mode, pattern]); 311 [this.contentType, this.mode, pattern]);
319 } 312 }
320 313
321 chrome.send('setException', 314 chrome.send('setException',
322 [this.contentType, this.mode, this.pattern, this.setting]); 315 [this.contentType, this.mode, newPattern, newSetting]);
323 } 316 }
324 } 317 }
325 }; 318 };
326 319
327 /** 320 /**
328 * Creates a new exceptions list. 321 * Creates a new exceptions list.
329 * @constructor 322 * @constructor
330 * @extends {cr.ui.List} 323 * @extends {cr.ui.List}
331 */ 324 */
332 var ExceptionsList = cr.ui.define('list'); 325 var ExceptionsList = cr.ui.define('list');
333 326
334 ExceptionsList.prototype = { 327 ExceptionsList.prototype = {
335 __proto__: List.prototype, 328 __proto__: List.prototype,
336 329
337 /** 330 /**
338 * Called when an element is decorated as a list. 331 * Called when an element is decorated as a list.
339 */ 332 */
340 decorate: function() { 333 decorate: function() {
341 List.prototype.decorate.call(this); 334 List.prototype.decorate.call(this);
342 335
343 this.dataModel = new ArrayDataModel([]); 336 this.dataModel = new ArrayDataModel([]);
344 337
338 this.contentType = this.parentNode.getAttribute('contentType');
339 this.mode = this.getAttribute('mode');
340
341 var exceptionList = this;
342 function handleBlur(e) {
343 // When the blur event happens we do not know who is getting focus so we
344 // delay this a bit since we want to know if the other input got focus
345 // before deciding if we should clear the selection.
346 var doc = e.target.ownerDocument;
347 window.setTimeout(function() {
348 var activeElement = doc.activeElement;
349 if (!exceptionList.contains(activeElement))
350 exceptionList.selectionModel.clear();
351 }, 50);
352 }
353
354 this.addEventListener('blur', handleBlur, true);
355
345 // Whether the exceptions in this list allow an 'Ask every time' option. 356 // Whether the exceptions in this list allow an 'Ask every time' option.
346 this.enableAskOption = (this.contentType == 'plugins' && 357 this.enableAskOption = (this.contentType == 'plugins' &&
347 templateData.enable_click_to_play); 358 templateData.enable_click_to_play);
359
360 this.reset();
348 }, 361 },
349 362
350 /** 363 /**
351 * Creates an item to go in the list. 364 * Creates an item to go in the list.
352 * @param {Object} entry The element from the data model for this row. 365 * @param {Object} entry The element from the data model for this row.
353 */ 366 */
354 createItem: function(entry) { 367 createItem: function(entry) {
355 return new ExceptionsListItem(this.contentType, 368 return new ExceptionsListItem(this.contentType,
356 this.mode, 369 this.mode,
357 this.enableAskOption, 370 this.enableAskOption,
358 entry); 371 entry);
359 }, 372 },
360 373
361 /** 374 /**
362 * Adds an exception to the js model. 375 * Adds an exception to the js model.
363 * @param {Object} entry A dictionary of values for the exception. 376 * @param {Object} entry A dictionary of values for the exception.
364 */ 377 */
365 addException: function(entry) { 378 addException: function(entry) {
366 this.dataModel.push(entry); 379 this.dataModel.push(entry);
367
368 // When an empty row is added, put it into editing mode.
369 if (!entry['displayPattern'] && !entry['setting']) {
370 var index = this.dataModel.length - 1;
371 var sm = this.selectionModel;
372 sm.anchorIndex = sm.leadIndex = sm.selectedIndex = index;
373 this.scrollIndexIntoView(index);
374 var li = this.getListItemByIndex(index);
375 li.editing = true;
376 }
377 }, 380 },
378 381
379 /** 382 /**
380 * The browser has finished checking a pattern for validity. Update the 383 * The browser has finished checking a pattern for validity. Update the
381 * list item to reflect this. 384 * list item to reflect this.
382 * @param {string} pattern The pattern. 385 * @param {string} pattern The pattern.
383 * @param {bool} valid Whether said pattern is valid in the context of 386 * @param {bool} valid Whether said pattern is valid in the context of
384 * a content exception setting. 387 * a content exception setting.
385 */ 388 */
386 patternValidityCheckComplete: function(pattern, valid) { 389 patternValidityCheckComplete: function(pattern, valid) {
387 for (var i = 0; i < this.dataModel.length; i++) { 390 for (var i = this.firstIndex_; i < this.lastIndex_; i++) {
388 var listItem = this.getListItemByIndex(i); 391 var listItem = this.getListItemByIndex(i);
389 if (listItem) 392 // Don't do anything for messages for the item if it is not the intended
390 listItem.maybeSetPatternValid(pattern, valid); 393 // recipient, or if the response is stale (i.e. the input value has
394 // changed since we sent the request to analyze it).
395 if (pattern == listItem.input.value)
396 listItem.setPatternValid(valid);
391 } 397 }
392 }, 398 },
393 399
394 /** 400 /**
395 * Removes all exceptions from the js model. 401 * Removes all exceptions from the js model.
396 */ 402 */
397 clear: function() { 403 reset: function() {
398 this.dataModel = new ArrayDataModel([]); 404 this.dataModel = new ArrayDataModel([]);
405 var addItemEntry = [];
406 this.dataModel.push(addItemEntry);
399 }, 407 },
400 408
401 /** 409 /**
402 * Removes all selected rows from browser's model. 410 * Removes all selected rows from browser's model.
403 */ 411 */
404 removeSelectedRows: function() { 412 removeSelectedRows: function() {
405 // The first member is the content type; the rest of the values describe 413 // The first member is the content type; the rest of the values describe
406 // the patterns we are removing. 414 // the patterns we are removing.
407 var args = [this.contentType]; 415 var args = [this.contentType];
408 var selectedItems = this.selectedItems; 416 var selectedItems = this.selectedItems;
(...skipping 21 matching lines...) Expand all
430 if (li) 438 if (li)
431 li.editing = true; 439 li.editing = true;
432 } 440 }
433 }; 441 };
434 442
435 return { 443 return {
436 ExceptionsListItem: ExceptionsListItem, 444 ExceptionsListItem: ExceptionsListItem,
437 ExceptionsList: ExceptionsList, 445 ExceptionsList: ExceptionsList,
438 }; 446 };
439 }); 447 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698