OLD | NEW |
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 Loading... |
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 var patternLabel = cr.doc.createElement('span'); |
| 46 patternLabel.textContent = this.pattern; |
| 47 patternLabel.className = 'exceptionPattern'; |
| 48 this.appendChild(patternLabel); |
| 49 this.patternLabel = patternLabel; |
46 | 50 |
47 var settingLabel = cr.doc.createElement('span'); | 51 var settingLabel = cr.doc.createElement('span'); |
48 settingLabel.textContent = this.settingForDisplay(); | 52 settingLabel.textContent = this.settingForDisplay(); |
49 settingLabel.className = 'exceptionSetting'; | 53 settingLabel.className = 'exceptionSetting'; |
50 this.appendChild(settingLabel); | 54 this.appendChild(settingLabel); |
| 55 this.settingLabel = settingLabel; |
| 56 } |
51 | 57 |
52 // Elements for edit mode. | 58 // Elements for edit mode. |
53 var input = cr.doc.createElement('input'); | 59 var input = cr.doc.createElement('input'); |
54 input.type = 'text'; | 60 input.type = 'text'; |
55 this.appendChild(input); | 61 this.appendChild(input); |
56 input.className = 'exceptionInput hidden'; | 62 input.className = 'exceptionPattern hidden'; |
57 | 63 |
58 var select = cr.doc.createElement('select'); | 64 var select = cr.doc.createElement('select'); |
59 var optionAllow = cr.doc.createElement('option'); | 65 var optionAllow = cr.doc.createElement('option'); |
60 optionAllow.textContent = templateData.allowException; | 66 optionAllow.textContent = templateData.allowException; |
61 select.appendChild(optionAllow); | 67 select.appendChild(optionAllow); |
62 | 68 |
63 if (this.enableAskOption) { | 69 if (this.enableAskOption) { |
64 var optionAsk = cr.doc.createElement('option'); | 70 var optionAsk = cr.doc.createElement('option'); |
65 optionAsk.textContent = templateData.askException; | 71 optionAsk.textContent = templateData.askException; |
66 select.appendChild(optionAsk); | 72 select.appendChild(optionAsk); |
(...skipping 19 matching lines...) Expand all Loading... |
86 // current text in the input is valid. Changing the text resets this to | 92 // 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. | 93 // 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 | 94 // It starts off as false for empty string (new exceptions) or true for |
89 // already-existing exceptions (which we assume are valid). | 95 // already-existing exceptions (which we assume are valid). |
90 this.inputValidityKnown = this.pattern; | 96 this.inputValidityKnown = this.pattern; |
91 // This one tracks the actual validity of the pattern in the input. This | 97 // 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 | 98 // starts off as true so as not to annoy the user when he adds a new and |
93 // empty input. | 99 // empty input. |
94 this.inputIsValid = true; | 100 this.inputIsValid = true; |
95 | 101 |
96 this.patternLabel = patternLabel; | |
97 this.settingLabel = settingLabel; | |
98 this.input = input; | 102 this.input = input; |
99 this.select = select; | 103 this.select = select; |
100 this.optionAllow = optionAllow; | 104 this.optionAllow = optionAllow; |
101 this.optionBlock = optionBlock; | 105 this.optionBlock = optionBlock; |
102 | 106 |
103 this.updateEditables(); | 107 this.updateEditables(); |
104 if (!this.pattern) | |
105 input.value = templateData.examplePattern; | |
106 | 108 |
107 var listItem = this; | 109 var listItem = this; |
108 this.ondblclick = function(event) { | 110 |
| 111 this.addEventListener('selectedChange', function(event) { |
109 // Editing notifications and geolocation is disabled for now. | 112 // Editing notifications and geolocation is disabled for now. |
110 if (listItem.contentType == 'notifications' || | 113 if (listItem.contentType == 'notifications' || |
111 listItem.contentType == 'location') | 114 listItem.contentType == 'location') |
112 return; | 115 return; |
113 | 116 |
114 listItem.editing = true; | 117 listItem.editing = listItem.selected; |
115 }; | 118 }); |
116 | 119 |
117 // Handle events on the editable nodes. | 120 // Handle events on the editable nodes. |
118 input.oninput = function(event) { | 121 input.oninput = function(event) { |
119 listItem.inputValidityKnown = false; | 122 listItem.inputValidityKnown = false; |
120 chrome.send('checkExceptionPatternValidity', | 123 chrome.send('checkExceptionPatternValidity', |
121 [listItem.contentType, listItem.mode, input.value]); | 124 [listItem.contentType, listItem.mode, input.value]); |
122 }; | 125 }; |
123 | 126 |
124 // Handles enter and escape which trigger reset and commit respectively. | 127 // Handles enter and escape which trigger reset and commit respectively. |
125 function handleKeydown(e) { | 128 function handleKeydown(e) { |
126 // Make sure that the tree does not handle the key. | 129 // Make sure that the tree does not handle the key. |
127 e.stopPropagation(); | 130 e.stopPropagation(); |
128 | 131 |
129 // Calling list.focus blurs the input which will stop editing the list | 132 // Calling list.focus blurs the input which will stop editing the list |
130 // item. | 133 // item. |
131 switch (e.keyIdentifier) { | 134 switch (e.keyIdentifier) { |
132 case 'U+001B': // Esc | 135 case 'U+001B': // Esc |
133 // Reset the inputs. | 136 // Reset the inputs. |
134 listItem.updateEditables(); | 137 listItem.updateEditables(); |
135 if (listItem.pattern) | 138 listItem.setPatternValid(true); |
136 listItem.maybeSetPatternValid(listItem.pattern, true); | |
137 case 'Enter': | 139 case 'Enter': |
138 if (listItem.parentNode) | 140 listItem.ownerDocument.activeElement.blur(); |
139 listItem.parentNode.focus(); | |
140 } | 141 } |
141 } | 142 } |
142 | 143 |
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); | 144 input.addEventListener('keydown', handleKeydown); |
157 input.addEventListener('blur', handleBlur); | |
158 | |
159 select.addEventListener('keydown', handleKeydown); | 145 select.addEventListener('keydown', handleKeydown); |
160 select.addEventListener('blur', handleBlur); | |
161 }, | 146 }, |
162 | 147 |
163 /** | 148 /** |
164 * The pattern (e.g., a URL) for the exception. | 149 * The pattern (e.g., a URL) for the exception. |
165 * @type {string} | 150 * @type {string} |
166 */ | 151 */ |
167 get pattern() { | 152 get pattern() { |
168 return this.dataItem['displayPattern']; | 153 return this.dataItem['displayPattern']; |
169 }, | 154 }, |
170 set pattern(pattern) { | 155 set pattern(pattern) { |
(...skipping 21 matching lines...) Expand all Loading... |
192 return templateData.allowException; | 177 return templateData.allowException; |
193 else if (setting == 'block') | 178 else if (setting == 'block') |
194 return templateData.blockException; | 179 return templateData.blockException; |
195 else if (setting == 'ask') | 180 else if (setting == 'ask') |
196 return templateData.askException; | 181 return templateData.askException; |
197 else if (setting == 'session') | 182 else if (setting == 'session') |
198 return templateData.sessionException; | 183 return templateData.sessionException; |
199 }, | 184 }, |
200 | 185 |
201 /** | 186 /** |
202 * Update this list item to reflect whether the input is a valid pattern | 187 * 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 | 188 * @param {boolean} valid Whether said pattern is valid in the context of |
206 * a content exception setting. | 189 * a content exception setting. |
207 */ | 190 */ |
208 maybeSetPatternValid: function(pattern, valid) { | 191 setPatternValid: function(valid) { |
209 // Don't do anything for messages where we are not the intended recipient, | 192 if (valid || !this.input.value) |
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) | |
216 this.input.setCustomValidity(''); | 193 this.input.setCustomValidity(''); |
217 else | 194 else |
218 this.input.setCustomValidity(' '); | 195 this.input.setCustomValidity(' '); |
219 this.inputIsValid = valid; | 196 this.inputIsValid = valid; |
220 this.inputValidityKnown = true; | 197 this.inputValidityKnown = true; |
221 }, | 198 }, |
222 | 199 |
223 /** | 200 /** |
| 201 * Set the <input> to its original contents. Used when the user quits |
| 202 * editing. |
| 203 */ |
| 204 resetInput: function() { |
| 205 this.input.value = this.pattern; |
| 206 }, |
| 207 |
| 208 /** |
224 * Copy the data model values to the editable nodes. | 209 * Copy the data model values to the editable nodes. |
225 */ | 210 */ |
226 updateEditables: function() { | 211 updateEditables: function() { |
227 this.input.value = this.pattern; | 212 this.resetInput(); |
228 | 213 |
229 if (this.setting == 'allow') | 214 if (this.setting == 'allow') |
230 this.optionAllow.selected = true; | 215 this.optionAllow.selected = true; |
231 else if (this.setting == 'block') | 216 else if (this.setting == 'block') |
232 this.optionBlock.selected = true; | 217 this.optionBlock.selected = true; |
233 else if (this.setting == 'session' && this.optionSession) | 218 else if (this.setting == 'session' && this.optionSession) |
234 this.optionSession.selected = true; | 219 this.optionSession.selected = true; |
235 else if (this.setting == 'ask' && this.optionAsk) | 220 else if (this.setting == 'ask' && this.optionAsk) |
236 this.optionAsk.selected = true; | 221 this.optionAsk.selected = true; |
237 }, | 222 }, |
238 | 223 |
239 /** | 224 /** |
| 225 * Fiddle with the display of elements of this list item when the editing |
| 226 * mode changes. |
| 227 */ |
| 228 toggleVisibilityForEditing: function() { |
| 229 this.patternLabel.classList.toggle('hidden'); |
| 230 this.settingLabel.classList.toggle('hidden'); |
| 231 this.input.classList.toggle('hidden'); |
| 232 this.select.classList.toggle('hidden'); |
| 233 }, |
| 234 |
| 235 /** |
240 * Whether the user is currently able to edit the list item. | 236 * Whether the user is currently able to edit the list item. |
241 * @type {boolean} | 237 * @type {boolean} |
242 */ | 238 */ |
243 get editing() { | 239 get editing() { |
244 return this.hasAttribute('editing'); | 240 return this.hasAttribute('editing'); |
245 }, | 241 }, |
246 set editing(editing) { | 242 set editing(editing) { |
247 var oldEditing = this.editing; | 243 var oldEditing = this.editing; |
248 if (oldEditing == editing) | 244 if (oldEditing == editing) |
249 return; | 245 return; |
250 | 246 |
251 var listItem = this; | |
252 var pattern = this.pattern; | |
253 var setting = this.setting; | |
254 var patternLabel = this.patternLabel; | |
255 var settingLabel = this.settingLabel; | |
256 var input = this.input; | 247 var input = this.input; |
257 var select = this.select; | |
258 var optionAllow = this.optionAllow; | |
259 var optionBlock = this.optionBlock; | |
260 var optionSession = this.optionSession; | |
261 var optionAsk = this.optionAsk; | |
262 | 248 |
263 // Just delete this row if it was added via the Add button. | 249 this.toggleVisibilityForEditing(); |
264 if (!editing && !pattern && !input.value) { | |
265 var model = listItem.parentNode.dataModel; | |
266 model.splice(model.indexOf(listItem.dataItem), 1); | |
267 return; | |
268 } | |
269 | |
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 | 250 |
288 if (editing) { | 251 if (editing) { |
289 this.setAttribute('editing', ''); | 252 this.setAttribute('editing', ''); |
290 cr.ui.limitInputWidth(input, this, 20); | 253 cr.ui.limitInputWidth(input, this, 20); |
291 input.focus(); | 254 // When this is called in response to the selectedChange event, |
292 input.select(); | 255 // the list grabs focus immediately afterwards. Thus we must delay |
| 256 // our focus grab. |
| 257 window.setTimeout(function() { |
| 258 input.focus(); |
| 259 input.select(); |
| 260 }, 50); |
| 261 |
| 262 // TODO(estade): should we insert example text here for the AddNewRow |
| 263 // input? |
293 } else { | 264 } else { |
294 this.removeAttribute('editing'); | 265 this.removeAttribute('editing'); |
295 | 266 |
| 267 // Check that we have a valid pattern and if not we do not, abort |
| 268 // changes to the exception. |
| 269 if (!this.inputValidityKnown || !this.inputIsValid) { |
| 270 this.updateEditables(); |
| 271 this.setPatternValid(true); |
| 272 return; |
| 273 } |
| 274 |
296 var newPattern = input.value; | 275 var newPattern = input.value; |
297 | 276 |
298 var newSetting; | 277 var newSetting; |
| 278 var optionAllow = this.optionAllow; |
| 279 var optionBlock = this.optionBlock; |
| 280 var optionSession = this.optionSession; |
| 281 var optionAsk = this.optionAsk; |
299 if (optionAllow.selected) | 282 if (optionAllow.selected) |
300 newSetting = 'allow'; | 283 newSetting = 'allow'; |
301 else if (optionBlock.selected) | 284 else if (optionBlock.selected) |
302 newSetting = 'block'; | 285 newSetting = 'block'; |
303 else if (optionSession && optionSession.selected) | 286 else if (optionSession && optionSession.selected) |
304 newSetting = 'session'; | 287 newSetting = 'session'; |
305 else if (optionAsk && optionAsk.selected) | 288 else if (optionAsk && optionAsk.selected) |
306 newSetting = 'ask'; | 289 newSetting = 'ask'; |
307 | 290 |
308 // Empty edit - do nothing. | 291 this.finishEdit(newPattern, newSetting); |
309 if (pattern == newPattern && newSetting == this.setting) | 292 } |
310 return; | 293 }, |
311 | 294 |
312 this.pattern = patternLabel.textContent = newPattern; | 295 /** |
313 this.setting = newSetting; | 296 * Editing is complete; update the model. |
314 settingLabel.textContent = this.settingForDisplay(); | 297 * @type {string} newPattern The pattern that the user entered. |
| 298 * @type {string} newSetting The setting the user chose. |
| 299 */ |
| 300 finishEdit: function(newPattern, newSetting) { |
| 301 // Empty edit - do nothing. |
| 302 if (newPattern == this.pattern && newSetting == this.setting) |
| 303 return; |
315 | 304 |
316 if (pattern != this.pattern) { | 305 this.patternLabel.textContent = newPattern; |
317 chrome.send('removeExceptions', | 306 this.settingLabel.textContent = this.settingForDisplay(); |
318 [this.contentType, this.mode, pattern]); | 307 var oldPattern = this.pattern; |
319 } | 308 this.pattern = newPattern; |
| 309 this.setting = newSetting; |
320 | 310 |
321 chrome.send('setException', | 311 if (oldPattern != newPattern) { |
322 [this.contentType, this.mode, this.pattern, this.setting]); | 312 chrome.send('removeExceptions', |
| 313 [this.contentType, this.mode, oldPattern]); |
323 } | 314 } |
| 315 |
| 316 chrome.send('setException', |
| 317 [this.contentType, this.mode, newPattern, newSetting]); |
324 } | 318 } |
325 }; | 319 }; |
326 | 320 |
327 /** | 321 /** |
| 322 * Creates a new list item for the Add New Item row, which doesn't represent |
| 323 * an actual entry in the exceptions list but allows the user to add new |
| 324 * exceptions. |
| 325 * @param {string} contentType The type of the list. |
| 326 * @param {string} mode The browser mode, 'otr' or 'normal'. |
| 327 * @param {boolean} enableAskOption Whether to show an 'ask every time' |
| 328 * option in the select. |
| 329 * @constructor |
| 330 * @extends {cr.ui.ExceptionsListItem} |
| 331 */ |
| 332 function ExceptionsAddRowListItem(contentType, mode, enableAskOption) { |
| 333 var el = cr.doc.createElement('li'); |
| 334 el.mode = mode; |
| 335 el.contentType = contentType; |
| 336 el.enableAskOption = enableAskOption; |
| 337 el.dataItem = []; |
| 338 el.__proto__ = ExceptionsAddRowListItem.prototype; |
| 339 el.decorate(); |
| 340 |
| 341 return el; |
| 342 } |
| 343 |
| 344 ExceptionsAddRowListItem.prototype = { |
| 345 __proto__: ExceptionsListItem.prototype, |
| 346 |
| 347 decorate: function() { |
| 348 ExceptionsListItem.prototype.decorate.call(this); |
| 349 |
| 350 this.input.placeholder = templateData.addNewExceptionInstructions; |
| 351 this.input.classList.remove('hidden'); |
| 352 this.select.classList.remove('hidden'); |
| 353 |
| 354 // Do we always want a default of allow? |
| 355 this.setting = 'allow'; |
| 356 }, |
| 357 |
| 358 /** |
| 359 * Clear the <input> and let the placeholder text show again. |
| 360 */ |
| 361 resetInput: function() { |
| 362 this.input.value = ''; |
| 363 }, |
| 364 |
| 365 /** |
| 366 * No elements show or hide when going into edit mode, so do nothing. |
| 367 */ |
| 368 toggleVisibilityForEditing: function() { |
| 369 // No-op. |
| 370 }, |
| 371 |
| 372 /** |
| 373 * Editing is complete; update the model. As long as the pattern isn't |
| 374 * empty, we'll just add it. |
| 375 * @type {string} newPattern The pattern that the user entered. |
| 376 * @type {string} newSetting The setting the user chose. |
| 377 */ |
| 378 finishEdit: function(newPattern, newSetting) { |
| 379 if (newPattern == '') |
| 380 return; |
| 381 |
| 382 chrome.send('setException', |
| 383 [this.contentType, this.mode, newPattern, newSetting]); |
| 384 }, |
| 385 }; |
| 386 |
| 387 /** |
328 * Creates a new exceptions list. | 388 * Creates a new exceptions list. |
329 * @constructor | 389 * @constructor |
330 * @extends {cr.ui.List} | 390 * @extends {cr.ui.List} |
331 */ | 391 */ |
332 var ExceptionsList = cr.ui.define('list'); | 392 var ExceptionsList = cr.ui.define('list'); |
333 | 393 |
334 ExceptionsList.prototype = { | 394 ExceptionsList.prototype = { |
335 __proto__: List.prototype, | 395 __proto__: List.prototype, |
336 | 396 |
337 /** | 397 /** |
338 * Called when an element is decorated as a list. | 398 * Called when an element is decorated as a list. |
339 */ | 399 */ |
340 decorate: function() { | 400 decorate: function() { |
341 List.prototype.decorate.call(this); | 401 List.prototype.decorate.call(this); |
342 | 402 |
343 this.dataModel = new ArrayDataModel([]); | 403 this.contentType = this.parentNode.getAttribute('contentType'); |
| 404 this.mode = this.getAttribute('mode'); |
| 405 |
| 406 var exceptionList = this; |
| 407 function handleBlur(e) { |
| 408 // When the blur event happens we do not know who is getting focus so we |
| 409 // delay this a bit until we know if the new focus node is outside the l
ist. |
| 410 var doc = e.target.ownerDocument; |
| 411 window.setTimeout(function() { |
| 412 var activeElement = doc.activeElement; |
| 413 if (!exceptionList.contains(activeElement)) |
| 414 exceptionList.selectionModel.clear(); |
| 415 }, 50); |
| 416 } |
| 417 |
| 418 this.addEventListener('blur', handleBlur, true); |
344 | 419 |
345 // Whether the exceptions in this list allow an 'Ask every time' option. | 420 // Whether the exceptions in this list allow an 'Ask every time' option. |
346 this.enableAskOption = (this.contentType == 'plugins' && | 421 this.enableAskOption = (this.contentType == 'plugins' && |
347 templateData.enable_click_to_play); | 422 templateData.enable_click_to_play); |
| 423 |
| 424 this.reset(); |
348 }, | 425 }, |
349 | 426 |
350 /** | 427 /** |
351 * Creates an item to go in the list. | 428 * Creates an item to go in the list. |
352 * @param {Object} entry The element from the data model for this row. | 429 * @param {Object} entry The element from the data model for this row. |
353 */ | 430 */ |
354 createItem: function(entry) { | 431 createItem: function(entry) { |
355 return new ExceptionsListItem(this.contentType, | 432 if (entry) { |
356 this.mode, | 433 return new ExceptionsListItem(this.contentType, |
357 this.enableAskOption, | 434 this.mode, |
358 entry); | 435 this.enableAskOption, |
| 436 entry); |
| 437 } else { |
| 438 return new ExceptionsAddRowListItem(this.contentType, |
| 439 this.mode, |
| 440 this.enableAskOption); |
| 441 } |
359 }, | 442 }, |
360 | 443 |
361 /** | 444 /** |
362 * Adds an exception to the js model. | 445 * Adds an exception to the js model. |
363 * @param {Object} entry A dictionary of values for the exception. | 446 * @param {Object} entry A dictionary of values for the exception. |
364 */ | 447 */ |
365 addException: function(entry) { | 448 addException: function(entry) { |
366 this.dataModel.push(entry); | 449 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 }, | 450 }, |
378 | 451 |
379 /** | 452 /** |
380 * The browser has finished checking a pattern for validity. Update the | 453 * The browser has finished checking a pattern for validity. Update the |
381 * list item to reflect this. | 454 * list item to reflect this. |
382 * @param {string} pattern The pattern. | 455 * @param {string} pattern The pattern. |
383 * @param {bool} valid Whether said pattern is valid in the context of | 456 * @param {bool} valid Whether said pattern is valid in the context of |
384 * a content exception setting. | 457 * a content exception setting. |
385 */ | 458 */ |
386 patternValidityCheckComplete: function(pattern, valid) { | 459 patternValidityCheckComplete: function(pattern, valid) { |
387 for (var i = 0; i < this.dataModel.length; i++) { | 460 var listItems = this.items; |
388 var listItem = this.getListItemByIndex(i); | 461 for (var i = 0; i < listItems.length; i++) { |
389 if (listItem) | 462 var listItem = listItems[i]; |
390 listItem.maybeSetPatternValid(pattern, valid); | 463 // Don't do anything for messages for the item if it is not the intended |
| 464 // recipient, or if the response is stale (i.e. the input value has |
| 465 // changed since we sent the request to analyze it). |
| 466 if (pattern == listItem.input.value) |
| 467 listItem.setPatternValid(valid); |
391 } | 468 } |
392 }, | 469 }, |
393 | 470 |
394 /** | 471 /** |
395 * Removes all exceptions from the js model. | 472 * Removes all exceptions from the js model. |
396 */ | 473 */ |
397 clear: function() { | 474 reset: function() { |
398 this.dataModel = new ArrayDataModel([]); | 475 // The null creates the Add New Exception row. |
| 476 this.dataModel = new ArrayDataModel([null]); |
399 }, | 477 }, |
400 | 478 |
401 /** | 479 /** |
402 * Removes all selected rows from browser's model. | 480 * Removes all selected rows from browser's model. |
403 */ | 481 */ |
404 removeSelectedRows: function() { | 482 removeSelectedRows: function() { |
405 // The first member is the content type; the rest of the values describe | 483 // The first member is the content type; the rest of the values describe |
406 // the patterns we are removing. | 484 // the patterns we are removing. |
407 var args = [this.contentType]; | 485 var args = [this.contentType]; |
408 var selectedItems = this.selectedItems; | 486 var selectedItems = this.selectedItems; |
(...skipping 18 matching lines...) Expand all Loading... |
427 */ | 505 */ |
428 editSelectedRow: function() { | 506 editSelectedRow: function() { |
429 var li = this.getListItem(this.selectedItem); | 507 var li = this.getListItem(this.selectedItem); |
430 if (li) | 508 if (li) |
431 li.editing = true; | 509 li.editing = true; |
432 } | 510 } |
433 }; | 511 }; |
434 | 512 |
435 return { | 513 return { |
436 ExceptionsListItem: ExceptionsListItem, | 514 ExceptionsListItem: ExceptionsListItem, |
| 515 ExceptionsAddRowListItem: ExceptionsAddRowListItem, |
437 ExceptionsList: ExceptionsList, | 516 ExceptionsList: ExceptionsList, |
438 }; | 517 }; |
439 }); | 518 }); |
OLD | NEW |