Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/JAVA/openclaw/docs/reference/templates/   (KI Agentensystem Version 22©)  Datei vom 18.1.2026 mit Größe 479 B image not shown  

Quelle  contentSearchUI.js   Sprache: JAVA

 
/* 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";

this.ContentSearchUIController = (function () {
  const MAX_DISPLAYED_SUGGESTIONS = 6;
  const SUGGESTION_ID_PREFIX = "searchSuggestion";
  const ONE_OFF_ID_PREFIX = "oneOff";
  const HTML_NS = "http://www.w3.org/1999/xhtml";

  /**
   * Creates a new object that manages search suggestions and their UI for a text
   * box.
   *
   * The UI consists of an html:table that's inserted into the DOM after the given
   * text box and styled so that it appears as a dropdown below the text box.
   *
   * @param {DOMElement} inputElement
   *        Search suggestions will be based on the text in this text box.
   *        Assumed to be an html:input.
   * @param {DOMElement} tableParent
   *        The suggestion table is appended as a child to this element.  Since
   *        the table is absolutely positioned and its top and left values are set
   *        to be relative to the top and left of the page, either the parent and
   *        all its ancestors should not be positioned elements (i.e., their
   *        positions should be "static"), or the parent's position should be the
   *        top left of the page.
   * @param {string} healthReportKey
   *        This will be sent with the search data for BrowserUsageTelemetry to
   *        record the search.
   * @param {string} searchPurpose
   *        Sent with search data, see nsISearchEngine.getSubmission.
   * @param {sring} idPrefix
   *        The IDs of elements created by the object will be prefixed with this
   *        string.
   */

  function ContentSearchUIController(
    inputElement,
    tableParent,
    healthReportKey,
    searchPurpose,
    idPrefix = ""
  ) {
    this.input = inputElement;
    this._idPrefix = idPrefix;
    this._healthReportKey = healthReportKey;
    this._searchPurpose = searchPurpose;
    this._isPrivateEngine = false;

    let tableID = idPrefix + "searchSuggestionTable";
    this.input.autocomplete = "off";
    this.input.setAttribute("aria-autocomplete""true");
    this.input.setAttribute("aria-controls", tableID);
    tableParent.appendChild(this._makeTable(tableID));

    this.input.addEventListener("keydown"this);
    this.input.addEventListener("input"this);
    this.input.addEventListener("focus"this);
    this.input.addEventListener("blur"this);
    window.addEventListener("ContentSearchService"this);

    this._stickyInputValue = "";
    this._hideSuggestions();

    this._getSearchEngines();
    this._getStrings();
  }

  ContentSearchUIController.prototype = {
    _oneOffButtons: [],
    // Setting up the one off buttons causes an uninterruptible reflow. If we
    // receive the list of engines while the newtab page is loading, this reflow
    // may regress performance - so we set this flag and only set up the buttons
    // if it's set when the suggestions table is actually opened.
    _pendingOneOffRefresh: undefined,

    get defaultEngine() {
      return this._defaultEngine;
    },

    set defaultEngine(engine) {
      if (this._defaultEngine && this._defaultEngine.icon) {
        URL.revokeObjectURL(this._defaultEngine.icon);
      }
      let icon;
      if (engine.iconData) {
        icon = this._getFaviconURIFromIconData(engine.iconData);
      } else {
        icon = "chrome://global/skin/icons/defaultFavicon.svg";
      }
      this._defaultEngine = {
        name: engine.name,
        icon,
        isAppProvided: engine.isAppProvided,
      };
      this._updateDefaultEngineHeader();
      this._updateDefaultEngineIcon();

      if (engine && document.activeElement == this.input) {
        this._speculativeConnect();
      }
    },

    get engines() {
      return this._engines;
    },

    set engines(val) {
      this._engines = val;
      this._pendingOneOffRefresh = true;
    },

    // The selectedIndex is the index of the element with the "selected" class in
    // the list obtained by concatenating the suggestion rows, one-off buttons, and
    // search settings button.
    get selectedIndex() {
      let allElts = [
        ...this._suggestionsList.children,
        ...this._oneOffButtons,
        document.getElementById("contentSearchSettingsButton"),
      ];
      for (let i = 0; i < allElts.length; ++i) {
        let elt = allElts[i];
        if (elt.classList.contains("selected")) {
          return i;
        }
      }
      return -1;
    },

    set selectedIndex(idx) {
      // Update the table's rows, and the input when there is a selection.
      this._table.removeAttribute("aria-activedescendant");
      this.input.removeAttribute("aria-activedescendant");

      let allElts = [
        ...this._suggestionsList.children,
        ...this._oneOffButtons,
        document.getElementById("contentSearchSettingsButton"),
      ];
      // If we are selecting a suggestion and a one-off is selected, don't deselect it.
      let excludeIndex =
        idx < this.numSuggestions && this.selectedButtonIndex > -1
          ? this.numSuggestions + this.selectedButtonIndex
          : -1;
      for (let i = 0; i < allElts.length; ++i) {
        let elt = allElts[i];
        let ariaSelectedElt = i < this.numSuggestions ? elt.firstChild : elt;
        if (i == idx) {
          elt.classList.add("selected");
          ariaSelectedElt.setAttribute("aria-selected""true");
          this.input.setAttribute("aria-activedescendant", ariaSelectedElt.id);
        } else if (i != excludeIndex) {
          elt.classList.remove("selected");
          ariaSelectedElt.setAttribute("aria-selected""false");
        }
      }
    },

    get selectedButtonIndex() {
      let elts = [
        ...this._oneOffButtons,
        document.getElementById("contentSearchSettingsButton"),
      ];
      for (let i = 0; i < elts.length; ++i) {
        if (elts[i].classList.contains("selected")) {
          return i;
        }
      }
      return -1;
    },

    set selectedButtonIndex(idx) {
      let elts = [
        ...this._oneOffButtons,
        document.getElementById("contentSearchSettingsButton"),
      ];
      for (let i = 0; i < elts.length; ++i) {
        let elt = elts[i];
        if (i == idx) {
          elt.classList.add("selected");
          elt.setAttribute("aria-selected""true");
        } else {
          elt.classList.remove("selected");
          elt.setAttribute("aria-selected""false");
        }
      }
    },

    get selectedEngineName() {
      let selectedElt = this._oneOffsTable.querySelector(".selected");
      if (selectedElt) {
        return selectedElt.engineName;
      }
      return this.defaultEngine.name;
    },

    get numSuggestions() {
      return this._suggestionsList.children.length;
    },

    selectAndUpdateInput(idx) {
      this.selectedIndex = idx;
      let newValue = this.suggestionAtIndex(idx) || this._stickyInputValue;
      // Setting the input value when the value has not changed commits the current
      // IME composition, which we don't want to do.
      if (this.input.value != newValue) {
        this.input.value = newValue;
      }
      this._updateSearchWithHeader();
    },

    suggestionAtIndex(idx) {
      let row = this._suggestionsList.children[idx];
      return row ? row.textContent : null;
    },

    deleteSuggestionAtIndex(idx) {
      // Only form history suggestions can be deleted.
      if (this.isFormHistorySuggestionAtIndex(idx)) {
        let suggestionStr = this.suggestionAtIndex(idx);
        this._sendMsg("RemoveFormHistoryEntry", suggestionStr);
        this._suggestionsList.children[idx].remove();
        this.selectAndUpdateInput(-1);
      }
    },

    isFormHistorySuggestionAtIndex(idx) {
      let row = this._suggestionsList.children[idx];
      return row && row.classList.contains("formHistory");
    },

    addInputValueToFormHistory() {
      let entry = {
        value: this.input.value,
        engineName: this.selectedEngineName,
      };
      this._sendMsg("AddFormHistoryEntry", entry);
      return entry;
    },

    handleEvent(event) {
      // The event handler is triggered by external events while the search
      // element may no longer be present
      if (!document.contains(this.input)) {
        return;
      }
      this["_on" + event.type[0].toUpperCase() + event.type.substr(1)](event);
    },

    _onCommand(aEvent) {
      if (this.selectedButtonIndex == this._oneOffButtons.length) {
        // Settings button was selected.
        this._sendMsg("ManageEngines");
        return;
      }

      this.search(aEvent);

      if (aEvent) {
        aEvent.preventDefault();
      }
    },

    search(aEvent) {
      if (!this.defaultEngine) {
        return// Not initialized yet.
      }

      let searchText = this.input;
      let searchTerms;
      if (
        this._table.hidden ||
        (aEvent.originalTarget &&
          aEvent.originalTarget.id == "contentSearchDefaultEngineHeader") ||
        aEvent instanceof KeyboardEvent
      ) {
        searchTerms = searchText.value;
      } else {
        searchTerms =
          this.suggestionAtIndex(this.selectedIndex) || searchText.value;
      }
      // Send an event that will perform a search and Firefox Health Report will
      // record that a search from the healthReportKey passed to the constructor.
      let eventData = {
        engineName: this.selectedEngineName,
        searchString: searchTerms,
        healthReportKey: this._healthReportKey,
        searchPurpose: this._searchPurpose,
        originalEvent: {
          shiftKey: aEvent.shiftKey,
          ctrlKey: aEvent.ctrlKey,
          metaKey: aEvent.metaKey,
          altKey: aEvent.altKey,
        },
      };
      if ("button" in aEvent) {
        eventData.originalEvent.button = aEvent.button;
      }

      if (this.suggestionAtIndex(this.selectedIndex)) {
        eventData.selection = {
          index: this.selectedIndex,
          kind: undefined,
        };
        if (aEvent instanceof MouseEvent) {
          eventData.selection.kind = "mouse";
        } else if (aEvent instanceof KeyboardEvent) {
          eventData.selection.kind = "key";
        }
      }

      this._sendMsg("Search", eventData);
      this.addInputValueToFormHistory();
    },

    _onInput() {
      if (!this.input.value) {
        this._stickyInputValue = "";
        this._hideSuggestions();
      } else if (this.input.value != this._stickyInputValue) {
        // Only fetch new suggestions if the input value has changed.
        this._getSuggestions();
        this.selectAndUpdateInput(-1);
      }
      this._updateSearchWithHeader();
    },

    _onKeydown(event) {
      let selectedIndexDelta = 0;
      let selectedSuggestionDelta = 0;
      let selectedOneOffDelta = 0;

      switch (event.keyCode) {
        case event.DOM_VK_UP:
          if (this._table.hidden) {
            return;
          }
          if (event.getModifierState("Accel")) {
            if (event.shiftKey) {
              selectedSuggestionDelta = -1;
              break;
            }
            this._cycleCurrentEngine(true);
            break;
          }
          if (event.altKey) {
            selectedOneOffDelta = -1;
            break;
          }
          selectedIndexDelta = -1;
          break;
        case event.DOM_VK_DOWN:
          if (this._table.hidden) {
            this._getSuggestions();
            return;
          }
          if (event.getModifierState("Accel")) {
            if (event.shiftKey) {
              selectedSuggestionDelta = 1;
              break;
            }
            this._cycleCurrentEngine(false);
            break;
          }
          if (event.altKey) {
            selectedOneOffDelta = 1;
            break;
          }
          selectedIndexDelta = 1;
          break;
        case event.DOM_VK_TAB:
          if (this._table.hidden) {
            return;
          }
          // Shift+tab when either the first or no one-off is selected, as well as
          // tab when the settings button is selected, should change focus as normal.
          if (
            (this.selectedButtonIndex <= 0 && event.shiftKey) ||
            (this.selectedButtonIndex == this._oneOffButtons.length &&
              !event.shiftKey)
          ) {
            return;
          }
          selectedOneOffDelta = event.shiftKey ? -1 : 1;
          break;
        case event.DOM_VK_RIGHT:
          // Allow normal caret movement until the caret is at the end of the input.
          if (
            this.input.selectionStart != this.input.selectionEnd ||
            this.input.selectionEnd != this.input.value.length
          ) {
            return;
          }
          if (
            this.numSuggestions &&
            this.selectedIndex >= 0 &&
            this.selectedIndex < this.numSuggestions
          ) {
            this.input.value = this.suggestionAtIndex(this.selectedIndex);
            this.input.setAttribute("selection-index"this.selectedIndex);
            this.input.setAttribute("selection-kind""key");
          } else {
            // If we didn't select anything, make sure to remove the attributes
            // in case they were populated last time.
            this.input.removeAttribute("selection-index");
            this.input.removeAttribute("selection-kind");
          }
          this._stickyInputValue = this.input.value;
          this._hideSuggestions();
          return;
        case event.DOM_VK_RETURN:
          this._onCommand(event);
          return;
        case event.DOM_VK_DELETE:
          if (this.selectedIndex >= 0) {
            this.deleteSuggestionAtIndex(this.selectedIndex);
          }
          return;
        case event.DOM_VK_ESCAPE:
          if (!this._table.hidden) {
            this._hideSuggestions();
          }
          return;
        default:
          return;
      }

      let currentIndex = this.selectedIndex;
      if (selectedIndexDelta) {
        let newSelectedIndex = currentIndex + selectedIndexDelta;
        if (newSelectedIndex < -1) {
          newSelectedIndex = this.numSuggestions + this._oneOffButtons.length;
        }
        // If are moving up from the first one off, we have to deselect the one off
        // manually because the selectedIndex setter tries to exclude the selected
        // one-off (which is desirable for accel+shift+up/down).
        if (currentIndex == this.numSuggestions && selectedIndexDelta == -1) {
          this.selectedButtonIndex = -1;
        }
        this.selectAndUpdateInput(newSelectedIndex);
      } else if (selectedSuggestionDelta) {
        let newSelectedIndex;
        if (currentIndex >= this.numSuggestions || currentIndex == -1) {
          // No suggestion already selected, select the first/last one appropriately.
          newSelectedIndex =
            selectedSuggestionDelta == 1 ? 0 : this.numSuggestions - 1;
        } else {
          newSelectedIndex = currentIndex + selectedSuggestionDelta;
        }
        if (newSelectedIndex >= this.numSuggestions) {
          newSelectedIndex = -1;
        }
        this.selectAndUpdateInput(newSelectedIndex);
      } else if (selectedOneOffDelta) {
        let newSelectedIndex;
        let currentButton = this.selectedButtonIndex;
        if (
          currentButton == -1 ||
          currentButton == this._oneOffButtons.length
        ) {
          // No one-off already selected, select the first/last one appropriately.
          newSelectedIndex =
            selectedOneOffDelta == 1 ? 0 : this._oneOffButtons.length - 1;
        } else {
          newSelectedIndex = currentButton + selectedOneOffDelta;
        }
        // Allow selection of the settings button via the tab key.
        if (
          newSelectedIndex == this._oneOffButtons.length &&
          event.keyCode != event.DOM_VK_TAB
        ) {
          newSelectedIndex = -1;
        }
        this.selectedButtonIndex = newSelectedIndex;
      }

      // Prevent the input's caret from moving.
      event.preventDefault();
    },

    _currentEngineIndex: -1,
    _cycleCurrentEngine(aReverse) {
      if (
        (this._currentEngineIndex == this._engines.length - 1 && !aReverse) ||
        (this._currentEngineIndex == 0 && aReverse)
      ) {
        return;
      }
      this._currentEngineIndex += aReverse ? -1 : 1;
      let engineName = this._engines[this._currentEngineIndex].name;
      this._sendMsg("SetCurrentEngine", engineName);
    },

    _onFocus() {
      if (this._mousedown) {
        return;
      }
      // When the input box loses focus to something in our table, we refocus it
      // immediately. This causes the focus highlight to flicker, so we set a
      // custom attribute which consumers should use for focus highlighting. This
      // attribute is removed only when we do not immediately refocus the input
      // box, thus eliminating flicker.
      this.input.setAttribute("keepfocus""true");
      this._speculativeConnect();
    },

    _onBlur() {
      if (this._mousedown) {
        // At this point, this.input has lost focus, but a new element has not yet
        // received it. If we re-focus this.input directly, the new element will
        // steal focus immediately, so we queue it instead.
        setTimeout(() => this.input.focus(), 0);
        return;
      }
      this.input.removeAttribute("keepfocus");
      this._hideSuggestions();
    },

    _onMousemove(event) {
      let idx = this._indexOfTableItem(event.target);
      if (idx >= this.numSuggestions) {
        // Deselect any search suggestion that has been selected.
        this.selectedIndex = -1;
        this.selectedButtonIndex = idx - this.numSuggestions;
        return;
      }
      this.selectedIndex = idx;
    },

    _onMouseup(event) {
      if (event.button == 2) {
        return;
      }
      this._onCommand(event);
    },

    _onMouseout(event) {
      // We only deselect one-off buttons and the settings button when they are
      // moused out.
      let idx = this._indexOfTableItem(event.originalTarget);
      if (idx >= this.numSuggestions) {
        this.selectedButtonIndex = -1;
      }
    },

    _onClick(event) {
      this._onMouseup(event);
    },

    _onContentSearchService(event) {
      let methodName = "_onMsg" + event.detail.type;
      if (methodName in this) {
        this[methodName](event.detail.data);
      }
    },

    _onMsgFocusInput() {
      this.input.focus();
    },

    _onMsgBlur() {
      this.input.blur();
      this._hideSuggestions();
    },

    _onMsgSuggestions(suggestions) {
      // Ignore the suggestions if their search string or engine doesn't match
      // ours.  Due to the async nature of message passing, this can easily happen
      // when the user types quickly.
      if (
        this._stickyInputValue != suggestions.searchString ||
        this.defaultEngine.name != suggestions.engineName
      ) {
        return;
      }

      this._clearSuggestionRows();

      // Position and size the table.
      let { left } = this.input.getBoundingClientRect();
      this._table.style.top = this.input.offsetHeight + "px";
      this._table.style.minWidth = this.input.offsetWidth + "px";
      this._table.style.maxWidth = window.innerWidth - left - 40 + "px";

      // Add the suggestions to the table.
      let searchWords = new Set(
        suggestions.searchString.trim().toLowerCase().split(/\s+/)
      );
      for (let i = 0; i < MAX_DISPLAYED_SUGGESTIONS; i++) {
        let type, idx;
        if (i < suggestions.formHistory.length) {
          [type, idx] = ["formHistory", i];
        } else {
          let j = i - suggestions.formHistory.length;
          if (j < suggestions.remote.length) {
            [type, idx] = ["remote", j];
          } else {
            break;
          }
        }
        this._suggestionsList.appendChild(
          this._makeTableRow(type, suggestions[type][idx], i, searchWords)
        );
      }

      if (this._table.hidden) {
        this.selectedIndex = -1;
        if (this._pendingOneOffRefresh) {
          this._setUpOneOffButtons();
          delete this._pendingOneOffRefresh;
        }
        this._currentEngineIndex = this._engines.findIndex(
          aEngine => aEngine.name == this.defaultEngine.name
        );
        this._table.hidden = false;
        this.input.setAttribute("aria-expanded""true");
      }
    },

    _onMsgSuggestionsCancelled() {
      if (!this._table.hidden) {
        this._hideSuggestions();
      }
    },

    _onMsgState(state) {
      // Not all state messages broadcast the windows' privateness info.
      if ("isPrivateWindow" in state) {
        this._isPrivateEngine = state.isPrivateEngine;
      }

      this.engines = state.engines;

      let currentEngine = state.currentEngine;
      if (this._isPrivateEngine) {
        currentEngine = state.currentPrivateEngine;
      }

      // No point updating the default engine (and the header) if there's no change.
      if (
        this.defaultEngine &&
        this.defaultEngine.name == currentEngine.name &&
        this.defaultEngine.icon == currentEngine.icon
      ) {
        return;
      }
      this.defaultEngine = currentEngine;
    },

    _onMsgCurrentState(state) {
      this._onMsgState(state);
    },

    _onMsgCurrentEngine(engine) {
      if (this._isPrivateEngine) {
        return;
      }
      this.defaultEngine = engine;
      this._pendingOneOffRefresh = true;
    },

    _onMsgCurrentPrivateEngine(engine) {
      if (!this._isPrivateEngine) {
        return;
      }
      this.defaultEngine = engine;
      this._pendingOneOffRefresh = true;
    },

    _onMsgStrings(strings) {
      this._strings = strings;
      this._updateDefaultEngineHeader();
      this._updateSearchWithHeader();
      document.getElementById("contentSearchSettingsButton").textContent =
        this._strings.searchSettings;
    },

    _updateDefaultEngineIcon() {
      // We only show the engine's own icon for app provided engines, otherwise show
      // a default. xref https://bugzilla.mozilla.org/show_bug.cgi?id=1449338#c19
      let icon = this.defaultEngine.isAppProvided
        ? this.defaultEngine.icon
        : "chrome://global/skin/icons/search-glass.svg";

      document.body.style.setProperty(
        "--newtab-search-icon",
        "url(" + icon + ")"
      );
    },

    _updateDefaultEngineHeader() {
      let header = document.getElementById("contentSearchDefaultEngineHeader");
      header.firstChild.setAttribute("src"this.defaultEngine.icon);
      if (!this._strings) {
        return;
      }
      while (header.firstChild.nextSibling) {
        header.firstChild.nextSibling.remove();
      }
      header.appendChild(
        document.createTextNode(
          this._strings.searchHeader.replace("%S"this.defaultEngine.name)
        )
      );
    },

    _updateSearchWithHeader() {
      if (!this._strings) {
        return;
      }
      let searchWithHeader = document.getElementById(
        "contentSearchSearchWithHeader"
      );
      let labels = searchWithHeader.querySelectorAll("label");
      if (this.input.value) {
        let header = this._strings.searchForSomethingWith2;
        // Translators can use both %S and %1$S.
        header = header.replace("%1$S""%S").split("%S");
        labels[0].textContent = header[0];
        labels[1].textContent = this.input.value;
        labels[2].textContent = header[1];
      } else {
        labels[0].textContent = this._strings.searchWithHeader;
        labels[1].textContent = "";
        labels[2].textContent = "";
      }
    },

    _speculativeConnect() {
      if (this.defaultEngine) {
        this._sendMsg("SpeculativeConnect"this.defaultEngine.name);
      }
    },

    _makeTableRow(type, suggestionStr, currentRow, searchWords) {
      let row = document.createElementNS(HTML_NS, "tr");
      row.dir = "auto";
      row.classList.add("contentSearchSuggestionRow");
      row.classList.add(type);
      row.setAttribute("role""presentation");
      row.addEventListener("mousemove"this);
      row.addEventListener("mouseup"this);

      let entry = document.createElementNS(HTML_NS, "td");
      let img = document.createElementNS(HTML_NS, "div");
      img.setAttribute("class""historyIcon");
      entry.appendChild(img);
      entry.classList.add("contentSearchSuggestionEntry");
      entry.setAttribute("role""option");
      entry.id = this._idPrefix + SUGGESTION_ID_PREFIX + currentRow;
      entry.setAttribute("aria-selected""false");

      let suggestionWords = suggestionStr.trim().toLowerCase().split(/\s+/);
      for (let i = 0; i < suggestionWords.length; i++) {
        let word = suggestionWords[i];
        let wordSpan = document.createElementNS(HTML_NS, "span");
        if (searchWords.has(word)) {
          wordSpan.classList.add("typed");
        }
        wordSpan.textContent = word;
        entry.appendChild(wordSpan);
        if (i < suggestionWords.length - 1) {
          entry.appendChild(document.createTextNode(" "));
        }
      }

      row.appendChild(entry);
      return row;
    },

    /**
     * If the favicon is an iconData object, convert it into a Blob URI.
     * Otherwise just return the plain URI.
     *
     * @param {string|iconData} data
     *   The icon's URL or an iconData object containing the icon data.
     * @returns {string}
     *   A blob URL or the plain icon URI.
     */

    _getFaviconURIFromIconData(data) {
      if (typeof data == "string") {
        return data;
      }

      // If typeof(data) != "string", the iconData object is returned.
      let blob = new Blob([data.icon], { type: data.mimeType });
      return URL.createObjectURL(blob);
    },

    // Adds "@2x" to the name of the given PNG url for "retina" screens.
    _getImageURIForCurrentResolution(uri) {
      if (window.devicePixelRatio > 1) {
        return uri.replace(/\.png$/, "@2x.png");
      }
      return uri;
    },

    _getSearchEngines() {
      this._sendMsg("GetState");
    },

    _getStrings() {
      this._sendMsg("GetStrings");
    },

    _getSuggestions() {
      this._stickyInputValue = this.input.value;
      if (this.defaultEngine) {
        this._sendMsg("GetSuggestions", {
          engineName: this.defaultEngine.name,
          searchString: this.input.value,
        });
      }
    },

    _clearSuggestionRows() {
      while (this._suggestionsList.firstElementChild) {
        this._suggestionsList.firstElementChild.remove();
      }
    },

    _hideSuggestions() {
      this.input.setAttribute("aria-expanded""false");
      this.selectedIndex = -1;
      this.selectedButtonIndex = -1;
      this._currentEngineIndex = -1;
      this._table.hidden = true;
    },

    _indexOfTableItem(elt) {
      if (elt.classList.contains("contentSearchOneOffItem")) {
        return this.numSuggestions + this._oneOffButtons.indexOf(elt);
      }
      if (elt.classList.contains("contentSearchSettingsButton")) {
        return this.numSuggestions + this._oneOffButtons.length;
      }
      while (elt && elt.localName != "tr") {
        elt = elt.parentNode;
      }
      if (!elt) {
        throw new Error("Element is not a row");
      }
      return elt.rowIndex;
    },

    _makeTable(id) {
      this._table = document.createElementNS(HTML_NS, "table");
      this._table.id = id;
      this._table.hidden = true;
      this._table.classList.add("contentSearchSuggestionTable");
      this._table.setAttribute("role""presentation");

      // When the search input box loses focus, we want to immediately give focus
      // back to it if the blur was because the user clicked somewhere in the table.
      // onBlur uses the _mousedown flag to detect this.
      this._table.addEventListener("mousedown", () => {
        this._mousedown = true;
      });
      document.addEventListener("mouseup", () => {
        delete this._mousedown;
      });

      // Deselect the selected element on mouseout if it wasn't a suggestion.
      this._table.addEventListener("mouseout"this);

      let headerRow = document.createElementNS(HTML_NS, "tr");
      let header = document.createElementNS(HTML_NS, "td");
      headerRow.setAttribute("class""contentSearchHeaderRow");
      header.setAttribute("class""contentSearchHeader");
      let iconImg = document.createElementNS(HTML_NS, "img");
      header.appendChild(iconImg);
      header.id = "contentSearchDefaultEngineHeader";
      headerRow.appendChild(header);
      headerRow.addEventListener("click"this);
      this._table.appendChild(headerRow);

      let row = document.createElementNS(HTML_NS, "tr");
      row.setAttribute("class""contentSearchSuggestionsContainer");
      let cell = document.createElementNS(HTML_NS, "td");
      cell.setAttribute("class""contentSearchSuggestionsContainer");
      this._suggestionsList = document.createElementNS(HTML_NS, "table");
      this._suggestionsList.setAttribute(
        "class",
        "contentSearchSuggestionsList"
      );
      cell.appendChild(this._suggestionsList);
      row.appendChild(cell);
      this._table.appendChild(row);
      this._suggestionsList.setAttribute("role""listbox");

      this._oneOffsTable = document.createElementNS(HTML_NS, "table");
      this._oneOffsTable.setAttribute("class""contentSearchOneOffsTable");
      this._oneOffsTable.classList.add("contentSearchSuggestionsContainer");
      this._oneOffsTable.setAttribute("role""group");
      this._table.appendChild(this._oneOffsTable);

      headerRow = document.createElementNS(HTML_NS, "tr");
      header = document.createElementNS(HTML_NS, "td");
      headerRow.setAttribute("class""contentSearchHeaderRow");
      header.setAttribute("class""contentSearchHeader");
      headerRow.appendChild(header);
      header.id = "contentSearchSearchWithHeader";
      let start = document.createElement("label");
      let inputLabel = document.createElement("label");
      inputLabel.setAttribute(
        "class",
        "contentSearchSearchWithHeaderSearchText"
      );
      let end = document.createElement("label");
      header.appendChild(start);
      header.appendChild(inputLabel);
      header.appendChild(end);
      this._oneOffsTable.appendChild(headerRow);

      let button = document.createElementNS(HTML_NS, "button");
      button.setAttribute("class""contentSearchSettingsButton");
      button.classList.add("contentSearchHeaderRow");
      button.classList.add("contentSearchHeader");
      button.id = "contentSearchSettingsButton";
      button.addEventListener("click"this);
      button.addEventListener("mousemove"this);
      this._table.appendChild(button);

      return this._table;
    },

    _setUpOneOffButtons() {
      // Sometimes we receive a CurrentEngine message from the ContentSearch service
      // before we've received a State message - i.e. before we have our engines.
      if (!this._engines) {
        return;
      }

      while (this._oneOffsTable.firstChild.nextSibling) {
        this._oneOffsTable.firstChild.nextSibling.remove();
      }

      this._oneOffButtons = [];

      let engines = this._engines
        .filter(aEngine => aEngine.name != this.defaultEngine.name)
        .filter(aEngine => !aEngine.hidden);
      if (!engines.length) {
        this._oneOffsTable.hidden = true;
        return;
      }

      const kDefaultButtonWidth = 49; // 48px + 1px border.
      let rowWidth = this.input.offsetWidth - 2; // 2px border.
      let enginesPerRow = Math.floor(rowWidth / kDefaultButtonWidth);
      let buttonWidth = Math.floor(rowWidth / enginesPerRow);

      let row = document.createElementNS(HTML_NS, "tr");
      let cell = document.createElementNS(HTML_NS, "td");
      row.setAttribute("class""contentSearchSuggestionsContainer");
      cell.setAttribute("class""contentSearchSuggestionsContainer");

      for (let i = 0; i < engines.length; ++i) {
        let engine = engines[i];
        if (i > 0 && i % enginesPerRow == 0) {
          row.appendChild(cell);
          this._oneOffsTable.appendChild(row);
          row = document.createElementNS(HTML_NS, "tr");
          cell = document.createElementNS(HTML_NS, "td");
          row.setAttribute("class""contentSearchSuggestionsContainer");
          cell.setAttribute("class""contentSearchSuggestionsContainer");
        }
        let button = document.createElementNS(HTML_NS, "button");
        button.setAttribute("class""contentSearchOneOffItem");
        let img = document.createElementNS(HTML_NS, "img");
        let uri;
        if (engine.iconData) {
          uri = this._getFaviconURIFromIconData(engine.iconData);
        } else {
          uri = this._getImageURIForCurrentResolution(
            "chrome://browser/skin/search-engine-placeholder.png"
          );
        }
        img.setAttribute("src", uri);
        img.addEventListener(
          "load",
          function () {
            URL.revokeObjectURL(uri);
          },
          { once: true }
        );
        button.appendChild(img);
        button.style.width = buttonWidth + "px";
        button.setAttribute("engine-name", engine.name);

        button.engineName = engine.name;
        button.addEventListener("click"this);
        button.addEventListener("mousemove"this);

        if (engines.length - i <= enginesPerRow - (i % enginesPerRow)) {
          button.classList.add("last-row");
        }

        if ((i + 1) % enginesPerRow == 0) {
          button.classList.add("end-of-row");
        }

        button.id = ONE_OFF_ID_PREFIX + i;
        cell.appendChild(button);
        this._oneOffButtons.push(button);
      }
      row.appendChild(cell);
      this._oneOffsTable.appendChild(row);
      this._oneOffsTable.hidden = false;
    },

    _sendMsg(type, data = null) {
      dispatchEvent(
        new CustomEvent("ContentSearchClient", {
          detail: {
            type,
            data,
          },
        })
      );
    },
  };

  return ContentSearchUIController;
})();

Messung V0.5
C=95 H=96 G=95

¤ Dauer der Verarbeitung: 0.22 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.