Chromium Code Reviews| Index: third_party/WebKit/Source/devtools/front_end/accessibility/ARIAAttributesView.js |
| diff --git a/third_party/WebKit/Source/devtools/front_end/accessibility/ARIAAttributesView.js b/third_party/WebKit/Source/devtools/front_end/accessibility/ARIAAttributesView.js |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..c5f19c058a5534f2c94afe81da5a4bfc68dd9a76 |
| --- /dev/null |
| +++ b/third_party/WebKit/Source/devtools/front_end/accessibility/ARIAAttributesView.js |
| @@ -0,0 +1,316 @@ |
| +// Copyright 2016 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +/** |
| + * @constructor |
| + * @extends {WebInspector.AccessibilitySubPane} |
| + */ |
| +WebInspector.AXARIAAttributesSubPane = function() |
|
dgozman
2016/07/18 22:38:19
ARIAAttributesPane
aboxhall
2016/07/19 17:20:54
Done.
|
| +{ |
| + WebInspector.AccessibilitySubPane.call(this, WebInspector.UIString("ARIA Attributes")); |
| + |
| + this._noPropertiesInfo = this.createInfo(WebInspector.UIString("No ARIA attributes")); |
| + this._treeOutline = this.createTreeOutline(); |
| +}; |
| + |
| + |
| +WebInspector.AXARIAAttributesSubPane.prototype = { |
| + /** |
| + * @override |
| + * @param {?WebInspector.DOMNode} node |
| + */ |
| + setNode: function(node) |
| + { |
| + WebInspector.AccessibilitySubPane.prototype.setNode.call(this, node); |
| + this._treeOutline.removeChildren(); |
| + if (!this.node()) |
| + return; |
| + var target = this.node().target(); |
| + var foundARIAAttr = false; |
|
dgozman
2016/07/18 22:38:19
Avoid abbreviations: attr -> attribute. I'd call t
aboxhall
2016/07/19 17:20:54
Done.
|
| + var attributes = node.attributes(); |
| + for (var i = 0; i < attributes.length; ++i) { |
| + var attribute = attributes[i]; |
| + if (WebInspector.AXARIAAttributesSubPane.ARIAAttributes.indexOf(attribute.name) < 0) |
| + continue; |
| + this._treeOutline.appendChild(new WebInspector.ARIAAttributesTreeElement(this, attribute, target)); |
| + foundARIAAttr = true; |
| + } |
| + |
| + if (foundARIAAttr) { |
|
dgozman
2016/07/18 22:38:20
Can just check |this._treeOutline.rootElement().ch
aboxhall
2016/07/19 17:20:53
Done.
|
| + this._noPropertiesInfo.classList.add("hidden"); |
| + this._treeOutline.element.classList.remove("hidden"); |
| + } else { |
| + this._noPropertiesInfo.classList.remove("hidden"); |
| + this._treeOutline.element.classList.add("hidden"); |
|
dgozman
2016/07/18 22:38:19
Use toggle instead:
this._treeOutline.element.clas
aboxhall
2016/07/19 17:20:54
Nice, TIL. Done.
|
| + } |
| + }, |
| + |
| + __proto__: WebInspector.AccessibilitySubPane.prototype |
| +}; |
| + |
| +/** |
| + * @constructor |
| + * @extends {WebInspector.AXNodePropertyTreeElement} |
| + * @param {!WebInspector.AXARIAAttributesSubPane} parentPane |
| + * @param {!Object} attribute |
|
dgozman
2016/07/18 22:38:19
Let's make it a typedef. I've found that DOMNode.a
aboxhall
2016/07/19 17:20:53
Done.
|
| + * @param {!WebInspector.Target} target |
| + */ |
| +WebInspector.ARIAAttributesTreeElement = function(parentPane, attribute, target) |
| +{ |
| + WebInspector.AXNodePropertyTreeElement.call(this, target); |
| + |
| + this._parentPane = parentPane; |
| + this._attribute = attribute; |
| +}; |
| + |
| +WebInspector.ARIAAttributesTreeElement.prototype = { |
| + /** |
| + * @override |
| + */ |
| + onattach: function() |
| + { |
| + this._populateListItem(); |
| + this.listItemElement.addEventListener("click", this._mouseClick.bind(this)); |
| + }, |
| + |
| + _populateListItem: function() { |
|
dgozman
2016/07/18 22:38:20
{ on next line
aboxhall
2016/07/19 17:20:54
Done.
|
| + this.listItemElement.removeChildren(); |
| + this.appendNameElement(this._attribute.name); |
| + this.listItemElement.createChild("span", "separator").textContent = ":\u00A0"; |
| + this.appendAttributeValueElement(this._attribute.value); |
| + }, |
| + |
| + /** |
| + * @override |
| + * @param {string} name |
| + */ |
| + appendNameElement: function(name) |
| + { |
| + this.nameElement = createElement("span"); |
|
dgozman
2016/07/18 22:38:20
_nameElement
aboxhall
2016/07/19 17:20:54
Done.
|
| + this.nameElement.textContent = name; |
| + this.nameElement.classList.add("ax-name"); |
| + this.nameElement.classList.add("monospace"); |
| + this.listItemElement.appendChild(this.nameElement); |
| + }, |
| + |
| + /** |
| + * @param {string} value |
| + * @return {?Element} |
|
dgozman
2016/07/18 22:38:19
- !Element
- Is it used at all? Let's not return a
aboxhall
2016/07/19 17:20:54
Done.
|
| + */ |
| + appendAttributeValueElement: function(value) |
| + { |
| + this.valueElement = WebInspector.ARIAAttributesTreeElement.createARIAValueElement(value); |
|
dgozman
2016/07/18 22:38:19
_valueElement
aboxhall
2016/07/19 17:20:53
Done.
|
| + this.listItemElement.appendChild(this.valueElement); |
| + return this.valueElement; |
| + }, |
| + |
| + _mouseClick: function(event) |
|
dgozman
2016/07/18 22:38:20
Annotate please.
aboxhall
2016/07/19 17:20:54
Done.
|
| + { |
| + event.consume(true); |
| + |
| + if (event.target === this.listItemElement) |
| + return; |
| + |
| + this.startEditing(); |
| + }, |
| + |
| + startEditing: function() |
|
dgozman
2016/07/18 22:38:19
All these editing methods should be private.
aboxhall
2016/07/19 17:20:54
Done.
|
| + { |
| + var valueElement = this.valueElement; |
| + |
| + if (WebInspector.isBeingEdited(valueElement)) |
| + return; |
| + |
| + var previousContent = valueElement.textContent; |
| + |
| + if (valueElement.parentElement) |
| + valueElement.parentElement.classList.add("child-editing"); |
| + |
| + /** |
| + * @param {string} previousContent |
| + * @param {!Event} event |
| + * @this {WebInspector.ARIAAttributesTreeElement} |
| + */ |
| + function blurListener(previousContent, event) |
| + { |
| + var text = event.target.textContent; |
| + text = this.value || text; |
|
dgozman
2016/07/18 22:38:20
What is |this.value| ?
aboxhall
2016/07/19 17:20:54
Ugh, should have been this._attribute.value, but I
|
| + this.editingCommitted(text, previousContent); |
| + } |
| + |
| + this._prompt = new WebInspector.AXARIAAttributesSubPane.ARIAAttributePrompt(this); |
| + this._prompt.setAutocompletionTimeout(0); |
| + var proxyElement = this._prompt.attachAndStartEditing(valueElement, blurListener.bind(this, previousContent)); |
| + |
| + proxyElement.addEventListener("keydown", this._editingValueKeyDown.bind(this, previousContent), false); |
| + |
| + valueElement.getComponentSelection().setBaseAndExtent(valueElement, 0, valueElement, 1); |
| + }, |
| + |
| + _removePrompt: function() |
| + { |
| + if (!this._prompt) |
| + return; |
| + this._prompt.detach(); |
| + delete this._prompt; |
| + }, |
| + |
|
dgozman
2016/07/18 22:38:19
nit: extra blank line
aboxhall
2016/07/19 17:20:53
Done.
|
| + |
| + /** |
| + * @param {string} userInput |
| + * @param {string} previousContent |
| + */ |
| + editingCommitted: function(userInput, previousContent) |
| + { |
| + this._removePrompt(); |
| + this.editingEnded(); |
| + |
| + // Make the changes to the attribute |
| + if (userInput !== previousContent) { |
| + var newText = this._attribute.name + '="' + userInput + '"'; |
| + this._parentPane.node().setAttribute(this._attribute.name, newText); |
|
dgozman
2016/07/18 22:38:19
Let's use setAttributeValue to not craft html here
aboxhall
2016/07/19 17:20:54
Done.
|
| + } |
| + }, |
| + |
| + editingEnded: function() |
| + { |
| + var editedElement = this.valueElement; |
| + // The proxyElement has been deleted, no need to remove listener. |
| + if (editedElement.parentElement) |
| + editedElement.parentElement.classList.remove("child-editing"); |
|
dgozman
2016/07/18 22:38:20
Is "child-editing" used anywhere?
aboxhall
2016/07/19 17:20:54
Hm, doesn't look like it.
|
| + }, |
| + |
| + editingCancelled: function() |
| + { |
| + this._removePrompt(); |
| + this._populateListItem(); |
| + this.editingEnded(); |
| + }, |
| + |
| + /** |
| + * @param {string} previousContent |
| + * @param {!Event} event |
| + */ |
| + _editingValueKeyDown: function(previousContent, event) |
| + { |
| + if (event.handled) |
| + return; |
| + |
| + var result; |
| + |
| + if (isEnterKey(event)) { |
| + result = "commit"; |
| + event.preventDefault(); |
| + } else if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code || event.keyIdentifier === "U+001B") { |
| + result = "cancel"; |
| + } |
| + |
| + if (result) { |
| + switch (result) { |
| + case "cancel": |
| + this.editingCancelled(); |
|
dgozman
2016/07/18 22:38:19
Just call this instead of |result = "commit"|.
aboxhall
2016/07/19 17:20:54
Done.
|
| + break; |
| + case "commit": |
| + this.editingCommitted(event.target.textContent, result); |
|
dgozman
2016/07/18 22:38:19
Same for cancel.
aboxhall
2016/07/19 17:20:54
Done.
|
| + break; |
| + } |
| + |
| + event.consume(); |
|
dgozman
2016/07/18 22:38:20
And consume in both cases. Note: consume(true) wil
aboxhall
2016/07/19 17:20:53
Done.
|
| + return; |
| + } |
| + }, |
| + |
| + __proto__: WebInspector.AXNodePropertyTreeElement.prototype |
| +}; |
| + |
| +/** |
| + * @param {string} value |
| + * @return {!Element} |
| + */ |
| +WebInspector.ARIAAttributesTreeElement.createARIAValueElement = function(value) |
| +{ |
| + var valueElement = createElementWithClass("span", "monospace"); |
| + // TODO(aboxhall): quotation marks? |
| + valueElement.setTextContentTruncatedIfNeeded(value || ""); |
| + return valueElement; |
| +}; |
| + |
| +/** |
| + * @constructor |
| + * TODO: completions; see StylesSidebarPane.js |
| + * @extends {WebInspector.TextPrompt} |
| + * @param {!WebInspector.ARIAAttributesTreeElement} treeElement |
| + */ |
| +WebInspector.AXARIAAttributesSubPane.ARIAAttributePrompt = function(treeElement) |
| +{ |
| + WebInspector.TextPrompt.call(this, this._buildPropertyCompletions.bind(this)); |
| + |
| + this._treeElement = treeElement; |
| +}; |
| + |
| +WebInspector.AXARIAAttributesSubPane.ARIAAttributePrompt.prototype = { |
| + /** |
| + * @param {!Element} proxyElement |
| + * @param {string} text |
| + * @param {number} cursorOffset |
| + * @param {!Range} wordRange |
| + * @param {boolean} force |
| + * @param {function(!Array.<string>, number=)} completionsReadyCallback |
| + */ |
| + _buildPropertyCompletions: function(proxyElement, text, cursorOffset, wordRange, force, completionsReadyCallback) |
| + { |
| + // TODO(aboxhall): replace placeholder implementation with real implementation |
| + |
| + var prefix = wordRange.toString().toLowerCase(); |
|
dgozman
2016/07/18 22:38:20
Let's remove this code for now, until we have an i
aboxhall
2016/07/19 17:20:54
Done.
|
| + if (!prefix && !force && (proxyElement.textContent.length)) { |
| + completionsReadyCallback([]); |
| + return; |
| + } |
| + |
| + var results = []; |
| + completionsReadyCallback(results, 0); |
| + }, |
| + |
| + __proto__: WebInspector.TextPrompt.prototype |
| +} |
| + |
| + |
| +WebInspector.AXARIAAttributesSubPane.ARIAAttributes = [ |
|
dgozman
2016/07/18 22:38:20
WebInspector.ARIAAttributesPane._attributes
aboxhall
2016/07/19 17:20:54
Done.
|
| + "role", |
| + "aria-busy", |
| + "aria-checked", |
| + "aria-disabled", |
| + "aria-expanded", |
| + "aria-grabbed", |
| + "aria-hidden", |
| + "aria-invalid", |
| + "aria-pressed", |
| + "aria-selected", |
| + "aria-activedescendant", |
| + "aria-atomic", |
| + "aria-autocomplete", |
| + "aria-controls", |
| + "aria-describedby", |
| + "aria-dropeffect", |
| + "aria-flowto", |
| + "aria-haspopup", |
| + "aria-label", |
| + "aria-labelledby", |
| + "aria-level", |
| + "aria-live", |
| + "aria-multiline", |
| + "aria-multiselectable", |
| + "aria-orientation", |
| + "aria-owns", |
| + "aria-posinset", |
| + "aria-readonly", |
| + "aria-relevant", |
| + "aria-required", |
| + "aria-setsize", |
| + "aria-sort", |
| + "aria-valuemax", |
| + "aria-valuemin", |
| + "aria-valuenow", |
| + "aria-valuetext", |
| +]; |