222 lines · 4.4 KB · JavaScript
RawDownload
app.views.Search = class Search extends app.View {  static SEARCH_PARAM = app.config.search_param;   static el = "._search";  static activeClass = "_search-active";   static elements = {    input: "._search-input",    resetLink: "._search-clear",  };   static events = {    input: "onInput",    click: "onClick",    submit: "onSubmit",  };   static shortcuts = {    typing: "focus",    altG: "google",    altS: "stackoverflow",    altD: "duckduckgo",  };   static routes = { after: "afterRoute" };   static HASH_RGX = new RegExp(`^#${Search.SEARCH_PARAM}=(.*)`);   init() {    this.addSubview((this.scope = new app.views.SearchScope(this.el)));     this.searcher = new app.Searcher();    this.searcher      .on("results", (results) => this.onResults(results))      .on("end", () => this.onEnd());     this.scope.on("change", () => this.onScopeChange());     app.on("ready", () => this.onReady());    $.on(window, "hashchange", () => this.searchUrl());    $.on(window, "focus", (event) => this.onWindowFocus(event));  }   focus() {    if (document.activeElement === this.input) {      return;    }    if (app.settings.get("noAutofocus")) {      return;    }    this.input.focus();  }   autoFocus() {    if (app.isMobile() || $.isAndroid() || $.isIOS()) {      return;    }    if (document.activeElement?.tagName === "INPUT") {      return;    }    if (app.settings.get("noAutofocus")) {      return;    }    this.input.focus();  }   onWindowFocus(event) {    if (event.target === window) {      return this.autoFocus();    }  }   getScopeDoc() {    if (this.scope.isActive()) {      return this.scope.getScope();    }  }   reset(force) {    if (force || !this.input.value) {      this.scope.reset();    }    this.el.reset();    this.onInput();    this.autoFocus();  }   onReady() {    this.value = "";    this.delay(this.onInput);  }   onInput() {    if (      this.value == null || // ignore events pre-"ready"      this.value === this.input.value    ) {      return;    }    this.value = this.input.value;     if (this.value.length) {      this.search();    } else {      this.clear();    }  }   search(url) {    if (url == null) {      url = false;    }    this.addClass(this.constructor.activeClass);    this.trigger("searching");     this.hasResults = null;    this.flags = { urlSearch: url, initialResults: true };    this.searcher.find(this.scope.getScope().entries.all(), "text", this.value);  }   searchUrl() {    let value;    if (location.pathname === "/") {      this.scope.searchUrl();    } else if (!app.router.isIndex()) {      return;    }     if (!(value = this.extractHashValue())) {      return;    }    this.input.value = this.value = value;    this.input.setSelectionRange(value.length, value.length);    this.search(true);    return true;  }   clear() {    this.removeClass(this.constructor.activeClass);    this.trigger("clear");  }   externalSearch(url) {    let value;    if ((value = this.value)) {      if (this.scope.name()) {        value = `${this.scope.name()} ${value}`;      }      $.popup(`${url}${encodeURIComponent(value)}`);      this.reset();    }  }   google() {    this.externalSearch("https://www.google.com/search?q=");  }   stackoverflow() {    this.externalSearch("https://stackoverflow.com/search?q=");  }   duckduckgo() {    this.externalSearch("https://duckduckgo.com/?t=devdocs&q=");  }   onResults(results) {    if (results.length) {      this.hasResults = true;    }    this.trigger("results", results, this.flags);    this.flags.initialResults = false;  }   onEnd() {    if (!this.hasResults) {      this.trigger("noresults");    }  }   onClick(event) {    if (event.target === this.resetLink) {      $.stopEvent(event);      this.reset();    }  }   onSubmit(event) {    $.stopEvent(event);  }   onScopeChange() {    this.value = "";    this.onInput();  }   afterRoute(name, context) {    if (app.shortcuts.eventInProgress?.name === "escape") {      return;    }    if (!context.init && app.router.isIndex()) {      this.reset(true);    }    if (context.hash) {      this.delay(this.searchUrl);    }    requestAnimationFrame(() => this.autoFocus());  }   extractHashValue() {    const value = this.getHashValue();    if (value != null) {      app.router.replaceHash();      return value;    }  }   getHashValue() {    try {      return Search.HASH_RGX.exec($.urlDecode(location.hash))?.[1];    } catch (error) {}  }};