--- a/devtools/client/shared/autocomplete-popup.js
+++ b/devtools/client/shared/autocomplete-popup.js
@@ -1,54 +1,52 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
-const {Cc, Ci, Cu} = require("chrome");
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const Services = require("Services");
const {gDevTools} = require("devtools/client/framework/devtools");
-const events = require("devtools/shared/event-emitter");
+const events = require("devtools/shared/event-emitter");
/**
* Autocomplete popup UI implementation.
*
* @constructor
- * @param nsIDOMDocument aDocument
+ * @param {nsIDOMDocument} document
* The document you want the popup attached to.
- * @param Object aOptions
+ * @param {Object} options
* An object consiting any of the following options:
* - panelId {String} The id for the popup panel.
* - listBoxId {String} The id for the richlistbox inside the panel.
* - position {String} The position for the popup panel.
* - theme {String} String related to the theme of the popup.
* - autoSelect {Boolean} Boolean to allow the first entry of the popup
* panel to be automatically selected when the popup shows.
- * - direction {String} The direction of the text in the panel. rtl or ltr
+ * - direction {String} The direction of the text in the panel. rtl / ltr
* - onSelect {String} The select event handler for the richlistbox
* - onClick {String} The click event handler for the richlistbox.
- * - onKeypress {String} The keypress event handler for the richlistitems.
+ * - onKeypress {String} The keypress event handler for richlistitems.
*/
-function AutocompletePopup(aDocument, aOptions = {})
-{
- this._document = aDocument;
+function AutocompletePopup(document, options = {}) {
+ this._document = document;
- this.autoSelect = aOptions.autoSelect || false;
- this.position = aOptions.position || "after_start";
- this.direction = aOptions.direction || "ltr";
+ this.autoSelect = options.autoSelect || false;
+ this.position = options.position || "after_start";
+ this.direction = options.direction || "ltr";
- this.onSelect = aOptions.onSelect;
- this.onClick = aOptions.onClick;
- this.onKeypress = aOptions.onKeypress;
+ this.onSelect = options.onSelect;
+ this.onClick = options.onClick;
+ this.onKeypress = options.onKeypress;
- let id = aOptions.panelId || "devtools_autoCompletePopup";
- let theme = aOptions.theme || "dark";
+ let id = options.panelId || "devtools_autoCompletePopup";
+ let theme = options.theme || "dark";
// If theme is auto, use the devtools.theme pref
if (theme == "auto") {
theme = Services.prefs.getCharPref("devtools.theme");
this.autoThemeEnabled = true;
// Setup theme change listener.
this._handleThemeChange = this._handleThemeChange.bind(this);
gDevTools.on("pref-changed", this._handleThemeChange);
}
@@ -57,48 +55,46 @@ function AutocompletePopup(aDocument, aO
if (!this._panel) {
this._panel = this._document.createElementNS(XUL_NS, "panel");
this._panel.setAttribute("id", id);
this._panel.className = "devtools-autocomplete-popup devtools-monospace "
+ theme + "-theme";
this._panel.setAttribute("noautofocus", "true");
this._panel.setAttribute("level", "top");
- if (!aOptions.onKeypress) {
+ if (!options.onKeypress) {
this._panel.setAttribute("ignorekeys", "true");
}
// Stop this appearing as an alert to accessibility.
this._panel.setAttribute("role", "presentation");
let mainPopupSet = this._document.getElementById("mainPopupSet");
if (mainPopupSet) {
mainPopupSet.appendChild(this._panel);
- }
- else {
+ } else {
this._document.documentElement.appendChild(this._panel);
}
- }
- else {
+ } else {
this._list = this._panel.firstChild;
}
if (!this._list) {
this._list = this._document.createElementNS(XUL_NS, "richlistbox");
this._panel.appendChild(this._list);
// Open and hide the panel, so we initialize the API of the richlistbox.
this._panel.openPopup(null, this.position, 0, 0);
this._panel.hidePopup();
}
this._list.setAttribute("flex", "1");
this._list.setAttribute("seltype", "single");
- if (aOptions.listBoxId) {
- this._list.setAttribute("id", aOptions.listBoxId);
+ if (options.listBoxId) {
+ this._list.setAttribute("id", options.listBoxId);
}
this._list.className = "devtools-autocomplete-listbox " + theme + "-theme";
if (this.onSelect) {
this._list.addEventListener("select", this.onSelect, false);
}
if (this.onClick) {
@@ -123,62 +119,59 @@ AutocompletePopup.prototype = {
// Event handlers.
onSelect: null,
onClick: null,
onKeypress: null,
/**
* Open the autocomplete popup panel.
*
- * @param nsIDOMNode aAnchor
+ * @param {nsIDOMNode} anchor
* Optional node to anchor the panel to.
- * @param Number aXOffset
+ * @param {Number} xOffset
* Horizontal offset in pixels from the left of the node to the left
* of the popup.
- * @param Number aYOffset
+ * @param {Number} yOffset
* Vertical offset in pixels from the top of the node to the starting
* of the popup.
- * @param integer index
+ * @param {Number} index
* The position of item to select.
*/
- openPopup: function AP_openPopup(aAnchor, aXOffset = 0, aYOffset = 0, index)
- {
+ openPopup: function(anchor, xOffset = 0, yOffset = 0, index) {
this.__maxLabelLength = -1;
this._updateSize();
- this._panel.openPopup(aAnchor, this.position, aXOffset, aYOffset);
+ this._panel.openPopup(anchor, this.position, xOffset, yOffset);
if (this.autoSelect) {
this.selectItemAtIndex(index);
}
this.emit("popup-opened");
},
/**
* Select item at the provided index.
*
* @param {Number} index
* The position of the item to select.
*/
- selectItemAtIndex: function AP_selectItemAtIndex(index)
- {
+ selectItemAtIndex: function(index) {
if (typeof index != "number") {
// If no index was provided, select the item closest to the input.
let isAboveInput = this.position.includes("before");
index = isAboveInput ? this.itemCount - 1 : 0;
}
this.selectedIndex = index;
this._list.ensureIndexIsVisible(this._list.selectedIndex);
},
/**
* Hide the autocomplete popup panel.
*/
- hidePopup: function AP_hidePopup()
- {
+ hidePopup: function() {
// Return accessibility focus to the input.
this._document.activeElement.removeAttribute("aria-activedescendant");
this._panel.hidePopup();
},
/**
* Check if the autocomplete popup is open.
*/
@@ -188,18 +181,17 @@ AutocompletePopup.prototype = {
},
/**
* Destroy the object instance. Please note that the panel DOM elements remain
* in the DOM, because they might still be in use by other instances of the
* same code. It is the responsability of the client code to perform DOM
* cleanup.
*/
- destroy: function AP_destroy()
- {
+ destroy: function() {
if (this.isOpen) {
this.hidePopup();
}
if (this.onSelect) {
this._list.removeEventListener("select", this.onSelect, false);
}
@@ -220,54 +212,51 @@ AutocompletePopup.prototype = {
this._document = null;
this._list = null;
this._panel = null;
},
/**
* Get the autocomplete items array.
*
- * @param Number aIndex The index of the item what is wanted.
+ * @param {Number} index
+ * The index of the item what is wanted.
*
- * @return The autocomplete item at index aIndex.
+ * @return {Object} The autocomplete item at index index.
*/
- getItemAtIndex: function AP_getItemAtIndex(aIndex)
- {
- return this._list.getItemAtIndex(aIndex)._autocompleteItem;
+ getItemAtIndex: function(index) {
+ return this._list.getItemAtIndex(index)._autocompleteItem;
},
/**
* Get the autocomplete items array.
*
- * @return array
- * The array of autocomplete items.
+ * @return {Array} The array of autocomplete items.
*/
- getItems: function AP_getItems()
- {
+ getItems: function() {
let items = [];
- Array.forEach(this._list.childNodes, function(aItem) {
- items.push(aItem._autocompleteItem);
+ Array.forEach(this._list.childNodes, function(item) {
+ items.push(item._autocompleteItem);
});
return items;
},
/**
* Set the autocomplete items list, in one go.
*
- * @param array aItems
+ * @param {Array} items
* The list of items you want displayed in the popup list.
- * @param integer index
+ * @param {Number} index
* The position of the item to select.
*/
- setItems: function AP_setItems(aItems, index)
- {
+ setItems: function(items, index) {
this.clearItems();
- aItems.forEach(this.appendItem, this);
+ items.forEach(this.appendItem, this);
// Make sure that the new content is properly fitted by the XUL richlistbox.
if (this.isOpen) {
if (this.autoSelect) {
this.selectItemAtIndex(index);
}
this._updateSize();
}
@@ -291,51 +280,44 @@ AutocompletePopup.prototype = {
}
this.__maxLabelLength = max;
return this.__maxLabelLength;
},
/**
* Update the panel size to fit the content.
- *
- * @private
*/
- _updateSize: function AP__updateSize()
- {
+ _updateSize: function() {
if (!this._panel) {
return;
}
- this._list.style.width = (this._maxLabelLength + 3) +"ch";
+ this._list.style.width = (this._maxLabelLength + 3) + "ch";
this._list.ensureIndexIsVisible(this._list.selectedIndex);
},
/**
* Update accessibility appropriately when the selected item is changed.
- *
- * @private
*/
- _updateAriaActiveDescendant: function AP__updateAriaActiveDescendant()
- {
+ _updateAriaActiveDescendant: function() {
if (!this._list.selectedItem) {
// Return accessibility focus to the input.
this._document.activeElement.removeAttribute("aria-activedescendant");
return;
}
// Focus this for accessibility so users know about the selected item.
this._document.activeElement.setAttribute("aria-activedescendant",
this._list.selectedItem.id);
},
/**
* Clear all the items from the autocomplete list.
*/
- clearItems: function AP_clearItems()
- {
+ clearItems: function() {
// Reset the selectedIndex to -1 before clearing the list
this.selectedIndex = -1;
while (this._list.hasChildNodes()) {
this._list.removeChild(this._list.firstChild);
}
this.__maxLabelLength = -1;
@@ -349,256 +331,242 @@ AutocompletePopup.prototype = {
this._panel.height = "";
this._panel.top = "";
this._panel.left = "";
},
/**
* Getter for the index of the selected item.
*
- * @type number
+ * @type {Number}
*/
get selectedIndex() {
return this._list.selectedIndex;
},
/**
* Setter for the selected index.
*
- * @param number aIndex
+ * @param {Number} index
* The number (index) of the item you want to select in the list.
*/
- set selectedIndex(aIndex) {
- this._list.selectedIndex = aIndex;
+ set selectedIndex(index) {
+ this._list.selectedIndex = index;
if (this.isOpen && this._list.ensureIndexIsVisible) {
this._list.ensureIndexIsVisible(this._list.selectedIndex);
}
this._updateAriaActiveDescendant();
},
/**
* Getter for the selected item.
- * @type object
+ * @type Object
*/
get selectedItem() {
return this._list.selectedItem ?
this._list.selectedItem._autocompleteItem : null;
},
/**
* Setter for the selected item.
*
- * @param object aItem
+ * @param {Object} item
* The object you want selected in the list.
*/
- set selectedItem(aItem) {
- this._list.selectedItem = this._findListItem(aItem);
+ set selectedItem(item) {
+ this._list.selectedItem = this._findListItem(item);
if (this.isOpen) {
this._list.ensureIndexIsVisible(this._list.selectedIndex);
}
this._updateAriaActiveDescendant();
},
/**
* Append an item into the autocomplete list.
*
- * @param object aItem
+ * @param {Object} item
* The item you want appended to the list.
* The item object can have the following properties:
* - label {String} Property which is used as the displayed value.
* - preLabel {String} [Optional] The String that will be displayed
* before the label indicating that this is the already
* present text in the input box, and label is the text
* that will be auto completed. When this property is
* present, |preLabel.length| starting characters will be
* removed from label.
* - count {Number} [Optional] The number to represent the count of
* autocompleted label.
*/
- appendItem: function AP_appendItem(aItem)
- {
+ appendItem: function(item) {
let listItem = this._document.createElementNS(XUL_NS, "richlistitem");
// Items must have an id for accessibility.
listItem.id = this._panel.id + "_item_" + this._itemIdCounter++;
if (this.direction) {
listItem.setAttribute("dir", this.direction);
}
let label = this._document.createElementNS(XUL_NS, "label");
- label.setAttribute("value", aItem.label);
+ label.setAttribute("value", item.label);
label.setAttribute("class", "autocomplete-value");
- if (aItem.preLabel) {
+ if (item.preLabel) {
let preDesc = this._document.createElementNS(XUL_NS, "label");
- preDesc.setAttribute("value", aItem.preLabel);
+ preDesc.setAttribute("value", item.preLabel);
preDesc.setAttribute("class", "initial-value");
listItem.appendChild(preDesc);
- label.setAttribute("value", aItem.label.slice(aItem.preLabel.length));
+ label.setAttribute("value", item.label.slice(item.preLabel.length));
}
listItem.appendChild(label);
- if (aItem.count && aItem.count > 1) {
+ if (item.count && item.count > 1) {
let countDesc = this._document.createElementNS(XUL_NS, "label");
- countDesc.setAttribute("value", aItem.count);
+ countDesc.setAttribute("value", item.count);
countDesc.setAttribute("flex", "1");
countDesc.setAttribute("class", "autocomplete-count");
listItem.appendChild(countDesc);
}
- listItem._autocompleteItem = aItem;
+ listItem._autocompleteItem = item;
this._list.appendChild(listItem);
},
/**
* Find the richlistitem element that belongs to an item.
*
* @private
*
- * @param object aItem
+ * @param {Object} item
* The object you want found in the list.
*
- * @return nsIDOMNode|null
- * The nsIDOMNode that belongs to the given item object. This node is
- * the richlistitem element.
+ * @return {nsIDOMNode} The nsIDOMNode that belongs to the given item object.
+ * This node is the richlistitem element. Can be null.
*/
- _findListItem: function AP__findListItem(aItem)
- {
+ _findListItem: function(item) {
for (let i = 0; i < this._list.childNodes.length; i++) {
let child = this._list.childNodes[i];
- if (child._autocompleteItem == aItem) {
+ if (child._autocompleteItem == item) {
return child;
}
}
return null;
},
/**
* Remove an item from the popup list.
*
- * @param object aItem
+ * @param {Object} item
* The item you want removed.
*/
- removeItem: function AP_removeItem(aItem)
- {
- let item = this._findListItem(aItem);
- if (!item) {
+ removeItem: function(item) {
+ let listItem = this._findListItem(item);
+ if (!listItem) {
throw new Error("Item not found!");
}
- this._list.removeChild(item);
+ this._list.removeChild(listItem);
},
/**
* Getter for the number of items in the popup.
- * @type number
+ * @type {Number}
*/
get itemCount() {
return this._list.childNodes.length;
},
/**
* Getter for the height of each item in the list.
*
- * @private
- *
- * @type number
+ * @type {Number}
*/
get _itemHeight() {
return this._list.selectedItem.clientHeight;
},
/**
* Select the next item in the list.
*
- * @return object
+ * @return {Object}
* The newly selected item object.
*/
- selectNextItem: function AP_selectNextItem()
- {
+ selectNextItem: function() {
if (this.selectedIndex < (this.itemCount - 1)) {
this.selectedIndex++;
- }
- else {
+ } else {
this.selectedIndex = 0;
}
return this.selectedItem;
},
/**
* Select the previous item in the list.
*
- * @return object
+ * @return {Object}
* The newly-selected item object.
*/
- selectPreviousItem: function AP_selectPreviousItem()
- {
+ selectPreviousItem: function() {
if (this.selectedIndex > 0) {
this.selectedIndex--;
- }
- else {
+ } else {
this.selectedIndex = this.itemCount - 1;
}
return this.selectedItem;
},
/**
* Select the top-most item in the next page of items or
* the last item in the list.
*
- * @return object
+ * @return {Object}
* The newly-selected item object.
*/
- selectNextPageItem: function AP_selectNextPageItem()
- {
+ selectNextPageItem: function() {
let itemsPerPane = Math.floor(this._list.scrollHeight / this._itemHeight);
let nextPageIndex = this.selectedIndex + itemsPerPane + 1;
this.selectedIndex = nextPageIndex > this.itemCount - 1 ?
this.itemCount - 1 : nextPageIndex;
return this.selectedItem;
},
/**
* Select the bottom-most item in the previous page of items,
* or the first item in the list.
*
- * @return object
+ * @return {Object}
* The newly-selected item object.
*/
- selectPreviousPageItem: function AP_selectPreviousPageItem()
- {
+ selectPreviousPageItem: function() {
let itemsPerPane = Math.floor(this._list.scrollHeight / this._itemHeight);
let prevPageIndex = this.selectedIndex - itemsPerPane - 1;
this.selectedIndex = prevPageIndex < 0 ? 0 : prevPageIndex;
return this.selectedItem;
},
/**
* Focuses the richlistbox.
*/
- focus: function AP_focus()
- {
+ focus: function() {
this._list.focus();
},
/**
* Manages theme switching for the popup based on the devtools.theme pref.
*
* @private
*
- * @param String aEvent
+ * @param {String} event
* The name of the event. In this case, "pref-changed".
- * @param Object aData
+ * @param {Object} data
* An object passed by the emitter of the event. In this case, the
* object consists of three properties:
* - pref {String} The name of the preference that was modified.
* - newValue {Object} The new value of the preference.
* - oldValue {Object} The old value of the preference.
*/
- _handleThemeChange: function AP__handleThemeChange(aEvent, aData)
- {
- if (aData.pref == "devtools.theme") {
- this._panel.classList.toggle(aData.oldValue + "-theme", false);
- this._panel.classList.toggle(aData.newValue + "-theme", true);
- this._list.classList.toggle(aData.oldValue + "-theme", false);
- this._list.classList.toggle(aData.newValue + "-theme", true);
+ _handleThemeChange: function(event, data) {
+ if (data.pref == "devtools.theme") {
+ this._panel.classList.toggle(data.oldValue + "-theme", false);
+ this._panel.classList.toggle(data.newValue + "-theme", true);
+ this._list.classList.toggle(data.oldValue + "-theme", false);
+ this._list.classList.toggle(data.newValue + "-theme", true);
}
},
};
--- a/devtools/client/shared/inplace-editor.js
+++ b/devtools/client/shared/inplace-editor.js
@@ -226,17 +226,17 @@ function InplaceEditor(options, event) {
if (typeof options.advanceChars === "function") {
this._advanceChars = options.advanceChars;
} else {
let advanceCharcodes = {};
let advanceChars = options.advanceChars || "";
for (let i = 0; i < advanceChars.length; i++) {
advanceCharcodes[advanceChars.charCodeAt(i)] = true;
}
- this._advanceChars = aCharCode => aCharCode in advanceCharcodes;
+ this._advanceChars = charCode => charCode in advanceCharcodes;
}
// Hide the provided element and add our editor.
this.originalDisplay = this.elt.style.display;
this.elt.style.display = "none";
this.elt.parentNode.insertBefore(this.input, this.elt);
this.input.focus();
@@ -301,19 +301,21 @@ InplaceEditor.prototype = {
// Already cleared.
return;
}
this.input.removeEventListener("blur", this._onBlur, false);
this.input.removeEventListener("keypress", this._onKeyPress, false);
this.input.removeEventListener("keyup", this._onKeyup, false);
this.input.removeEventListener("input", this._onInput, false);
- this.input.removeEventListener("dblclick", this._stopEventPropagation, false);
+ this.input.removeEventListener("dblclick", this._stopEventPropagation,
+ false);
this.input.removeEventListener("click", this._stopEventPropagation, false);
- this.input.removeEventListener("mousedown", this._stopEventPropagation, false);
+ this.input.removeEventListener("mousedown", this._stopEventPropagation,
+ false);
this._stopAutosize();
this.elt.style.display = this.originalDisplay;
if (this.doc.activeElement == this.input) {
this.elt.focus();
}
@@ -476,23 +478,25 @@ InplaceEditor.prototype = {
if (newValue) {
incrementedValue = newValue.value;
selection = newValue.selection;
}
} else {
if (type === "rgb" || type === "hsl") {
info = {};
let part = value.substring(range.start, selStart).split(",").length - 1;
- if (part === 3) { // alpha
+ if (part === 3) {
+ // alpha
info.minValue = 0;
info.maxValue = 1;
} else if (type === "rgb") {
info.minValue = 0;
info.maxValue = 255;
- } else if (part !== 0) { // hsl percentage
+ } else if (part !== 0) {
+ // hsl percentage
info.minValue = 0;
info.maxValue = 100;
// select the previous number if the selection is at the end of a
// percentage sign.
if (value.charAt(selStart - 1) === "%") {
--selStart;
}
@@ -547,17 +551,17 @@ InplaceEditor.prototype = {
*
* @param {String} value
* Property value.
* @param {Number} offset
* Starting index of value.
* @return {Object} object with properties 'value', 'start', 'end', and
* 'type'.
*/
- _parseCSSValue: function(value, offset) {
+ _parseCSSValue: function(value, offset) {
const reSplitCSS = /(url\("?[^"\)]+"?\)?)|(rgba?\([^)]*\)?)|(hsla?\([^)]*\)?)|(#[\dA-Fa-f]+)|(-?\d*\.?\d+(%|[a-z]{1,4})?)|"([^"]*)"?|'([^']*)'?|([^,\s\/!\(\)]+)|(!(.*)?)/;
let start = 0;
let m;
// retreive values from left to right until we find the one at our offset
while ((m = reSplitCSS.exec(value)) &&
(m.index + m[0].length < offset)) {
value = value.substr(m.index + m[0].length);
@@ -616,18 +620,20 @@ InplaceEditor.prototype = {
// the disallowed case of just part of a known number being selected.
// Use that number.
start = offset;
end = offsetEnd;
} else {
// Parse periods as belonging to the number only if we are in a known
// number context. (This makes incrementing the 1 in 'image1.gif' work.)
let pattern = "[" + (info ? "0-9." : "0-9") + "]*";
- let before = new RegExp(pattern + "$").exec(value.substr(0, offset))[0].length;
- let after = new RegExp("^" + pattern).exec(value.substr(offset))[0].length;
+ let before = new RegExp(pattern + "$")
+ .exec(value.substr(0, offset))[0].length;
+ let after = new RegExp("^" + pattern)
+ .exec(value.substr(offset))[0].length;
start = offset - before;
end = offset + after;
// Expand the number to contain an initial minus sign if it seems
// free-standing.
if (value.charAt(start - 1) === "-" &&
(start - 1 === 0 || /[ (:,='"]/.test(value.charAt(start - 2)))) {
@@ -1199,33 +1205,35 @@ InplaceEditor.prototype = {
// This emit is mainly to make the test flow simpler.
this.emit("after-suggest", "nothing to autocomplete");
return;
}
// Detecting if cursor is at property or value;
let match = query.match(/([:;"'=]?)\s*([^"';:=]+)?$/);
if (match && match.length >= 2) {
- if (match[1] == ":") { // We are in CSS value completion
+ if (match[1] == ":") {
+ // We are in CSS value completion
let propertyName =
query.match(/[;"'=]\s*([^"';:= ]+)\s*:\s*[^"';:=]*$/)[1];
list =
["!important;",
...domUtils.getCSSValuesForProperty(propertyName)];
let matchLastQuery = /([^\s,.\/]+$)/.exec(match[2] || "");
if (matchLastQuery) {
startCheckQuery = matchLastQuery[0];
} else {
startCheckQuery = "";
}
if (!match[2]) {
// Don't suggest '!important' without any manually typed character
list.splice(0, 1);
}
- } else if (match[1]) { // We are in CSS property name completion
+ } else if (match[1]) {
+ // We are in CSS property name completion
list = CSSPropertyList;
startCheckQuery = match[2];
}
if (startCheckQuery == null) {
// This emit is mainly to make the test flow simpler.
this.emit("after-suggest", "nothing to autocomplete");
return;
}