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

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: manual filter 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 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
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
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
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 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698