--- a/toolkit/content/widgets/menu.xml
+++ b/toolkit/content/widgets/menu.xml
@@ -225,17 +225,17 @@
</binding>
<binding id="menuitem-iconic-noaccel" extends="chrome://global/content/bindings/menu.xml#menuitem">
<content>
<xul:hbox class="menu-iconic-left" align="center" pack="center"
xbl:inherits="selected,disabled,checked">
<xul:image class="menu-iconic-icon" xbl:inherits="src=image,validate,src"/>
</xul:hbox>
- <xul:label class="menu-iconic-text" flex="1" xbl:inherits="value=label,accesskey,crop" crop="right"/>
+ <xul:label class="menu-iconic-text" flex="1" xbl:inherits="xbl:text=label,accesskey,crop" crop="right"/>
</content>
</binding>
<binding id="menucaption-inmenulist" extends="chrome://global/content/bindings/menu.xml#menucaption">
<content>
<xul:hbox class="menu-iconic-left" align="center" pack="center"
xbl:inherits="selected,disabled,checked">
<xul:image class="menu-iconic-icon" xbl:inherits="src=image,validate,src"/>
@@ -266,21 +266,21 @@
<xul:label class="menu-iconic-accel" xbl:inherits="value=acceltext"/>
</xul:hbox>
<xul:hbox align="center" class="menu-right" xbl:inherits="_moz-menuactive,disabled">
<xul:image/>
</xul:hbox>
<children includes="menupopup|template"/>
</content>
</binding>
-
+
<binding id="menubutton-item" extends="chrome://global/content/bindings/menu.xml#menuitem-base">
<content>
<xul:label class="menubutton-text" flex="1" xbl:inherits="value=label,accesskey,crop" crop="right"/>
<children includes="menupopup"/>
</content>
- </binding>
-
+ </binding>
+
<binding id="menuseparator" role="xul:menuseparator"
extends="chrome://global/content/bindings/menu.xml#menuitem-base">
</binding>
</bindings>
--- a/toolkit/modules/SelectParentHelper.jsm
+++ b/toolkit/modules/SelectParentHelper.jsm
@@ -3,30 +3,39 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
this.EXPORTED_SYMBOLS = [
"SelectParentHelper"
];
+
+const {classes: Cc, utils: Cu, interfaces: Ci} = Components;
+
+const domUtil = Cc["@mozilla.org/inspector/dom-utils;1"]
+ .getService(Ci.inIDOMUtils);
+
+
// Maximum number of rows to display in the select dropdown.
const MAX_ROWS = 20;
var currentBrowser = null;
var currentMenulist = null;
var currentZoom = 1;
var closedWithEnter = false;
this.SelectParentHelper = {
+
populate: function(menulist, items, selectedIndex, zoom) {
// Clear the current contents of the popup
menulist.menupopup.textContent = "";
currentZoom = zoom;
currentMenulist = menulist;
+
populateChildren(menulist, items, selectedIndex, zoom);
},
open: function(browser, menulist, rect) {
menulist.hidden = false;
currentBrowser = browser;
closedWithEnter = false;
this._registerListeners(browser, menulist.menupopup);
@@ -140,30 +149,32 @@ this.SelectParentHelper = {
popup.removeEventListener("mouseout", this);
browser.ownerDocument.defaultView.removeEventListener("keydown", this, true);
browser.ownerDocument.defaultView.removeEventListener("fullscreen", this, true);
browser.messageManager.removeMessageListener("Forms:UpdateDropDown", this);
},
};
+// CPST - added first search parameter
function populateChildren(menulist, options, selectedIndex, zoom,
- parentElement = null, isGroupDisabled = false, adjustedTextSize = -1) {
+ parentElement = null, isGroupDisabled = false, adjustedTextSize = -1, firstSearch = true) {
let element = menulist.menupopup;
// -1 just means we haven't calculated it yet. When we recurse through this function
// we will pass in adjustedTextSize to save on recalculations.
if (adjustedTextSize == -1) {
let win = element.ownerDocument.defaultView;
// Grab the computed text size and multiply it by the remote browser's fullZoom to ensure
// the popup's text size is matched with the content's. We can't just apply a CSS transform
// here as the popup's preferred size is calculated pre-transform.
let textSize = win.getComputedStyle(element).getPropertyValue("font-size");
adjustedTextSize = (zoom * parseFloat(textSize, 10)) + "px";
+
}
for (let option of options) {
let isOptGroup = (option.tagName == 'OPTGROUP');
let item = element.ownerDocument.createElement(isOptGroup ? "menucaption" : "menuitem");
item.setAttribute("label", option.textContent);
item.style.direction = option.textDirection;
@@ -174,19 +185,20 @@ function populateChildren(menulist, opti
element.appendChild(item);
// A disabled optgroup disables all of its child options.
let isDisabled = isGroupDisabled || option.disabled;
if (isDisabled) {
item.setAttribute("disabled", "true");
}
+ // CPST - added false argument
if (isOptGroup) {
populateChildren(menulist, option.children, selectedIndex, zoom,
- item, isDisabled, adjustedTextSize);
+ item, isDisabled, adjustedTextSize, false);
} else {
if (option.index == selectedIndex) {
// We expect the parent element of the popup to be a <xul:menulist> that
// has the popuponly attribute set to "true". This is necessary in order
// for a <xul:menupopup> to act like a proper <html:select> dropdown, as
// the <xul:menulist> does things like remember state and set the
// _moz-menuactive attribute on the selected <xul:menuitem>.
menulist.selectedItem = item;
@@ -201,9 +213,125 @@ function populateChildren(menulist, opti
item.setAttribute("value", option.index);
if (parentElement) {
item.classList.add("contentSelectDropdown-ingroup")
}
}
}
+
+ // CPST Check if first iteration through list and if list is long enough for
+ // a search element to be added
+ if(firstSearch && element.childElementCount > 40){
+
+ // CPST Add a search field to top of list
+ let searchbox = element.ownerDocument.createElement('textbox');
+ searchbox.setAttribute("type", "search");
+
+ // CPST add focus event listener to search box
+ searchbox.addEventListener("focus", onSearchFocus);
+ // CPST add blur event listener to search box
+ searchbox.addEventListener("blur", onSearchBlur);
+ // CPST add input event listener to search box
+ searchbox.addEventListener("input", onSearchInput);
+
+ // CPST insert searchbox element at top of dropdown
+ element.insertBefore(searchbox, element.childNodes[0]);
+
+ // CPST ignore key list navigation so text goes to search
+ element.setAttribute("ignorekeys", "true");
+ }
+
}
+
+// CPST - Search focus event
+function onSearchFocus(){
+ dump("CPST - Search Focus\n");
+ let searchObj = this;
+ let menupopup = searchObj.parentElement;
+ menupopup.setAttribute("ignorekeys", "true");
+ dump("CPST - Ignore keys true");
+}
+
+// CPST - Search blur event
+function onSearchBlur(){
+ dump("CPST - Search Blur\n");
+ let searchObj = this;
+ let menupopup = searchObj.parentElement;
+ menupopup.setAttribute("ignorekeys", "false");
+ dump("CPST - Ignore keys false");
+}
+
+// CPST - Search input event
+function onSearchInput(){
+ let doc = this.ownerDocument;
+ let win = doc.defaultView;
+ let selection = doc.defaultView.getSelection();
+ selection.removeAllRanges();
+
+ let searchObj = this;
+ // Get input from search field, set to all lower case for comparison
+ let input = searchObj.value.toLowerCase();
+
+ let menupopup = searchObj.parentElement;
+ let menuItems = menupopup.querySelectorAll("menuitem, menucaption");
+
+ let allHidden = true;
+ let prevCaption = null;
+
+ // Iterate through options to show/hide
+ for (let currentItem of menuItems) {
+
+ // Get label and tooltip (title) from option and change to
+ // lower case for comparison
+ let itemLabel = currentItem.getAttribute("label").toLowerCase();
+ let itemTooltip = currentItem.getAttribute("title").toLowerCase();
+
+ if(input==""){
+ currentItem.setAttribute("hidden", "false");
+
+ } else if(currentItem.localName=="menucaption"){
+
+ if(prevCaption!=null){
+ prevCaption.setAttribute("hidden", (allHidden ? "true" : "false"));
+ }
+ prevCaption = currentItem;
+ allHidden = true;
+
+ } else{
+
+ if(!currentItem.classList.contains("contentSelectDropdown-ingroup") && currentItem.previousSibling.classList.contains("contentSelectDropdown-ingroup")){
+ if(prevCaption!=null){
+ prevCaption.setAttribute("hidden", (allHidden ? "true" : "false"));
+ }
+ prevCaption = null;
+ allHidden = true;
+ }
+
+ if(itemLabel.includes(input) || itemTooltip.includes(input)){
+ currentItem.setAttribute("hidden", "false");
+
+ let start = itemLabel.indexOf(input);
+ if(start!=-1){
+ // Getting label this way does not work on OSX? Find better way
+ let label = currentItem.boxObject.firstChild;
+ let textNode = label.firstChild;
+ let range = new win.Range();
+ range.setStart(textNode, start);
+ range.setEnd(textNode, (start+input.length));
+ let selection = doc.defaultView.getSelection();
+ selection.addRange(range);
+ }
+
+ allHidden = false;
+ } else{
+ currentItem.setAttribute("hidden", "true");
+ }
+ }
+
+ }
+
+ if(prevCaption!=null && allHidden){
+ prevCaption.setAttribute("hidden", (allHidden ? "true" : "false"));
+ }
+
+}