Bug 893965 - Autocomplete CSS properties in the rule view, r=mratcliffe, msucan

This commit is contained in:
Girish Sharma
2013-07-26 04:35:05 +05:30
parent 6e3888ec2e
commit 2a613d554e
9 changed files with 478 additions and 16 deletions

View File

@@ -24,9 +24,16 @@
"use strict";
const {Ci, Cu} = require("chrome");
const {Ci, Cu, Cc} = require("chrome");
const HTML_NS = "http://www.w3.org/1999/xhtml";
const CONTENT_TYPES = {
PLAIN_TEXT: 0,
CSS_VALUE: 1,
CSS_MIXED: 2,
CSS_PROPERTY: 3,
};
const MAX_POPUP_ENTRIES = 10;
const FOCUS_FORWARD = Ci.nsIFocusManager.MOVEFOCUS_FORWARD;
const FOCUS_BACKWARD = Ci.nsIFocusManager.MOVEFOCUS_BACKWARD;
@@ -160,6 +167,8 @@ function InplaceEditor(aOptions, aEvent)
this.initial = aOptions.initial ? aOptions.initial : this.elt.textContent;
this.multiline = aOptions.multiline || false;
this.stopOnReturn = !!aOptions.stopOnReturn;
this.contentType = aOptions.contentType || CONTENT_TYPES.PLAIN_TEXT;
this.popup = aOptions.popup;
this._onBlur = this._onBlur.bind(this);
this._onKeyPress = this._onKeyPress.bind(this);
@@ -208,6 +217,8 @@ function InplaceEditor(aOptions, aEvent)
exports.InplaceEditor = InplaceEditor;
InplaceEditor.CONTENT_TYPES = CONTENT_TYPES;
InplaceEditor.prototype = {
_createInput: function InplaceEditor_createEditor()
{
@@ -679,6 +690,10 @@ InplaceEditor.prototype = {
return;
}
if (this.popup) {
this.popup.hidePopup();
}
this._applied = true;
if (this.done) {
@@ -732,9 +747,31 @@ InplaceEditor.prototype = {
increment *= smallIncrement;
}
let cycling = false;
if (increment && this._incrementValue(increment) ) {
this._updateSize();
prevent = true;
} else if (increment && this.popup && this.popup.isOpen) {
cycling = true;
prevent = true;
if (increment > 0) {
this.popup.selectPreviousItem();
} else {
this.popup.selectNextItem();
}
this.input.value = this.popup.selectedItem.label;
this._updateSize();
}
if (aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_BACK_SPACE ||
aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_DELETE ||
aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_LEFT ||
aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_RIGHT) {
if (this.popup && this.popup.isOpen) {
this.popup.hidePopup();
}
} else if (!cycling) {
this._maybeSuggestCompletion();
}
if (this.multiline &&
@@ -756,6 +793,9 @@ InplaceEditor.prototype = {
direction = null;
}
// Now we don't want to suggest anything as we are moving out.
this._preventSuggestions = true;
let input = this.input;
this._apply();
@@ -775,6 +815,8 @@ InplaceEditor.prototype = {
this._clear();
} else if (aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE) {
// Cancel and blur ourselves.
// Now we don't want to suggest anything as we are moving out.
this._preventSuggestions = true;
prevent = true;
this.cancelled = true;
this._apply();
@@ -821,6 +863,79 @@ InplaceEditor.prototype = {
if (this.change) {
this.change(this.input.value.trim());
}
},
/**
* Handles displaying suggestions based on the current input.
*/
_maybeSuggestCompletion: function() {
// Since we are calling this method from a keypress event handler, the
// |input.value| does not include currently typed character. Thus we perform
// this method async.
this.doc.defaultView.setTimeout(() => {
if (this._preventSuggestions) {
this._preventSuggestions = false;
return;
}
if (this.contentType == CONTENT_TYPES.PLAIN_TEXT) {
return;
}
let input = this.input;
// Input can be null in cases when you intantaneously switch out of it.
if (!input) {
return;
}
let query = input.value.slice(0, input.selectionStart);
if (!query) {
return;
}
let list = [];
if (this.contentType == CONTENT_TYPES.CSS_PROPERTY) {
list = CSSPropertyList;
}
list.some(item => {
if (item.startsWith(query)) {
input.value = item;
input.setSelectionRange(query.length, item.length);
this._updateSize();
return true;
}
});
if (!this.popup) {
return;
}
let finalList = [];
let length = list.length;
for (let i = 0, count = 0; i < length && count < MAX_POPUP_ENTRIES; i++) {
if (list[i].startsWith(query)) {
count++;
finalList.push({
preLabel: query,
label: list[i]
});
}
else if (count > 0) {
// Since count was incremented, we had already crossed the entries
// which would have started with query, assuming that list is sorted.
break;
}
else if (list[i][0] > query[0]) {
// We have crossed all possible matches alphabetically.
break;
}
}
if (finalList.length > 1) {
this.popup.setItems(finalList);
this.popup.openPopup(this.input);
} else {
this.popup.hidePopup();
}
}, 0);
}
};
@@ -849,3 +964,8 @@ function moveFocus(aWin, aDirection)
XPCOMUtils.defineLazyGetter(this, "focusManager", function() {
return Services.focus;
});
XPCOMUtils.defineLazyGetter(this, "CSSPropertyList", function() {
let domUtils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
return domUtils.getCSSPropertyNames(domUtils.INCLUDE_ALIASES).sort();
});