Index: chrome/browser/resources/options/content_settings_exceptions_area.js |
diff --git a/chrome/browser/resources/options/content_settings_exceptions_area.js b/chrome/browser/resources/options/content_settings_exceptions_area.js |
index f727476bee28a9e8e6e33398bf672301979f3b5a..70775599055e4600905fc9228411f3b903eba1ed 100644 |
--- a/chrome/browser/resources/options/content_settings_exceptions_area.js |
+++ b/chrome/browser/resources/options/content_settings_exceptions_area.js |
@@ -39,21 +39,27 @@ cr.define('options.contentSettings', function() { |
decorate: function() { |
ListItem.prototype.decorate.call(this); |
- // Labels for display mode. |
- var patternLabel = cr.doc.createElement('span'); |
- patternLabel.textContent = this.pattern; |
- this.appendChild(patternLabel); |
- |
- var settingLabel = cr.doc.createElement('span'); |
- settingLabel.textContent = this.settingForDisplay(); |
- settingLabel.className = 'exceptionSetting'; |
- this.appendChild(settingLabel); |
+ // Labels for display mode. |pattern| will be null for the 'add new |
+ // exception' row. |
+ if (this.pattern) { |
+ var patternLabel = cr.doc.createElement('span'); |
+ patternLabel.textContent = this.pattern; |
+ patternLabel.className = 'exceptionPattern'; |
+ this.appendChild(patternLabel); |
+ this.patternLabel = patternLabel; |
+ |
+ var settingLabel = cr.doc.createElement('span'); |
+ settingLabel.textContent = this.settingForDisplay(); |
+ settingLabel.className = 'exceptionSetting'; |
+ this.appendChild(settingLabel); |
+ this.settingLabel = settingLabel; |
+ } |
// Elements for edit mode. |
var input = cr.doc.createElement('input'); |
input.type = 'text'; |
this.appendChild(input); |
- input.className = 'exceptionInput hidden'; |
+ input.className = 'exceptionPattern hidden'; |
var select = cr.doc.createElement('select'); |
var optionAllow = cr.doc.createElement('option'); |
@@ -93,26 +99,23 @@ cr.define('options.contentSettings', function() { |
// empty input. |
this.inputIsValid = true; |
- this.patternLabel = patternLabel; |
- this.settingLabel = settingLabel; |
this.input = input; |
this.select = select; |
this.optionAllow = optionAllow; |
this.optionBlock = optionBlock; |
this.updateEditables(); |
- if (!this.pattern) |
- input.value = templateData.examplePattern; |
var listItem = this; |
- this.ondblclick = function(event) { |
+ |
+ this.addEventListener('selectedChange', function(event) { |
// Editing notifications and geolocation is disabled for now. |
if (listItem.contentType == 'notifications' || |
listItem.contentType == 'location') |
return; |
- listItem.editing = true; |
- }; |
+ listItem.editing = listItem.selected; |
+ }); |
// Handle events on the editable nodes. |
input.oninput = function(event) { |
@@ -132,32 +135,14 @@ cr.define('options.contentSettings', function() { |
case 'U+001B': // Esc |
// Reset the inputs. |
listItem.updateEditables(); |
- if (listItem.pattern) |
- listItem.maybeSetPatternValid(listItem.pattern, true); |
+ listItem.setPatternValid(true); |
case 'Enter': |
- if (listItem.parentNode) |
- listItem.parentNode.focus(); |
+ listItem.ownerDocument.activeElement.blur(); |
} |
} |
- function handleBlur(e) { |
- // When the blur event happens we do not know who is getting focus so we |
- // delay this a bit since we want to know if the other input got focus |
- // before deciding if we should exit edit mode. |
- var doc = e.target.ownerDocument; |
- window.setTimeout(function() { |
- var activeElement = doc.activeElement; |
- if (!listItem.contains(activeElement)) { |
- listItem.editing = false; |
- } |
- }, 50); |
- } |
- |
input.addEventListener('keydown', handleKeydown); |
- input.addEventListener('blur', handleBlur); |
- |
select.addEventListener('keydown', handleKeydown); |
- select.addEventListener('blur', handleBlur); |
}, |
/** |
@@ -199,20 +184,12 @@ cr.define('options.contentSettings', function() { |
}, |
/** |
- * Update this list item to reflect whether the input is a valid pattern |
- * if |pattern| matches the text currently in the input. |
- * @param {string} pattern The pattern. |
+ * Update this list item to reflect whether the input is a valid pattern. |
* @param {boolean} valid Whether said pattern is valid in the context of |
* a content exception setting. |
*/ |
- maybeSetPatternValid: function(pattern, valid) { |
- // Don't do anything for messages where we are not the intended recipient, |
- // or if the response is stale (i.e. the input value has changed since we |
- // sent the request to analyze it). |
- if (pattern != this.input.value) |
- return; |
- |
- if (valid) |
+ setPatternValid: function(valid) { |
+ if (valid || !this.input.value) |
this.input.setCustomValidity(''); |
else |
this.input.setCustomValidity(' '); |
@@ -221,10 +198,18 @@ cr.define('options.contentSettings', function() { |
}, |
/** |
+ * Set the <input> to its original contents. Used when the user quits |
+ * editing. |
+ */ |
+ resetInput: function() { |
+ this.input.value = this.pattern; |
+ }, |
+ |
+ /** |
* Copy the data model values to the editable nodes. |
*/ |
updateEditables: function() { |
- this.input.value = this.pattern; |
+ this.resetInput(); |
if (this.setting == 'allow') |
this.optionAllow.selected = true; |
@@ -237,6 +222,17 @@ cr.define('options.contentSettings', function() { |
}, |
/** |
+ * Fiddle with the display of elements of this list item when the editing |
+ * mode changes. |
+ */ |
+ toggleVisibilityForEditing: function() { |
+ this.patternLabel.classList.toggle('hidden'); |
+ this.settingLabel.classList.toggle('hidden'); |
+ this.input.classList.toggle('hidden'); |
+ this.select.classList.toggle('hidden'); |
+ }, |
+ |
+ /** |
* Whether the user is currently able to edit the list item. |
* @type {boolean} |
*/ |
@@ -248,54 +244,41 @@ cr.define('options.contentSettings', function() { |
if (oldEditing == editing) |
return; |
- var listItem = this; |
- var pattern = this.pattern; |
- var setting = this.setting; |
- var patternLabel = this.patternLabel; |
- var settingLabel = this.settingLabel; |
var input = this.input; |
- var select = this.select; |
- var optionAllow = this.optionAllow; |
- var optionBlock = this.optionBlock; |
- var optionSession = this.optionSession; |
- var optionAsk = this.optionAsk; |
- |
- // Just delete this row if it was added via the Add button. |
- if (!editing && !pattern && !input.value) { |
- var model = listItem.parentNode.dataModel; |
- model.splice(model.indexOf(listItem.dataItem), 1); |
- return; |
- } |
- // Check that we have a valid pattern and if not we do not change the |
- // editing mode. |
- if (!editing && (!this.inputValidityKnown || !this.inputIsValid)) { |
- input.focus(); |
- input.select(); |
- return; |
- } |
- |
- patternLabel.classList.toggle('hidden'); |
- settingLabel.classList.toggle('hidden'); |
- input.classList.toggle('hidden'); |
- select.classList.toggle('hidden'); |
- |
- var doc = this.ownerDocument; |
- var area = doc.querySelector('div[contentType=' + |
- listItem.contentType + '][mode=' + listItem.mode + ']'); |
- area.enableAddAndEditButtons(!editing); |
+ this.toggleVisibilityForEditing(); |
if (editing) { |
this.setAttribute('editing', ''); |
cr.ui.limitInputWidth(input, this, 20); |
- input.focus(); |
- input.select(); |
+ // When this is called in response to the selectedChange event, |
+ // the list grabs focus immediately afterwards. Thus we must delay |
+ // our focus grab. |
+ window.setTimeout(function() { |
+ input.focus(); |
+ input.select(); |
+ }, 50); |
+ |
+ // TODO(estade): should we insert example text here for the AddNewRow |
+ // input? |
} else { |
this.removeAttribute('editing'); |
+ // Check that we have a valid pattern and if not we do not, abort |
+ // changes to the exception. |
+ if (!this.inputValidityKnown || !this.inputIsValid) { |
+ this.updateEditables(); |
+ this.setPatternValid(true); |
+ return; |
+ } |
+ |
var newPattern = input.value; |
var newSetting; |
+ var optionAllow = this.optionAllow; |
+ var optionBlock = this.optionBlock; |
+ var optionSession = this.optionSession; |
+ var optionAsk = this.optionAsk; |
if (optionAllow.selected) |
newSetting = 'allow'; |
else if (optionBlock.selected) |
@@ -305,26 +288,103 @@ cr.define('options.contentSettings', function() { |
else if (optionAsk && optionAsk.selected) |
newSetting = 'ask'; |
- // Empty edit - do nothing. |
- if (pattern == newPattern && newSetting == this.setting) |
- return; |
+ this.finishEdit(newPattern, newSetting); |
+ } |
+ }, |
- this.pattern = patternLabel.textContent = newPattern; |
- this.setting = newSetting; |
- settingLabel.textContent = this.settingForDisplay(); |
+ /** |
+ * Editing is complete; update the model. |
+ * @type {string} newPattern The pattern that the user entered. |
+ * @type {string} newSetting The setting the user chose. |
+ */ |
+ finishEdit: function(newPattern, newSetting) { |
+ // Empty edit - do nothing. |
+ if (newPattern == this.pattern && newSetting == this.setting) |
+ return; |
- if (pattern != this.pattern) { |
- chrome.send('removeExceptions', |
- [this.contentType, this.mode, pattern]); |
- } |
+ this.patternLabel.textContent = newPattern; |
+ this.settingLabel.textContent = this.settingForDisplay(); |
+ var oldPattern = this.pattern; |
+ this.pattern = newPattern; |
+ this.setting = newSetting; |
- chrome.send('setException', |
- [this.contentType, this.mode, this.pattern, this.setting]); |
+ if (oldPattern != newPattern) { |
+ chrome.send('removeExceptions', |
+ [this.contentType, this.mode, oldPattern]); |
} |
+ |
+ chrome.send('setException', |
+ [this.contentType, this.mode, newPattern, newSetting]); |
} |
}; |
/** |
+ * Creates a new list item for the Add New Item row, which doesn't represent |
+ * an actual entry in the exceptions list but allows the user to add new |
+ * exceptions. |
+ * @param {string} contentType The type of the list. |
+ * @param {string} mode The browser mode, 'otr' or 'normal'. |
+ * @param {boolean} enableAskOption Whether to show an 'ask every time' |
+ * option in the select. |
+ * @constructor |
+ * @extends {cr.ui.ExceptionsListItem} |
+ */ |
+ function ExceptionsAddRowListItem(contentType, mode, enableAskOption) { |
+ var el = cr.doc.createElement('li'); |
+ el.mode = mode; |
+ el.contentType = contentType; |
+ el.enableAskOption = enableAskOption; |
+ el.dataItem = []; |
+ el.__proto__ = ExceptionsAddRowListItem.prototype; |
+ el.decorate(); |
+ |
+ return el; |
+ } |
+ |
+ ExceptionsAddRowListItem.prototype = { |
+ __proto__: ExceptionsListItem.prototype, |
+ |
+ decorate: function() { |
+ ExceptionsListItem.prototype.decorate.call(this); |
+ |
+ this.input.placeholder = templateData.addNewExceptionInstructions; |
+ this.input.classList.remove('hidden'); |
+ this.select.classList.remove('hidden'); |
+ |
+ // Do we always want a default of allow? |
+ this.setting = 'allow'; |
+ }, |
+ |
+ /** |
+ * Clear the <input> and let the placeholder text show again. |
+ */ |
+ resetInput: function() { |
+ this.input.value = ''; |
+ }, |
+ |
+ /** |
+ * No elements show or hide when going into edit mode, so do nothing. |
+ */ |
+ toggleVisibilityForEditing: function() { |
+ // No-op. |
+ }, |
+ |
+ /** |
+ * Editing is complete; update the model. As long as the pattern isn't |
+ * empty, we'll just add it. |
+ * @type {string} newPattern The pattern that the user entered. |
+ * @type {string} newSetting The setting the user chose. |
+ */ |
+ finishEdit: function(newPattern, newSetting) { |
+ if (newPattern == '') |
+ return; |
+ |
+ chrome.send('setException', |
+ [this.contentType, this.mode, newPattern, newSetting]); |
+ }, |
+ }; |
+ |
+ /** |
* Creates a new exceptions list. |
* @constructor |
* @extends {cr.ui.List} |
@@ -340,11 +400,29 @@ cr.define('options.contentSettings', function() { |
decorate: function() { |
List.prototype.decorate.call(this); |
- this.dataModel = new ArrayDataModel([]); |
+ this.contentType = this.parentNode.getAttribute('contentType'); |
+ this.mode = this.getAttribute('mode'); |
+ |
+ var exceptionList = this; |
+ function handleBlur(e) { |
+ // When the blur event happens we do not know who is getting focus so we |
+ // delay this a bit until we know if the new focus node is outside the |
+ // list. |
+ var doc = e.target.ownerDocument; |
+ window.setTimeout(function() { |
+ var activeElement = doc.activeElement; |
+ if (!exceptionList.contains(activeElement)) |
+ exceptionList.selectionModel.clear(); |
+ }, 50); |
+ } |
+ |
+ this.addEventListener('blur', handleBlur, true); |
// Whether the exceptions in this list allow an 'Ask every time' option. |
this.enableAskOption = (this.contentType == 'plugins' && |
templateData.enable_click_to_play); |
+ |
+ this.reset(); |
}, |
/** |
@@ -352,10 +430,16 @@ cr.define('options.contentSettings', function() { |
* @param {Object} entry The element from the data model for this row. |
*/ |
createItem: function(entry) { |
- return new ExceptionsListItem(this.contentType, |
- this.mode, |
- this.enableAskOption, |
- entry); |
+ if (entry) { |
+ return new ExceptionsListItem(this.contentType, |
+ this.mode, |
+ this.enableAskOption, |
+ entry); |
+ } else { |
+ return new ExceptionsAddRowListItem(this.contentType, |
+ this.mode, |
+ this.enableAskOption); |
+ } |
}, |
/** |
@@ -364,16 +448,6 @@ cr.define('options.contentSettings', function() { |
*/ |
addException: function(entry) { |
this.dataModel.push(entry); |
- |
- // When an empty row is added, put it into editing mode. |
- if (!entry['displayPattern'] && !entry['setting']) { |
- var index = this.dataModel.length - 1; |
- var sm = this.selectionModel; |
- sm.anchorIndex = sm.leadIndex = sm.selectedIndex = index; |
- this.scrollIndexIntoView(index); |
- var li = this.getListItemByIndex(index); |
- li.editing = true; |
- } |
}, |
/** |
@@ -384,18 +458,23 @@ cr.define('options.contentSettings', function() { |
* a content exception setting. |
*/ |
patternValidityCheckComplete: function(pattern, valid) { |
- for (var i = 0; i < this.dataModel.length; i++) { |
- var listItem = this.getListItemByIndex(i); |
- if (listItem) |
- listItem.maybeSetPatternValid(pattern, valid); |
+ var listItems = this.items; |
+ for (var i = 0; i < listItems.length; i++) { |
+ var listItem = listItems[i]; |
+ // Don't do anything for messages for the item if it is not the intended |
+ // recipient, or if the response is stale (i.e. the input value has |
+ // changed since we sent the request to analyze it). |
+ if (pattern == listItem.input.value) |
+ listItem.setPatternValid(valid); |
} |
}, |
/** |
* Removes all exceptions from the js model. |
*/ |
- clear: function() { |
- this.dataModel = new ArrayDataModel([]); |
+ reset: function() { |
+ // The null creates the Add New Exception row. |
+ this.dataModel = new ArrayDataModel([null]); |
}, |
/** |
@@ -434,6 +513,7 @@ cr.define('options.contentSettings', function() { |
return { |
ExceptionsListItem: ExceptionsListItem, |
+ ExceptionsAddRowListItem: ExceptionsAddRowListItem, |
ExceptionsList: ExceptionsList, |
}; |
}); |