mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 573560 - Simplify the Form Assistant code in browser-ui.js [r=mfinkle]
This commit is contained in:
parent
6a3f7f106d
commit
4490f52cd9
@ -1092,13 +1092,27 @@
|
||||
return;
|
||||
|
||||
this.focus();
|
||||
try {
|
||||
SelectHelper.show(new BasicWrapper(this));
|
||||
}
|
||||
catch(e) {
|
||||
Services.scriptloader.loadSubScript("chrome://browser/content/forms.js", this);
|
||||
SelectHelper.show(new BasicWrapper(this));
|
||||
|
||||
let choices = [];
|
||||
let children = this.menupopup.children;
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
let child = children[i];
|
||||
choices.push({ text: child.label, selected: child.selected, optionIndex: i });
|
||||
}
|
||||
|
||||
let self = this;
|
||||
let wrapper = {
|
||||
multiple: false,
|
||||
choices: choices,
|
||||
changeCallback: function() {
|
||||
let evt = document.createEvent("XULCommandEvent");
|
||||
evt.initCommandEvent("command", true, true, window, 0, false, false, false, false, null);
|
||||
self.dispatchEvent(evt);
|
||||
},
|
||||
selectCallback: function(aIndex) { self.selectedIndex = aIndex; }
|
||||
};
|
||||
|
||||
SelectHelperUI.show(wrapper);
|
||||
]]>
|
||||
</handler>
|
||||
</handlers>
|
||||
|
@ -398,7 +398,7 @@ var BrowserUI = {
|
||||
WeaveGlue.init();
|
||||
});
|
||||
|
||||
FormMessageReceiver.start();
|
||||
FormHelperUI.init();
|
||||
},
|
||||
|
||||
uninit : function() {
|
||||
@ -1408,57 +1408,160 @@ var BookmarkList = {
|
||||
};
|
||||
|
||||
/**
|
||||
* Responsible for handling the interface for navigating forms and filling in information.
|
||||
* - Navigating forms is handled by next and previous buttons.
|
||||
* Responsible for navigating forms and filling in information.
|
||||
* - Navigating forms is handled by next and previous commands.
|
||||
* - When an element is focused, the browser view zooms in to the control.
|
||||
* - The caret positionning and the view are sync to keep the type
|
||||
* in text into view for input fields (text/textarea).
|
||||
* - Provides autocomplete box for input fields.
|
||||
*/
|
||||
var FormHelper = {
|
||||
_open: false,
|
||||
_navigator: null,
|
||||
var FormHelperUI = {
|
||||
init: function formHelperInit() {
|
||||
this._container = document.getElementById("form-helper-container");
|
||||
this._cmdPrevious = document.getElementById("cmd_formPrevious");
|
||||
this._cmdNext = document.getElementById("cmd_formNext");
|
||||
this._helperSpacer = document.getElementById("form-helper-spacer");
|
||||
this._autofillContainer = document.getElementById("form-helper-autofill");
|
||||
|
||||
get _container() {
|
||||
delete this._container;
|
||||
return this._container = document.getElementById("form-helper-container");
|
||||
// Listen for form assistant messages from content
|
||||
messageManager.addMessageListener("FormAssist:Show", this);
|
||||
messageManager.addMessageListener("FormAssist:Hide", this);
|
||||
messageManager.addMessageListener("FormAssist:Update", this);
|
||||
messageManager.addMessageListener("FormAssist:AutoComplete", this);
|
||||
|
||||
// Listen for events where form assistant should be closed
|
||||
document.getElementById("tabs").addEventListener("TabSelect", this, true);
|
||||
document.getElementById("browsers").addEventListener("URLChanged", this, true);
|
||||
},
|
||||
|
||||
get _helperSpacer() {
|
||||
delete this._helperSpacer;
|
||||
return this._helperSpacer = document.getElementById("form-helper-spacer");
|
||||
show: function formHelperShow(aElement, aHasPrevious, aHasNext) {
|
||||
this._open = true;
|
||||
|
||||
// Update the next/previous commands
|
||||
this._cmdPrevious.setAttribute("disabled", !aHasPrevious);
|
||||
this._cmdNext.setAttribute("disabled", !aHasNext);
|
||||
|
||||
let lastElement = this._currentElement || null;
|
||||
this._currentElement = {
|
||||
name: aElement.name,
|
||||
value: aElement.value,
|
||||
maxLength: aElement.maxLength,
|
||||
list: aElement.choices
|
||||
}
|
||||
this._updateContainer(lastElement, this._currentElement);
|
||||
|
||||
this._zoom(Rect.fromRect(aElement.rect), Rect.fromRect(aElement.caretRect));
|
||||
},
|
||||
|
||||
get _autofillContainer() {
|
||||
delete this._autofillContainer;
|
||||
return this._autofillContainer = document.getElementById("form-helper-autofill");
|
||||
hide: function formHelperHide() {
|
||||
if (!this._open)
|
||||
return;
|
||||
|
||||
this._updateContainerForSelect(this._currentElement, null);
|
||||
this._open = false;
|
||||
},
|
||||
|
||||
doAutoFill: function formHelperDoAutoFill(aElement) {
|
||||
// Suggestions are only in <label>s. Ignore the rest.
|
||||
if (aElement instanceof Ci.nsIDOMXULLabelElement)
|
||||
this._navigator.getCurrent().autocomplete(aElement.value);
|
||||
handleEvent: function(aEvent) {
|
||||
if (aEvent.type == "TabSelect" || aEvent.type == "URLChanged")
|
||||
this.hide();
|
||||
},
|
||||
|
||||
goToPrevious: function formHelperGoToPrevious() {
|
||||
this._navigator.goToPrevious();
|
||||
},
|
||||
receiveMessage: function formHelperReceiveMessage(aMessage) {
|
||||
let json = aMessage.json;
|
||||
switch (aMessage.name) {
|
||||
case "FormAssist:Show":
|
||||
// if the user has manually disabled the Form Assistant UI we still
|
||||
// want to show a UI for <select /> element but not managed by
|
||||
// FormHelperUI
|
||||
let enabled = Services.prefs.getBoolPref("formhelper.enabled");
|
||||
if (enabled) {
|
||||
this.show(json.current, json.hasPrevious, json.hasNext);
|
||||
}
|
||||
else {
|
||||
SelectHelperUI.show(json.current.list);
|
||||
}
|
||||
break;
|
||||
|
||||
goToNext: function formHelperGoToNext() {
|
||||
this._navigator.goToNext();
|
||||
},
|
||||
case "FormAssist:AutoComplete":
|
||||
this._updateAutocompleteFor(json.current);
|
||||
this._updateHelperSize();
|
||||
break;
|
||||
|
||||
updateAutocompleteFor: function updateAutocompleteFor(aElement) {
|
||||
let suggestions = this.getAutocompleteSuggestions(aElement);
|
||||
this._setSuggestions(suggestions);
|
||||
|
||||
if (suggestions.length == 0) {
|
||||
let height = Math.floor(this._container.getBoundingClientRect().height);
|
||||
this._container.top = window.innerHeight - height;
|
||||
let containerHeight = this._container.getBoundingClientRect().height;
|
||||
this._helperSpacer.setAttribute("height", containerHeight);
|
||||
case "FormAssist:Update":
|
||||
this._zoom(null, Rect.fromRect(json.caretRect));
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
getAutocompleteSuggestions: function(aElement) {
|
||||
goToPrevious: function formHelperGoToPrevious() {
|
||||
Browser.selectedBrowser.messageManager.sendAsyncMessage("FormAssist:Previous", { });
|
||||
},
|
||||
|
||||
goToNext: function formHelperGoToNext() {
|
||||
Browser.selectedBrowser.messageManager.sendAsyncMessage("FormAssist:Next", { });
|
||||
},
|
||||
|
||||
doAutoComplete: function formHelperDoAutoComplete(aElement) {
|
||||
// Suggestions are only in <label>s. Ignore the rest.
|
||||
if (aElement instanceof Ci.nsIDOMXULLabelElement)
|
||||
Browser.selectedBrowser.messageManager.sendAsyncMessage("FormAssist:AutoComplete", { value: aElement.value });
|
||||
},
|
||||
|
||||
get _open() {
|
||||
return !this._container.hidden;
|
||||
},
|
||||
|
||||
set _open(aVal) {
|
||||
if (aVal == this._open)
|
||||
return;
|
||||
|
||||
let bv = Browser._browserView;
|
||||
bv.ignorePageScroll(aVal);
|
||||
this._container.hidden = !aVal;
|
||||
this._helperSpacer.hidden = !aVal;
|
||||
|
||||
if (aVal) {
|
||||
this._zoomStart();
|
||||
} else {
|
||||
this._currentElement = null;
|
||||
this._zoomFinish();
|
||||
|
||||
// give the form spacer area back to the content
|
||||
// XXX this should probably be removed with layers
|
||||
let bv = Browser._browserView;
|
||||
Browser.forceChromeReflow();
|
||||
Browser.contentScrollboxScroller.scrollBy(0, 0);
|
||||
bv.onAfterVisibleMove();
|
||||
}
|
||||
|
||||
let evt = document.createEvent("UIEvents");
|
||||
evt.initUIEvent("FormUI", true, true, window, aVal);
|
||||
this._container.dispatchEvent(evt);
|
||||
},
|
||||
|
||||
_updateAutocompleteFor: function _formHelperUpdateAutocompleteFor(aElement) {
|
||||
let suggestions = this._getAutocompleteSuggestions(aElement);
|
||||
this._displaySuggestions(suggestions);
|
||||
},
|
||||
|
||||
_displaySuggestions: function _formHelperDisplaySuggestions(aSuggestions) {
|
||||
let autofill = this._autofillContainer;
|
||||
while (autofill.hasChildNodes())
|
||||
autofill.removeChild(autofill.lastChild);
|
||||
|
||||
let fragment = document.createDocumentFragment();
|
||||
for (let i = 0; i < aSuggestions.length; i++) {
|
||||
let value = aSuggestions[i];
|
||||
let button = document.createElement("label");
|
||||
button.setAttribute("value", value);
|
||||
fragment.appendChild(button);
|
||||
}
|
||||
autofill.appendChild(fragment);
|
||||
autofill.collapsed = !aSuggestions.length;
|
||||
},
|
||||
|
||||
/** Retrieve the autocomplete list from the autocomplete service for an element */
|
||||
_getAutocompleteSuggestions: function _formHelperGetAutocompleteSuggestions(aElement) {
|
||||
if (!aElement.canAutocomplete)
|
||||
return [];
|
||||
|
||||
@ -1474,206 +1577,91 @@ var FormHelper = {
|
||||
return suggestions;
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows form helper. If helper is already shown, then it selects the new element.
|
||||
*/
|
||||
open: function formHelperOpen(navigator) {
|
||||
let bv = Browser._browserView;
|
||||
|
||||
if (!this._open) {
|
||||
this._open = true;
|
||||
bv.ignorePageScroll(true);
|
||||
this._container.hidden = false;
|
||||
this._helperSpacer.hidden = false;
|
||||
}
|
||||
|
||||
let lastWrapper = this._navigator ? this._navigator.getCurrent() : null;
|
||||
this._navigator = navigator;
|
||||
this._currentElementChange(lastWrapper, navigator.getCurrent());
|
||||
|
||||
let evt = document.createEvent("UIEvents");
|
||||
evt.initUIEvent("FormUI", true, true, window, this._open);
|
||||
this._container.dispatchEvent(evt);
|
||||
},
|
||||
|
||||
close: function formHelperHide() {
|
||||
if (!this._open)
|
||||
return;
|
||||
|
||||
this._updateContainerForSelect(this._navigator.getCurrent(), null);
|
||||
|
||||
this._helperSpacer.hidden = true;
|
||||
|
||||
this._zoomFinish();
|
||||
|
||||
// give the form spacer area back to the content
|
||||
let bv = Browser._browserView;
|
||||
Browser.forceChromeReflow();
|
||||
Browser.contentScrollboxScroller.scrollBy(0, 0);
|
||||
bv.onAfterVisibleMove();
|
||||
|
||||
bv.ignorePageScroll(false);
|
||||
|
||||
this._container.hidden = true;
|
||||
this._open = false;
|
||||
|
||||
if (this._navigator) {
|
||||
this._navigator.endSession();
|
||||
this._navigator = null;
|
||||
}
|
||||
|
||||
let evt = document.createEvent("UIEvents");
|
||||
evt.initUIEvent("FormUI", true, true, window, this._open);
|
||||
this._container.dispatchEvent(evt);
|
||||
},
|
||||
|
||||
/** The currently selected element has changed. */
|
||||
_currentElementChange: function(lastWrapper, currentWrapper) {
|
||||
this._updateContainer(lastWrapper, currentWrapper);
|
||||
this._zoom(currentWrapper.getRect());
|
||||
},
|
||||
|
||||
/** Update the form helper container to reflect new element user is editing. */
|
||||
_updateContainer: function(aLastWrapper, aCurrentWrapper) {
|
||||
this._updateContainerForSelect(aLastWrapper, aCurrentWrapper);
|
||||
_updateContainer: function _formHelperUpdateContainer(aLastElement, aCurrentElement) {
|
||||
this._updateContainerForSelect(aLastElement, aCurrentElement);
|
||||
|
||||
// Setup autofill UI
|
||||
this.updateAutocompleteFor(this._navigator.getCurrent().element);
|
||||
this._updateAutocompleteFor(aCurrentElement);
|
||||
this._updateHelperSize();
|
||||
},
|
||||
|
||||
_updateHelperSize: function _formHelperUpdateHelperSize() {
|
||||
let height = Math.floor(this._container.getBoundingClientRect().height);
|
||||
this._container.top = window.innerHeight - height;
|
||||
|
||||
let navigator = this._navigator;
|
||||
document.getElementById("form-helper-previous").disabled = !navigator.hasPrevious();
|
||||
document.getElementById("form-helper-next").disabled = !navigator.hasNext();
|
||||
|
||||
let containerHeight = this._container.getBoundingClientRect().height;
|
||||
this._helperSpacer.setAttribute("height", containerHeight);
|
||||
},
|
||||
|
||||
/** Helper for _updateContainer that handles the case where the new element is a select. */
|
||||
_updateContainerForSelect: function(aLastWrapper, aCurrentWrapper) {
|
||||
let lastHasChoices = aLastWrapper && aLastWrapper.hasChoices();
|
||||
let currentHasChoices = aCurrentWrapper && aCurrentWrapper.hasChoices();
|
||||
_updateContainerForSelect: function _formHelperUpdateContainerForSelect(aLastElement, aCurrentElement) {
|
||||
let lastHasChoices = aLastElement && (aLastElement.list != null);
|
||||
let currentHasChoices = aCurrentElement && (aCurrentElement.list != null);
|
||||
|
||||
if (!lastHasChoices && currentHasChoices) {
|
||||
SelectHelper.dock(this._container);
|
||||
SelectHelper.show(aCurrentWrapper);
|
||||
}
|
||||
else if (lastHasChoices && currentHasChoices) {
|
||||
SelectHelper.reset();
|
||||
SelectHelper.show(aCurrentWrapper);
|
||||
}
|
||||
else if (lastHasChoices && !currentHasChoices) {
|
||||
SelectHelper.hide();
|
||||
SelectHelperUI.dock(this._container);
|
||||
SelectHelperUI.show(aCurrentElement.list);
|
||||
} else if (lastHasChoices && currentHasChoices) {
|
||||
SelectHelperUI.reset();
|
||||
SelectHelperUI.show(aCurrentElement.list);
|
||||
} else if (lastHasChoices && !currentHasChoices) {
|
||||
SelectHelperUI.hide();
|
||||
}
|
||||
},
|
||||
|
||||
/** Zoom and move viewport so that element is legible and touchable. */
|
||||
_zoom: function formHelperZoom(elRect) {
|
||||
_zoom: function formHelperZoom(aElementRect, aCaretRect) {
|
||||
let bv = Browser._browserView;
|
||||
if (!bv.allowZoom)
|
||||
return;
|
||||
|
||||
let zoomLevel = Browser._getZoomLevelForRect(bv.browserToViewportRect(elRect.clone()));
|
||||
if (Services.prefs.getBoolPref("formhelper.autozoom")) {
|
||||
this._restore = {
|
||||
zoom: bv.getZoomLevel(),
|
||||
contentScrollOffset: Browser.getScrollboxPosition(Browser.contentScrollboxScroller),
|
||||
pageScrollOffset: Browser.getScrollboxPosition(Browser.pageScrollboxScroller)
|
||||
};
|
||||
let zoomRect = bv.getVisibleRect();
|
||||
|
||||
// Zoom to a specified Rect
|
||||
if (aElementRect && bv.allowZoom && Services.prefs.getBoolPref("formhelper.autozoom")) {
|
||||
// Zoom to an element by keeping the caret into view
|
||||
let zoomLevel = Browser._getZoomLevelForRect(bv.browserToViewportRect(aElementRect.clone()));
|
||||
zoomLevel = Math.min(Math.max(kBrowserFormZoomLevelMin, zoomLevel), kBrowserFormZoomLevelMax);
|
||||
let zoomRect = Browser._getZoomRectForPoint(elRect.center().x, elRect.y, zoomLevel);
|
||||
|
||||
let caretRect = Rect.fromRect(this._navigator.getCurrent().element.caretRect);
|
||||
if (caretRect) {
|
||||
caretRect = Browser._browserView.browserToViewportRect(caretRect);
|
||||
if (!zoomRect.contains(caretRect)) {
|
||||
let [deltaX, deltaY] = this._getOffsetForCaret(caretRect, zoomRect);
|
||||
zoomRect.translate(deltaX, deltaY);
|
||||
}
|
||||
zoomRect = Browser._getZoomRectForPoint(aElementRect.center().x, aElementRect.y, zoomLevel);
|
||||
Browser.animatedZoomTo(zoomRect);
|
||||
}
|
||||
|
||||
// Move the view to show the caret if needed
|
||||
if (aCaretRect) {
|
||||
let caretRect = bv.browserToViewportRect(aCaretRect);
|
||||
if (zoomRect.contains(caretRect))
|
||||
return;
|
||||
|
||||
let [deltaX, deltaY] = this._getOffsetForCaret(caretRect, zoomRect);
|
||||
if (deltaX != 0 || deltaY != 0) {
|
||||
Browser.contentScrollboxScroller.scrollBy(deltaX, deltaY);
|
||||
bv.onAfterVisibleMove();
|
||||
}
|
||||
|
||||
Browser.animatedZoomTo(zoomRect);
|
||||
}
|
||||
},
|
||||
|
||||
/* Store the current zoom level, and scroll positions to restore them if needed */
|
||||
_zoomStart: function _formHelperZoomStart() {
|
||||
if (!Services.prefs.getBoolPref("formhelper.restore"))
|
||||
return;
|
||||
|
||||
this._restore = {
|
||||
zoom: Browser._browserView.getZoomLevel(),
|
||||
contentScrollOffset: Browser.getScrollboxPosition(Browser.contentScrollboxScroller),
|
||||
pageScrollOffset: Browser.getScrollboxPosition(Browser.pageScrollboxScroller)
|
||||
};
|
||||
},
|
||||
|
||||
/** Element is no longer selected. Restore zoom level if setting is enabled. */
|
||||
_zoomFinish: function _zoomFinish() {
|
||||
_zoomFinish: function _formHelperZoomFinish() {
|
||||
if(!gPrefService.getBoolPref("formhelper.restore"))
|
||||
return;
|
||||
|
||||
let restore = this._restore;
|
||||
if (restore && Services.prefs.getBoolPref("formhelper.restore")) {
|
||||
bv.setZoomLevel(restore.zoom);
|
||||
Browser.contentScrollboxScroller.scrollTo(restore.contentScrollOffset.x,
|
||||
restore.contentScrollOffset.y);
|
||||
Browser.pageScrollboxScroller.scrollTo(restore.pageScrollOffset.x,
|
||||
restore.pageScrollOffset.y);
|
||||
}
|
||||
},
|
||||
|
||||
/** Populate autofill container with list of strings for suggestions */
|
||||
_setSuggestions: function(aSuggestions) {
|
||||
let autofill = this._autofillContainer;
|
||||
while (autofill.hasChildNodes())
|
||||
autofill.removeChild(autofill.lastChild);
|
||||
|
||||
let fragment = document.createDocumentFragment();
|
||||
for (let i = 0; i < aSuggestions.length; i++) {
|
||||
let value = aSuggestions[i];
|
||||
let button = document.createElement("label");
|
||||
button.setAttribute("value", value);
|
||||
fragment.appendChild(button);
|
||||
}
|
||||
autofill.appendChild(fragment);
|
||||
autofill.collapsed = !aSuggestions.length;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var FormMessageReceiver = {
|
||||
start: function() {
|
||||
messageManager.addMessageListener("FormAssist:Show", this);
|
||||
messageManager.addMessageListener("FormAssist:Hide", this);
|
||||
messageManager.addMessageListener("FormAssist:Update", this);
|
||||
messageManager.addMessageListener("FormAssist:AutoComplete", this);
|
||||
|
||||
document.getElementById("tabs").addEventListener("TabSelect", this, true);
|
||||
document.getElementById("browsers").addEventListener("URLChanged", this, true);
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
let json = aMessage.json;
|
||||
switch (aMessage.name) {
|
||||
case "FormAssist:Update":
|
||||
let bv = Browser._browserView;
|
||||
let visible = bv.getVisibleRect();
|
||||
let caretRect = bv.browserToViewportRect(Rect.fromRect(json.caretRect));
|
||||
let [deltaX, deltaY] = this._getOffsetForCaret(caretRect, visible);
|
||||
|
||||
if (deltaX != 0 || deltaY != 0) {
|
||||
Browser.contentScrollboxScroller.scrollBy(deltaX, deltaY);
|
||||
bv.onAfterVisibleMove();
|
||||
}
|
||||
break;
|
||||
|
||||
case "FormAssist:Show":
|
||||
json.showNavigation ? FormHelper.open(new FormNavigator(json))
|
||||
: SelectHelper.show(new FormWrapper(json.current));
|
||||
|
||||
break;
|
||||
|
||||
case "FormAssist:AutoComplete":
|
||||
let current = json.current;
|
||||
if (current.canAutocomplete) {
|
||||
FormHelper.updateAutocompleteFor(current);
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
handleEvent: function(aEvent) {
|
||||
if (aEvent.type == "TabSelect" || aEvent.type == "URLChanged")
|
||||
FormHelper.close();
|
||||
Browser._browserView.setZoomLevel(restore.zoom);
|
||||
Browser.contentScrollboxScroller.scrollTo(restore.contentScrollOffset.x, restore.contentScrollOffset.y);
|
||||
Browser.pageScrollboxScroller.scrollTo(restore.pageScrollOffset.x, restore.pageScrollOffset.y);
|
||||
},
|
||||
|
||||
_getOffsetForCaret: function formHelper_getOffsetForCaret(aCaretRect, aRect) {
|
||||
@ -1695,120 +1683,36 @@ var FormMessageReceiver = {
|
||||
}
|
||||
};
|
||||
|
||||
function FormNavigator(navObject) {
|
||||
this._navObject = navObject;
|
||||
this._current = new FormWrapper(navObject.current);
|
||||
}
|
||||
|
||||
FormNavigator.prototype = {
|
||||
hasPrevious: function() {
|
||||
return this._navObject.hasPrevious;
|
||||
},
|
||||
|
||||
hasNext: function() {
|
||||
return this._navObject.hasNext;
|
||||
},
|
||||
|
||||
getCurrent: function() {
|
||||
return this._current;
|
||||
},
|
||||
|
||||
endSession: function endSession() {
|
||||
Browser.selectedBrowser.messageManager.sendAsyncMessage("FormAssist:Close", { });
|
||||
},
|
||||
|
||||
goToPrevious: function goToPrevious() {
|
||||
try {
|
||||
let fl = getBrowser().QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
|
||||
fl.activateRemoteFrame();
|
||||
}
|
||||
catch(e) {}
|
||||
Browser.selectedBrowser.messageManager.sendAsyncMessage("FormAssist:Previous", { });
|
||||
},
|
||||
|
||||
goToNext: function goToNext() {
|
||||
try {
|
||||
let fl = getBrowser().QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
|
||||
fl.activateRemoteFrame();
|
||||
}
|
||||
catch(e) {}
|
||||
Browser.selectedBrowser.messageManager.sendAsyncMessage("FormAssist:Next", { });
|
||||
}
|
||||
};
|
||||
|
||||
function FormWrapper(aElementObject) {
|
||||
this.element = aElementObject;
|
||||
this._rect = Rect.fromRect(aElementObject.rect);
|
||||
}
|
||||
|
||||
FormWrapper.prototype = {
|
||||
hasChoices: function() {
|
||||
return this.element.choiceData != null;
|
||||
},
|
||||
|
||||
choiceSelect: function(aIndex, aSelected, aClearAll) {
|
||||
let json = {
|
||||
index: aIndex,
|
||||
selected: aSelected,
|
||||
clearAll: aClearAll
|
||||
};
|
||||
Browser.selectedBrowser.messageManager.sendAsyncMessage("FormAssist:ChoiceSelect", json);
|
||||
},
|
||||
|
||||
choiceChange: function() {
|
||||
Browser.selectedBrowser.messageManager.sendAsyncMessage("FormAssist:ChoiceChange", { });
|
||||
},
|
||||
|
||||
getChoiceData: function() {
|
||||
return this.element.choiceData;
|
||||
},
|
||||
|
||||
canAutocomplete: function() {
|
||||
return this.element.canAutocomplete;
|
||||
},
|
||||
|
||||
autocomplete: function(aValue) {
|
||||
Browser.selectedBrowser.messageManager.sendAsyncMessage("FormAssist:AutoComplete", { value: aValue });
|
||||
},
|
||||
|
||||
getRect: function() {
|
||||
return this._rect;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* SelectHelper: Provides an interface for making a choice in a list.
|
||||
* SelectHelperUI: Provides an interface for making a choice in a list.
|
||||
* Supports simultaneous selection of choices and group headers.
|
||||
* Needs a BasicWrapper for handling selected element.
|
||||
*/
|
||||
var SelectHelper = {
|
||||
var SelectHelperUI = {
|
||||
_list: null,
|
||||
_selectedIndexes: null,
|
||||
_navigator: null,
|
||||
|
||||
get _panel() {
|
||||
delete this._panel;
|
||||
return this._panel = document.getElementById("select-container");
|
||||
},
|
||||
|
||||
show: function(wrapper) {
|
||||
let choiceData = wrapper.getChoiceData();
|
||||
this._wrapper = wrapper;
|
||||
this._choiceData = choiceData;
|
||||
show: function(aList) {
|
||||
this._list = aList;
|
||||
|
||||
this._selectedIndexes = this._getSelectedIndexes(choiceData);
|
||||
this._list = document.getElementById("select-list");
|
||||
this._list.setAttribute("multiple", choiceData.multiple ? "true" : "false");
|
||||
this._container = document.getElementById("select-list");
|
||||
this._container.setAttribute("multiple", aList.multiple ? "true" : "false");
|
||||
|
||||
this._selectedIndexes = this._getSelectedIndexes();
|
||||
let firstSelected = null;
|
||||
|
||||
let choices = choiceData.choices;
|
||||
let choices = aList.choices;
|
||||
for (let i = 0; i < choices.length; i++) {
|
||||
let choice = choices[i];
|
||||
if (choice.group) {
|
||||
let group = document.createElement("option");
|
||||
group.setAttribute("label", choice.text);
|
||||
this._list.appendChild(group);
|
||||
this._container.appendChild(group);
|
||||
group.className = "optgroup";
|
||||
} else {
|
||||
let item = document.createElement("option");
|
||||
@ -1817,7 +1721,7 @@ var SelectHelper = {
|
||||
item.choiceIndex = i;
|
||||
if (choice.inGroup)
|
||||
item.className = "in-optgroup";
|
||||
this._list.appendChild(item);
|
||||
this._container.appendChild(item);
|
||||
if (choice.selected) {
|
||||
item.setAttribute("selected", "true");
|
||||
firstSelected = firstSelected || item;
|
||||
@ -1832,7 +1736,7 @@ var SelectHelper = {
|
||||
|
||||
this._scrollElementIntoView(firstSelected);
|
||||
|
||||
this._list.addEventListener("click", this, false);
|
||||
this._container.addEventListener("click", this, false);
|
||||
},
|
||||
|
||||
dock: function dock(aContainer) {
|
||||
@ -1850,16 +1754,15 @@ var SelectHelper = {
|
||||
|
||||
reset: function() {
|
||||
this._updateControl();
|
||||
let empty = this._list.cloneNode(false);
|
||||
this._list.parentNode.replaceChild(empty, this._list);
|
||||
this._list = empty;
|
||||
this._wrapper = null;
|
||||
this._choiceData = null;
|
||||
let empty = this._container.cloneNode(false);
|
||||
this._container.parentNode.replaceChild(empty, this._container);
|
||||
this._container = empty;
|
||||
this._list = null;
|
||||
this._selectedIndexes = null;
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
this._list.removeEventListener("click", this, false);
|
||||
this._container.removeEventListener("click", this, false);
|
||||
this._panel.hidden = true;
|
||||
|
||||
if (this._docked)
|
||||
@ -1871,7 +1774,7 @@ var SelectHelper = {
|
||||
},
|
||||
|
||||
unselectAll: function() {
|
||||
let choices = this._choiceData.choices;
|
||||
let choices = this._list.choices;
|
||||
this._forEachOption(function(aItem, aIndex) {
|
||||
aItem.selected = false;
|
||||
choices[aIndex].selected = false;
|
||||
@ -1879,9 +1782,9 @@ var SelectHelper = {
|
||||
},
|
||||
|
||||
selectByIndex: function(aIndex) {
|
||||
let choices = this._choiceData.choices;
|
||||
for (let i = 0; i < this._list.childNodes.length; i++) {
|
||||
let option = this._list.childNodes[i];
|
||||
let choices = this._list.choices;
|
||||
for (let i = 0; i < this._container.childNodes.length; i++) {
|
||||
let option = this._container.childNodes[i];
|
||||
if (option.optionIndex == aIndex) {
|
||||
option.selected = true;
|
||||
this._choices[i].selected = true;
|
||||
@ -1891,9 +1794,9 @@ var SelectHelper = {
|
||||
}
|
||||
},
|
||||
|
||||
_getSelectedIndexes: function(choiceData) {
|
||||
_getSelectedIndexes: function() {
|
||||
let indexes = [];
|
||||
let choices = choiceData.choices;
|
||||
let choices = this._list.choices;
|
||||
let choiceLength = choices.length;
|
||||
for (let i = 0; i < choiceLength; i++) {
|
||||
let choice = choices[i];
|
||||
@ -1918,9 +1821,9 @@ var SelectHelper = {
|
||||
if (index == -1)
|
||||
return;
|
||||
|
||||
let scrollBoxObject = this._list.boxObject.QueryInterface(Ci.nsIScrollBoxObject);
|
||||
let scrollBoxObject = this._container.boxObject.QueryInterface(Ci.nsIScrollBoxObject);
|
||||
let itemHeight = aElement.getBoundingClientRect().height;
|
||||
let visibleItemsCount = this._list.boxObject.height / itemHeight;
|
||||
let visibleItemsCount = this._container.boxObject.height / itemHeight;
|
||||
if ((index + 1) > visibleItemsCount) {
|
||||
let delta = Math.ceil(visibleItemsCount / 2);
|
||||
scrollBoxObject.scrollTo(0, ((index + 1) - delta) * itemHeight);
|
||||
@ -1931,7 +1834,7 @@ var SelectHelper = {
|
||||
},
|
||||
|
||||
_forEachOption: function(aCallback) {
|
||||
let children = this._list.children;
|
||||
let children = this._container.children;
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
let item = children[i];
|
||||
if (!item.hasOwnProperty("optionIndex"))
|
||||
@ -1941,7 +1844,7 @@ var SelectHelper = {
|
||||
},
|
||||
|
||||
_updateControl: function() {
|
||||
let currentSelectedIndexes = this._getSelectedIndexes(this._choiceData);
|
||||
let currentSelectedIndexes = this._getSelectedIndexes();
|
||||
|
||||
let isIdentical = currentSelectedIndexes.length == this._selectedIndexes.length;
|
||||
if (isIdentical) {
|
||||
@ -1953,8 +1856,11 @@ var SelectHelper = {
|
||||
}
|
||||
}
|
||||
|
||||
if (!isIdentical)
|
||||
this._wrapper.choiceChange();
|
||||
if (isIdentical)
|
||||
return;
|
||||
|
||||
this._list.changeCallback ? this._list.changeCallback()
|
||||
: Browser.selectedBrowser.messageManager.sendAsyncMessage("FormAssist:ChoiceChange", { });
|
||||
},
|
||||
|
||||
handleEvent: function(aEvent) {
|
||||
@ -1962,23 +1868,35 @@ var SelectHelper = {
|
||||
case "click":
|
||||
let item = aEvent.target;
|
||||
if (item && item.hasOwnProperty("optionIndex")) {
|
||||
if (this._choiceData.multiple) {
|
||||
if (this._list.multiple) {
|
||||
// Toggle the item state
|
||||
item.selected = !item.selected;
|
||||
this._choiceData.choices[item.choiceIndex].selected = item.selected;
|
||||
this._wrapper.choiceSelect(item.optionIndex, item.selected, false);
|
||||
}
|
||||
else {
|
||||
this.unselectAll();
|
||||
|
||||
// Select the new one and update the control
|
||||
item.selected = true;
|
||||
this._choiceData.choices[item.choiceIndex].selected = true;
|
||||
this._wrapper.choiceSelect(item.optionIndex, item.selected, true);
|
||||
}
|
||||
|
||||
this.onSelect(item.optionIndex, item.selected, this._list.multiple);
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
onSelect: function(aIndex, aSelected, aClearAll) {
|
||||
if (this._list.selectCallback) {
|
||||
this._list.selectCallback(aIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
let json = {
|
||||
index: aIndex,
|
||||
selected: aSelected,
|
||||
clearAll: aClearAll
|
||||
};
|
||||
Browser.selectedBrowser.messageManager.sendAsyncMessage("FormAssist:ChoiceSelect", json);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -147,9 +147,9 @@
|
||||
<command id="cmd_selectAll" label="&selectAll.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
|
||||
|
||||
<!-- forms navigation -->
|
||||
<command id="cmd_formPrevious" oncommand="FormHelper.goToPrevious();"/>
|
||||
<command id="cmd_formNext" oncommand="FormHelper.goToNext();"/>
|
||||
<command id="cmd_formClose" oncommand="FormHelper.close();"/>
|
||||
<command id="cmd_formPrevious" oncommand="FormHelperUI.goToPrevious();"/>
|
||||
<command id="cmd_formNext" oncommand="FormHelperUI.goToNext();"/>
|
||||
<command id="cmd_formClose" oncommand="FormHelperUI.hide();"/>
|
||||
</commandset>
|
||||
|
||||
<keyset id="mainKeyset">
|
||||
@ -270,7 +270,7 @@
|
||||
<!-- popup for form helper -->
|
||||
<vbox id="form-helper-container" hidden="true" class="window-width" top="0" pack="end">
|
||||
<arrowscrollbox id="form-helper-autofill" collapsed="true" align="center" flex="1" orient="horizontal"
|
||||
onclick="FormHelper.doAutoFill(event.target);"/>
|
||||
onclick="FormHelperUI.doAutoComplete(event.target);"/>
|
||||
<hbox id="form-buttons" class="panel-dark" pack="center">
|
||||
<button id="form-helper-previous" class="button-dark" label="&formHelper.previous;" command="cmd_formPrevious"/>
|
||||
<button id="form-helper-next" class="button-dark" label="&formHelper.next;" command="cmd_formNext"/>
|
||||
@ -486,7 +486,7 @@
|
||||
<vbox id="select-container-inner" class="dialog-dark" flex="1">
|
||||
<scrollbox id="select-list" flex="1" orient="vertical"/>
|
||||
<hbox id="select-buttons" pack="center">
|
||||
<button id="select-buttons-done" class="button-dark" label="&formHelper.done;" oncommand="SelectHelper.hide();"/>
|
||||
<button id="select-buttons-done" class="button-dark" label="&formHelper.done;" oncommand="SelectHelperUI.hide();"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
<spacer flex="1000"/>
|
||||
|
@ -62,11 +62,11 @@ function FormAssistant() {
|
||||
addMessageListener("FormAssist:ChoiceSelect", this);
|
||||
addMessageListener("FormAssist:ChoiceChange", this);
|
||||
addMessageListener("FormAssist:AutoComplete", this);
|
||||
|
||||
addEventListener("keyup", this, false);
|
||||
};
|
||||
|
||||
FormAssistant.prototype = {
|
||||
_enabled: false,
|
||||
|
||||
open: function(aElement) {
|
||||
if (!aElement)
|
||||
return false;
|
||||
@ -92,24 +92,14 @@ FormAssistant.prototype = {
|
||||
if (!this.getCurrent())
|
||||
return false;
|
||||
|
||||
addEventListener("keyup", this, false);
|
||||
sendAsyncMessage("FormAssist:Show", this.getJSON());
|
||||
sendSyncMessage("FormAssist:Show", this.getJSON());
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
close: function() {
|
||||
removeEventListener("keyup", this, false);
|
||||
sendAsyncMessage("FormAssist:Hide", { });
|
||||
},
|
||||
|
||||
receiveMessage: function receiveMessage(aMessage) {
|
||||
let json = aMessage.json;
|
||||
switch (aMessage.name) {
|
||||
case "FormAssist:Close":
|
||||
this.close();
|
||||
break;
|
||||
|
||||
case "FormAssist:Previous":
|
||||
this.goToPrevious();
|
||||
sendAsyncMessage("FormAssist:Show", this.getJSON());
|
||||
@ -163,6 +153,9 @@ FormAssistant.prototype = {
|
||||
},
|
||||
|
||||
handleEvent: function formHelperHandleEvent(aEvent) {
|
||||
if (!this._enabled)
|
||||
return;
|
||||
|
||||
let currentWrapper = this.getCurrent();
|
||||
let currentElement = currentWrapper.element;
|
||||
|
||||
@ -265,10 +258,9 @@ FormAssistant.prototype = {
|
||||
|
||||
getJSON: function() {
|
||||
return {
|
||||
hasNext: !!this.getNext(),
|
||||
hasPrevious: !!this.getPrevious(),
|
||||
current: this.getCurrent().getJSON(),
|
||||
showNavigation: this._enabled
|
||||
hasPrevious: !!this.getPrevious(),
|
||||
hasNext: !!this.getNext()
|
||||
};
|
||||
},
|
||||
|
||||
@ -465,7 +457,7 @@ BasicWrapper.prototype = {
|
||||
|
||||
// Build up a flat JSON array of the choices. In HTML, it's possible for select element choices
|
||||
// to be under a group header (but not recursively). We distinguish between headers and entries
|
||||
// using the boolean "choiceData.group".
|
||||
// using the boolean "choices.group".
|
||||
// XXX If possible, this would be a great candidate for tracing.
|
||||
let children = wrapper.getChildren();
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
@ -507,7 +499,7 @@ BasicWrapper.prototype = {
|
||||
value: this.element.value,
|
||||
maxLength: this.element.maxLength,
|
||||
canAutocomplete: this.canAutocomplete(),
|
||||
choiceData: this.getChoiceData(),
|
||||
choices: this.getChoiceData(),
|
||||
navigable: this.canNavigateTo(),
|
||||
assistable: this.canAssist(),
|
||||
rect: this.getRect(),
|
||||
|
Loading…
Reference in New Issue
Block a user